deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
面向 DeepSeek Harness(DSH)的 Tavily 搜索提供方插件,让 DSH 内置的 web_search 工具实际走 Tavily Search API。
DSH 的 web_search 工具与具体搜索服务解耦,通过中间层 ctx.web 工作:
@deepseek-ai/dsh-tool-web 只定义 web_search 工具的 schema、校验与展示,不碰搜索服务;ctx.web.registerSearchProvider(provider) 的「搜索提供方」;web 段配置 searchProvider(或环境变量 DSH_WEB_SEARCH_PROVIDER)选择提供方。DSH 开箱只带一个搜索提供方 @deepseek-ai/dsh-web-search-deepseek(走 DeepSeek 的 Anthropic 兼容 Messages API),没有内置 Tavily。本项目补齐了这块:安装并接线后,即可把 web_search 切换到 Tavily。
fetch 直连 POST https://api.tavily.com/searchresults[] → sources[]、answer → content 的规范化映射credentialRef),密钥只以引用形式出现、绝不明文写死WebError 错误码规范AbortSignal 用于取消inject: ['web'])| 层 | 包 | 职责 |
|---|---|---|
| 工具层 | @deepseek-ai/dsh-tool-web |
模型侧 web_search 工具的 schema、校验与展示 |
| 能力 seam | @deepseek-ai/dsh-web |
提供方注册表、选择策略、maxResults 兜底截断、WebError |
| 提供方(本项目) | @deepseek-ai/dsh-web-search-tavily |
把一次搜索请求翻译成 Tavily API 调用并规范化结果 |
本项目实现的是 seam 定义的 WebSearchProvider 接口:
interface WebSearchProvider {
readonly id: string // 本项目注册为 'tavily'
available(): boolean // 廉价本地检查,禁止发网络请求
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>
}
web profile 为例)本地源码 / GitHub clone 后
# clone 本仓库后,把本地路径装进 profile
dsh plugin --profile web add file:D:/path/to/dsh-web-search-tavily
dsh plugin底层就是转发给 pnpm;若未安装 pnpm,可等价地直接执行npx pnpm add file:D:/path/to/dsh-web-search-tavily(在 profile 目录下)。
编辑 profile 的补丁层(不是 node_modules 里的配置):
$DSH_HOME/profiles/web/cordis.patch.ymlC:\Users\<用户名>\.dsh\profiles\web\cordis.patch.yml# 新增 Tavily 搜索提供方
- insert:
- id: web-search-tavily
name: '@deepseek-ai/dsh-web-search-tavily'
config:
apiKeyEnv: TAVILY_API_KEY
# 把 web 段的 searchProvider 从 deepseek-official 改为 tavily
- id: web
name: '@deepseek-ai/dsh-web'
config:
searchProvider: tavily
三种方式任选其一(优先级:启动环境变量 > credentials 文件 > .env):
A(推荐):写入 DSH 凭据文件 $DSH_HOME/.credentials.yaml(Windows 默认 C:\Users\<你>\.dsh\.credentials.yaml):
TAVILY_API_KEY: tvly-你的key
B(持久环境变量,最高优先级):
setx TAVILY_API_KEY "tvly-你的key"
C(临时,仅当前终端):
$env:TAVILY_API_KEY = "tvly-你的key"
重启 DSH(关闭运行中的控制台窗口 / Ctrl+C,再按原方式启动),然后让模型执行一次 web_search(例如问「今天有什么新闻」):
answer + sources[])WEB_PROVIDER_CREDENTIAL_MISSING —— 这恰好证明选择逻辑已切到 Tavily,而非静默回退 DeepSeek| Key | 默认 | 说明 |
|---|---|---|
apiKeyEnv |
TAVILY_API_KEY |
credential 引用,每次 search 经 ctx.credentials 解析;无 seam 时回退启动环境变量 |
apiKey |
省略 | 可选字面量 key(role('secret')),非空时优先;建议用 apiKeyEnv 避免密钥进配置 |
POST https://api.tavily.com/searchContent-Type: application/json、Authorization: Bearer <TAVILY_API_KEY>{
"query": "<query>",
"search_depth": "advanced",
"max_results": 8
}
响应映射:
| Tavily 字段 | 目标字段 | 规则 |
|---|---|---|
results[].url |
sources[].url |
必填;缺失/空串的项被跳过 |
results[].title |
sources[].title |
缺失/空则省略 |
results[].content |
sources[].snippet |
缺失/空则省略 |
results[].published_date |
sources[].publishedAt |
缺失/空则省略 |
answer |
content |
非空则放入,否则省略 |
truncated 恒为 false:provider 不主动截断,maxResults 由 seam 统一截断并置位。max_results 在请求层设置仅是成本/延迟优化。
| 情况 | 错误码 |
|---|---|
| 无 key(字面量、credentials、环境均无) | WEB_PROVIDER_CREDENTIAL_MISSING |
| 网络失败 / 非 2xx / 响应无法解析 | WEB_PROVIDER_ERROR |
| 调用方取消 | WEB_ABORTED |
| 配置了 id 但未注册 / 不可用 / 多提供方歧义 | WEB_PROVIDER_CONFIGURED_MISSING / WEB_PROVIDER_CONFIGURED_UNAVAILABLE / WEB_PROVIDER_AMBIGUOUS(由 seam 抛出) |
dsh-web-search-tavily/
├── package.json
├── README.md
└── lib/
├── index.js # 插件入口 + provider + 映射 + 错误/取消助手
└── types/
├── index.d.ts # 插件导出 + Config 接口
├── provider.d.ts # TavilySearchProvider + Options
└── types.d.ts # Tavily 线上响应/错误类型
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。