dsh-web-fetch:CDP 渲染 + Tavily 提取,双源网页抓取插件

前言

给 DSH 智能体补网页抓取能力时,常遇到两个问题。一是页面类型不同:SPA 和 JS 重度页面必须经过真实浏览器渲染才能拿到内容,而普通文章页用提取 API 更快,往往要维护两套方案。二是 DSH 的 ctx.web.registerSearchProvider 在同一能力上注册多个提供者时会抛 WEB_PROVIDER_AMBIGUOUS,模型没有选择权。

dsh-web-fetch(作者 runfali,MIT 许可,分类:联网工具)同时处理这两个问题:把 CDP 浏览器渲染和 Tavily Extract 封装成两个独立的 LLM 工具,由模型按上下文自主选择。下面介绍它的设计、安装和使用。

它是什么

一句话定位:DeepSeek Harness 的双源网页内容抓取插件。每个数据源注册为一个独立的 DSH 工具,各自带 description 和 schema,LLM 根据上下文挑选,而不是依赖写死的规则。

两个工具的分工:

工具 适用场景 做法
web_fetch_cdp JS 重度页面、SPA、需要真实渲染的站点 经 CDP 连接远程 Chrome / cloakbrowser,返回渲染后内容
web_fetch_tavily 快速提取、无需浏览器 调用 Tavily Extract API

核心特性

双源独立注册,规避多提供者限制

每个数据源是独立的 DSH 工具,模型可以自主选择。两个工具可独立启用/禁用,禁用的工具对 LLM 完全隐藏。

零侵入、零额外依赖

这是一个 Cordis bundle 插件,不需要修改 DSH 核心,全部通过 ctx.tools.registersettings.installSection 实现(要求 dsh ≥ 0.1.2-alpha)。依赖只有三个包:@deepseek-ai/dsh-settings@deepseek-ai/dsh-tools@deepseek-ai/schemastery

配置热重载

设置界面是 UI 卡片加 ~/.dsh/settings.yaml 热重载,改配置不用重启。lib/client.js 内置中英文(zh/en)界面,React 实现。

并发安全与策略可插拔

工具声明 isConcurrencySafe: true,支持 AbortSignal。新增数据源只需实现 FetchStrategy 契约(1 个文件),再在 src/index.js 注册 1 行。

安装与启用

运行要求:Node.js >= 22,DSH >= 0.1.0-rc.7。先在插件目录执行 pnpm install,再注册插件,最后重启 DSH:

cd /data/dsh-workspace/dsh-web-fetch
pnpm install

dsh plugin --profile web add /data/dsh-workspace/dsh-web-fetch
# 发布后也可用:
# dsh plugin --profile web add dsh-web-fetch

# systemd 环境:
sudo systemctl restart dsh

pnpm install 这一步不能省:Cordis 加载器只从插件目录解析依赖,跳过它会报 ERR_MODULE_NOT_FOUND: @deepseek-ai/schemastery

配置

打开 Settings → Plugin Config → 通用 Web 内容获取(web-fetch),顶部是 CDP / Tavily 的启用复选框,下面是两组配置项:

  • CDP 组:CDP Endpoint(默认 http://10.200.0.5:9222)、Timeout ms(60000)、Extra wait after load ms(2000)
  • Tavily 组:Endpoint(https://api.tavily.com/extract)、API Key(留空即禁用)、Timeout ms(30000)

保存后写入 ~/.dsh/settings.yamlweb-fetch: 下并热重载。也可以用 profile 覆盖,编辑 ~/.dsh/profiles/web/cordis.patch.yml

- id: web-fetch
  config:
    cdpEnabled: true
    cdpEndpoint: 'http://10.200.0.5:9222'
    cdpTimeoutMs: 60000
    cdpWaitMs: 2000
    tavilyEnabled: false
    tavilyEndpoint: 'https://api.tavily.com/extract'
    tavilyApiKey: ''
    tavilyTimeoutMs: 30000

注意:覆盖 config 会替换整个配置块,必须包含全部键。

典型用法

重启后 LLM 会自动看到这两个工具,不需要额外指令。手动测试:

User:  web_fetch_tavily 提取 https://example.com 的正文
User:  web_fetch_cdp 抓取 https://example.com 这个需要渲染的页面

工具输出结构如下:

{
  "sources": [{ "url": "...", "title": "...", "snippet": "...", "content": "...", "provider": "cdp|tavily" }],
  "truncated": false
}

新增数据源

src/strategies/my.js 实现 FetchStrategy 契约:

export function makeMyStrategy(config) {
  return {
    id: "my",
    title: "My Fetcher",
    available() { return Boolean(config.apiKey) }, // 轻量检查,不做 I/O
    async fetch(req, signal) {
      return { sources: [{ url, title, snippet, content, provider: "my" }], truncated: false }
    },
  }
}

再在 src/index.js 注册一行:

import { makeMyStrategy } from "./strategies/my.js"
ctx.tools.register(makeToolDef("my", makeMyStrategy, "myEnabled", current))

如需新配置项,可选地在 lib/client.jsConfigFIELD_VIEWS 中补充字段。路由和核心逻辑不用改。

测试

仓库自带离线单测,不调用真实浏览器或 Tavily:

node tests/test-cdp-unit.mjs   # 21 个测试
node tests/test-tavily-unit.mjs # 9 个测试

适用场景与注意事项

适合两类人:需要同时覆盖渲染型页面和轻量提取的 DSH 智能体开发者;想以插件方式扩展 DSH、不想动核心代码的维护者。

使用前注意:

  1. 插件以当前 dsh 进程的权限运行,安装前建议检查仓库源码与许可证(MIT)。
  2. tavilyApiKey 以明文存储于 ~/.dsh/settings.yaml(settings 系统,非凭据保险库),敏感环境建议通过 profile 的 cordis.patch.yml 覆盖。
  3. CDP 实现用 Node 原生 http 加手写 WebSocket 帧(无 ws 依赖),未启用 permessage-deflate,与 cloakbrowser 默认配置(关闭压缩)兼容。
  4. 版本兼容性跟随 @deepseek-ai/dsh-settings / dsh-tools / schemastery

经过上面的步骤,DSH 就多了一组可插拔的双源抓取工具:模型自主选择渲染或提取,扩展新源只需一个文件加一行注册。仓库与目录页:

  • GitHub:https://github.com/runfali/dsh-web-fetch
  • 社区插件目录收录页:https://www.skillhub.cn/plugins/runfali/dsh-web-fetch
羽毛球分组比赛记分
小程序二维码

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

小夜