跳至内容
Context Engineering:让 AI Coding Agent 真正读懂大型代码库

Context Engineering:让 AI Coding Agent 真正读懂大型代码库

明明找对了文件,为什么还是改错了?

假设你给 AI Coding Agent 一个需求:

允许商家配置用户可以在下单后多少分钟内取消订单;没有配置时,仍然使用 15 分钟。

Agent 搜索 CANCEL_WINDOW_MINUTES,很快在订单服务里找到常量,把它改成配置项,补了一条单元测试,然后告诉你:“任务已完成。”

代码看上去挺干净。真正跑起来,问题却一个接一个:

  • Web 端仍然在第 15 分钟隐藏取消按钮;
  • 对外 API 仍然把截止时间写死;
  • 后台 Worker 里也有一个 15,但它控制的是另一种超时,根本不应该改;
  • 项目架构规定配置必须通过 PolicyService 读取;
  • 单元测试绿了,用户在第 20 分钟仍然无法取消。

Agent 找到了一个正确文件,却没有找到这项行为的完整路径。

很多人遇到这种情况,会立刻想到三个办法:

  1. 再多选一些文件;
  2. 换一个上下文窗口更大的模型;
  3. 给代码库加向量索引、RAG 或 RepoWiki。

这些办法都可能有用,但没有一个会自动解决问题。

AI Coding Agent 正在研究一张大型代码库地图

想把这些工具用好,我们得先弄明白一件事:Agent 所谓的“理解代码库”,并不是把整个项目背下来。它是在不断重复一个过程:找线索、读少量材料、做一次判断、执行,然后根据结果继续找。

上下文工程,就是认真设计“它下一步应该看见什么”。

一、Agent 不会从第一个文件读到最后一个文件

一个开发者刚加入项目,通常不会按字母顺序读完所有源码。他会先理解任务,再问几个问题,顺着调用链找下去,最后运行代码验证。

AI Coding Agent 的工作方式很像,只是速度更快、动作更机械:

理解需求
  → 查看项目规则和目录结构
  → 搜索可能相关的代码
  → 打开少量文件
  → 跟踪调用方和消费者
  → 修改代码
  → 运行测试、检查 Diff
  → 证据对不上时继续搜索

在这个过程中,模型不需要把整个仓库一直放在上下文里。它真正需要的是:足够做出下一步判断的证据。

以取消窗口为例,只搜索“哪些文件出现了 cancel”还不够。真正有用的问题是:

  • 谁负责判断订单还能不能取消?
  • 默认 15 分钟从哪里来?
  • 哪些模块会消费这个截止时间?
  • 哪个看起来相似的超时,其实属于另一项业务?
  • 哪条测试能证明用户在第 20 分钟仍可取消?

搜索工具当然重要,但会不会提出这些问题,同样重要。

二、Agent 寻找代码的五种办法

把大型代码库想象成一座图书馆。Agent 大致有五种找书方式。

AI Coding Agent 查找代码的五种方式

1. 直接逛书架:目录和精确搜索

最简单的工具,往往是最好的第一步:

rg -n "CANCEL_WINDOW_MINUTES|cancelUntil|canCancel" .

它像是拿着准确书名去查目录:速度快、结果容易核实,而且查到的是当前分支上真实存在的文件。

精确搜索特别适合:

  • 函数名和类名;
  • 配置 Key;
  • API 字段;
  • 错误信息;
  • 数据库字段;
  • 被复制的常量。

它的弱点是“只认字面”。如果需求写的是“取消窗口”,代码却叫 revocationDeadline,精确搜索就可能错过。

2. 描述意思找书:语义搜索和 Embedding

语义搜索不要求用词完全一样。你可以问:“系统在哪里判断订单是否仍然允许取消?”它可能找到包含 deadline、eligibility 或 policy 的代码。

背后常见的技术是 Embedding:系统把代码和问题都转换成一组数字,数字距离越近,就认为语义越相似。向量数据库负责快速找出距离较近的代码块。

