把搜索调到 Claude.ai 的水准

本文首发于 sg.yaoyuheng2001.me,转载请注明出处。

上一篇 eval 钉死了差距:固定模型、只变 harness,我的自建 agent 兑现了 Claude.ai37% / 80% ≈ 46% ,而搜索精度一个人就占了将近一半的失败题。这篇兑现那个"P0 跑完之后"的 flag。

Part 1 · 教程:Claude.ai 搜索的 5 个工程优化

先说结论:Claude.ai 的搜索不是某个魔法引擎 ------它后端其实就是 Brave Search第三方分析发现 Claude 的引用与 Brave 顶部结果重叠约 86.7%。它的"又快又准",是 5 个可拆解、可复刻的工程优化叠出来的。

先确立一个前提,后面每条都建立在它之上:claude.ai 用的是 Anthropic 的 server-side web_search 工具。调用方只声明 它,其余都在服务端:模型自行决定搜不搜、搜几次,整个多轮检索循环在 Anthropic 服务端、一次 HTTP 请求之内完成,结果加密内联回灌给模型,调用方无需 像普通 client tool 那样回 tool_result。这个 "server tool" 的身份解释了下面几乎每一条优化,也是它与"自己用 client tool 搭 harness"的根本区别------后者必须把这个循环跨多次请求自行重建(Part 2 的主题)。

两种调用形态:同一个搜索,代码差一整个循环

ini 复制代码
# A. server tool ------ 一次调用,多轮检索在服务端跑完
resp = client.messages.create(
    model="claude-opus-4-...",
    messages=[{"role":"user","content": q}],
    tools=[{"type":"web_search_20250305","name":"web_search","max_uses":5}],
)
# resp 里已经是带引用的最终答案。你不写循环、不回 tool_result。
# (仅当 stop_reason=="pause_turn" 才把 content 原样回传、续一次)
python 复制代码
# B. client tool ------ 循环和检索全得你自己写
messages = [{"role":"user","content": q}]
while True:
    resp = client.messages.create(model=..., messages=messages, tools=[WEB_SEARCH])
    messages.append({"role":"assistant","content": resp.content})
    if resp.stop_reason != "tool_use":
        break                                     # 收敛,结束
    results = []
    for b in resp.content:
        if b.type=="tool_use" and b.name=="web_search":
            hits = do_search(b.input["query"])    # ← 你的搜索 API + 抓取 + 抽取
            results.append({"type":"tool_result","tool_use_id":b.id,"content": hits})
    messages.append({"role":"user","content": results})  # ← 回灌,再来一轮

差别一眼可见:A 里没有循环 ------搜几次、怎么 refine、何时停,全在 Anthropic 服务端;B 里那个 while 循环、每轮的搜索执行、tool_result 回灌,全是你的代码。所以 server tool 自带的"内联内容、服务端循环、不限轮迭代",到了 client 侧都得一件件自己挣出来------这正是 Part 2 的工作。下图把这条结构差异并排画出来:

左:server tool,一次请求、循环藏在服务端;右:client tool,N 次往返、循环由你自己持有

1. 内容随结果"内联"返回------不再单独抓页

原理:调一次 web_search,每条结果里已经附带了约 500 词、按查询挑选的正文片段。一次搜索就同时拿到了相关正文,无需再抓页、也无需再调一个模型去抽取。

这 500 词从哪来?答案落在"它后端是 Brave"这件事上。Brave Search API 本身就做索引侧的段落选择 :每条结果除主摘要 description,还会返回 最多 5 条 extra_snippets------按查询实时挑出的相关摘录。实测一条结果的 description(~250 字符) + 4--5 条 extra_snippets(~1300 字符) 正好凑到 ~500 词。证据是:同一个 URL、换一个查询,返回的片段就不同 ------这正是搜索索引做 snippet 的本质(索引里存着全文,每次按关键词邻近度抽取当前查询相关的片段)。所以"内联内容"不是整页、也不靠额外的抽取模型,就是 Brave 索引选好的若干片段,Anthropic 透传过来而已。

示例:一次带搜索工具的请求

bash 复制代码
POST /v1/messages
{
  "model": "claude-opus-4-...",
  "tools": [{ "type": "web_search_20250305",
              "name": "web_search", "max_uses": 5 }],
  "messages": [{ "role": "user", "content": "苏格兰 2021 高地群岛 保守党得票" }]
}

