dsh-anysearch-fallback:AnySearch 抽取失败时的 web_fetch 直抓兜底

前言

在 DSH 里用 web_fetch 拉网页时,请求会走 AnySearch 的 /v1/extract 做正文抽取。不少站点或路径在上游会直接返回 HTTP 422 extract_failed,整次工具调用随之失败,模型拿不到任何页面内容。常见做法是换 URL、手写 curl,或自己包一层 fetch——都和 Harness 里「工具一次调用、结果可预期」的目标不太一致。

下面介绍社区插件 dsh-anysearch-fallback(维护者 ADkun,分类:联网工具)。它在不改动官方 @anysearch/anysearch-dsh 的前提下,注册自己的 fetch provider,先走官方 AnySearch,上游一旦失败(除取消外)再改用受约束的浏览器 UA 直抓,并在返回正文首行写明上游失败原因。插件为零运行时依赖的 DSH 插件。

这是什么

dsh-anysearch-fallback 给 DSH 的 web_fetch 增加一条降级路径:AnySearch 无法抽取页面(典型为 HTTP 422)或出现其他上游错误时,不再让整次调用直接报错,而是在安全边界内直抓 HTML,做朴素降噪后返回纯文本;成功走 AnySearch 时,返回字段与原官方 provider 一致。

插件以 provider id anysearch-fallback 注册,通过 profile 配置把 web 服务的 fetchProvider 指向它;内部再按 id 调用官方 anysearch provider,不修改 DSH 私有字段。

工作机制

经过上面的定位,可以把调用链理解成一层代理:

  1. web_fetch 选中本插件注册的 anysearch-fallback provider。
  2. 代理到官方 AnySearch 的 extract:成功则原样返回;取消 / abort 则原样抛错,绝不降级。
  3. 其他任何上游失败(422、401、402、403、429、5xx、超时、网络、非 JSON 等)进入直抓兜底。
  4. 直抓使用固定桌面 Chrome UA,配合协议/内网校验、5 MB 字节上限、15s 超时;结果顶部带一行降级标记,说明上游状态与直抓状态。

官方 AnySearch 插件升级或重装不影响本插件逻辑;若官方改了 provider id,需同步改仓库 index.mjs 中的 UPSTREAM_PROVIDER_ID。

核心行为摘要

项目 说明
触发降级 除取消外的任意上游错误
不降级 signal 已中止,或错误为 AbortError / ABORT_ERR / WEB_ABORTED / extract_canceled 等
协议 仅 http: / https:
内网 拒绝 localhost、私网段、链路本地等(含 DNS 解析后再核 IP;无地址钉扎,见下文边界)
直抓限制 5 MB 流式读取、15s 超时、最多 3 跳重定向且每跳重新校验;无 Authorization / Cookie / Referer
磁盘缓存 无

降级时正文第一行永远是标记,例如上游 422 且直抓 200 有正文时,会写明「上游抽取失败」与「直抓成功」,并提示以下为未经 AnySearch 清洗的原文。上游 401、402、429 等系统性问题也会走宽兜底:调用可能仍返回页面,需结合首行标记与日志中的 warn 判断集成是否异常。

安装与启用

README 给出的方式是本地插件挂到某个 DSH profile,没有 dsh plugin add github:... 一类的一行命令。按顺序做下面几步。

# 1) 把本目录放到 <DSH_HOME>/plugins/dsh-anysearch-fallback/
# 2) 在 profile 的 package.json 里加依赖(相对路径写法)
#    "dsh-anysearch-fallback": "file:C:/Users/you/.dsh/plugins/dsh-anysearch-fallback"
# 3) 把它追加到 profile 的 dsh.profile.bundles 末尾
#    (放最后 = 它的 `- id: web` 覆盖在官方 anysearch 之后生效)
# 4) 在 profile 的 node_modules 下建同名入口(本机既有形状:真目录 + 硬链接)
# 5) 重启 DSH —— 新开一个对话不够,profile 的配置树是启动时组合的

仓库内的 cordis.patch.yml 会 insert bundle dsh-anysearch-fallback,并把 web 的 fetchProvider 设为 anysearch-fallback;searchProvider 需与 profile 现状一并写全(patch 对 config 是整体替换、不深合并)。具体层内覆盖请按你当前 profile 调整。

注意: 改完插件或 profile 后必须重启 DSH 进程,新开对话不会加载新代码。

运行环境要求 Node >=22(见 package.json 的 engines)。许可证为 MIT。

典型用法与验证

日常无需额外配置项:profile 启用后,智能体照常调用 web_fetch;只有在上游失败时才会自动降级,成功路径与官方 AnySearch 一致。

开发者在仓库内可跑离线单测:

npm test            # 离线单测:63 通过 / 1 跳过(联网用例默认跳过)

联网集成测在 test/integration.test.mjs,默认跳过;需本机已能解析 @anysearch/anysearch-dsh 及其 peer,并按该文件头部注释搭临时 staging(含对 @deepseek-ai/* 的 junction)后再跑。

适用场景与注意

适合谁: 经常让智能体抓取开放网页,又希望 AnySearch 422 或偶发上游错误时不要整次 web_fetch 失败;能接受直抓正文质量低于 AnySearch 清洗、且愿意看首行标记区分「正常抽取」与「降级结果」的团队。

务必知晓:

  • 插件以当前 DSH 进程权限运行,安装前请阅读源码与 MIT 许可证,确认 SSRF 与直抓策略符合你的安全要求。
  • 直抓只做朴素 HTML 降噪;反爬站点可能对匿名请求返回 403 或空正文(README 举例 openai.com、知乎问题页),标记会说明情况,但内容仍可能不可用,需换来源。
  • 宽兜底意味着凭据失效、额度耗尽、限流等也可能表现为「仍有页面返回」;排查集成问题要看首行标记与 warn 日志,不能只看 HTTP 200。
  • DNS rebinding 仅做解析后复检,没有与建连绑定的地址钉扎;带 userinfo 的 URL 会明确以 WEB_BLOCKED_URL 拒绝,不静默剥离。
  • 社区目录 SkillHub 由社区维护,与 DeepSeek / 幻方无官方从属关系;DSH 生态理念是「一切皆插件」,本插件是联网工具类中的一个可选组件。

结尾

dsh-anysearch-fallback 在 AnySearch 抽取失败或上游报错时,为 web_fetch 提供受约束的直抓兜底,零运行时依赖,且不改动官方 AnySearch 包。若你的 profile 已依赖 AnySearch 做联网抓取,可把该插件作为 bundles 链末端的增强层,并养成看降级首行与重启 DSH 的习惯。

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

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

Xiaoye