dsh-web-firecrawl
Verifieddsh-web-firecrawl · v0.1.1 · MIT
Firecrawl as the DeepSeek Harness web backend: query routing, on-demand enrichment, credit budgets and failure memory for web_search/web_fetch.
Install
dsh plugin add dsh-web-firecrawl Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-web-firecrawl
把 Firecrawl 接成 DeepSeek Harness 的 web 后端。
它不新增任何工具 —— 模型照旧调 web_search / web_fetch,由本插件决定怎么问 Firecrawl。
dsh plugin --profile web add dsh-web-firecrawl
为什么需要它
官方 @firecrawl/dsh-firecrawl 把 peer 钉死在
@deepseek-ai/[email protected]。DSH 0.2.0 起新增了兼容性闸门:只要插件的
@deepseek-ai/dsh* peer 不匹配运行时版本,整包被跳过,它自带的 patch 层被丢弃,
web_search 就静默回落到别的后端。
本包不声明任何 @deepseek-ai/* peer 依赖(代码也零 import),而闸门只读 peerDependencies
—— 没有东西可拒,所以 dsh 升级后它依然能用。
而且它不只是把 query 转发了事:
| 官方 0.1.0 | 本包 | |
|---|---|---|
| dsh 0.2.x 能否加载 | ❌ 被闸门拒绝 | ✅ 零 dsh peer |
categories(research / developer / pdf) |
❌ 参数面里没有 | ✅ 按 query 意图自动选,免费 |
site: / -site: |
❌ 当普通文本传下去 | ✅ 转成硬过滤 includeDomains / excludeDomains |
| 结果信息量 | 只取 description(~200 字符) |
✅ 只对 top-K 定点升级 summary(+1 credit,4–7 倍文本);question 拿生成式答案填进 seam 的 content |
| 重复 query / URL | 每次都重新计费 | ✅ 进程内缓存,10 分钟内 0 请求 |
| 死路 URL | 模型会反复重试 | ✅ 失败记忆直接拒绝重抓 |
| 额度安全 | 无 | ✅ 按真实 creditsUsed 记账,自动区分计费 / 自建 |
| 工具路由 | 无 | ✅ 系统提示里带一张「何时用哪个 Firecrawl MCP 工具」的表 |
安装
dsh plugin --profile web add dsh-web-firecrawl
dsh plugin 会在 profile 里跑 pnpm,并自动把本包登记进 dsh.profile.bundles。
之后重启 dsh(profile 在启动时组合)。
端点通过环境变量给(项目 .env 或启动环境):
# 自建 Firecrawl —— 不需要任何 key
FIRECRAWL_BASE_URL=https://firecrawl.example.com
# 或者公网 API
FIRECRAWL_API_KEY=fc-your-key
验证:
dsh --profile web --dump-config | grep -A3 '^- id: web$'
# 期望:searchProvider: firecrawl / fetchProvider: firecrawl
不设
FIRECRAWL_BASE_URL就会指向公网计费的https://api.firecrawl.dev。 插件会探测出来、启用额度上限并打一条警告 —— 但那已经是可能花钱之后了。请显式设置端点。
为什么端点不写进包里的配置
cordis patch 的 config 是整体替换(target[key] = value),不是按键合并。
如果包里发布了 baseURL,任何人在自己那层写 config: 都会把它静默抹掉、回落到公网计费 API。
所以端点和凭据一律走环境变量(DSH 通过 launch environment 快照解析)。
一次查询发生了什么
- 先做免费的过滤:意图 →
categories(research / developer / pdf);site:/-site:→includeDomains/excludeDomains并从 query 剥离;limit向上取整到 10 的倍数 (搜索按 2 credits / 10 条计费,同一档内多要几条是免费的)。 - 按需定点升级,而不是整批抓:
scrapeOptions是按结果条数计费的,全抓很浪费。 插件只抓 top-K(enrichTopK,默认 1),且只在免费片段看起来太薄时才抓(enrichMode: adaptive)。summary花 +1 credit 换 4–7 倍文本;question花 +4 拿生成式答案。 - 额度守卫贴合实际:用
/v2/team/credit-usage判断这个后端到底计不计费 —— 公网和代付费代理会返回 余额数据;自建实例返回500 UNKNOWN_ERROR,甚至连假 key 都接受。计费后端启用额度上限, 自建只用调用量的防跑飞上限。 - 失败记忆:站点不可达、404、付费墙、空页会被记住,重复抓取直接拒绝并提示换来源; 网络抖动 / 429 / 5xx 从不入记忆 —— 那些重试是有意义的。
配置
全部可选,写在 profile 的 cordis.patch.yml 里。注意 config: 会整体替换,
所以要保留的键都得重写一遍:
- id: web-firecrawl
config:
enrichTopK: 1 # 升级前 K 条;0 = 只裸搜(最省)
enrichFormat: summary # summary(+1) | markdown(+1) | question(+4)
enrichMode: adaptive # 或 always
budgetWindowCredits: 60 # 仅计费后端:每 10 分钟额度
budgetWindowCalls: 120 # 两种后端都生效:防跑飞
minRemainingCredits: 0 # 仅计费后端:余额低于此值停手
failureTtlMs: 600000 # 失败记忆 TTL;0 = 关闭,负数 = 直到重启
billingMode: auto # auto | metered | unmetered
routingPrompt: true # 在系统提示里发布工具选择表
完整的 33 个参数见英文 README 的 “All options” 折叠表。
让升级稳定落地
一次 summary 抓取可能要 6–33 秒,而 dsh-tool-web 的 searchTimeoutMs 默认 30 秒就会掐断整次调用。
所以插件的 searchBudgetMs 压在 30 秒以下,并且宁可跳过升级也不中途 abort(abort 照样计费)。
想每次都拿到升级结果,两边一起调:
- id: tool-web
config:
searchTimeoutMs: 60000
- id: web-firecrawl
config:
searchBudgetMs: 50000
enrichTimeoutMs: 45000
车队部署
每人一条命令,bundles 自动登记:
dsh plugin --profile web add dsh-web-firecrawl # npm
dsh plugin --profile web add git+ssh://git@host/org/repo.git#v0.1.0 # git tag
dsh plugin --profile web add /srv/pkgs/dsh-web-firecrawl # 共享目录
dsh plugin --profile web remove dsh-web-firecrawl # 回滚
端点是部署级的,统一通过环境变量(项目 .env 里的 FIRECRAWL_BASE_URL)下发,不要走个人 patch。
车队体检:
dsh --profile web --dump-config | grep -A3 '^- id: web$'
测试
npm test # 打桩套件,不联网
npm run test:live # 打真实端点:FIRECRAWL_TEST_BASE_URL=...
npm run test:seam # 打真实 dsh 包:DSH_PACKAGES_DIR=...
兼容性
刻意不 import 任何 @deepseek-ai/*、也不声明它们的 peer。唯一契约就是 web seam 本身:
{ id, available(), search(request, signal) } // -> { sources[], content?, truncated }
{ id, available(), fetch(request, signal) } // -> { url, statusCode, body, truncated }
许可
MIT