DeepSeek Harness 接本地 Ollama:8192 上下文必崩的三个坑

能接,改一个 YAML 文件就行,十分钟能跑通。前提是把后端上下文开到 32768 以上,因为 dsh 在你敲第一句话之前已经吃掉大约 30000 tokens 的系统提示和工具 schema。后端还停在 8192 的,第一条请求就会返回 400。

桌面端和 Web 端读同一套配置语法,差别只在 profile 目录。下面走一遍从后端启动到验证的完整流程,配置片段可以直接复制。版本的边界先划清:0.2 系列字段没有变过,动手前跑一次 dsh --version 确认自己在哪一版。

配置文件在哪,先定位再动手

dsh 把运行时文件都收在 DSH_HOME 下。macOS 和 Linux 默认是 ~/.dsh,Windows 是 APPDATA 目录下的 dsh。如果环境变量 DSH_HOME 被改过,以环境变量为准,别再去找默认路径。

模型提供商写在 profile 的补丁文件里,文件名固定叫 cordis.patch.yml。

使用方式 补丁文件路径
Web 端,dsh web 启动 $DSH_HOME/profiles/web/cordis.patch.yml
桌面端客户端 $DSH_HOME/profiles/desktop/cordis.patch.yml
SDK 与 headless $DSH_HOME/profiles/sdk/cordis.patch.yml

文件不存在就新建,目录跟着建。在 Web UI 里走 设置 → 模型 → 添加模型提供商 → 自定义模型 API,填完保存,落到的也是同一份文件。图形界面能填的只有基础项,compat、reasoningEfforts、timeout 这些要手写进去。

第一步:后端上下文先开够

这一步做错,后面全白费。Ollama 拉下来的模型默认加载上下文常见是 8192,而 dsh 一次请求的固定开销在 30000 tokens 上下。失败时终端里是这个样子:

request (30228 tokens) exceeds the available context size (8192 tokens)

看到这行就去调服务端,别去翻 dsh 的配置文件。三个常用后端的参数不一样。

后端 加载命令要点 上下文参数 工具调用开关
Ollama ollama serve 后 ollama run 模型名 Modelfile 里 PARAMETER num_ctx 32768,或用带上下文参数的加载方式 走模型自带 chat template,Qwen 系列一般开箱可用
vLLM vllm serve 模型 --port 8000 --max-model-len 32768,提示与输出合计 --enable-auto-tool-choice 配 --tool-call-parser
llama.cpp llama-server -m 模型.gguf -c 32768 --jinja,必要时加 --chat-template-file
LM Studio Developer 页加载模型,或 lms server start 加载时设上下文窗口,端口默认 1234 图形界面里勾选工具调用

先确认后端活着,一条 curl 就够:

curl -s http://127.0.0.1:11434/v1/models

返回的 JSON 里出现你的模型 id 说明链路通。Ollama 还可以用 ollama show 模型名 看当前 num_ctx,这个数字要和后面配置里的 contextWindow 对齐,配置不能大于服务端实际加载值。

第二步:在 cordis.patch.yml 里加 provider

补丁文件是一个 YAML 列表,每项对应一个插件块。模型路由归 llm-pi-ai 这个插件管,本地服务和云端网关在它眼里是同一类东西,都是 OpenAI 兼容端点。

- id: llm-pi-ai
  config:
    providers:
      ollama-local:
        displayName: 本地 Ollama
        api: openai-completions
        baseURL: http://127.0.0.1:11434/v1
        reasoning: high
        compat:
          supportsDeveloperRole: false
          maxTokensField: max_tokens
          thinkingFormat: deepseek
        models:
          - id: qwen3:8b
            name: Qwen3 8B
            contextWindow: 32768
            maxTokens: 8192
      vllm-local:
        displayName: 本地 vLLM
        api: openai-completions
        baseURL: http://127.0.0.1:8000/v1
        apiKeyEnv: VLLM_API_KEY
        compat:
          supportsDeveloperRole: false
          maxTokensField: max_tokens
        models:
          - id: Qwen/Qwen3-8B-Instruct
            contextWindow: 32768
            maxTokens: 8192

三个字段值得单独盯。Provider ID 就是上面写的 ollama-local,保存之后改不了。请求日志、历史会话、默认模型都靠它做引用,想换名字只能新建一个 provider 再删旧的。

model 的 id 必须和服务端识别的一致,字符级对齐。Ollama 的 tag 后缀算在内,qwen3:8b 与 qwen3:latest 是两个不同的 id。填错的表现是 model not found,或者会话里根本选不到这个模型。

api 三选一,openai-completions、openai-responses、anthropic-messages。本地后端一律选第一项。一个 provider 只能绑一种协议,同一台机器既要 Completions 又要 Messages,就建两个 provider,用两个 ID。

再补一条默认路由,让新会话直接落到本地模型上:

- id: agent-default-model
  config:
    provider: ollama-local
    model: qwen3:8b

不写这条也能跑,只是每次新会话都要在模型选择器里手动点一次。改完不用重启,下一次请求就生效。想要本地模型也能带图片,就在模型项下加一行 input: [text, image],前提是模型本身收图。

第三步:兼容开关怎么填,不填会 400

dsh 会凭 provider id 和 baseURL 判断对面是谁,判断不出来就按 OpenAI 官方接口的规矩发请求。宽松的后端能吃下这一套,模板严格的会直接吐 400。

出事的是三个字段。

请求字段 默认发什么 严格后端的反应 对应开关
系统提示的角色 system 或 developer 不认 developer,400 无效角色 supportsDeveloperRole: false
输出上限字段 max_completion_tokens 未知字段,400 maxTokensField: max_tokens
思考控制 reasoning_effort 未知字段,400 supportsReasoningEffort: false 或 thinkingFormat: deepseek