返回的 content\[\] 是一串有序的 block(都在同一个 assistant turn 里)

json 复制代码
[
  { "type": "text", "text": "我来查一下......" },        // ① 宣告要搜
  { "type": "server_tool_use",                       // ② 实际查询(server tool)
    "id": "srvtoolu_01WYG...",                           //    注意 srvtoolu_ 前缀
    "name": "web_search", "input": { "query": "..." } },
  { "type": "web_search_tool_result",                // ③ 结果,紧跟在②后面
    "tool_use_id": "srvtoolu_01WYG...",
    "content": [{ "type": "web_search_result",
      "url": "https://en.wikipedia.org/...",
      "title": "2021 Scottish Parliament election",
      "encrypted_content": "EqgfCiB...",                 // ↓ ~500词正文,索引侧已选段
      "page_age": "..." }] },
  { "type": "text", "text": "保守党在...",              // ④ 带引用的最终答
    "citations": [{ "type": "web_search_result_location",
      "url": "...", "cited_text": "...(≤150字符)...",
      "encrypted_index": "Eo8BC... " }] }
]

这串 block 里有两点对自建 harness 很关键。第一,它是 server_tool_usesrvtoolu_ 前缀),不是普通 client tool,结果由服务端回灌,你不必回 tool_result。第二,encrypted_content(在结果里)和 encrypted_index(在引用里)用于 Anthropic 跨轮管理上下文与引用;多轮对话里要把整个 assistant turn 原样塞回 messages,不能抽出纯文本再拼回去,否则后续轮次的引用会断。

有一点容易误会:encrypted_content 对开发者是黑盒,对 Claude 不是。按社区逆向,调用方没有公钥、没有客户端解密,只能把它原样回传,无从查看内容;而抓取、加密、解密都在 Anthropic 服务端完成,模型照常用里面的正文作答、生成引用(官方文档也是这么描述的)。加密更多是出于商业与合规考虑:避免把 Claude API 当作免费爬虫,并尊重内容方"只用于 AI 上下文、不原样转发"的授权。所以在 claude.ai 这类全程服务端的产品里,搜索这一步就把相关片段一并交给了模型。

插曲:同一个引擎,Claude Code 走了相反的路

同样是 Anthropic、同样的 Brave 后端,命令行里的 Claude Code 却把内联片段丢掉了。对 Claude Code 网页工具的逆向显示,它拆成了两个工具、两条管线。

Claude Code 的 WebSearch:只留标题和链接,正文被丢弃

json 复制代码
// 同样的 web_search_result,但 Claude Code 只取这两个字段:
{ "title": "2021 Scottish Parliament election",
  "url":   "https://en.wikipedia.org/wiki/..." }
// page_age、encrypted_content(那 500 词正文)------ 直接扔掉

想要正文,得显式再调一次 WebFetch(自己重抓 + 自己提炼)

scss 复制代码
WebFetch(url, prompt)
  → 抓 HTML → Turndown 转 Markdown → 截到 ~100KB
  → 用一个小模型(Haiku)按 prompt 把这页"读"成一段答案
  → 返回的是这段答案,不是原始网页

为什么放着现成的 500 词内联片段不用、偏要重抓一遍?逆向作者给的解读是:这是一次刻意的工程取舍 ------CLI 里 agent 要长时间自主跑,把"搜索"和"读取"拆开能缩小注入面 (搜索结果只剩标题链接,不会把一大段不可信网页正文直接灌进主上下文)、更省版权风险 (默认不大段复述原文)、也让每一步的成本和行为更可预测。代价是慢、是多一跳------但 CLI 场景换来的是可控。

同一个引擎、两套内容管线,正好框住了一个工程取舍:*把正文随搜索内联端上来(claude.ai,快),还是搜完再选择性重抓(Claude Code,稳)。*两条路各有代价,也各有适用场景------Part 2 会看到,自建 harness 若不小心,容易落进两者里最差的组合:既要单独重抓(慢),又把不可信正文整段塞进上下文(脏)。

2. 整个检索循环在服务端、一次请求内跑完

