零 fork 换掉一个 provider
把默认的 DeepSeek 官方搜索换成 Exa,全程不改仓库一行代码,也不 fork。
M2 说「换后端只要改配置」。这一课就是去验证这句话——把默认的 DeepSeek 官方搜索换成 Exa,全程不改仓库任何一行代码,也不 fork。
做完你手上会多出一份属于自己的 profile 补丁层,以及一个「层序谁赢」的直觉。
开始前请确认
- 已跑通 Lab 1,会读
--dump-config - 一个 Exa API key
- 一个你不介意弄坏的 profile
1 先看清现在挂的是谁
$ dsh --profile web --dump-config
# == @deepseek-ai/dsh-base - id: web name: '@deepseek-ai/dsh-web' config: searchProvider: deepseek-official ← 就是这一行
注意那行 # == @deepseek-ai/dsh-base 注释:它告诉你这一段来自 base 组合包。你等下写的补丁会盖在它上面——同一个 id,后写的赢。
patch 是替换整个 config,不是合并。如果 web 那一行还有别的字段(比如 fetchProvider),你没写的都会消失。先用第 1 步的 dump 抄全,再改。
2 把 Exa provider 装进 profile
provider 就是一个普通的 npm 包。dsh plugin 会把它写进 profile 的依赖:
$ dsh plugin --profile web add @deepseek-ai/dsh-web-search-exa $ export EXA_API_KEY=...
dsh plugin 做的事很朴素:把 <args...> 原样转发给 pnpm,工作目录设成 profile 目录——add / remove / why / update 每个 pnpm 动词都照常工作(pnpm 必须在 PATH 上)。
每次成功之后它还会做一次对账:每一个解析出来的依赖,只要它的 manifest 声明了 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } },就自动加入层栈;没有这个声明的依赖保持为普通包,并给一次性警告。
为什么需要这一步:本站核对时确认,@deepseek-ai/dsh 这个 CLI 的 62 个生产依赖里没有 web-search-exa——树内有这个包,但它不在默认安装闭包里。所以你确实得装。待核对:不同发行版的闭包可能不同,装之前可以先 dsh plugin --profile web why @deepseek-ai/dsh-web-search-exa 看一眼。
3 写你自己的补丁层
这就是全部的「改动」。两段——一段把 seam 的选路改成 exa,一段把 provider 挂上去:
- id: web config: searchProvider: exa - insert: - id: web-search-exa name: '@deepseek-ai/dsh-web-search-exa' config: searchType: neural apiKey: !!js process.env.EXA_API_KEY
两种条目形态,别搞混:
- id-targeted(第一段):定位一个已存在的行,替换它整个
config。 insert(第二段):加一行本来不存在的。Exa provider 不在 base 里,所以必须 insert。
apiKey 留空的话,这个 provider 自己会回落到 $EXA_API_KEY——源码里就是 config.apiKey ?? launchEnvironmentOf(ctx).get('EXA_API_KEY')?.value ?? ''。所以上面那行 !!js 其实可以省掉。写出来是为了让你看见 !!js 长什么样:在插件 config 和条目的 disabled 字段里可以用 !!js(两个叹号,不是一个),其它元数据保持字面量。
4 再 dump 一次,确认你这一层赢了
$ dsh --profile web --dump-config
# == profile cordis.patch.yml - id: web config: searchProvider: exa ← 你的这一层赢了
然后启动 dsh --profile web,问一个需要联网的问题,工具轨迹里就是 Exa 了。
仓库代码改动行数:0。把 cordis.patch.yml 删掉,一切原样回退。
卡住了?
| 症状 | 先查这个 |
|---|---|
dump 里根本没有 web 这一行 | 你的 profile 可能没叠 base 组合包。看 package.json 里的 dsh.profile.bundles。 |
| 改了但没生效 | 层序是:各 bundle → profile 的 patch → home 级 patch → --patch overlay。home 级排在 profile 后面,所以它更大。检查是不是被 ~/.dsh/cordis.patch.yml 盖了。 |
| 启动时报错说服务重复注册 | 你可能同时挂了两个占同一个 key 的 provider。这是设计内的大声失败——base bundle 的 README 就拿 bash/pwsh 举过例:两个 executor 家族注册同一个 bash 服务,不完整的切换配方会在加载时失败。 |
| patch 指向的 id 找不到 | 不会崩,但会往 stderr 打一行警告,带上是哪一层。看你的 stderr。 |
| provider 静默不可用 | Exa 的 apiKey 为空时这个 provider 就是不可用的;baseURL 解析不了也一样。先确认环境变量真的传进去了。 |
同样这件事,在别家要几步?
待核对
待核对
Lab 3 · 写一个最小的树外插件——一个 prompt section,或者一个工具。45 分钟。