生命不息,折腾不止。装好 dsh、写了插件、让 Agent 组了队,今天把 settings.yaml 吃透——一个 dsh 同时驾驭多家模型,什么活配什么模型,折腾起来又省又爽。

前几篇我们把 dsh 从「装上」一路玩到了「多 Agent 协作」。但如果你一直只用一个模型从头干到尾,等于开着一家修车行却只雇了一个师傅:写代码、查资料、写文案全是他。其实 dsh 最大的好处之一是模型和运行时解耦——换个模型只是改配置,不用动插件、不用动工作流。这篇就把 settings.yaml 这个「模型总开关」拆开讲透。

一、先搞清:配置到底存在哪

dsh 的配置不是散落的,它有个「家」:环境变量 DSH_HOME,没显式设置时默认是 ~/.dsh。第一次跑 npx @deepseek-ai/dsh web 的时候,这个目录会自动初始化,里面几个文件各管一摊:

文件 / 目录 管什么
settings.yaml 模型与提供方配置(本篇主角),改模型、加网关都在这里
.credentials.yaml 真正的密钥,只写不读,Web 界面保存后只会拿到脱敏描述符
profiles/ 各 profile 的目录,组合不同插件和权限
cordis.patch.yml 你自己的「补丁层」,用来覆盖框架默认配置

记住一件事:settings.yaml 里不存明文密钥。你填的 apiKeyEnv 只是一个「环境变量名」,dsh 运行时去读那个环境变量,真 key 不在配置文件里。所以配置能放心进 git、放心贴出来。

想确认「我改了半天到底生效没」,用这条命令把实际合成的配置全打印出来,别靠猜:

1
npx @deepseek-ai/dsh web --dump-config

它会输出当前 profile 最终拼出来的插件树和配置,任何一个字段都能被你的补丁覆盖——排查配置问题先看这个。

二、settings.yaml 长什么样:逐行解剖

模型路由由 llm-pi-ai 这个插件负责,所以配置的顶层键就是它,下面按「提供方」组织。一个最小可用的自定义提供方长这样:

1
2
3
4
5
6
7
8
9
10
# ~/.dsh/settings.yaml
llm-pi-ai:
providers:
my-gateway: # Provider ID,永久不可改
apiKeyEnv: GATEWAY_API_KEY # 环境变量名,不是 key 本身
api: openai-completions # OpenAI 兼容协议
baseURL: https://api.example.com/v1 # 你的端点
models:
- id: deepseek-v4-flash
- id: deepseek-v4-pro

逐字段解释一下,这几行是整个配置的核心:

  • apiKeyEnv:填环境变量名。启动前 export GATEWAY_API_KEY='sk-xxx',dsh 自己去环境里取,key 不落配置文件。
  • api:协议类型。接 OpenAI 兼容端点(绝大多数中转、网关、开源推理服务)都用 openai-completions
  • baseURL:端点地址,注意一般要带 /v1(以你网关实际为准)。
  • models:这条路由能用的模型清单。它是「替换」而不是「追加」——路由没列的模型,请求在离开本机之前就会以 UNKNOWN_MODEL 失败,不存在「先发出去再说」这条路。模型 ID 必须和服务端 /models 接口实际返回的名称完全一致。

改完保存,模型配置下次请求自动生效,不用重启服务。但如果你改的是端口、启动参数这类,才需要重启。稳妥做法:改完在 Web UI 的 Settings → Models 刷新一眼,确认读到了。

三、多 provider:一家不够就接几家

providers 是个映射,想接几家就列几家,每家独立的 apiKeyEnvbaseURLmodels。配好之后,模型选择器里会同时展示所有已配置提供方的模型,按会话自由切换。典型配置长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
llm-pi-ai:
providers:
zhongzhuan: # 聚合网关,一个 key 接多家
apiKeyEnv: GATEWAY_API_KEY
api: openai-completions
baseURL: https://ai.aklibk.com/v1
compat:
supportsDeveloperRole: false # 见下文踩坑
maxTokensField: max_tokens
models:
- id: deepseek-v4-pro
- id: deepseek-v4-flash
- id: claude-sonnet-4-5
moonshot: # 厂商官方端点
apiKeyEnv: MOONSHOT_API_KEY
api: openai-completions
baseURL: https://api.moonshot.cn/v1
models:
- id: kimi-k3
- id: glm-5.3

上面 claude-sonnet-4-5kimi-k3glm-5.3 这些是演示用的示例 ID,实际以你网关 /models 返回为准,不要照抄。

