search-and-navigation.mdx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. ---
  2. title: "搜索与导航工具 - 代码库精准定位"
  3. description: "解析 Claude Code 的搜索导航工具:Glob 文件匹配、Grep 内容搜索,基于 ripgrep 的高性能代码检索,帮助 AI 在百万行代码中精准定位。"
  4. keywords: ["代码搜索", "Glob", "Grep", "ripgrep", "文件搜索"]
  5. ---
  6. ## 两种搜索维度
  7. | 维度 | 工具 | 底层实现 | 适用场景 |
  8. |------|------|----------|---------|
  9. | **按名称找文件** | Glob | ripgrep `--files` + glob 过滤 | "找到所有测试文件"、"找 config 开头的文件" |
  10. | **按内容找代码** | Grep | ripgrep 正则搜索 | "哪里定义了这个函数"、"谁在调用这个 API" |
  11. 两者共享同一个 ripgrep 引擎,通过不同的参数组合实现不同搜索模式。
  12. ## ripgrep 的内嵌方式
  13. Claude Code 不依赖系统安装的 ripgrep——它在 `src/utils/ripgrep.ts` 中实现了三级降级策略:
  14. ```
  15. 优先级 1: 系统 ripgrep (USE_BUILTIN_RIPGREP=false)
  16. → 使用 PATH 中的 rg 二进制
  17. → 安全考虑:只用命令名 'rg',不用完整路径,防止 PATH 劫持
  18. 优先级 2: 内嵌模式 (bundled/native build)
  19. → process.execPath 自身,argv0='rg'
  20. → Bun 将 rg 静态编译进二进制,通过 argv0 分发
  21. 优先级 3: vendor 目录 (npm build)
  22. → vendor/ripgrep/{arch}-{platform}/rg
  23. → macOS 需要 codesign 签名 + 移除 quarantine xattr
  24. ```
  25. 平台适配示例:
  26. ```
  27. vendor/ripgrep/
  28. ├── x86_64-darwin/rg # macOS Intel
  29. ├── arm64-darwin/rg # macOS Apple Silicon
  30. ├── x86_64-linux/rg # Linux Intel
  31. ├── arm64-linux/rg # Linux ARM
  32. └── x86_64-win32/rg.exe # Windows
  33. ```
  34. ### macOS 代码签名
  35. vendor 模式下的 rg 二进制需要 ad-hoc 签名才能通过 Gatekeeper(`codesignRipgrepIfNecessary()`):
  36. ```typescript
  37. // 首次使用时执行:
  38. // 1. 检查是否已是有效签名
  39. codesign -vv -d <rg-path>
  40. // 2. 如果只是 linker-signed,重新签名
  41. codesign --sign - --force --preserve-metadata=entitlements,requirements,flags,runtime <rg-path>
  42. // 3. 移除隔离属性
  43. xattr -d com.apple.quarantine <rg-path>
  44. ```
  45. ## 搜索结果的设计考量
  46. ### head_limit 与 Token 预算
  47. 大型项目的搜索结果可能有数十万条。默认最多返回 250 条匹配——这不是随意选择,而是**token 预算**的约束:
  48. - 每条匹配行约 50-100 token
  49. - 250 条 ≈ 12,500-25,000 token
  50. - 这大约占 200k 上下文窗口的 6-12%
  51. - 超过这个比例,AI 的推理质量会下降
  52. Grep 工具的 `head_limit` 参数让 AI 可以按需调整——搜索小项目时可以用更大的值。
  53. ### 按修改时间排序
  54. Glob 默认把**最近修改的文件排在前面**。这不是默认的文件系统排序,而是刻意的设计决策:
  55. ```
  56. 设计假设:最近修改的文件最可能与当前任务相关
  57. 实际效果:AI 优先看到"活"的代码,而不是沉寂的历史文件
  58. ```
  59. 在 `src/tools/GlobTool/` 中,ripgrep 的输出在返回给 AI 前按 mtime 排序。
  60. ### ripgrep 的错误处理
  61. ripgrep 执行有专门的错误恢复链(`src/utils/ripgrep.ts`):
  62. | 错误 | 处理 |
  63. |------|------|
  64. | **EAGAIN**(资源不足) | 自动以单线程模式 `-j 1` 重试 |
  65. | **超时**(默认 20s,WSL 60s) | 返回已有部分结果,丢弃可能不完整的最后一行 |
  66. | **缓冲区溢出** | 截断到 20MB,返回已收集的结果 |
  67. | **SIGTERM 失效** | 5 秒后升级为 SIGKILL |
  68. ## ToolSearch:在 50+ 工具中发现目标
  69. 当可用工具超过 50 个时(含 MCP 提供的外部工具),AI 可能不知道该用哪个。**ToolSearch**(`src/tools/ToolSearchTool/`)提供了工具发现机制。
  70. ### 搜索算法
  71. ToolSearch 实现了基于关键词的加权搜索(`searchToolsWithKeywords()`):
  72. ```
  73. 输入: query = "database connection"
  74. 1. 精确匹配: 检查是否有工具名完全匹配(快速路径)
  75. 2. MCP 前缀匹配: "mcp__postgres" → 匹配所有 postgres 相关工具
  76. 3. 关键词拆分: ["database", "connection"]
  77. 4. 工具名解析:
  78. - MCP 工具: "mcp__server__action" → ["server", "action"]
  79. - 普通工具: "FileEditTool" → ["file", "edit", "tool"]
  80. 5. 加权评分:
  81. - 工具名精确匹配: 10 分(MCP: 12 分)
  82. - 工具名部分匹配: 5 分(MCP: 6 分)
  83. - searchHint 匹配: 4 分
  84. - 描述匹配: 2 分
  85. 6. 必选词过滤: "+database" 前缀表示必须包含
  86. 7. 按分数排序,返回 top-N
  87. ```
  88. ### `select:` 直接选择
  89. AI 也可以用 `select:ToolName` 精确选择已知工具。这比搜索更快,且支持逗号分隔的批量选择(`select:A,B,C`)。
  90. ### 延迟加载(Deferred Tools)
  91. 不是所有工具都常驻内存。MCP 工具和低频工具被标记为 `isDeferredTool`,只有在 ToolSearch 选中后才真正加载。这减少了每次 API 调用的 token 开销(工具描述占用大量 token)。
  92. ### 缓存策略
  93. 工具描述的获取是 memoized 的——只在延迟工具集合变化时清除缓存:
  94. ```typescript
  95. // 工具名排序后拼接作为缓存 key
  96. function getDeferredToolsCacheKey(deferredTools: Tools): string {
  97. return deferredTools.map(t => t.name).sort().join(',')
  98. }
  99. ```
  100. ## Web 搜索与抓取
  101. AI 的信息获取不局限于本地代码:
  102. - **WebSearch**:搜索互联网获取最新信息
  103. - **WebFetch**:抓取特定网页内容,转换为 Markdown 供 AI 阅读
  104. 这让 AI 可以查阅文档、搜索 Stack Overflow、阅读 GitHub issue——和人类开发者的工作方式一致。
  105. ### ripgrep 的流式输出
  106. 对于交互式场景(如 QuickOpen),ripgrep 支持**流式输出**(`ripGrepStream()`):
  107. ```
  108. rg --files → 逐 chunk 到达 → 按行分割 → onLines(lines) 回调
  109. ```
  110. 不需要等 ripgrep 完成整个搜索——第一批结果在 rg 仍在遍历目录树时就已展示。调用者可以通过 AbortSignal 提前终止搜索(例如找到足够多的结果后)。