开关写在 provider 级的 compat 下面,也可以按模型单独覆盖。写了一定要给值,键后面空着不放值会被拒,不会当成默认处理。thinkingFormat 填 deepseek 时,思考控制会切成 DeepSeek 的写法,关掉思考发出去的是 thinking: {type: "disabled"}。

这些开关是保险,不是必需品。八月那批实测里,当时的 Ollama 和 vLLM 按原样也能跑完一期任务。真正的差别在思考方言,你要控制模型想不想,就必须有这一层。另外请求体里还会固定带一个 store 字段,这是 OpenAI 侧的历史遗留,本地后端通常忽略。

上下文到底该开多大,算一遍就清楚

固定开销按 30000 tokens 计,系统提示加二十多个工具 schema,实测空会话的请求体在 30228 上下。扣掉这部分才是你和模型之间的对话空间。

服务端加载 固定开销 留给对话 实际表现
8192 30000 负 21808 第一条请求就被拒,报错直接给出两个 token 数
16384 30000 负 13616 同样被拒,只是报错里的总数变得好看一点
32768 30000 2768 能回一句话,两三个工具调用之后开始压缩
65536 30000 35536 日常单任务够用,长文件操作还会顶到边
131072 30000 101072 宽裕,多轮改造和多文件来回都不慌

结论很直接。显存 24GB 以下先按 32768 跑,能通但要接受频繁压缩带来的迟钝。真拿它干活,65536 才算起点。配置里的 contextWindow 填的是服务端实际加载值,填高了不会让服务端变宽,只会让 dsh 误判还有空间,然后在半路断掉。

验证:三个信号都要过

保存不等于通。按顺序验三件事,任何一件不过都算没接通。

列表信号,模型选择器里出现你写的那个 id,拼写完全一致。看不到就回去检查 id 和文件路径,多半是 profile 目录写错了层。

请求信号,新开会话发一句话,能正常回。同时看日志里的请求地址是不是打到 127.0.0.1,串到云端说明 provider 压根没被选中。

能力信号,让它调一次工具,读一个文件或者跑一条命令,确认工具调用真的执行了。只回文字不算通过。不少本地模型对话正常,工具调用一压就断。

三个都过之后再谈能不能干活。前两个过、第三个挂,问题在模型和解析器,不在 dsh。

踩坑:三种失败长什么样

坑一,第一句话就 400,报错里带两个 token 数。这是上下文没开够,配置本身没写错。判断方法很快,报错括号里的 available 值就是服务端实际加载值,拿它减 30000,结果为负就说明必然失败。修法是把服务端加载参数提到 32768 以上,再回来对齐 contextWindow。

坑二,能跑,几分钟后静默中止,一行报错都没有。两个原因叠在一起。本地推理单次几十秒,默认超时是按云端端点设的;压缩环节在思考被彻底关掉时拿不到可用输出,会话会直接断而不是平滑收缩。先在 provider 上加 timeout,单位是毫秒,提到分钟级;再看思考相关开关有没有把它关死。

坑三,baseURL 多写或漏写路径,报 404。Anthropic Messages 协议这条最容易翻车,baseURL 填 https://网关地址/v1,dsh 会自己再拼 /v1/messages,实际请求变成 /v1/v1/messages,网关直接 404。OpenAI Completions 协议反过来必须带 /v1。两套协议两套规矩,别用同一份 URL 互换。

还有两个小坑记一下。模型列表有缓存,改完配置先刷新再开会话,否则选择器里还是旧的。图形界面里点获取模型列表返回 401 或 404,对本地后端和部分网关属于正常现象,它们不实现 GET /models,手动把 id 打进去照样能用。

什么样的本地模型带得动 agent loop

agent loop 同时向模型提三件事,稳定的工具调用、长上下文不漂、几十轮之后还记得计划。参数小的模型最先崩的是第三件,前两件往往装得很好看。

社区里讨论最多的组合是 Qwen3 27B 的 Q6 量化版走 vLLM,工具调用格式稳定,上下文开到 65536 也不炸。7B 到 8B 这一档适合验链路、跑短任务,别指望它做多文件改造。

有个容易误判的地方。工具调用解析器选错,表现就是模型答非所问、参数乱码,看着像模型不行,其实是解析器和模型家族不匹配。怀疑模型之前,先确认启动参数里那项 parser 对不对。

如果这台机器只是偶尔用,桌面端装好直接走官方账号登录就行,这一整套都不用配。想省 API 费、或者数据不能出内网,才值得花半小时折腾。

FAQ

dsh 能跑在完全断网的机器上吗?

后端在本地就可以。推理、读文件、跑命令都不需要外网。不过首次安装、装插件、账号登录这几步要联网,装完之后再把网断掉,已配好的 provider 不看网络状态。

Ollama 和 vLLM 该选哪个?

试一下选 Ollama,一条命令拉模型,参数少。要稳定跑 agent 任务选 vLLM,工具调用、上下文长度、并发都能显式控制,代价是多记几个启动参数。同一台机器上两个可以共存,端口不冲突就行。

本地模型能替代 DeepSeek 官方模型吗?

日常改代码、读文件、跑脚本可以,隐私和成本这两项本地占优。面对真正复杂的多步任务,官方模型更稳,差距集中在长上下文保持和压缩环节。把它当离线备用和降本方案,比当替代品靠谱。

站内接着看

桌面端怎么装、四种会话模式分别干什么,见 DeepSeek Harness 桌面端安装教程,那篇的配置模型一节讲的是云端提供商,本地后端从这里接下去。想走命令行和源码构建,看 npx 安装这条路。插件怎么装、哪些值得装,见 插件清单 与 四档分级。