关于 webfetch
webfetch 是可自托管的网页搜索 MCP 服务,面向 LLM Agent,采用搜索、抓取、排序的流程,并带有语义缓存。它以 web_search 工具的形式提供给模型调用,目标是降低输入 token 与费用。
主要功能
- 01多引擎 RRF 融合,DDG 无需密钥即可使用
- 02本地抓取与提取页面内容,支持 JS 渲染
- 03混合排序加交叉编码器,并进行句子级压缩
- 04精确与语义双层缓存,TTL 随查询类型变化
- 05save_finding 与 status、savings_report 等工具
适用场景
- 01为 Claude Code 等客户端提供零额外费用的网页搜索
- 02减少长对话中搜索结果占用的上下文 token
- 03对改写后的重复查询复用缓存结果
MCP 相关
该项目是 MCP 服务,向 MCP 客户端暴露 web_search、fetch_url、save_finding、status 和 savings_report 五个工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx webfetch-llmBRAVE_API_KEY密钥Optional - adds Brave to the multi-engine fusion. DDG works with no keys at all.SERPER_API_KEY密钥Optional - adds Serper (Google results) to the multi-engine fusion.TAVILY_API_KEY密钥Optional - adds Tavily to the multi-engine fusion.WEBFETCH_PROVIDEROptional - search strategy: multi (RRF fusion of available engines, default), fallback (priority failover), or a single engine name like ddg.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
webfetch
面向自行运行的 LLM Agent 的网页搜索——在准确度相同的情况下,输入 token 最多减少 8 倍,成本降低 3 倍,相较于托管式 web_search。
托管式网页搜索工具每千次搜索收费 10 美元,之后还会对其传入上下文窗口的每个内容 token 再次收费。webfetch 以本地流水线取代它们——多引擎搜索、网页获取与提取、语义重排、句子级压缩——并将其作为 web_search 工具提供,让模型像调用其他工具一样调用它。与我们调查过的所有托管工具和搜索 API 不同,重复或改写后的查询都由语义缓存免费处理。
(使用 pip install webfetch-llm 安装;导入名称为 webfetch。)
跳转至: 核心结论 · 你将获得什么 · 快速开始 · 检查设置 · 完整基准测试结果 · Claude Code · Agent 循环 · 节省报告 · 工作原理 · 注意事项
准确度相同。成本降至三分之一。Token 降至八分之一。
一个 Agent 循环、一个模型、一个评判器、50 个 SimpleQA 问题。每行之间唯一变化的是搜索工具:
| 搜索工具 | 准确度 | 每次查询的输入 token | 每次查询的成本 |
|---|---|---|---|
| Anthropic 托管式 web_search | 96% | 17,408 | $0.108 |
| webfetch(4 引擎融合) | 92% | 3,467 | $0.035 |
| webfetch(仅 DDG,费用为 $0) | 84% | 3,623 | $0.026 |
将 Opus 换成 gpt-5.6-sol,同一个 webfetch 工具即可达到 96% 的准确度——与托管式工具持平——每次查询仅需 $0.040 和 2,156 个 token:仅为托管式工具传入上下文的八分之一。完整结果涵盖了我们测试的所有方案。
这些数据是 webfetch 表现的最差情况——测试时缓存为空。实际使用中,差距会自然扩大:重复查询和改写后的查询都由缓存免费处理,而且后续每一轮只要继续保留搜索结果,token 优势就会再次体现。积少成多,就能得到这样的账单。这是一段普通 Claude Code 会话中的节省报告:

本 README 中的每项声明都由此仓库附带的评测工具生成——问题集、逐题记录、评判协议和负面结果均位于 evals/ 中,所有表格都能通过一条命令重新生成。评分结果不必只听我们说:evals/results/README.md 将每行表格数据对应到生成它的原始结果文件,细化到逐题评判结果。
你将获得什么
**由你掌控的搜索管线(4 引擎 RRF 融合、本地提取、句子级压缩)。**搜索结果由 DuckDuckGo、Brave、Serper 和 Tavily 的倒数排名融合而来,你有哪家的密钥就能用哪家。DDG 无需密钥,因此该工具开箱即可零成本使用;你添加的每个密钥都会自动加入融合。网页会在本地抓取和提取(trafilatura、readability、newspaper4k,以及用于 JS 页面和 403 拦截页面的 Playwright 渲染),然后进行分块,再通过混合 BM25 + 双编码器级联排序,并在顶部使用交叉编码器,最后压缩为回答查询所需的句子——实测在召回率零损失的情况下,令牌数减少了 50%。
**别处没有的缓存(精确 + 语义匹配、感知内容时效性的 TTL)。**在同一个 sqlite 文件中使用两层缓存:按 URL 缓存网页文本,按查询缓存排序结果。完全相同的查询会命中精确缓存。改述后的查询会命中语义缓存——先通过嵌入模型生成候选列表,再由 NLI 交叉编码器验证;我们优先针对精度进行评测调优(在我们进行的所有线上运行中,均未出现错误目标匹配)。缓存时长会根据查询动态调整:价格和分数在 15 分钟后过期,近期话题在 7 天后过期,发布说明和规格在 90 天后过期——分类依据是调用模型提供的提示,或本地分类器。每条缓存结果都会向模型显示来源信息([cache: semantic match to "...", 2h old, recent]),模型若不认可,可发送 force_fresh。我们调研过的托管工具或搜索 API 均未提供客户端可见的缓存功能。
**模型可以维护自己的缓存(save_finding,标记为 UNVERIFIED,含熔断开关)。**如果搜索没有结果,而模型从其他来源(例如托管搜索回退)获得了答案,就可以调用 save_finding 存储所学内容——标记为模型贡献,提供时附带明确的 UNVERIFIED 警告和 force_fresh 退出方式,并按常规 TTL 规则过期。对于永远不想缓存未经验证内容的部署,可使用熔断开关(SAVE_FINDING_ENABLED = False)。
**模型真正需要的控制项(full_results、fetch_url、freshness、force_fresh)。**对于列表和排序查询,full_results 会返回未压缩的摘录(压缩会缩减并列列表项——这是我们的实测结果)。fetch_url 会完整抓取一篇引用的网页;如果管线之前抓取过该网页,则可立即返回。freshness 提示控制缓存时长;force_fresh 可绕过缓存。
**用量凭据(会话和累计用量,精确计数)。**用量计数器会持久保存在缓存文件中;webfetch-savings(或 savings_report 工具)会显示你省下了哪些托管搜索费用,并分别统计本次会话和累计用量。
下方示例。
开始使用
一行命令即可(uv 会在首次启动时安装软件包,@latest 会自动获取新版本):
claude mcp add webfetch -- uvx --from webfetch-llm@latest webfetch-mcp
或者自行管理安装,只需两条命令:
pip install "webfetch-llm[all]"
claude mcp add webfetch -- webfetch-mcp
精简安装(不含额外项的 webfetch-llm)可在数秒内启动并以降级模式运行:BM25 排序、仅精确匹配的缓存、词法压缩。[all] 会安装语义组件(torch,首次安装需几分钟),以及 Playwright、PDF 和表格提取功能;本 README 中的所有基准数据均基于此配置测得。在全新的 venv 中需要 pip >= 24(运行 python -m pip install -U pip——较旧的 pip 会因我们的依赖树中有重复的 extra 而崩溃)。如果安装到 venv 中,请注册 webfetch-mcp 的绝对路径,或保持 venv 处于激活状态。
密钥为可选项(DDG 无需密钥即可使用),但能提高召回率——所有引擎都提供免费套餐:
cp .env.example .env # fill in what you have
控制台脚本和 MCP 服务会读取其运行目录中的 .env;导出的环境变量和 claude mcp add 上的 --env 标志始终具有更高优先级。将 webfetch 作为库导入时,永远不会读取 .env——你的进程环境由你掌控。
检查设置
webfetch-status(或让模型调用 status 工具)会回答
“我的密钥是否已被读取,以及我实际运行的是什么?”:
webfetch 0.1.3
search engines:
ddg ready (no key needed)
brave ready (BRAVE_API_KEY is set)
serper off (SERPER_API_KEY not set)
tavily ready (TAVILY_API_KEY is set)
active provider: multi(ddg+brave+tavily) (RRF fusion)
optional features:
semantic ranking/cache/compression: on
JS-page rendering (playwright): on
PDF extraction (pdfplumber): OFF (pip install 'webfetch-llm[pdf]')
HTML tables (pandas+tabulate): on
result compression: on (crossencoder)
cache: /Users/you/.webfetch/cache.db
5.4 MB, 70 lifetime searches (webfetch-savings for the receipt)
仅显示密钥名称——绝不打印密钥值。配置通过环境变量提供,并在服务启动时读取:
| 变量 | 作用 |
|---|---|
BRAVE_API_KEY、SERPER_API_KEY、TAVILY_API_KEY | 每个密钥都会为融合添加一个引擎 |
WEBFETCH_PROVIDER | multi(默认:融合所有已配置密钥的引擎)、fallback(由 DDG 提供服务,已配置密钥的引擎在 DDG 受阻时接手),或单个引擎名称 |
WEBFETCH_CACHE_DB | 更改缓存文件位置(默认 ~/.webfetch/cache.db) |
如果没有配置任何密钥,默认会使用纯 DDG;状态输出会如实说明这一点,而不会称之为融合。库的使用者可以完全跳过环境变量:Pipeline(search=get_search_adapter("fallback"), cache=...)。
完整基准测试结果
所有测试组均使用相同的 50 个 SimpleQA 问题和相同的评判器,在同一天运行一次 (2026-07-14)。Agent 循环测试组采用完全相同的循环;唯一不同的是搜索工具。成本包括模型 token 和各提供商公布的每次搜索费用;我们的成本还包括估算的引擎费用。
| 搜索工具 | 模型 | 准确率 | 每题输入 token | 每题成本 |
|---|---|---|---|---|
| OpenAI 托管的 web_search | gpt-5.6-sol | 100% | 10,027 | $0.066 |
| webfetch(4 引擎融合) | gpt-5.6-sol | 96% | 2,156 | $0.040 |
| Anthropic 托管的 web_search | Opus 4.7 | 96% | 17,408 | $0.108 |
| webfetch(4 引擎融合) | Opus 4.7 | 92% | 3,467 | $0.035 |
| Exa(搜索 + 内容) | Opus 4.7 | 90% | 5,496 | $0.053 |
| Tavily | Opus 4.7 | 88% | 6,387 | $0.047 |
| webfetch(仅 DDG) | Opus 4.7 | 84% | 3,623 | $0.026 |
| webfetch(4 引擎融合) | Haiku 4.5 | 76%* | 3,021 | $0.031 |
* Haiku 的失败大多是因为在轮次上限之后仍反复搜索,而且在基准测试中途耗尽免费额度后,最后几个问题使用了性能下降的引擎组合。应将其视为下限。
请注意 token 列:webfetch 结果所需的输入 token 是摘要 API 的一半、托管工具的五分之一到八分之一——这就是为什么即使每次搜索的费用相近,我们的总成本仍然最低。
在另一组包含 27 个关于运行前两周内事件的问题的数据集上(手工编写,从未发布,因此没有供应商能够据此调整),webfetch 使用融合和仅 DDG 时的得分均为 100%;托管工具也均为 100%。新近事件并不难——真正的难题是日期注入陷阱。
复现方法:python evals/run_e2e_eval.py --arms ours-multi,hosted——测试框架、数据集和逐题记录都在 evals/ 中。上表的数据来自
e2e_eval_20260714_005531.json
(以及 Haiku 测试和新近事件数据集——完整的声明与文件对应关系见 evals/results/README.md)。这些问题是从 OpenAI 的公开 SimpleQA 数据集中按随机种子(seed 42)抽取的样本,因此 build_datasets.py 会在你的机器上重新生成相同的 50 个问题。有一点需要坦诚说明:实时网络内容会随运行时间而变化,而且在 n=50 时,96% 对 92% 的准确率差距相当于答对 48 题与 46 题——在噪声范围内持平。token 和成本两列则是通过机械方式计算、结果稳定的指标。
在 Claude Code / Claude Desktop 中使用
在 Claude Code 中最快的方法是将其安装为插件,提供两个斜杠命令(底层运行相同的 uv 单行命令):
/plugin marketplace add firish/webfetch
/plugin install webfetch@webfetch
或者自行注册 MCP 服务。无论采用哪种方式,新会话中都会在 /mcp 下显示 webfetch,并提供五个工具:web_search、fetch_url、save_finding、status 和 savings_report。询问任何近期事件并观察工具调用;再用不同措辞询问同一件事,结果会带有 [cache: semantic match ...] 标头,而不是重新搜索。会话中的首次调用较慢(编码器预热加上实际网页抓取,需要 10-40 秒);缓存调用则会即时返回。
可在 /mcp 中按会话关闭服务;通过 /plugin uninstall webfetch 或 claude mcp remove webfetch 将其移除,具体取决于安装方式。缓存及其回执历史记录位于 ~/.webfetch/,重新安装后仍会保留。每台机器运行一个服务即可——语义缓存假定只有一个进程拥有其文件。
更新通知:每个进程都会向 pypi.org 发送一次请求,以检查是否有更新版本,并在 savings_report 输出中显示一行提示。在 webfetch/config.py 中设置 UPDATE_CHECK_ENABLED = False 即可禁用此功能;除此之外,绝不会向任何地方发送数据。
在你自己的 Agent 循环中使用
import time
import anthropic
from webfetch import WEB_SEARCH_TOOL, handle_web_search
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "What did the FOMC decide this week?"}]
system = (f"Today's date is {time.strftime('%Y-%m-%d')}. "
"Use web_search for recent facts.")
while True:
response = client.messages.create(
model="claude-opus-4-7", max_tokens=2000, system=system,
tools=[WEB_SEARCH_TOOL], messages=messages,
)
if response.stop_reason != "tool_use":
break
messages.append({"role": "assistant", "content": response.content})
results = [{"type": "tool_result", "tool_use_id": b.id,
"content": handle_web_search(b.input)}
for b in response.content if b.type == "tool_use"]
messages.append({"role": "user", "content": results})
带提示词缓存和自适应思考的完整版本见 examples/agent_loop.py。工具架构在设计上与提供商无关——OpenAI 函数调用只需对字典进行机械式改造。
请在系统提示词中加入今天的日期。 这不是可选项。模型会拒绝搜索它们认为尚未发生的事件:在我们的近期事件数据集中,未提供日期的循环中,模型对多达 27 个问题中的 10 个甚至拒绝调用工具(“Wimbledon 2026 还没有举行”)。只需一行即可解决。托管搜索工具会在服务端处理这一点,所以只有在运行自己的工具时,人们才会注意到这个问题。
handle_web_search 不会抛出异常。引擎故障、空结果和格式错误的输入都会以可读字符串的形式返回,供模型处理,因为对话中途出现异常会导致整个 Agent 循环中断。
节省情况报告
搜索时,计数器会累积在缓存文件中。让模型运行报告,或者自行运行 webfetch-savings。MCP 工具会分别统计本次会话和累计数据,因此你可以查看一次对话节省了多少:

下面是这一行输出的详细内容——这台机器上的一次 30 次搜索会话:
| 本次会话 | 累计 | |
|---|---|---|
| 已处理的搜索 | 30 | 76 |
| 来自缓存 | 17(57%)- 10 次精确匹配,7 次语义匹配 | - |
| 避免的搜索费用 | ~$0.30 | |
| 避免的内容令牌成本 | ~$2.56 | |
| 估算的总节省 | ~$2.86 | ~$7.21 |
计数器数据精确;美元金额为估算值,所采用的假设(每次托管搜索费用、每次调用的托管令牌数——两者均经过测量,以及你的模型令牌价格)可作为参数传入 webfetch.savings_report()。
工作原理
query -> multi-engine search (RRF fusion, circuit breakers)
-> semantic query cache? serve with provenance header
-> fetch pages concurrently (page cache by URL)
-> extract (trafilatura -> readability -> newspaper4k -> playwright)
-> chunk (400 chars) -> BM25 + bi-encoder fusion -> cross-encoder top 5
-> compress to query-relevant sentences -> source-labeled result
所有重量级功能都是可选的:不安装 [rerank] 时仍可使用 BM25 排名、精确匹配缓存和词法压缩,并会记录警告,而不是抛出 ImportError。DDG 值得特别说明:它会通过不返回任何内容的空响应,对自动化客户端进行指纹拦截。webfetch 会检测这种情况(融合搜索中出现“部分引擎有结果、部分为空”,或回退链中出现任何空结果),通过熔断器暂时停用该引擎并改用其他引擎;TLS 相关处理由 ddgs 依赖项负责。
设计决策及其测量结果会随进展记录在 docs/architecture.md 和 docs/ROADMAP.md 中,其中也包括负面结果。
坦诚说明
- 延迟。全新搜索需要 10-40 秒——页面会被实际获取并在本地排序。托管搜索约 10 秒返回,摘要 API 约 6 秒。命中缓存则即时返回。如果你需要亚秒级搜索且不在意成本,这个工具并不适合你。
- 缓存是单进程的。一次只能用于一个 Agent 循环、一个 MCP 服务或一个笔记本;共享团队缓存后端已列入路线图。
- 引擎的免费额度是真实配额。基准测试期间,我们用完了 Brave 的月度额度。弹性层会平稳降级,但召回率也会随之下降。表格中的引擎费用是根据公开价格估算的。
- 答案质量取决于驱动工具的模型——能力较弱的模型会构造出更差的查询,并反复搜索而不是阅读(参见 Haiku 行)。
- 对于跨形式改写(“X 添加了什么”与“X 的新功能”),语义缓存的召回率有限:调优后,在真实配对上的测量结果为 6/10,因为某些释义形式会让所有可用的验证器都无法判断。漏检会导致重新搜索,但绝不会产生错误答案。
基准测试与数据
共有三个评估层,全部位于 evals/ 中:用于确定语义缓存阈值的离线匹配器评估、实时检索评估(召回率、令牌、缓存诊断),以及上文引用的端到端答案评估(SimpleQA 协议:先进行标准化精确匹配,再由大模型评审)。数据集根据 SimpleQA(MIT)、QQP/GLUE 和 FreshQA(CC-BY-SA)确定性构建,每个文件旁都附有来源说明文件。fresh-events 集合是在基准测试运行前几天,根据经过核实的新闻来源手动编写的,专门确保没有任何模型或供应商流水线接触过它。
许可证
MIT。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。