到这里就体现 dsh 的「多模型对接 + 价格便宜」了:一个 key 就能把 DeepSeek、Claude、Kimi 全接上,人民币按量付费,写代码用 V4、写文案换 Claude,不用来回换工具。折腾阶段尤其划算——这种聚合网关的配置跟前几篇接中转站是一回事,同一个 baseURL 填进去,模型 ID 写全就行(中转站配置见前几篇,ai.aklibk.com)。

四、按场景切换:三个层次,从粗到细

「什么活配什么模型」在 dsh 里有三个粒度:

第一层:会话级切换。 配好多个 provider 后,每个会话在模型选择器里选一个默认模型。新会话用默认,老会话记住自己当时选的模型,互不干扰。这是最常用的「换着用」。

第二层:预设(preset)。 dsh 自带四个预设:minimalstandardcodecordis。预设决定这个 Agent 挂哪些工具、用什么系统提示。结合上篇的多 Agent,子 Agent 可以套不同预设——比如配一个「只读、绝不写文件」的审查员。换预设 = 换这个 Agent 的能力组合,而模型又可以在预设之上单独指定,两者是正交的。

第三层:子 Agent 按调用指定模型。 官方内置的 subagent 工具只接受 descriptionpromptrun_in_background 这几个参数,不能直接指定模型。想让子 Agent 换个模型干活,社区插件 dsh-subagent-tools 给它加了 per-call 覆盖,用法大概是这样:

1
subagent(description="审校译文", prompt="...", model="kimi-code/k3", persona="@preset:审校员")

注意:dsh-subagent-tools 是社区包,不是官方出品,接进重要工作流之前先看它的 issue 区是否在活跃维护。拿不准就先停留在「会话级 + 预设」两层,足够覆盖绝大多数场景。

省钱搭配一个例子就够:研究型子 Agent 用 deepseek-v4-flash(快、省),主 Agent 坐镇用 deepseek-v4-pro(稳),需要强写作的子任务临时换 Claude。多 Agent 天然适合这么拆,token 花在刀刃上。

五、踩坑与调优清单

这几条是「配置正确但就是不工作」的 90% 根因,建议收藏:

  1. compat 两行救命。推理类模型会把系统提示发成 role: "developer",很多网关直接拒;输出上限默认发 max_completion_tokens,只认 max_tokens 的服务也会拒。给自定义网关加上下面两行,能解决 80% 以上的兼容问题:

    1
    2
    3
    compat:
    supportsDeveloperRole: false
    maxTokensField: max_tokens
  2. Provider ID 永久不可改。请求、已保存会话、模型默认值、凭据引用全都拿它当键。想「改名」的正确姿势是:新建一个正确 ID 的提供方,把使用切过去,再删旧的——旧 ID 名下的会话仍然指着旧的。

  3. models 是替换不是追加。路由没列出的模型,本机就直接报 UNKNOWN_MODEL,不会发给服务端。手动填的模型 ID 必须和服务端完全一致,拿不准就点 Web UI 的「获取可用模型」拉一次列表。

  4. 手填的模型默认纯文本。想让它收图片,必须在 settings.yaml 里补一行 input: [text, image];声明了端点根本不支持的能力,最终还是会由服务端拒绝。路由级别想给所有手填模型一个回退值,用 defaultInput;想收窄某个目录模型的模态,用 modelOverrides 按模型 ID 覆盖。

  5. 改完先 --dump-config 再下结论。配置的合成顺序是:各 bundle 补丁 → profile 的 cordis.patch.yml~/.dsh/cordis.patch.yml--patch 覆盖层。看着改了没生效,多半是别的层把它盖掉了,dump 出来一看便知。

  6. 版本别乱跟。dsh 目前还是开发者预览,官方明说会有破坏兼容的变更,rc 系列迭代很快——有的小版本升级连会话存储格式都不向下兼容,升级前先备份 $DSH_HOMEecho $DSH_HOME 为空时默认是 ~/.dsh)。日常用 npx @deepseek-ai/dsh web 就好,想锁版本就 npx @deepseek-ai/dsh@<版本号> web,当前版本号用 npx @deepseek-ai/dsh web --version 查。Node 版本要求 ^22.19 || >=24,奇数版本(如 23)会直接启动失败。

把这几条吃透,dsh 的模型层就彻底在你掌控之中了:接什么、换什么、什么时候换,全是你说了算。

生命不息,折腾不止。下一篇:《DeepSeek Harness 结合 MCP:给 dsh 的 Agent 接上 MCP 工具》——模型能换、插件能写,再给 Agent 接上外部工具协议,让 dsh 的手真正伸到浏览器、数据库和第三方服务,敬请期待。