**原理:**这是 "server tool" 身份最值钱的地方。你一次请求发出去,多轮检索的 agentic loop 全在服务端跑 :Claude 判断要搜 → 生成 query → Anthropic 执行 Brave → 结果回灌 → 还不够就拿上一轮结果 refine 下一轮、再搜 → 直到信息够了或撞 max_uses → 吐出带引用的答案。**这中间没有一次回到你这儿。**你通常一个 HTTP 请求就拿到完整答案。

ini 复制代码
你的一次请求
  └─[服务端]  搜 → 读 → refine → 再搜 → ... → 带引用作答   // 多轮全在里面
            stop_reason = "end_turn"   // 正常:一次拿全
            stop_reason = "pause_turn"  // 仅当这轮太长被切断 → 原样回传接力

client tool 复刻不了这条。用 client tool 时,模型每决定搜一次,就要把 tool_use 流回客户端、客户端执行搜索、再开 一次新的 /v1/messages 请求 把结果送回去------服务端一个请求内做完的 7--11 次搜索,在客户端是 7--11 个公网往返 + 每轮重新 prefill。这是结构性差异:官方把整个循环关在服务端,客户端只能把它摊开成多次请求。这也正是为什么下面 #4 的缓存对自建 harness 是关键、对 server tool 却无关紧要。

3. "代码过滤"而不是"再叫一个模型"

原理:结果太多时,它的动态过滤让模型写一段代码、在沙箱里跑一遍 来筛/排/抽,只把代码输出送进上下文。确定性、token 便宜,零额外模型往返 。这其实对应工具的两个版本:基础版 web_search_20250305 没有动态过滤;新版 web_search_20260209 才有,而它内部依赖 code execution 来跑这段过滤逻辑(代价:默认不符合 ZDR 零数据保留,要 ZDR 得退回基础版)。

示例:开启代码执行后,模型自己写过滤代码

ini 复制代码
# Claude 在 code_execution 工具里生成并运行:
results = load_search_results()
hits = [r for r in results
        if "Highlands and Islands" in r["text"]
        and any(y in r["text"] for y in ("2021","2016"))]
print(hits[:3])   # 只有这几条进上下文

这套"让模型写代码来编排/过滤工具结果",Anthropic 官方叫 programmatic tool calling。它和"再叫一个 LLM 去读去筛"是两个量级:后者是一次完整推理往返(几百 ms~数秒),前者只是跑一段确定性脚本(毫秒级)。这条区别在 Part 2 会反复出现------用模型做本该用代码做的过滤,是最常见的延迟来源之一。

4. Prompt Caching:缓存稳定前缀,长 prompt 延迟最高降 85%

官方数据延迟最高降 85%、成本降 90% ,一个 10 万 token 的例子从 11.5s 降到 2.4s 。但"缓存"究竟缓存了什么、cache_control 又在标什么,值得从底层讲清楚------它直接决定了多轮 agent 的成本与延迟。

第一步:为什么 prompt 能被缓存------KV cache

Transformer 读每个 token 时,注意力层会为它算三个向量:Query(我在找什么)、Key(我含什么)、Value(我携带什么信息)。关键性质是因果性 :某个位置的 Key/Value 只依赖它前面的 token一旦算出来就永远不变 。所以这些 K/V 张量完全可以存下来复用,而且不影响输出质量 (因为位置 i 的注意力本就只看 i 之前)。这堆存下来的 K/V,就是 KV cache。不缓存的话,每次请求都要把整段前缀的 K/V 从头算一遍------纯属浪费。

第二步:cache_control 在标什么

原生 KV cache 只在一次 生成内复用。Prompt caching 把它跨请求复用 :把前缀的 K/V 张量留在推理服务器的显存里,用 token 序列的哈希 做索引。下次请求来,服务器哈希你的前缀、命中就直接取那批张量、跳过重算。而 cache_control: {type:"ephemeral"} 就是你打的一个**"断点标记"------告诉服务器"到这个 block 为止的前缀是稳定的,请缓存它"。Anthropic 要你显式**打这个断点(OpenAI 是自动的)。

