DSH plugin 自定义联网能力:WebSearchRequest、WebFetchRequest 与错误分类
DSH plugin 的联网 seam 把「搜索」与「抓取」放在同一个 ctx.web 上,但不是为了复用请求结构——两者连 schema 都不共享。 共享的是三件中间件式的东西:提供方选择策略的唯一所有者、一套中止与错误词汇、一个面向产品的配置界面。代价是服务上并行的 searchX/fetchX 方法对,官方明确说这是有意设计。
写提供方时只要记住两条硬规则:maxResults 由消费方定、由 seam 在返回时强制执行;非 2xx 是结果而不是错误。
DSH plugin 联网 seam 的分层:定义、提供方与唯一消费方
一句话结论:提供方注册的是能力而不是工具;工具名、schema、提示词与展示都集中在唯一的消费方。
- 服务定义:
dsh-web拥有ctx.web与提供方注册表。 - 提供方:搜索侧有 exa、perplexity、deepseek 等,抓取侧有 http 实现。
- 消费方:
dsh-tool-web独占web_search与web_fetch的 schema 与渲染。 - 换提供方不改调用方式:模型提交查询与请求 URL 的方式不变。
- 可选能力:不属于 agent loop 主干,词汇定义在 web 页而不是 core 页。
DSH plugin 的 WebSearchRequest:一条 query、一个上限
一句话结论:seam 请求只带一条 query;上限由消费方设、由 seam 在返回时截断。
interface WebSearchRequest { query: string; maxResults?: number }
interface WebSearchResult { content?: string; sources: WebSearchSource[]; truncated: boolean }
- 扇出:消费方接受
queries数组,拆成多个独立 seam 请求;单元素就是一次搜索。 - 上限来源:
maxResults是消费方配置(默认 8),透传给 seam。 - 强制执行:提供方返回超量时,seam 截断
sources并置truncated。 - content 可选:有的提供方返回生成式答案,有的只给来源;不要强求。
- 来源形状:
url必填,title、snippet、publishedAt可选——强制适配器编造会让 seam 说谎。 - 展示兜底:消费方用
title ?? hostname(url)渲染标题。
DSH plugin 的 WebFetchRequest:URL 一个字段,状态码是资源状态
一句话结论:请求故意只带 URL;非 2xx 仍返回结果,WebError 只留给无法安全获取的情况。
interface WebFetchRequest { url: string }
interface WebFetchResult { url: string; statusCode: number; body: WebFetchBody; truncated: boolean }
- 为什么只有一个字段:超时与取消是执行参数,格式、提示词与抽取属于更高层,不属于安全抓取。
- 状态码语义:404 或 500 也会产出结果,正文按内容类型解码并有上限。
- 最终 URL:结果里的
url是允许重定向之后的最终地址。 - 正文是闭合联合:
html或text,消费方用switch穷尽处理,新增类型会编译失败直到处理完。 - 截断标记:
truncated说明提供方对正文设了上限。 - 安全边界:重定向与协议限制由抓取网络策略负责,别在提供方里自行放宽。
DSH plugin 的提供方可用性与选择:不看注册顺序
一句话结论:available() 只做本地检查;选择靠显式 id 或唯一可用,歧义直接报错。
available()的纪律:凭证是否存在、配置能否解析——禁止网络调用。- 选择时机:执行时读取它来选择提供方,不是后台健康检查。
- 显式 id:
searchProvider/fetchProvider配置或对应环境变量。 - 唯一可用:恰好一个可用时自动选择。
- 歧义:多个可用却未配置 id →
WEB_PROVIDER_AMBIGUOUS,不会挑最先注册的那个。 - 顺序无关:注册顺序、配置顺序、热重载顺序都不参与选择。
DSH plugin 的联网错误分类:什么时候才叫失败
一句话结论:拿不到安全可表示的资源才失败;能拿到状态码与正文,就返回结果。
- 选择失败:
WEB_PROVIDER_AMBIGUOUS一类,错误码与消息会指出缺失 id 或歧义候选。 - 抓取失败:网络不可达、重定向不被允许、正文无法安全表示。
- 搜索失败:提供方调用失败或响应无法解析。
- 不算失败:非 2xx、正文被截断、来源被截断——这些都是结果里的字段。
- 分支写法:先看
kind与错误码,再决定重试、换提供方还是向用户说明。 - 验证:故意请求一个 404 页面,应拿到带
statusCode: 404的结果而不是异常。
DSH plugin 排查注意事项
一句话结论:联网问题先分「选不到提供方」「请求被安全策略拦」「结果被截断」三类。
- 没有提供方 → 检查凭证与配置,
available()为 false 时不会被选中。 - 报歧义 → 显式配置提供方 id,别依赖自动选择。
- 正文缺一半 → 看
truncated,属上限行为不是失败。 - 来源少几条 → 到达
maxResults上限,seam 主动截断。 - 抓到 500 却没重试 → 非 2xx 是结果,重试策略在你这一层。
- 修复状态如实:本文的两项操作共享中间件、请求/结果形状、可用性与选择规则均取自官方「Web 访问」页面;抓取网络策略与接口签名以该页及 Cordis API 为准。
常见问题
DSH plugin 的搜索与抓取共用一层中间件:一个提供方选择策略的所有者、一套中止与错误词汇、一个面向产品的配置界面。代价是服务上并行的 searchX/fetchX 方法对,这是有意设计而不是遗漏。
DSH plugin 的 maxResults 由消费方决定并通过 seam 传递,返回时由 seam 强制执行。提供方返回超量时,seam 截断 sources 并把 truncated 置为 true,提供方也可以在自己的 API 层提前限制以省成本。
不算,DSH plugin 把 HTTP 状态码视为被抓取资源状态的一部分:非 2xx 仍产出一个带状态码与有界正文的结果。WebError 只用于无法安全获取或表示资源的情况。
DSH plugin 的选择从不依赖注册顺序:有显式提供方 id 就用 id,恰好只有一个可用时才自动选择;多个可用却未配置 id 会抛 WEB_PROVIDER_AMBIGUOUS,而不是挑最先注册的那个。
不可以,DSH plugin 的 available() 必须是廉价的本地检查,例如凭证是否存在、配置能否解析,禁止发起网络调用。它是执行时选择提供方的输入,不是健康检查系统。
相关术语
- WebSearchRequest(搜索请求)
- WebSearchRequest 是 DSH plugin 联网 seam 的单次搜索请求,只携带一个 query 与可选的 maxResults 上限。消费方会把多查询扇出成多个独立请求。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/web
- WebFetchResult(抓取结果)
- WebFetchResult 是一次抓取的结果,含重定向后的最终 URL、HTTP 状态码、按内容类型分类的正文与截断标记。非 2xx 响应同样是结果而不是错误。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/web
- WebError
- WebError 是 DSH plugin 联网 seam 的结构化错误,只用于无法安全获取或表示资源的情况,并带可据以分支的错误码,例如提供方选择歧义。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/web
- WEB_PROVIDER_AMBIGUOUS
- WEB_PROVIDER_AMBIGUOUS 是提供方选择失败的错误码:存在多个可用提供方却没有配置显式 id。它保证选择不会退化为「用最先注册的那个」。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/web
来源
- DeepSeek Harness 文档 — Web 访问(搜索/抓取请求、提供方可用性、网络策略、错误)· deepseek-harness.github.io
- dsh-web 源码(类型与提供方注册表)· deepseek-ai(GitHub)
- dsh-tool-web(面向模型的两个工具 schema)· deepseek-ai(GitHub)