dsh-websearch-keyless:让 DSH 原生 web_search 无需 API Key

前言

DeepSeek Harness(DSH)里,智能体通过 web_search 做联网检索。若 profile 没有可用的搜索 provider,会回退到 @deepseek-ai/dsh-web-search-deepseek,经 DeepSeek 对话 API 完成搜索。DeepSeek API Key 缺失或无效时,每次搜索都会失败,例如:

DeepSeek API error (HTTP 401): Authentication Fails, your api key: ****d09c is invalid

社区里也有直接读 DuckDuckGo HTML、Bing 等公开端点的无 Key 插件,往往只能拿到较薄的摘要、缺少第一方元数据。下面介绍维护者 lovehin 的 dsh-websearch-keyless:对齐 OpenCode WebSearch 服务里三种可无 Key 调用的后端,注册为 ctx.web 的搜索 provider,无需注册、无需配置即可接管 web_search(web_fetch 仍走内置 HTTP provider)。

这是什么

插件以 bundle 形式发布,自带 cordis.patch.yml,安装后自动把 web 服务的 searchProvider 指到 opencode-keyless。

核心功能

三路后端与降级

按优先级依次尝试,某一端失败、超时或无结果时落到下一个:

顺序 后端 端点 无 Key 信号
1 Tavily POST https://api.tavily.com/search X-Tavily-Access-Mode: keyless
2 TinyFish POST https://agent.tinyfish.ai/mcp X-TinyFish-Access-Mode: keyless
3 Exa POST https://mcp.exa.ai/mcp 无 key 参数

实现集中在 lib/providers.js(纯 CJS、零依赖)与 lib/index.js(Cordis 插件,向 ctx.web 注册 WebSearchProvider)。available() 恒为 true;全部后端均失败时映射为 seam 错误码 WEB_PROVIDER_ERROR。

与同类无 Key 插件的差异(README 自述)

  • 调用与 OpenCode 相同的第一方搜索 API 的 keyless 模式,标题与正文片段更完整,而非 scraped 结果页 markup。
  • 三个独立 provider,单点限流不会拖垮整条搜索链。
  • 两个源文件、零依赖、无构建步骤、不声明 DSH peer dependencies。

兼容性说明

README 写明在 DSH 0.2.1-rc.3 上做过测试;manifest deliberately 不声明 @deepseek-ai/* peer 与 engines.dsh,避免版本范围不匹配导致启动时被跳过。

安装与启用

将 <profile> 换成你实际启动的 profile:desktop 对应桌面应用,web 对应 dsh web。

npx -y @deepseek-ai/dsh plugin --profile <profile> add github:lovehin/dsh-websearch-keyless

插件管理器会同时写入 dependencies 与 dsh.profile.bundles;作为 bundle,其 cordis.patch.yml 会自动生效,不必手改 profile 的 patch 层。重启 DSH 或 reload profile 后 web_search 即可使用。

确认配置是否生效:

npx -y @deepseek-ai/dsh --profile <profile> --dump-config | grep -A3 "^- id: web"

期望看到类似:

- id: web
  name: '@deepseek-ai/dsh-web'
  config:
    searchProvider: opencode-keyless
    fetchProvider: http

手动安装(CLI 被应用托管拒绝时)

若 profile 由 Electron 独占管理(例如提示 profile "desktop" is managed exclusively by the Electron application),需将插件放到 ~/.dsh/local-plugins/dsh-websearch-keyless、在 profile 的 node_modules 做 symlink,并在 package.json 的 dependencies 与 cordis.patch.yml 中手工写入 bundle 条目;patch 中必须同时设置 searchProvider: opencode-keyless 与 fetchProvider: http(见下文 Gotchas)。逐步命令见 GitHub README 的 Manual install 小节。

典型用法与验证

DSH 侧无需额外配置项:安装 bundle 并 reload 后,Harness 内对 web_search 的调用会走 opencode-keyless provider。

仓库自带无依赖测试(在插件源码根目录执行):

npm test              # 离线:解析、降级与插件契约(14 个测试)
npm run test:live     # live:探测三个真实端点

npm test 不需要网络,CI 在 Node 20、22、24 上运行。test:live 会访问第三方共享端点,适合作为人工冒烟而非 CI 门禁。

适用场景与注意

适合谁:希望在 DSH 里用原生 web_search,但不想维护 Tavily / Exa 等 API Key,且能接受 keyless 共享配额、偶发零结果或限流的用户。

运行权限:插件以当前 DSH 进程权限出站访问上述 HTTPS 端点;安装前请自行审阅 源码 与 MIT 许可证。

三个常见坑(Gotchas)

  1. fetchProvider 必须写全。 对 id: web 的 patch 替换整个 config 对象,不会 deep-merge。只改 searchProvider 会丢掉默认的 fetchProvider: http,web_fetch 可能报:multiple usable web providers are registered (deepseek-fetch-api, http); configure one explicitly。

  2. 空的 cordis.patch.yml 可能是字面量 []。 直接 append 会变成第二份 YAML 文档,解析失败。需先删掉 [] 再追加块。

  3. 不要给插件加 @deepseek-ai/* peer。 范围不覆盖当前 DSH 版本时,启动会 skip bundle 并提示 incompatible。

故障对照(节选)

现象 可能原因
WEB_PROVIDER_CONFIGURED_MISSING 配置了 searchProvider: opencode-keyless 但插件未加载 — 检查 dependencies、bundles 与 node_modules 解析
all keyless search backends failed 三路均失败 — 无外网、代理或共享配额;可试 npm run test:live
搜索无结果 keyless 配额共享,生僻 query 可能 legitimately 为零 — 可改写 query

卸载:

npx -y @deepseek-ai/dsh plugin --profile <profile> remove dsh-websearch-keyless

结尾

dsh-websearch-keyless 把 OpenCode 同款的 Tavily、TinyFish、Exa keyless 链路搬进 DSH,用 bundle 一键切换 searchProvider,在零 Key、零构建的前提下恢复 web_search。社区索引见 SkillHub 插件页,实现与安装细节以 GitHub 仓库 README 为准。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