不懂数学也不妨碍使用。记住一句话就够了:

向量检索擅长找到“可能在附近”的代码,不擅长证明“谁真正负责这项行为”。

它可能同时找回退款超时、订阅宽限期和 Worker 重试窗口。Agent 仍然要打开源码,确认它们承担的是不是同一项职责。

3. 翻通讯录:符号、定义和引用

找到 canCancel 以后,下一个问题通常不再是“还有什么代码和它长得像”,而是“谁调用了它?它实现了哪个接口?”

这正是 LSP、SCIP 等符号索引擅长的事情。Sourcegraph 的精准代码导航就公开说明了基于 SCIP 的定义与引用关系。

符号导航比文本搜索更能看清显式调用关系,但它也不是全能的。框架自动装配、反射、生成代码、事件消费者和 Shell Script,都可能不在这本“通讯录”里。

4. 看城市地图:Repo Map 和代码图谱

Repo Map 会把重要文件、符号和关系压缩成一张小地图,让模型先知道大致往哪里走,再打开具体文件。Aider 的 Repository Map会对重要符号进行排序,并在有限 Token 内把高价值内容放进地图。

代码图谱则会保存 Import、Call、继承、模块依赖等关系。

它们适合回答:

“哪条路线值得继续追?”

却不能单独回答:

“这条路线是不是正确的架构?”

图谱能看见 Web 调用了 API,却不能判断 Web 是否应该自己计算业务规则。

5. 找导游:文档、Memory 和 RepoWiki

架构文档、ADR、AGENTS.md、生成式 Wiki 和 Agent Memory,会用自然语言解释项目。

它们特别适合快速了解全貌。RepoWiki 可能直接告诉新人:“订单领域负责取消资格,策略值统一从 Policy Service 获取。”这句话能省下很多次搜索。

但导游手册也会过期。Qoder 的 Repo Wiki 介绍说明了如何分析仓库、生成结构化知识,并在代码变化时更新内容。即使有更新机制,Wiki 里的句子仍然是从源码派生出来的。真正修改前,必须在当前分支重新核实它点名的文件、符号和调用路径。

三、Codebase Index 到底是什么?

“代码库索引”听起来像一项庞大的单体技术。现实中,它通常只是对一组可搜索目录的统称:

字面目录      → 标识符、路径、字符串
语义目录      → 代码与文档的 Embedding
符号目录      → 定义、引用、签名
关系目录      → Import、Call、模块连接
附加信息      → 语言、Package、分支、Revision

有些产品会组合其中几种;有些 Agent 更依赖实时搜索和语言工具。界面上出现“Search codebase”,并不能证明它底层一定使用了某种向量数据库。

如果要构建语义索引,大致会经过下面的过程:

仓库文件
  → 排除密钥、生成文件和 Ignore 路径
  → 按函数、类或有意义的段落切成 Chunk
  → 补充路径、符号、语言和 Revision
  → 计算 Embedding
  → 保存起来供后续检索

当你提出问题时,系统会找出几段可能相关的代码,也可能把它们和精确搜索结果合并,再进行排序,最后只把选中的部分交给模型。

这里有个很容易忽略的重点:**索引通常在模型当前上下文之外。**它负责挑选什么内容进入上下文,并不会把整个向量库一次性塞给模型。

可以把向量库理解为仓库目录。真正搬到 Agent 工作台上的,仍然只是少数几个箱子。

Cursor 的安全文档提供了一个具体例子:它公开描述的索引边界涉及代码 Chunk、Embedding 和经过混淆的文件路径元数据。其他工具可能选择不同的本地或托管方式。敏感仓库启用索引前,一定要看当前产品的真实文档,而不是只听“本地知识库”这个名字。

四、主流 AI Coding Agent 只是组合方式不同

行业里并不存在唯一的“标准 Agent 架构”。从用户能够观察的行为来看,大致可以分成几类。

