为什么 Claude Code 不用 RAG 检索代码,而是用 grep?
- 核心问题
- RAG 在代码场景的五大痛点
- Claude Code 的反向思路:把检索还给模型自己
- 检索三件套详解
- 当三件套不够用:派子 agent 去探索
- LLM-driven 的多轮迭代循环
- 回到原题:为什么 Claude Code 不用 RAG?
- RAG 还有用吗?
- 开放问题
- 总结
原文:字节面试官:”为什么Claude Code不用RAG检索代码,而是用grep?”我:”因为…省钱?” 他沉默了三秒
核心问题
字节面试官问:“为什么 Claude Code 不用 RAG 检索代码,而是用 grep?”
RAG 在代码场景的五大痛点
痛点 1:代码不像散文,切不动
文章是流式的,拦腰切一刀损失不大。但代码有严格结构:
- 一个函数 200 行,按 100 行切段,上半段是
if开头,下半段是else结尾 - 函数 A 调用函数 B,但 A 和 B 在不同片段里,模型只看到 A 不知道 B 是干嘛的
痛点 2:精确匹配的活,向量干不了
向量召回的本质是「找相似的」,不是「找对的」。
你跟 Claude Code 说:「帮我看下 getUserById 这个函数的实现」。
向量召回会找一堆「跟用户相关的查询函数」:getUserByName、getUserByEmail、fetchUserInfo、queryUser… 但你要的就 getUserById 这一个。
向量擅长「模糊」,但代码很多时候要的就是「精确」。
痛点 3:代码每天在变,索引咋办
建好索引后,开发同学一个 commit 改了 20 个文件:
- 要重建?整个项目重新切片、重新 embedding,成本爆炸
- 不重建?模型查到的全是旧版本,用过期信息写 bug
- 做增量更新?一堆边界情况:哪些 chunk 要删、哪些要重新算、跨文件引用怎么同步
痛点 4:冷启动慢得要命
百万行代码库跑 RAG,光建索引就要十几分钟。用户打开工具看着进度条转几分钟?体验直接劝退。
痛点 5:黑盒,不可解释
向量召回回来 5 个片段,你问为什么是这 5 个?没人答得上。
出了 bug 不知道从哪儿查起:是召回错了?还是召回对了但模型理解错了?追溯链路非常痛苦。
Claude Code 的反向思路:把检索还给模型自己
程序员的检索工作流:
- 看目录结构:
ls一下心里有数 - 全局搜关键字:
grep -r - 看具体文件:
cat或编辑器打开
Claude Code 的设计哲学:让模型像程序员一样,自己去找。
工具就给三个:
| 工具 | 作用 | 对应程序员的操作 |
|---|---|---|
| Glob | 按文件名 pattern 找文件 | find |
| Grep | 按内容关键字找代码 | grep -r |
| Read | 按需读文件内容 | cat / 编辑器打开 |
检索三件套详解
Grep:基于 ripgrep,绝不是简单封装
三层考虑:
- 权限统一管控 - Bash 太万能,grep 单独包成工具是一道权限闸门
- 输出格式可控 - 结构化输出:行号、上下文行、按文件分组,三种粒度
- 性能 - ripgrep 是 Rust 写的,多线程并行,自动尊重 .gitignore
Glob:按文件名找,按修改时间排序
两个小巧思:
- 结果按修改时间倒序排列(最近改过的最相关)
- 结果有 100 文件硬上限(避免输出爆炸)
Read:按需读取,绝不贪心
- 默认只读 2000 行,超出截断
- 可指定
offset和limit分段读取 - 每次直接 stat 磁盘文件,不缓存、不索引、不预处理
这意味着:只要你刚改了文件,下一次 Read 立刻能看到新内容。没有索引层,就没有索引滞后。
三件套的组合用法
场景:「这个项目登录功能在哪实现的?」
- Glob 找候选文件:
**/*login*.{ts,tsx,js}→ 拉回 5 个候选 - Grep 在这些文件里搜关键字:
passport|auth|login→ 定位到具体命中行 - Read 读命中文件的相关行段 → 看具体实现
关键:每一步都基于上一步结果调整方向,没有「一次性召回所有代码」。
当三件套不够用:派子 agent 去探索
问题:「调研一下整个项目的认证模块流程」这种任务,需要十几个工具调用,主 agent 上下文会被中间结果塞满。
解决方案:派一个子 agent 出去探索。
机制:
- 主 agent 通过 Agent 工具派子 agent,子 agent 有独立的上下文
- 子 agent 拿只读工具池:Grep、Glob、Read、Bash(只读命令)
- 子 agent 在自己的上下文里多轮迭代,grep 几十次、read 几十次
- 子 agent 完成后,只把最终结论返回给主 agent
临界点:少于 3 次查询直接用 Grep/Glob/Read,多于 3 次就派 Explore 子 agent。
加分项:子 agent 可以并行派多个,同时调研多个模块。
检索系统层次
- 底层:Grep / Glob / Read 三件套,处理简单定向检索
- 中层:派 Explore 子 agent,处理开放式探索和上下文隔离
- 上层:主 agent 编排整体任务
LLM-driven 的多轮迭代循环
RAG 的范式:「考试发卷子」→ 用户提问 → 一次性召回 Top-K → 一次性生成答案。没有循环,没有反悔。
Claude Code 的范式:「现场探案」→ 用户提问 → Grep → 看结果 → 「嗯 Read 一下」→ 看内容 → 「找到了」。
每一步:模型说话 → 可能带 tool_use → 执行工具 → 把结果回填到对话历史 → 模型继续说话。直到模型自己说「我搞定了」。
关键能力:每个回合都能根据上一步结果调整方向:
- Grep 结果是空的?改关键字再搜
- Read 出来的代码不像你想的?再 Grep 相关函数
- 文件引用了另一个文件?跟过去看下那个文件
RAG 召回错了就是错了,模型只能将错就错。Agent 召回错了,下一轮自己就调整了。
回到原题:为什么 Claude Code 不用 RAG?
六个原因:
| # | 原因 | 说明 |
|---|---|---|
| 1 | 冷启动 | grep 毫秒级响应,RAG 分钟级冷启动 |
| 2 | 实时性 | grep 每次现读磁盘最新版本,RAG 索引会滞后 |
| 3 | 精确性 | grep 是确定性字符匹配,RAG 是向量近似匹配 |
| 4 | Token 经济 | grep+Read 按需读取,RAG 要给整个代码库做 embedding |
| 5 | 可解释性 | grep 每步透明可审计,RAG 的 Top-K 召回是黑盒 |
| 6 | 决策权 | grep 让 LLM 自己决定每轮搜什么,RAG 一次性丢材料 |
两种设计哲学:
- RAG 派:LLM 不够强,用工程手段帮它把材料准备好(chunking、embedding、向量召回,本质都是「替模型做决定」)
- Claude Code 派:LLM 已经足够强,工程的角色是给它准备好工具,把决策权还给它
Anthropic 押注的是「模型会越来越强」,这是个长期主义的选择。
RAG 还有用吗?
RAG 仍然有适合的场景:
- 巨型代码库 + 跨仓库检索 - 千万行级别,grep 性能扛不住
- 纯语义查询 - 「找一下处理用户认证相关的代码」这种模糊问题
- 多人协作知识库 - 代码 + 文档 + Wiki 混合检索
Claude Code 的 grep 方案 适合:
- ✅ 单项目
- ✅ 探索式开发
- ✅ 需要精确性
- ✅ 要求实时性
工具是为场景服务的,没有银弹。
开放问题
如果未来 LLM 的上下文窗口能到 1 亿 token,整个代码库都能塞进去,RAG 还有意义吗?grep 还有意义吗?
总结
Agent 不是带工具的聊天机器人,而是会自己做决策的执行体。工程师的职责是给它一套好用的工具,而不是替它做决策。