scss 复制代码
请求 = [  tools  ][  system  ][  历史消息  ][ 这一轮新增 ]
         └─── cache_control 缓存到这(稳定前缀) ───┘
        // 第 1 轮:建缓存(cache_creation_input_tokens)
        // 第 2+ 轮:前缀命中,直接读(cache_read_input_tokens,便宜 ~10×)
        // ※ 历史若"只追加不改写",断点可往后滚,连历史也一起缓存

示例:在 system 和最后一个 tool 上打断点

json 复制代码
{
  "system": [{ "type": "text", "text": "<长系统提示>",
               "cache_control": { "type": "ephemeral" } }],
  "tools":  [ ...,
    { "name": "web_search", ...,
      "cache_control": { "type": "ephemeral" } } ]  // 缓存整个 tools 块
}

关键纪律:命中按前缀哈希精确匹配,任何字节变动------空格、JSON key 顺序、工具定义------都会从变动处往后 整段 miss**。需要厘清的是:纪律不是"别缓存工具结果",而是 "别改写前缀"。历史只要 只追加、不回头改**,累计的搜索结果就能持续命中缓存(甚至可以把断点滚到最后一条消息,把整段历史也缓存住)。真正打碎缓存的是"每轮回头重写/压缩历史"------比如把工具结果有损拍平成摘要再塞回、或用 compaction 函数每轮重新截断旧消息,都会让前缀字节不断变化。"Don't Break the Cache"(arXiv 2601.06007) 量化了这点:边界控制得当能改善 TTFT 13--31%,乱缓存反而增延迟。
**为什么这对"搜索"尤其重要:**搜索结果正文一旦进了上下文,会在后续每一轮继续按 input token 计 (因为它还留在 messages 里)------这是长对话成本悄悄上涨的主因。所以"缓存累计的搜索历史"并非锦上添花:不缓存时,跑到第 12 轮,前 11 轮的搜索正文每一轮都在重新计费、重新 prefill;缓存住,它们近乎免费。能否负担多轮检索,往往就取决于此。

想深入:这套机制的学术脉络

这不是 Anthropic 独家黑科技,而是一条清晰的研究线:Prompt Cache(arXiv 2311.04934)最早提出"模块化注意力复用",把可复用的 prompt 片段预算好 K/V;SGLang 的 RadixAttention(arXiv 2312.07104)用一棵 radix 树自动发现并跨请求复用共享前缀(vLLM 的 PagedAttention 则在显存分块层面做这件事);到 KVFlow(arXiv 2507.07400),研究对象已经正是多 agent / agentic workflow 的前缀缓存 ------和我们这种"每轮重复 system+tools、反复多轮调工具"的场景严丝合缝。有句话很到位:"前缀缓存是这串优化里唯一一个'上下文越长越省'的。"

验证它是否生效也简单:看响应里的 cache_creation_input_tokens(写入缓存)与 cache_read_input_tokens(从缓存读),命中率 = read / (read + creation)。缓存生效时,靠后轮次的非缓存 input token 会从上千掉到个位数。另外存进显存的是张量、不是文本,所以原始 prompt 不会以文本形式留存。

5. 搜索后"选择性抓取",不是每次都抓一堆

原理: 官方建议 search → selective retrieve:简单事实题 1--3 次搜索即可,用 max_uses 给搜索次数兜底,避免一上来就 fetch 一大批、把上下文撑爆。注意它和优化 #1 是一对------因为内联片段已经够用,大多数时候根本不需要"深抓"那一步,深抓是例外不是默认。

示例:给搜索次数设上限

json 复制代码
"tools": [{ "type": "web_search_20250305",
            "name": "web_search",
            "max_uses": 3 }]   // 延迟敏感场景:3 次封顶

Claude.ai 的搜索链复原

5 个优化拆完,现在把它们拼回去,看一次完整搜索的原貌------从你按下回车,到它给出带引用的答案,中间到底发生了什么:

一次 Claude.ai 搜索:检索循环整个关在"服务端·一次请求"的方框内(含 refine 回环);下方灰色支线是 Claude Code 的另一套内容管线

  1. 决策 :模型判断这题需要实时信息,自主生成一个 web_search 工具调用(带 query)。
  2. 缓存命中 :请求的 tools + system 前缀已缓存(优化 #4),这一步几乎不花 prefill 时间。
  3. 服务端搜索 :在 Anthropic 基础设施内打 Brave(优化 #2,无客户端往返);Brave 索引侧把每条结果的 description + extra_snippets(约 500 词相关片段)打包进 encrypted_content随结果内联返回(优化 #1)------搜索这一步就把"该读的段落"端上来了。
  4. 代码过滤(按需):结果多时,模型写一段代码把结果筛到几条,只把输出送进上下文(优化 #3,零额外模型往返)。
  5. 读 & 决定 :模型直接读内联正文;信息够就作答,不够才选择性 web_fetch 深读某个 URL(优化 #5,深抓是例外不是默认)。
  6. 多轮迭代(全在服务端) :3--5 步是个循环------不够就拿上一轮结果 refine、再搜,整个 7--11 次的检索都在这一个请求内、服务端跑完 ,中间不回到客户端;正常以 end_turn 收尾,只有这轮太长被切断才返回 pause_turn 让你原样回传接力。
  7. 带引用作答 :输出里每个来自网页的论断都挂 cited_text + url(不计入 token),可回溯。

同一条链,Claude Code 在第 3--5 步岔开:WebSearch 只取 title/url、把内联正文丢掉;要正文时显式 WebFetch(抓 HTML → Turndown → Haiku 提炼)。多一跳、更慢,换来更小的注入面和更可控的成本------这是 CLI 自主长跑的取舍。

**"又快又准"拆开就是:**快 = 不走 「搜索 → 抓取 → 再调模型抽取」这条慢路(内联 #1 + 代码过滤 #3 + 缓存 #4 让多轮变便宜);准 = 索引侧按查询选段 + 代码过滤 + 不限轮的选择性深抓(#1 #3 #5)。用 client tool 自建 harness,则要把上面这条服务端链路在客户端逐步重建------下面 Part 2 用延迟、token、逐题 pass/fail,逐个验证每一步到底值多少。

Part 2 · 实验报告:把检索管线重写到 Claude.ai 的形状

实验设置

**对象:**自建 harness(agent-research 的 runner,与生产 claude-ai-harness 同族)。

**控制变量:**模型固定(同一上游),只改检索/抓取/上下文管线。

**数据集:**GAIA + FRAMES 中 30 道"裸模型答不对"的题,全量跑。

判分: contains------模型最终 FINAL ANSWER 是否包含标准答案(大小写不敏感)。

**基线:**裸模型 0 / 30、原 harness(B)11 / 30(37%)、Claude.ai(C)24 / 30(80%)。

**成色声明:**上游(luckyapi)并非 vanilla Anthropic,temperature 0 也不完全复现,单跑是点估计、不是定论(这也是下面 frames_241 时好时坏的根因)。

过去的架构

原 harness 是最朴素的**「搜索 → 抓取 → 字符截断」三段式**,每一层都在丢信息:

  • **检索:**主力 Serper,只拿 snippet------没有正文,必须再单独抓页。
  • **抓取:**一次 web_search 自动抓 top-3 页,且裸 fetch 不渲染 JS,常被反爬挡。
  • **摄入:**抓到正文后按字符位置硬截断 slice(0, 6000) 进上下文------query 无关,答案若在第 6000 字之后会被静默切掉(一个隐藏 bug)。
  • **上下文:**仅在 >30k token 时用 Haiku 压缩历史(也 query 无关),每轮把历史重写一遍、打碎可缓存前缀。
  • **循环:**上限 8 轮。

三个致命点:搜不到正文、抓取脆、答案被位置截断;多跳题往往撑不到收敛。

目前的架构

照着 Part 1 拆出的 5 条优化,把每一层都换成**「检索式、零额外生成、可缓存」**的形状:

过去 现在
检索 Serper,只 snippet Brave 主力 + extra_snippets(内联 ~500 词);9 级兜底链
抓取 每搜抓 top-3 内联优先:snippet 够富不抓;否则只选择性抓 top-1
抽取 slice(6000) 位置截断 Exa /contents highlights(embedding 选段,逐字原文,无 LLM 往返)
上下文 每轮重写历史摘要 只追加 + 给 system/toolscache_control
循环 8 轮 16 轮 + 多跳规划提示 + 网络重试

同一条检索管线逐层重写:左边每层丢信息、撑不到多轮就超时;右边内联优先 + 缓存,16 轮也跑得动

改进了哪些

压成四条真正改变行为的动作(其余调参省略):

  1. **内联优先(最大提速):**让相关正文随搜索结果一起回来,能不抓页就不抓------把每搜默认的"3 抓 + 3 次模型抽取"降到常见 0 次。
  2. **抽取用检索、不用生成:**把"读到正确那一段"交给 Exa highlights(语义选段),不再每抓一页调一次 Haiku。
  3. **缓存稳定前缀 + 历史只追加:**system+tools 缓存住、消息只追加,去掉每轮重写历史的 hack------多轮迭代不再每轮全量重算。
  4. **修掉位置截断 bug:**query 感知抽取替掉 slice(6000),"答案在第 6000 字后被切掉"的静默失败消失。

支撑第 2 条的受控测量(同 URL、同 query,唯一变量是 Exa contents 参数): summary:{query} 在 Exa 侧跑生成式 LLM = 3284mshighlights:{query} 是 embedding 选段、返回逐字原文 = 613ms(快 5.4×) 。教训:抽取要的是"检索式逐字摘录",不是再叫一个模型生成摘要------这正是 Claude.ai 内联片段的本质。Exa Search/Contents API

实验表现

全部改动叠满(temperature 0、16 轮),对 30 题全量跑一遍,对照基线:

harness(模型固定不变) 30 题通过 相对位置
裸模型(无工具) 0 / 30 地板
原自建 harness(B) 11 / 30 · 37% 起点
本文重构后(v3) 20 / 30 · 67% +30 点
Claude.ai(C) 24 / 30 · 80% 天花板

37% → 67%,把对 Claude.ai 的差距从 43 点压到 13 点、吃掉约 ⅔

更关键的是用时------这是过去架构"准了却超时"的死穴。补上每组的时间 / 轮次 / 检索行为:

题数 均用时 中位用时 均轮数 均搜索 均抓取 纯 inline
PASS 20 54s 35s 4.4 2.2 2.4 89%
FAIL 10 93s 40s 8.0 3.4 3.9 65%

PASS 题几搜即中(中位 35s、2.2 搜),又准又省;全 30 题平均 67s/题。FAIL 题反而搜得更多、跑得更久仍失败(均 3.4 搜 / 8 轮)------卡点不在"搜不到"。

把范围收紧到第一次实验(原 B harness,8 轮上限)就答对的 8 道题(v3 这 8 道也全 PASS),做纯用时对照:

任务 第一次(B) 现在(v3) 用时变化
gaia_016 32s 7s 快 4.5×
gaia_150 37s 22s 更快
gaia_096 37s 35s 持平
gaia_052 56s 57s 持平
gaia_136 16s 25s 略慢
gaia_158 16s 33s 略慢
gaia_059 12s 31s 略慢
gaia_153 41s 79s 更慢
8 题汇总(都 PASS) 均 31s · 中位 34.5s 均 36s · 中位 32s 同一量级

3 道更快、5 道略慢,均值 31s→36s、中位 34.5s→32s------基本同一量级 (均值被 gaia_153 一题拉高)。结论:重构在硬题上多拿的正确率,没有拖慢原本就能解的题

分析改进来源

整体数会骗人------67% 也可能"碰巧"。把逐轮日志拆开,每条改动都在机制层面留下了可量化的证据。先看现在每一次搜索实际怎么走:

78% 的搜索靠内联片段直接答、零抓取;不够富才抓 top-1 + Exa 选段。兜底链只在 Brave 返空时接力

每条优化都在日志里留下指纹:

  • **内联优先 → 速度:**78 次搜索里 61 次(78%)零抓取(PASS 题 89%)------这是用时从"超时"回到"几十秒"的主因。
  • **兜底链 → 覆盖:**67 次纯 Brave、11 次落兜底、每次都恢复到 ≥3 结果(0 瘦果);1 次打满 4 层(Brave→SerpAPI→SearchApi→Exa),Exa 救回 9 条、直接换来 gaia_008 一道 PASS。
  • **highlights → 又快又准:**87 次抓取 100% 由 Exa /contents 命中、零 LLM 往返,逐字保真。(鲁棒抓取链 Jina/Firecrawl 一次没触发:休眠保险,这轮没拿到它的鲁棒性证据。)
  • **缓存 → 撑住多轮:**靠后轮非缓存 input 掉到个位数 token(gaia_001 跑 11 轮,后段每轮 ~7--44 token)------多轮在算力上不再贵,这是能"跑完"而非超时的底座。

**一句话归因:**速度来自不走「搜 → 抓 → 生成式抽取」这条慢路 (内联 + highlights + 缓存);覆盖来自 Brave 内联片段 + 兜底链补稀缺结果

剩下 10 道失败已基本不在搜索 / 抓取管线 ,拆成三类、只有一类是检索精度问题:① 判断墙 (gaia_110 / 014 / 128 跑满 15 轮,读到正文却选错实体);② 检索精度墙 (仅 gaia_029:niche 事实没被索引,13 次换词、10 次被迫转抓仍未锁定);③ 触发纪律(frames_543 / 647 搜 1 次就答、frames_562 干脆 0 搜------没搜够就开口)。

诚实的边界:frames_241 这类多跳题 PASS 是不稳定的 ~⅓ (专跑 4 遍 1/4,head-to-head 也 FAIL 过),根因是上游非 vanilla、temperature 0 都不复现。所以升级把瓶颈从"基建 / 轮数"挪到了"多跳推理选实体 + 上游确定性",不是"攻克"。

结论与可迁移原则

这轮实验把瓶颈从"基建"推进到了"判断" :搜得准、抓得到、轮次便宜------都用数据验证做到了;剩下两道差的是多跳推理里选对实体的能力,那是下一座山(更强的规划/自我验证),不再是搜索管线的事。五条可迁移原则:

  1. 内容内联优先:能让检索 API 直接返回查询相关内容(Exa highlights / Tavily),就别"搜→抓→再调模型抽"。
  2. 抽取用检索,不用生成:语义高亮(embedding 选段)比"叫模型写摘要"快一个量级(实测 613ms vs 3284ms),且逐字保真。
  3. 缓存稳定前缀、历史只追加:system+tools 缓存住,别每轮重写历史------这是"负担得起多轮"的前提(实测靠后轮 input token 降到个位数)。
  4. 位置截断是隐形 bug:按字符切正文会静默丢答案,要按查询相关性留。
  5. 护城河要认:服务端 co-location 和"边生成边执行"复刻不了,但它们不是速度大头------内联、缓存、代码过滤才是,这三样都能自己做。

Yao Yuheng / 姚钰珩

NTU Data Science 硕士。专注:AI Agent 系统工程、Eval 驱动开发、LLM 应用。

本文首发于 sg.yaoyuheng2001.me,转载请注明出处。

Blog · GitHub · 掘金 · Substack · RSS

相关链接:上一篇:用 30 道题量化我和 Claude.ai 的差距 · 白皮书:Agent Harness 工程化方法论 · GitHub 仓库

权威来源: Claude Web Search · Web Fetch · Parallel Tool Use · Prompt Caching · Advanced Tool Use · "Don't Break the Cache"(arXiv) · Claude Code Web Tools 逆向 · Exa API

相关推荐
柒和远方1 小时前
后端认证、鉴权、高并发:从 Session 到 JWT 再到 Redis
前端·后端·面试
Linlingu1 小时前
openClaw不能操作我的电脑提示没有权限如何解决?
人工智能·windows·办公自动化·数字员工·小龙虾
前端Hardy1 小时前
前端圈沸腾!这个动画库月下载超 3000 万次,已经快成行业标准了
前端
snpgroupcn1 小时前
SNP亮相2026思爱普中国峰会,助力企业加速数据价值兑现
人工智能
IT乐手1 小时前
Anthropic 为何限制中国大陆使用 Claude?
人工智能
To_OC1 小时前
用 ESM 模块化搭建 DeepSeek LLM 调用,顺带用 Prompt 实现轻量 NLP 任务
人工智能·nlp·deepseek
jrjrgood1 小时前
现货黄金和黄金期货的区别有哪些?如何投资?
大数据·人工智能·区块链
属于自己的天空1 小时前
确认弹窗太多?一次配好 Claude Code 权限,安心让 AI 干活
人工智能
文阿花1 小时前
Echarts实现自动旋转柱状3D扇形图
前端·3d·echarts