❝AI 编码工具不用向量搜索,靠 Grep 就能理解代码库?那我们的项目需要预先构建什么样的知识体系来帮助 AI?❞
你可能已经在用 Claude Code、Cursor、Copilot 这类 AI 编码工具。你抛出一个需求,AI 能从你的项目中找到相关代码、理解模块关系、生成符合项目风格的代码。但你有没有想过:「它到底是怎么"看懂"你几万行代码的?」
一个直觉的答案是:预先把代码分块、生成 embedding、存入向量数据库,用户提问时跑语义搜索找到相关代码片段——这就是经典的 RAG(Retrieval-Augmented Generation)方案。但 Claude Code 的创建者 Boris Cherny 在多个公开场合揭示了一个让人意外的事实:
❝"Early versions of Claude Code used RAG + a local vector db, but we found pretty quickly that agentic search generally works better."❞
❝"Plain glob and grep, driven by the model, beat everything."❞
「Claude Code 不用 RAG,不用 embedding,不建索引。核心搜索就靠 LLM 驱动的 Grep。」 2026 年 3 月泄露的 Claude Code 源码也证实了这一点——源码中没有任何 embedding、vector、similarity search 相关实现。更有意思的是,OpenAI 的 Codex CLI 独立做出了几乎相同的架构选择。两个互为竞争对手的产品殊途同归,这不太可能是巧合。
这引出了两个值得深入讨论的问题:
要理解 AI 编码工具的代码搜索,需要了解三个层面:搜索是怎么运转的、为什么暴力搜索够快、以及代码搜索为什么天然适合精确匹配。
Claude Code 的搜索机制可以用一句话概括:「LLM 自己决定搜什么、用什么工具搜、搜到后要不要继续搜,直到信息充分为止。」
整套机制的核心是一个工具调用循环:LLM 分析用户问题 → 选择搜索工具发起调用 → 拿到结果后决定下一步。具体可用的搜索工具包括:GrepTool(正则搜索文件内容)、GlobTool(按文件名模式查找)、FileReadTool(读取指定文件的指定行范围)、AgentTool(启动子 agent 做独立的多步探索)。此外还有 LSP 工具提供 "go to definition" 等精确的语义操作。
以一个真实搜索过程为例:当你问 Claude Code "bridge 系统是怎么追踪 GrepTool 调用的?"时,LLM 实际执行了四轮搜索——第 1 轮用 Grep 广撒网找到 4 个相关文件;第 2 轮切换到 content 模式看上下文代码片段;第 3 轮用 Read 读取完整文件找到了工具映射表、摘要生成函数和活动解析器;第 4 轮追踪引用链,一次 Grep 同时找到类型定义、轮询逻辑和 UI 渲染三个文件。最终拼出完整的数据流转链路。
这种"主动搜索"模式和传统 RAG 的"被动检索"有本质区别:传统 RAG 在问题出现之前就预先决定"你可能需要看什么",一次性塞一批代码块进 context;而 Agent 模式下,每一步搜什么都由上一步的发现决定——这条搜索路径是任何预检索方案都猜不出来的。
Claude Code 底层用的不是古老的 GNU grep,而是 ripgrep——用 Rust 重写的现代实现。它通过五层过滤(.gitignore 剪枝 → 路径限制 → 文件类型过滤 → 二进制检测 → 正则匹配)把搜索范围层层缩小,再配合 SIMD 向量化加速、Boyer-Moore 跳跃、操作系统 Page Cache、mmap 零拷贝、多线程并行等手段,在 4,500 文件(95 万行代码)的项目上搜索耗时仅 0.09~0.10 秒,比 GNU grep 快 25~33 倍。
更本质的原因在数据规模:开发者本地项目通常在 MB 到几百 MB 之间。一个 250MB 的代码库在 Page Cache 命中的情况下(日常开发项目几乎总是命中),按 30GB/s 的内存带宽算,数据搬运下界仅约 8 毫秒。「这个量级根本用不着离线索引。」
一项系统性研究(GrepRAG, ISSTA '26)揭示了一个关键事实:「代码搜索的关键词 95% 是标识符——类名(36%)、方法名(41%)、变量名(18%)」。标识符本身就是代码的语义,getUserById 就是 getUserById,不会被改述成 fetchPersonByIdentifier。在这个基准上,即使是单轮 Grep 检索,代码补全效果也超过了 embedding RAG 基线(Exact Match 38.61% vs 24.99%)。
这与自然语言搜索形成鲜明对比。自然语言中"词汇不匹配"是常态——同一个概念有十几种表达方式。但代码里的函数名、类名是程序员亲手埋的精确锚点,Grep 几乎总能命中。「Claude Code 选择放弃 embedding,不是因为向量检索本身不行,而是因为代码搜索恰好是 Grep 最适合的地方。」
OpenAI 的 Codex CLI 的代码搜索架构和 Claude Code 惊人地相似:「同样不建索引、不用 embedding、不用向量数据库。」 社区提交的向量索引功能请求被 OpenAI 团队关闭,明确表示 "not currently on our roadmap"。
但两者的实现路径有一个关键分歧——「搜索工具的封装方式不同」:
shell(执行任意 shell 命令)和 apply_patch(专用 diff 格式编辑文件),此外还有 update_plan、view_image、web_search、spawn_agent 等。「所有代码搜索操作通过 shell 工具完成」——LLM 直接组合 rg、find、cat、git 等 Unix 命令来搜索代码。Codex 的多个 system prompt 文件中都写了同一条指令:
❝"When searching for text or files, prefer using rg or rg --files respectively because rg is much faster than alternatives like grep."❞
搜索模式同样是多轮迭代:Grep → 读文件片段 → 调整关键词 → 再搜。
方面 | Claude Code | Codex CLI |
|---|---|---|
搜索工具 | 专用工具(GrepTool、Glob、Read),有结构化参数 | 通过 shell 执行 rg、find、cat,无专用搜索工具 |
索引 | 无 | 无 |
子 agent | 内置(Explore、Plan 等类型,context 隔离) | 内置(spawn_agent / send_message / wait_agent) |
编辑方式 | Edit(字符串替换) | apply_patch(diff 格式) |
两种路径的核心分歧在搜索工具的封装程度上。Claude Code 把 Grep 封装成带结构化参数的专用工具,LLM 不需要解析原始 shell 输出,减少出错概率,也让系统更容易控制信息量(比如 GrepTool 的 head_limit 默认 250 条,即使搜到 10,000 条匹配也只返回前 250 条)。Codex 让模型直接写 shell 命令调用 rg,给予最大灵活性(可以自由组合管道、正则、路径过滤),但需要模型自己处理非结构化的文本输出。
即使是把语义搜索当核心卖点的 Cursor(为此构建了 100 亿+ 向量、1,000 万+ 命名空间的向量基础设施),在其 2025 年泄露的 Agent system prompt 中,grep_search 被明确标注为"主要探索工具"(MAIN exploration tool),codebase_search(语义搜索)只在"概念性查询"时作为补充。「向量检索解决的是 Grep 覆盖不到的长尾,而不是反过来。」
当然,Grep 方案也有明确的成本代价:多轮搜索意味着更多的 token 消耗。Claude Code 通过 prompt cache(92% 的 prompt 前缀在相邻轮次间相同,成本降低约 81%)、auto-compaction(对话历史自动压缩)、子 agent context 隔离三层机制来控制,但本质 tradeoff 不变——「用更多的搜索轮次换取零索引、零维护、零启动延迟的工程简洁性」。
上面的分析揭示了一个核心事实:「AI 理解你项目的主要方式是搜索代码、读取文件。」 它不是预先"学习"了你的项目,而是在每次对话中实时搜索、逐步拼凑出对项目的理解。
这直接引出一个实践问题:「既然 AI 主要靠搜索来理解代码,我们应该做什么来帮助它搜得更准、理解得更快?」
社区目前有两种主流思路:
思路 | 做法 | 代表 |
|---|---|---|
「构建向量知识库」 | 预先分块 → embedding → 向量库 | Greptile、Continue Embeddings |
「构建结构化上下文」 | 项目规则文件 + 知识文档 + 规范的代码结构 | CLAUDE.md、.cursorrules、ADR |
前面的分析已经说明,对于大多数本地项目,向量知识库的 ROI 算不过来(建设成本高、维护成本高、代码搜索场景下收益有限)。「真正高杠杆的投入方向是第二种:构建"结构化上下文"。」
但"结构化上下文"具体该包含什么?这就涉及到一个很具体的问题:「有没有必要给项目预先加上架构设计文档、模块说明这类知识文档?」
答案是分层的:「有些知识文档必须有,有些锦上添花,有些则完全可以不做。」 关键判断标准是:这份文档提供的信息,AI 能不能通过搜索代码自己推断出来?
回顾第二章的分析,AI 的代码搜索能力有三个特点:
基于这个边界,我们可以把项目知识分成三类:
知识类型 | AI 能否自行推断 | 是否需要文档 | 举例 |
|---|---|---|---|
「代码结构和实现」 | 能通过搜索获取 | 不需要专门文档 | 函数签名、调用关系、数据流转 |
「约束和规范」 | 代码中隐含但不显式 | 「必须有文档」 | 分层约束、命名规范、错误处理规则 |
「决策和背景」 | 完全无法推断 | 「强烈建议有文档」 | 为什么选 MongoDB、为什么用 Temporal |
这是投入产出比最高的知识文档,也是唯一一种「不需要 AI 搜索就能直接获取的知识」——它被直接注入 system prompt。
一份好的项目规则文件应该包含 AI 无法从代码推断的约束信息:
├── 架构约束
│ ├── 分层规则(Service → Logic → Repo,禁止跨层调用)
│ ├── 依赖方向(Logic 之间禁止循环依赖)
│ └── 模块边界(各模块的职责范围)
│
├── 编码规范
│ ├── 命名规范(Manager 类用 XXXManager、CRUD 方法用 Create/Get/Update/Delete)
│ ├── 错误处理(用 fmt.Errorf 包装、error 必须是最后一个返回参数)
│ └── 注释要求(导出函数必须有注释)
│
├── 开发工作流
│ ├── 新增功能的标准流程(Entity → Repo → Logic → Service)
│ └── 修改现有代码的检查清单
│
└── API 约定
├── 路由格式(/api/v1/{module}/{action})
├── 分页规范(默认页码 1,默认每页 20)
└── 错误码体系
「为什么这些信息无法通过搜索代码获取?」 因为它们是"应该怎么做"的规范,而不是"代码里实际做了什么"的事实。代码可能存在不符合规范的地方,AI 如果只从代码推断,可能会学到错误的模式。
「Architecture Decision Records(ADR)是最容易被忽视但价值极高的知识文档。」 它回答的是"为什么"——这恰恰是 AI 无论怎么搜代码都推断不出来的信息。
举几个例子:
「ADR 不需要很长,一个决策记录 3~5 段话就够。」 关键是记录"考虑了什么"、"选择了什么"、"为什么"。放在项目的 docs/adr/ 目录中,AI 需要时可以搜索和读取。
这是最容易产生争议的一类。很多团队会问:「要不要给每个模块都写一份详细的说明文档,包括模块职责、核心流程、数据模型、接口说明?」
我的判断是:「大部分情况下不需要单独维护模块说明文档。」 理由有三:
「第一,AI 能通过搜索获取大部分信息。」 回顾 Grep 搜索的实际表现——AI 通过 4 轮搜索就拼出了 bridge 系统的完整工具追踪链路。模块的接口定义、函数签名、数据结构,AI 读代码就能理解。你写一份"Task 模块提供了 Create、Get、Update、Delete 四个接口"的文档,AI 搜一下 service/task.go 也能知道。
「第二,模块文档极容易过时。」 代码变了文档没更新是常态。过时的文档比没有文档更危险——AI 会参考错误的信息生成代码。而项目规则文件(架构约束、编码规范)相对稳定,一年可能只改几次。
「第三,好的代码结构本身就是最好的模块说明。」 清晰的目录结构让 AI 用 Glob 几秒定位到模块;规范的命名让 Grep 一步精确匹配;完善的 GoDoc/JSDoc 注释让 AI 读文件就能理解意图;接口驱动设计让 AI 看 interface 就知道模块契约。「代码规范是 Grep 方案的加速器——代码写得好,AI 搜得就准。」
「例外情况」:如果模块涉及复杂的状态机、非直观的流程编排、或跨多个服务的交互链路,用一份简短的流程说明或状态转换图来辅助是值得的。比如 Temporal Workflow 的节点执行流程、DAG 的调度逻辑,这些光看代码不容易一眼理清。
如果你的代码已经有完善的注释(GoDoc、JSDoc、Swagger 注解),「额外维护一份代码级的 API 文档没有意义」。AI 直接读注释比读外部文档更可靠,因为注释和代码在同一个文件里,不存在不一致的风险。
这也是为什么编码规范中要求"每个导出函数必须有注释"——这些注释不只是给人看的,也是给 AI 看的。
优先级 | 投入方向 | 成本 | 收益 | 维护频率 |
|---|---|---|---|---|
「P0」 | 项目规则文件(CLAUDE.md / .cursorrules) | 极低 | 极高 | 一年几次 |
「P1」 | 保持代码结构清晰(命名规范、分层清晰、注释完善) | 中 | 极高 | 持续 |
「P2」 | 架构决策文档(ADR) | 低 | 高 | 每次重大决策 |
「P3」 | 复杂流程的简要说明 | 低 | 中 | 流程变更时 |
「P4」 | LSP 集成(gopls、tsserver) | 低 | 中 | 一次性 |
「P5」 | 向量知识库 | 高 | 低~中 | 持续 |
「核心原则:优先投入那些 AI 无法通过搜索代码自行获取的知识,且不容易过时的信息。」
「中小项目(几百文件、几万行)」:
「大型项目(上千文件、十几万行)」:
「超大规模 / 多仓库(GB 级 monorepo)」:
如果你的项目目前什么知识文档都没有,「花两个小时写一份项目规则文件」是回报最高的第一步。它应该回答 AI 在帮你写代码时最常遇到的问题:
与其花两周搭一套向量知识库,不如花两小时写一份好的规则文件。前者是给 AI 建了一个复杂的检索系统,后者是直接把最重要的知识喂给了 AI。
回到开头的问题:AI 编码助手到底是怎么"看懂"你的项目的?
答案已经清楚了:「它主要靠搜索。」 LLM 驱动 Grep 在代码搜索场景下足够好用,因为代码里的标识符天然是精确搜索的锚点。Claude Code 和 Codex CLI 殊途同归地选择了零索引方案(2.4 节),Cursor 也把 Grep 放在语义搜索之前(2.5 节)。这不是巧合,而是代码搜索这个特定场景的数据特性和规模决定的。
基于这个事实,项目该做的不是去建一套复杂的向量知识库,而是:
「有时候,最好的基础设施不是你额外建设的系统,而是你精心组织的代码本身。」 好的代码结构让 Grep 一步命中,好的注释让 Read 一次看懂,好的规则文件让 AI 不用搜就已经知道。这三者的组合,在大多数项目上,比任何向量知识库都管用。
❝觉得有用的话,「点赞👍 + 分享👀 + 收藏⭐」 一键三连,下次找起来方便,也能让更多人看到。❞