Claude Code 为什么不先给代码库做 RAG?

它用 Grep / Glob / Read 在代码库里反复试探,而不是先建一套向量索引。这不是偷懒,而是对「代码」这个特殊语料的工程权衡。

00先说清楚:Claude Code 实际在怎么做检索

很多人默认「AI 读代码库 = 把代码向量化 → 存向量库 → 查询时召回」。但 Claude Code 走的完全是另一条路:

Claude Code 的 Agentic 检索循环
用户提问 "登录校验在哪" Agent 推理 决定下一步搜什么 Grep 精确字符匹配 Glob 按路径模式找文件 Read 读真实文件内容 拼出完整上下文 多轮收敛后作答 不满意 → 换个词再搜,反复多轮
没有向量库,没有预建索引。Grep 读的是磁盘上此刻的真实内容。

关键观察:Claude Code 的工具箱里压根没有「向量检索」这一项。它的检索原语是 Grep(正则匹配)、Glob(文件名模式)、Read(读文件)——全是精确、确定性、零前置成本的操作。

而外面套的是一层会推理、会迭代的 Agent:搜一个词 → 看结果 → 判断要不要换个词 → 顺着函数名找调用方 → 反复几轮直到拼出全貌。

01核心矛盾:RAG 的交易,在代码场景未必划算

RAG 的本质是一笔交易:用「预建索引的成本」换「查询时的低延迟与语义召回」。这笔交易在「文档 / 知识库」场景非常划算,因为语料稳定、查询需要语义理解。

但代码库有几个特性,让这笔交易变得可疑:

两种检索范式的对比
RAG 路径 建索引 切分+向量化(分钟级) 存向量库 需随代码持续更新 查询召回 语义相似度 top-k 结果可能过期 代码已改,索引未更新 Claude Code 路径 零准备 clone 完即可开工 Grep 精确搜 读磁盘当前真实内容 Agent 迭代 多轮试探拼出全貌 天然实时 不存在索引过期 代价:建索引时间 + 持续维护 + 过期风险 收益:查询快、支持语义模糊匹配 代价:每次查询要多轮推理(秒级) 收益:零维护、永远实时、结果可复现
左边把「试探」挪到建索引时;右边把「试探」留在查询时。选哪边,取决于场景约束。

02六个具体理由:为什么这个交易在代码上不划算

① 代码一直在变,索引一直在过期

文档型语料几周一更新,代码库一天几十次 commit。索引一旦落后,Agent 会基于已经被删除或改写的函数去思考——这比"没搜到"更危险。

而 Grep 读的是磁盘当前状态,天然实时,零维护成本。

② 代码是精确寻址的,不需要语义模糊

自然语言问"怎么处理登录失败"才有语义需求;代码问 verify_token 定义在哪,是精确符号查找。

代码标识符唯一、可枚举,Grep 命中率接近 100%;向量召回反而引入噪声。

③ 冷启动成本:用户不肯等

大仓库建索引动辄几分钟到几十分钟。用户打开工具就想立刻干活,不可能先等一轮索引构建。

Grep 是零前置成本的——clone 完就能搜。

④ 可复现、可验证、可调试

grep "verify_token" 的结果完全可预测,用户自己敲一遍就能验证。

向量 top-k 是概率性的黑盒,"为什么搜出这个"难以解释。在要改代码、要对正确性负责的场景,可预测性比模糊智能更值钱。

⑤ 隐私与外部依赖的硬约束

向量化通常要调 embedding API,等于把代码发到外部服务。企业代码库对此极其敏感,这常常是硬性红线。

本地 Grep:零外部依赖、零数据外泄、零 API 成本。

⑥ 切分(chunk)会劈开代码结构

文档按字数切一段,语义还算完整。代码按行数切,很容易把函数从中间劈成两半,召回的片段语法不完整、无法直接引用。

代码有天然切分单位(文件、函数、类)——但按 AST 切已经是"结构化索引",不再是向量 RAG 了。

最致命的是 ① 和 ② 的组合:代码既要实时(① 过期代价高),又不需要语义模糊(② 精确匹配就够)。RAG 最核心的两个卖点——"语义召回"和"快速响应"——在代码场景里,一个用不太上,一个要拿新鲜度去换。

03把「索引过期」这件事画出来

这是最核心的一条理由。RAG 默认语料是静态快照,而代码是持续流动的:

索引新鲜度随时间衰减
时间 → 索引准确率 T0 建索引 与代码完全一致 T1 若干次 commit 后 部分结果已失效 检索开始"指错路" T2 大量重构后 索引严重失真 Grep / Read:始终 100% 新鲜(读当前磁盘)
RAG 索引是一条持续下滑的曲线,Grep 是一条水平线。要维持曲线不下滑,就得付出持续的重建成本。

为什么"过期"在代码场景特别致命?因为 Agent 拿到检索结果后是要据此改代码的。召回一个已被重命名的函数、一份已废弃的配置,AI 会基于错误前提写出错误补丁——而它自己还不知道。宁可"没搜到,我再换词",也不要"搜到一个错的"。

04那 Claude Code 用什么替代了「索引」?

它并不是"完全没有索引"。它把索引这件事换了一种形态——从「向量的、黑盒的、机器生成的」,变成了「文本的、可读的、人机共同维护的」:

CLAUDE.md + Memory:可读可编辑的「活索引」
代码库 持续变动的事实源 ① 实时检索(Grep/Read) 保证「现在是什么样」永远正确 零维护 · 永远新鲜 ② CLAUDE.md(人工/生成) 架构、命令、约定、关键文件位置 可读 · 可编辑 · 随代码进 git ③ Auto Memory(自动沉淀) 跨会话记住踩过的坑与偏好 越用越准 · 可人工修正 Agent 上下文窗口 带全局观再动手 精准改动 不靠猜
三条通道各司其职:① 保证"事实正确";② 提供"人类视角的全局观";③ 积累"历史经验"。

这个替代方案的巧妙之处

一句话概括这个思路:把"机器自动生成的隐式索引",换成"人机共同维护的显式索引"。前者省人力但会过期且不可审计;后者要人(或 AI)花一点时间写,但永远准确、可读、可控。

05横向对比:三种方案各自的适用面

维度 向量 RAG Claude Code(Grep + Agent) CLAUDE.md / Memory
前置成本 高(需建索引) 零 低(写文档)
新鲜度 会过期,需持续重建 永远实时 半衰期长(记约定不记实现)
精确匹配 弱(模糊召回有噪声) 强(正则精确命中) 取决于写得多清楚
语义模糊匹配 强(核心卖点) 弱(但 Agent 会换词补位) 中(靠人写清楚)
查询延迟 毫秒级 秒级(多轮推理) 直接进上下文
可审计 / 可复现 黑盒,难解释 完全可复现 白盒,可人工修订
隐私 / 外部依赖 通常需 embedding 服务 纯本地 纯本地文本
超大仓(几十万文件) 适合 需更多轮试探 写不完

06那什么时候还是得上 RAG?

前面的论证有明确前提:单人交互、能接受秒级响应、仓库规模可控。前提一变,结论就变。

决策路径
要给代码库做检索 先问三个问题 有大量并发用户? 要求毫秒级响应? 仓库几十万文件? 现场多轮试探扛不住? 没有强推理 Agent 兜底? 只是个轻量搜索 API? 是 → 上 RAG 预建索引换查询延迟 是 → 上 RAG / 调用图 把结构关系提前算好 否 → Grep + Agent 够用 零维护且永远实时
Claude Code 落在最右侧分支:单人、能等、有强推理兜底 → 不上 RAG 是理性选择。

还要补一句:这两条路不是二选一。现在不少 Agent 系统就是混合打法——grep 做精确匹配、向量检索做语义兜底、调用图查询做结构还原,各取所长。Claude Code 当前选择纯精确检索,是因为它的场景(单人 + 有强推理 + 要实时)让这笔交易不划算;场景一变,加一层向量召回完全合理。

07一句话总结

为什么 Claude Code 不先给代码库做 RAG?

因为 RAG 的核心交易是「预建索引 → 换查询时的语义召回与低延迟」,而代码这个语料既持续变动(索引会过期,且过期会导致 AI 基于错误信息改代码),又高度精确可寻址(根本不需要语义模糊匹配)——RAG 的两个卖点在这里一个打折、一个用不上。

与此同时,Grep / Read 是零前置成本、永远实时、完全可复现、纯本地无依赖的;它唯一的短板"语义召回弱",由外层那个会换词、会顺藤摸瓜的强推理 Agent 补位。

而"索引"这个角色,Claude Code 交给了 CLAUDE.md + Auto Memory——一份可读、可编辑、可进 git、可人工修正的活索引,而不是一堆黑盒向量。

这不是"RAG 不好",而是"在这个场景约束下,精确检索 + 强推理 + 显式记忆,是更划算的组合"。

08相关阅读