Harness 的 web_search Tool

今天尝试理解了 web_search 在 Agent Harness 中是如何调用的。

"body": {
    "id": "072c2cfe7fdde5e95bae19eaeef015d8",
    "type": "message",
    "role": "assistant",
    "content": [
        {
            "type": "server_tool_use",
            "id": "call_atk6nJiZMtZNEKbsIrCkoamC",
            "name": "web_search",
            "input": {
                "query": "Claude Code WebSearch tool implementation how it works server side"
            }
        },
        {
            "type": "web_search_tool_result",
            "tool_use_id": "call_atk6nJiZMtZNEKbsIrCkoamC",
            "content": [
                {
                    "type": "web_search_result",
                    "title": "Intrusion Detection Systems",

也就是模型在想要进行网络搜索的时候,会发起一个 Web Search 的 Tool Call。由 Harness 完成搜索,把结果塞到响应里面继续循环。这个 WebSearch 的 Tool 是如何实现的,则是由 Harness 由代码定义的。Claude Code 就会连接到 Server Tool,当接入第三方模型的时候,如果他们也支持这个 Server Tool,就能直接使用。OpenCode 就会走到内建的几个大的 Search API 提供平台。而这些 Search API 平台都会提供对应的 MCP,则是一方面是给不支持 Server to Code 的平台使用,另一方面也可以固定使用某个平台的查询 API 来固定搜索行为。

特别是对 MiniMax,在控制台用量界面区分了两个工具 web_search 和 coding-plan-search,又在 coding-plan-search 下区分了来源。实际上从实测和 网络搜索 MCP 以及 服务端工具(Server Tools) 文档可知,搜索功能分为客户端和服务端实现,不论是 Tool Call 还是 MCP 最终都会在某个环境中调用具体的搜索服务 API。使用 Server Tools 和使用 MCP 实际上都一样,走到的都是服务端搜索,只是在用量账单和来源标记上有区别。

像 Exa、Firecrawl、Parallel、Tavily、TinyFish 以及最新的 DeepSeek 都属于提供 Search API 的服务商。如果 Harness 工具没有内建接入方式到自己的 web_search 中,就需要靠 MCP。所以 OpenCode 想要接入 MiniMax / DeepSeek 提供的 Search API 就需要靠 MCP。

七个搜索 API 的横向对比

AIGC

今天把手头能用的搜索通道都拉出来跑了一遍:Tavily、Parallel、Firecrawl、Exa、MiniMax 的 CLI 和 MCP 两路,再用 Harness 内建的 WebSearch (Claude Code + MiniMax) 当基线。同一套查询分别喂给七个引擎,横向差异比预期大得多。

引擎官方一手命中噪声率招牌能力主要短板
Parallel最高(≥70%)最低(10–15%)llms.txt 全量索引,一次 extract 枚举整个文档站英文查询偏弱,跨产品串味(同名论文、竞品)
Firecrawl高低抓取零失败;--country CN 与 US 双跑(两轮各 5 条只重叠 1 条);developer 模式能挖官方仓库与 issuehighlights 基本为空,结论得靠二次 scrape;官方 changelog 在普通搜索里排名极低
Exa高中objective 可控,highlights 直接带原文证据,省一次抓取镜像双域不去重;对「官方到底有没有明文表态」这类否定型问题最弱
Tavily中高高中文召回最高(72.7%)、去重率最低容易跑到同名主题;--json 必须放子命令前,批量 extract 易 exit 4
MiniMax CLI官方优先中高官方中文 changelog 抓得全精确术语召回差,得靠泛词 + 抓 changelog 反推官方命名
MiniMax MCP中中唯一能直接进官方开源仓库源码(api.github.com tree + raw),机制类问题能从代码回答官方文档站多级跳转最终 403,正文只能靠 raw;tracking 参数不去重、不区分 fork
内置 WebSearch最低最高结果最多官方一手常为 0,自带摘要会编精确数字,必须回抓原文核实

推荐优先级:

  • 中文长尾:Tavily 打召回 → Parallel 保准确 → MiniMax 系补中文一手与源码
  • 英文官方文档:Firecrawl → Parallel → Exa
  • 通用组合:Exa 或 Parallel 主检索,Firecrawl 与 MiniMax MCP 补一手源码,Tavily 补中文长尾

只能留一个的话选 Exa MCP。它的 objective 能把「要哪类文档、排除什么、抽哪些事实」写死,同一套查询在中英文场景都可控;更关键的是 highlights 直接带原文证据,能省一轮抓取——而官方文档站普遍有 404/403/多级跳转,二次抓取经常直接失败。Runner-up 是 Parallel,官方命中率和噪声控制都最好,只是英文查询容易泛。

几条通用经验:官方文档站几乎都会暴露 llms.txt 和每页的 .md 原文,抓正文优先走这两个入口,HTML 页经常 404;GitHub 用 raw 加 api.github.com contents 逐级下钻能读到源码;Firecrawl 的 --country 得中文英文各跑一遍再取并集;Exa 的 objective 里写具体字段名和版本号能显著提高 issue 命中率;Tavily 抓不了中文镜像站,但中文召回反而最高。

基于此,我会给 Claude Code 添加 Exa MCP,并且关闭自带的 Web Search,来避免使用 MiniMax 模型的时候顶向 MiniMax 的后端,或者直接改为使用 OpenCode。

Windows 系统没有 readline 导致 tvly 的 REPL 崩溃

AIGC

tavily-cli 0.1.8 在 Windows 上敲 tvly 报 ModuleNotFoundError: No module named 'readline'。readline 是 Unix 专属的 CPython 标准库模块,Windows 版 Python 不提供(本机是 vfox 装的 3.14.3),而 tavily_cli/repl.py:5 无条件 import 它,包自己没做保护。上游没修,PyPI 上 0.1.8 已经是最新版,gnureadline 又没有 Windows wheel,能补的只剩 pyreadline3。

解决办法是让 uv 带额外依赖重装:

uv tool install --with pyreadline3 tavily-cli

装上的是 pyreadline3 3.5.6,纯 Python wheel(py3-none-any),由它提供 readline 模块。用 --with 而不是手动 uv pip install,是因为 uv 会把额外依赖写进 uv-receipt.toml 的 requirements,uv tool upgrade 重新解析时不会丢,tvly update 走的也是 uv tool upgrade。这里用 uv 装是因为缺的是 Python 模块,winget 和 bin 都覆盖不到。

装完裸 tvly 在 Git Bash、cmd、PowerShell 三边都能进 REPL 并正常退出,tvly search、tvly extract、tvly map、tvly --status 都正常。