Codex、Claude Code 这类终端型 Agent,可以实时查看目录、执行命令行搜索、读取文件、检查 Git 和运行测试。即使没有先给项目建立专用向量库,它们也可以一边工作一边探索。AGENTS.md、CLAUDE.md 等文件则负责提供长期项目规则。

Cursor 这类编辑器型产品,除了代码库检索和项目 Rules,还能利用当前打开的文件、光标位置、诊断信息和最近看过的代码。编辑器本身就提供了一部分天然上下文。

Sourcegraph 这类代码智能平台,更强调经过索引的搜索、符号关系和跨仓库导航。当“查找所有引用”要跨多个仓库时,它们的价值会更明显。

Aider Repo Map、RepoWiki 这类地图和知识工具,会先压缩仓库再交给模型。前者偏结构导航,后者偏自然语言解释。

真正好用的流程,往往会把它们组合起来:

项目规则告诉 Agent 应该怎样工作
精确搜索寻找已知名称
语义搜索发现不同说法
符号和地图连接文件关系
文档解释设计意图
测试告诉 Agent 现在到底是不是真的

所以,不要再问“哪个工具能一次理解整个仓库”。更实用的问题是:

我下一步最需要消除哪一种不确定性?

五、为什么窗口更大,Agent 仍然会迷路?

桌子大当然有用,但把整个仓库倒在桌上并没有用。

大上下文窗口与高质量工作上下文的区别

假设你一次附加 80 个文件、三份架构文档、一个月的对话历史和完整测试日志。正确答案可能真的在里面,但模型现在多出了好几个难题:

  • 哪份文档描述的是当前分支?
  • 四个相似服务中,谁拥有这项规则?
  • 最新修改之后,那段长日志还有没有用?
  • 旧对话和当前源码冲突时应该信谁?
  • 哪条验收条件最重要?

长上下文利用研究曾发现,重要信息被埋在很长输入中间时,模型不一定能稳定利用。不同模型和任务会有差异,但工程结论很朴素:装得下,和选得对,是两件事。

Agent 会话还有另一个问题。每次工具调用都会继续增加搜索结果、源码、测试日志和中间计划。任务做得越久,工作台可能越乱。Anthropic 的上下文工程指南也强调,要把上下文看成需要持续整理的有限资源。

好上下文并不只是“少”,而是同时满足:

  • 和当前判断有关;
  • 对当前分支足够新;
  • 权威性足以支撑这条结论;
  • 最后有办法验证。

六、跟着一个任务走完整条代码路径

回到取消窗口需求。

搜索前,先把一句需求整理成小型任务契约:

行为
- 商家可以配置取消窗口,单位为分钟。

兼容性
- 未配置的商家继续使用 15 分钟。
- 现有 API 客户端保持兼容。

明确不改
- 支付捕获时间。
- 废弃取消请求的过期逻辑。

验收证据
- 默认值和自定义值的单元测试。
- API 契约测试。
- 端到端:配置 30 分钟后,第 20 分钟仍可取消。

这样一来,Agent 的每次搜索都有明确目的。

先找显眼的名字

rg -n "CANCEL_WINDOW_MINUTES|cancelUntil|canCancel" \
  services packages apps tests docs

这一步会找到当前常量、对外字段和明显消费者。

再找背后的职责

rg -n "CancellationPolicy|PolicyService|eligib|deadline" \
  services packages apps tests docs

如果工具支持语义搜索,还可以直接问:“系统在哪里判断订单是否仍然允许取消?”它可能找到完全不同的命名。

对强候选项向外扩一跳

找到 canCancel 后,继续检查:

  • 谁调用它;
  • 哪个公开响应暴露了结果;
  • 哪段 UI 消费截止时间;
  • 策略和配置从哪里读取;
  • 哪些测试真正观察这项行为;
  • 最近是否有 Commit 移动过这项职责。

可配置取消窗口经过核实的代码路径

正是在这“一跳”里,Agent 会发现两个重要事实:

  1. Web 端复制了 15 分钟规则,不应该继续自己做决定;
  2. Worker 的 15 分钟只是数字相同,实际属于另一项行为,不能改。

