Skip to main content

两种搜索维度

两者共享同一个 ripgrep 引擎,通过不同的参数组合实现不同搜索模式。

ripgrep 的内嵌方式

Claude Code 不依赖系统安装的 ripgrep——它在 src/utils/ripgrep.ts 中实现了三级降级策略:
平台适配示例:

macOS 代码签名

vendor 模式下的 rg 二进制需要 ad-hoc 签名才能通过 Gatekeeper(codesignRipgrepIfNecessary()):

搜索结果的设计考量

head_limit 与 Token 预算

大型项目的搜索结果可能有数十万条。默认最多返回 250 条匹配——这不是随意选择,而是token 预算的约束:
  • 每条匹配行约 50-100 token
  • 250 条 ≈ 12,500-25,000 token
  • 这大约占 200k 上下文窗口的 6-12%
  • 超过这个比例,AI 的推理质量会下降
Grep 工具的 head_limit 参数让 AI 可以按需调整——搜索小项目时可以用更大的值。

按修改时间排序

Glob 默认把最近修改的文件排在前面。这不是默认的文件系统排序,而是刻意的设计决策:
packages/builtin-tools/src/tools/GlobTool/ 中,ripgrep 的输出在返回给 AI 前按 mtime 排序。

ripgrep 的错误处理

ripgrep 执行有专门的错误恢复链(src/utils/ripgrep.ts):

ToolSearch:在 50+ 工具中发现目标

当可用工具超过 50 个时(含 MCP 提供的外部工具),AI 可能不知道该用哪个。ToolSearchpackages/builtin-tools/src/tools/ToolSearchTool/)提供了工具发现机制。

搜索算法

ToolSearch 实现了基于关键词的加权搜索(searchToolsWithKeywords()):

select: 直接选择

AI 也可以用 select:ToolName 精确选择已知工具。这比搜索更快,且支持逗号分隔的批量选择(select:A,B,C)。

延迟加载(Deferred Tools)

不是所有工具都常驻内存。MCP 工具和低频工具被标记为 isDeferredTool,只有在 ToolSearch 选中后才真正加载。这减少了每次 API 调用的 token 开销(工具描述占用大量 token)。

缓存策略

工具描述的获取是 memoized 的——只在延迟工具集合变化时清除缓存:

Web 搜索与抓取

AI 的信息获取不局限于本地代码:
  • WebSearchpackages/builtin-tools/src/tools/WebSearchTool/):调用 Anthropic API 的 web_search_20250305 server tool 搜索互联网
  • WebFetchpackages/builtin-tools/src/tools/WebFetchTool/):抓取特定 URL 内容,转换为 Markdown 供 AI 阅读
这让 AI 可以查阅文档、搜索 Stack Overflow、阅读 GitHub issue——和人类开发者的工作方式一致。

WebSearch 实现机制

WebSearch 通过适配器模式支持三种搜索后端,由 packages/builtin-tools/src/tools/WebSearchTool/adapters/ 中的工厂函数 createAdapter() 选择:

适配器选择逻辑

adapters/index.ts 中的工厂函数按以下优先级选择后端: 适配器是无状态的,同一会话内缓存复用。

ApiSearchAdapter — API 服务端搜索

将搜索请求委托给 Anthropic API 的 web_search_20250305 server tool:

BingSearchAdapter — Bing 搜索页面解析

直接抓取 Bing 搜索 HTML 并用正则提取结果,无需 API 密钥:
反爬策略:Bing 对非浏览器 UA 返回需要 JS 渲染的空页面。适配器使用完整的 Edge 浏览器请求头(包含 Sec-Ch-UaSec-Fetch-* 等现代浏览器标头)确保获得完整 HTML。同时使用 setmkt=en-US 参数统一市场定位,避免 Bing 基于用户 IP 做区域化定向(如跳转到德语/新加坡市场导致结果不相关)。 URL 解码:Bing 搜索结果中的 URL 为重定向格式(bing.com/ck/a?...&u=a1aHR0cHM6Ly9...),resolveBingUrl()u 参数中 base64 解码出真实目标 URL(a1 前缀 = https,a0 = http)。 摘要提取extractSnippet())按优先级尝试三个来源:
  1. <p class="b_lineclamp..."> — 带行截断的摘要段落
  2. <div class="b_caption"> 内的 <p> — 普通摘要段落
  3. <div class="b_caption"> 的直接文本内容 — 兜底方案

WebSearchTool 统一接口

WebSearchToolpackages/builtin-tools/src/tools/WebSearchTool/WebSearchTool.ts)是面向主循环的工具定义,所有 provider 均可使用(isEnabled() 始终返回 true)。它将适配器返回的 SearchResult[] 转换为内部 Output 格式,mapToolResultToToolResultBlockParam 将搜索结果格式化为带 markdown 超链接的文本,并附加 “REMINDER” 要求主模型在回复中包含 Sources。

WebFetch 实现机制

WebFetch 是一个完整的 HTTP 客户端 + 内容处理管线:
安全防护多层设计: 预批准域名(packages/builtin-tools/src/tools/WebFetchTool/preapproved.ts): 用户无需手动授权即可抓取的域名列表,包含 ~90 个主流技术文档站点(MDN、Python docs、React docs、AWS docs 等)。列表分为 hostname-only 和 path-prefix 两类,查找复杂度 O(1)。 对预批准域名,WebFetch 跳过 Haiku 摘要步骤(如果内容是 Markdown 且 < 100K 字符),直接返回原文——因为技术文档本身的结构化程度已经足够好。 权限模型方面,WebFetch 按 hostname 生成 domain:xxx 规则匹配用户的 allow/deny/ask 规则,支持用户对特定域名配置永久允许或拒绝。

ripgrep 的流式输出

对于交互式场景(如 QuickOpen),ripgrep 支持流式输出ripGrepStream()):
不需要等 ripgrep 完成整个搜索——第一批结果在 rg 仍在遍历目录树时就已展示。调用者可以通过 AbortSignal 提前终止搜索(例如找到足够多的结果后)。