Appearance
3.11 Web 能力:搜索与抓取
本章概览:web 能力缝在同一个
ctx.web服务上跨越两个操作——搜索(search)与抓取(fetch)。本章拆解:为什么搜索与抓取共享一个缝、提供方如何注册"能力"而非工具、执行时选择策略、WebError分类,以及唯一的模型侧消费方dsh-tool-web。
概述 / 定位
web 能力缝跨两个操作于同一个 ctx.web 中间层。搜索与抓取没有共享的请求 schema、也没有共享的业务逻辑,但刻意共用一个缝:一个提供方选择策略所有者、一个中止/错误词汇、一个产品面向的"harness 如何触达 web"配置面。代价是 searchX/fetchX 方法对并行——这是有意的设计,不是漏掉的抽取。搜索结果:搜索提供方切换不改变模型问 query 的方式,抓取提供方切换不改变模型问 URL 的方式。
能力族六包拆分:dsh-web(Service Definition:服务、提供方注册表、选择策略、请求/结果词汇、WebError 分类)、三个搜索提供方(web-search-exa / web-search-perplexity / web-search-deepseek)、一个抓取提供方(web-fetch-http)、一个 Consumer(dsh-tool-web,web_search/web_fetch 工具 schema)。
提供方注册"能力"而非工具
registerSearchProvider(provider) / registerFetchProvider(provider) 注册后端,重复 id 抛 WEB_DUPLICATE_PROVIDER,返回 disposer。提供方注册的是能力——WebSearchProvider 或 WebFetchProvider,不是工具:模型可见的名字、描述、提示指导、JSON schema 与呈现全部属于唯一的 dsh-tool-web。工具只经 ctx.web.search()/fetch() 执行,从不 import 具体提供方,因此选择只有一个所有者。
工具注册跟随产品 enablement 而非后端可用性:选中提供方缺失、错配、歧义或暂不可用时工具仍可见,执行时抛结构化 WebError(如 WEB_PROVIDER_UNAVAILABLE、WEB_PROVIDER_AMBIGUOUS),ToolRuntime.execute() 将其转为模型可读的错误工具结果。要移除 web 工具只能在 dsh-tool-web 配置里禁用。
执行时选择策略
search()/fetch() 在调用时解析提供方,与注册、配置或 HMR 顺序无关。显式 id 来自配置 searchProvider/fetchProvider(或同名 env 变量喂入同一字段):
| 情形 | 执行 |
|---|---|
配置 id 已注册且 available() | 运行该提供方 |
| 配置 id 未注册 | WEB_PROVIDER_CONFIGURED_MISSING |
| 配置 id 已注册但不可用 | WEB_PROVIDER_CONFIGURED_UNAVAILABLE |
| 无 id,恰好一个已注册可用提供方 | 运行它 |
| 无 id,无可用提供方 | WEB_PROVIDER_UNAVAILABLE |
| 无 id,多个可用提供方 | WEB_PROVIDER_AMBIGUOUS(非 first-wins) |
available() 是廉价的本地检查(凭证存在、配置可解析),必须不做网络调用;它是执行时选择的输入,不是健康系统。dsh-tool-web 从不调用它——工具的唯一执行路径是 ctx.web.search()/fetch(),不可用性以选择抛出的错误码到达,选择策略只有一个所有者。
出处:
packages/web/web/README.md(deepseek-harness-src/packages/web/web/README.md)
WebError 分类
WebError extends HarnessError,code 是开放字符串(与其他缝的 LlmError/SubagentError 同构)——提供方可抛自有 code 而无需改 dsh-web,消费方必须容忍未知 code。错误按属主拆分:
- 缝中立码(由共享
WebRuntime契约抛出):WEB_PROVIDER_UNAVAILABLE、WEB_PROVIDER_CONFIGURED_MISSING、WEB_PROVIDER_CONFIGURED_UNAVAILABLE、WEB_PROVIDER_AMBIGUOUS、WEB_DUPLICATE_PROVIDER(注册期编程错误)、WEB_ABORTED、WEB_PROVIDER_ERROR(提供方自身失败的总括,含网络/传输失败:DNS、连接拒绝、TLS)。 - 抓取传输码(由
dsh-web-fetch-http拥有,换抓取后端不必抛):WEB_INVALID_URL、WEB_BLOCKED_URL、WEB_REDIRECT_BLOCKED、WEB_FETCH_TOO_LARGE、WEB_FETCH_TIMEOUT、WEB_UNSUPPORTED_CONTENT_TYPE。
请求/结果词汇与提供方
WebSearchRequest(query, maxResults?) → WebSearchResult(content?, sources[], truncated):模型侧参数只有 query,maxResults 是 consumer 层 bound(dsh-tool-web 的 searchMaxResults,默认 8)穿透缝并在回程强制——提供方超发则截断 sources[] 并置 truncated。WebSearchSource 的 url 必填,title/snippet/publishedAt 可选(Perplexity 的 citation 可能只有 URL,强制适配器编造会令缝说谎)。WebFetchRequest(url) → WebFetchResult(final url, statusCode, body, truncated):非 2xx 是结果不是错误——HTTP 状态是被抓资源状态的一部分;WebError 保留给"无法安全检索或表示资源"。WebFetchBody 是封闭判别联合(html | text),新 kind 是跨已知包协调的编译期变更。
提供方差异:Exa 调专用 POST /search(无生成答案);Perplexity 调 OpenAI 兼容 /chat/completions(生成答案进 content);DeepSeek 无专用搜索端点,发完整 Anthropic 兼容 Messages 调用携带原生 web_search 服务器工具——一次搜索 = 一次完整模型轮次(延迟与 token 更重),解析结构化 web_search_tool_result 块、绝不从模型散文里抓 URL,strict 模式下无块即 WEB_PROVIDER_ERROR;web-fetch-http 负责安全检索:仅 http:/https:、拒绝 URL 内凭证、只跟同源重定向(跨源 WEB_REDIRECT_BLOCKED,需新工具调用)、字节/字符/时间上限、显式产品 User-Agent 而非浏览器伪装。SSRF/内网防护是已记录的延后工作:web_fetch 是 SSRF 原语,不应在可触达敏感内网目标的部署中启用。
模型侧工具只属于 tool-web
web_search 与 web_fetch 的 schema、提示段、结果呈现(presentCall/presentResult,card: 'web' 按 kind: 'search' | 'fetch' 判别)都定义在 dsh-tool-web。max_results 不是模型参数(配置 searchMaxResults);超时也不是模型参数——fetchTimeoutMs/searchTimeoutMs(默认 30000)声明为 ToolDefinition.timeoutMs,由 guard 包组的 dsh-tool-call-timeout-policy 以 tools/execute 包裹器强制,工具只把 exec.signal 转发给缝。HTML body 渲染为 markdown(turndown + GFM),文本 body 直通;fetchMaxOutputChars(默认 200000)同时约束同步转换工作量与完整输出。两工具独立注册,{ search: false } / { fetch: false } 可只留其一;搜索提示仅在 fetch 也启用时才提及 web_fetch。
出处:
packages/web/tool-web/README.md(deepseek-harness-src/packages/web/tool-web/README.md)
关键设计决策
- 搜索与抓取共用一个缝:选择策略、错误词汇、产品配置面各只有一个所有者;方法对并行是刻意的。
- 注册"能力"而非工具:模型契约稳定,后端可整体替换(vendor swap 不改模型提问方式)。
- 选择在调用时解析且与顺序无关:注册/配置/HMR 顺序永不参与选择;歧义是错误码而非 first-wins。
available()必须廉价无网络:它是选择输入,不是健康探测,也不被工具层直接调用。- 非 2xx 是结果:状态码属于资源状态;错误码只留给安全检索失败。
- 模型侧 schema 极简(query / url):结果数与超时是部署设置,
max_results提升为模型参数是已记录的延后项。
扩展点
- 新搜索/抓取后端:实现
WebSearchProvider/WebFetchProvider并registerSearchProvider/registerFetchProvider;自己的错误码无需改缝;凭证经ctx.credentials解析。 - 产品选择:配置
searchProvider/fetchProvider固定 id,或仅注册一个提供方让 auto-select 生效;$DSH_WEB_SEARCH_PROVIDER/$DSH_WEB_FETCH_PROVIDER走同一字段。 - 工具形态:在
dsh-tool-web配置调整searchMaxResults、超时预算与fetchMaxOutputChars;新增web_extract式能力是缝外延后项。 - 审批挂接:web 工具默认无权限策略,需要确认的部署在
tools/pre-execute加策略(参见 3.13 审批)。
参考资料
- packages/web/web/README.md — web Service Definition(
deepseek-harness-src/packages/web/web/README.md) - packages/web/web-search-exa/README.md — Exa 搜索提供方(
deepseek-harness-src/packages/web/web-search-exa/README.md) - packages/web/web-search-perplexity/README.md — Perplexity 搜索提供方(
deepseek-harness-src/packages/web/web-search-perplexity/README.md) - packages/web/web-search-deepseek/README.md — DeepSeek 原生搜索提供方(
deepseek-harness-src/packages/web/web-search-deepseek/README.md) - packages/web/web-fetch-http/README.md — HTTP 抓取提供方(
deepseek-harness-src/packages/web/web-fetch-http/README.md) - packages/web/tool-web/README.md — 模型侧工具套件(
deepseek-harness-src/packages/web/tool-web/README.md) - docs/subsystems/web.md — web 子系统参考(
deepseek-harness-src/docs/subsystems/web.md) - web capability seam Agent Note(
deepseek-harness-src/.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) - docs/tool-catalog.md — web_search / web_fetch schema(
deepseek-harness-src/docs/tool-catalog.md)