能够正确排除一条看似相关的路径,也是“理解代码库”的一部分。检索结果更多,不代表检索更好。

七、把探索结果整理成一个小工作包

搜索完成后,不要让所有打开过的文件一直占据前台。让 Agent 把已经核实的内容压缩成一个上下文包:

# 上下文包:可配置的取消窗口

## 必须改变什么
- 每个商家可配置分钟数,默认仍为 15。
- 现有 API 客户端继续兼容。

## 当前分支上已经核实的事实
- PolicyService 已经提供商家策略值。
- 订单服务拥有 canCancel,并计算 cancelUntil。
- Web 端重复了旧的 15 分钟判断。
- 过期 Worker 的 15 分钟属于另一条生命周期。

## 计划修改
- 在策略读取模型中增加 cancellationWindowMinutes。
- 保持订单服务为唯一决策 Owner。
- 让 Web 端消费 cancelUntil。

## 怎样证明完成
- 单元测试:默认 15、自定义 30。
- 契约测试:现有响应仍然有效。
- 端到端:第 20 分钟可取消。
- Diff:支付和过期 Worker 没有变化。

## 仍然不知道
- 产品是否规定了最大值。

这种上下文包不是所有小修改都必须走的形式流程。跨模块、公共契约、数据模型、权限或业务规则发生变化时,它才特别有价值。

它最大的好处,是把事实、计划和未知问题分开了。

八、明天就能使用的四步工作法

完整流程可以记成四个词:定框 → 画图 → 装包 → 证明。

定框、画图、装包、证明的上下文工程循环

第一步:定框——先说清用户行为

告诉 Agent:用户应该观察到什么、什么必须保持兼容、哪些内容明确不改、什么结果算成功。

太弱的需求:

把取消时间改成可配置。

更好的写法:

按商家配置订单取消窗口,默认仍为 15 分钟。保持现有 API 结构兼容。
不要修改支付和废弃请求过期逻辑。用“配置 30 分钟后,第 20 分钟
仍能取消”的端到端场景证明完成。

第二步:画图——陌生区域先别急着改

对风险较高或不熟悉的模块,可以直接告诉 Agent:

先不要修改代码。找出决策 Owner、配置来源、公开契约、消费者和测试。
每个候选文件都说明为什么相关。名字相似但核实后排除的路径也要记录。

这样可以避免“第一个看起来合理的搜索结果”直接变成全部方案。

第三步:装包——只保留核实过的证据

要求 Agent 简短整理:当前事实、项目约束、计划修改、验证方法和未知问题。整理后的上下文包,应该明显小于用来构建它的材料。

如果摘要里出现五次“可能”“大概”,说明还没查清;如果摘要里列了 40 个文件,说明还没压缩好。

第四步:证明——让执行结果刷新地图

执行会继续产生新的仓库知识:

  • 编译错误暴露了另一个消费者;
  • 契约测试暴露了兼容性边界;
  • 运行日志暴露了缓存或另一个进程;
  • Diff 暴露了意外修改。

不要把这些只当成需要绕开的障碍。它们是新证据。先更新地图,再继续修改。

九、看到什么症状,就用什么办法

不是每个任务都需要全套上下文工具。可以从眼前的失败现象出发。

“Agent 只改了最显眼的文件”

修改前要求它找出 Owner、调用方、消费者、公开契约、配置来源和测试。配合符号引用或“一跳扩展”。

“Agent 读了很多文件,却迟迟没有计划”

先补任务契约,再给一个探索预算。让它输出候选文件和理由,只沿最强的几条路线继续。

“语义搜索总是返回相似但不相关的代码”

增加一个精确锚点:符号、Package、API 字段或领域边界。语义检索负责提高召回,精确条件负责提高精度。

“RepoWiki 说得很肯定,但路径已经不存在”

检查它对应的来源 Revision。重新生成,或者只把 Wiki 当成搜索线索。判断当前代码库状态时,以当前源码和测试为准。

“会话开始时很聪明,做久了越来越乱”

新目标使用新任务,或者围绕当前上下文包做一次压缩。旧日志、废弃计划和无关探索不要一直留在活动工作区。

“每次任务都要重新发现相同命令和项目约定”

把稳定的仓库级规则写进 AGENTS.md 或对应产品的规则文件。AGENTS.md 规范支持按目录放置更具体的说明。一次性验收条件仍然留在当前任务里,不要写成永久规则。

十、什么时候值得建设本地知识库?

当下面一种或多种情况反复出现时,本地 Index、Repo Map 或 Wiki 才值得维护:

  • 新成员总要花很久寻找同一批子系统边界;
  • Monorepo 横跨很多 Package 或编程语言;
  • 仅靠符号导航无法解释架构和业务意图;
  • 团队经常重复相同的跨模块任务;
  • 仓库数据必须留在受控环境中。

不要因为“RAG 听起来先进”就建设知识库。对于命名清楚的小仓库,rg、LSP、可靠测试和一份简洁的 AGENTS.md,可能已经是更好的系统。

无论采用什么工具,都要分清三层:

真相源      代码、Schema、测试、已接受 Spec、ADR
导航层      项目规则、搜索方法、Repo Map
缓存层      Embedding、生成式 Wiki、图谱快照、Memory

缓存可以很快,也可以很好用,但它应该能够重建。如果一条架构决策只存在于生成式 Wiki,就把它移到经过评审的真相源文档中。

还要确认安全边界:

  • 哪些路径会被排除?
  • 源码是否离开本机?
  • Chunk、Embedding 和路径元数据存在哪里?
  • 谁可以查询共享索引?
  • 文件删除或权限收紧后,索引中的副本怎样清理?

上下文质量也包括:不要检索出 Agent 本来就不该看到的材料。

十一、怎样判断上下文真的变好了?

不要看生成式 Wiki 有多漂亮,而要拿几个真实的跨文件任务来验证:

  • 第一版计划是否找到了正确的决策 Owner?
  • Review 时发现的漏改消费者是否减少?
  • Agent 是否少读了无关文件?
  • 每条验收条件是否都有对应测试?
  • 过期摘要是否曾把实现带偏?
  • “再试一个 Patch”的循环是否减少?

在取消窗口案例里,新流程只有同时找到策略来源、订单决策、API 契约、Web 消费端和端到端测试,并且正确排除 Worker 超时,才算成功。

这比“Prompt 少用了 30% Token”更有意义。目标不是上下文越小越好,而是:在不漏掉完整行为的前提下,让上下文尽可能小。

一段可以直接复用的 Prompt

修改前,先为这个任务建立代码路径地图。

1. 重述用户可观察行为、兼容性要求和明确排除项。
2. 阅读当前目录适用的项目规则。
3. 找出决策 Owner、数据或配置来源、公开契约、消费者和测试。
4. 已知名称使用精确搜索,不熟悉的说法使用语义搜索。
5. 从强候选项沿定义和引用向外扩一跳。
6. 分开记录已核实事实、推断和未决问题。
7. 返回一个简短上下文包,包含改动集合与验收证据。

然后再实施。把编译错误、失败测试、日志和 Diff 当成新证据。如果它们
改变了地图,先更新计划,再继续修改。

结语:不要把整个仓库倒在 Agent 桌上

Codebase Index、向量库、符号图谱、Repo Map 和 RepoWiki,并不是同一个问题的五种竞品。它们是帮助 Agent 找到下一条有效线索的不同办法。

模型仍然需要判断:

  • 什么行为真正重要;
  • 哪些证据符合当前分支;
  • 哪条关系必须继续追;
  • 哪条看似相关的路径应该排除;
  • 什么结果能够证明任务完成。

这些判断,一部分由 Agent 完成,一部分由工具提供,另一部分来自你设计的上下文。

大窗口只是给 Agent 一张更大的桌子;上下文工程负责把正确的证据放到桌上。

权威资料

最后更新于