Agent 在大代码库里的主要任务不是写代码,而是先找到要改的那几行

问题规模

主流大模型当前的上下文窗口在 100K 到 1M token 之间。听起来很大,但放到真实代码库面前并不够用:

代码库大致行数估计 token 数
小型项目1-5 万0.5-2M
中型项目(典型业务系统)10-50 万5-20M
大型项目(Django、React 等)50-200 万20-100M
超大型项目(Chromium、Linux kernel)1000 万+数百 M

即使最大的上下文窗口也装不下一个中型项目。所以 Code Agent 在真实项目里工作时,主要的工作不是写代码,而是先定位到要看哪几个文件、要改哪几行


几种定位策略

1. 关键词搜索(grep-based)

最直接,也最实用:

grep -r "UserAuthService" --include="*.py" ./src

适合 Agent 已经知道要找的具体符号名、变量名、错误信息时使用。SWE-agent 的 search_file / search_dir 工具本质上就是 grep 的封装。

优点:精确、快、易理解。 缺点:不能处理语义上相似但用词不同的情况。

2. 文件树浏览

把目录结构展示给 Agent,让它根据文件名和路径推测哪些文件相关:

src/
├── auth/
│   ├── service.py
│   ├── middleware.py
│   └── handlers.py
├── models/
│   ├── user.py
│   └── session.py
└── api/
    └── auth_routes.py

Agent 看到 "auth" 相关目录,会优先去那里看。适合熟悉的项目结构、命名规范明确的项目。

3. 语义检索(Embedding-based)

对代码库做向量索引:把每个函数、类、文件转成 embedding,按语义相似度检索。

query = "处理用户登录失败的逻辑"
results = vector_db.search(embed(query), top_k=5)
# 返回最相关的 5 段代码

适合 Agent 描述的是功能性需求,而不是具体符号名。

工具:

  • Cody(Sourcegraph):商业产品,基于代码 embedding 检索
  • Cursor:编辑器内置,对工作目录建索引
  • Codebase Indexing(Continue.dev 等):开源方案

代价:索引需要预先构建,代码改动后需要增量更新,存储开销不小。

4. 调用图分析(AST-based)

用语言服务器(LSP)或 AST 分析工具构建调用图:

函数 A 被这些地方调用:
  - module_x.py:45
  - module_y.py:120
  - tests/test_a.py:30

函数 A 调用了:
  - helpers.format_date
  - models.User.get_by_id

适合需要追踪"改这个函数会影响哪些地方"的修改任务。SWE-agent、Aider 等工具会用 LSP 提供这类信息。

5. 历史变更(git-based)

通过 git blame、git log 找到与某段代码相关的历史:

git log --all --source --remotes -- src/auth/service.py
git blame src/auth/service.py

适合需要理解"这段代码为什么是这样写的"的场景。最近修改过相关文件的提交,往往和当前问题相关。


实际工作流的组合

成熟的 Code Agent 通常不是只用一种策略,而是按场景组合:

[阶段 1:粗筛]
关键词搜索 + 文件树浏览
→ 锁定候选文件(10-30 个)

[阶段 2:精读]
对候选文件做语义检索或直接读取
→ 锁定候选函数(5-10 个)

[阶段 3:理解依赖]
调用图分析
→ 理解修改的影响范围

[阶段 4:编辑]
精确编辑 + 跑相关测试

每个阶段都在收窄信息范围。如果跳过粗筛直接读所有文件,上下文很快就会爆。


窗口内的信息组织

定位到了相关代码,怎么放到上下文里?

完整文件 vs 片段

完整文件:信息完整,但占 token 多。 片段(按行号范围):节省 token,但容易丢失上下文(比如类定义的开头被截掉了)。

实际做法通常是:

  • 当前编辑的目标文件:完整放入
  • 相关的其他文件:只放相关函数或类
  • 更远的依赖:只放签名(函数名、参数、返回类型)

摘要 vs 原文

对于大文件,可以先生成摘要:

[文件摘要:src/services/user_service.py]
- 提供用户相关的业务逻辑
- 主要类:UserService(管理用户 CRUD)、AuthService(认证)
- 关键方法:
  - UserService.create_user(...)
  - UserService.update_profile(...)
  - AuthService.login(...)
- 依赖:models.User, repositories.UserRepository

让 Agent 先看摘要决定要不要进一步查看完整代码。Cursor、Aider 等工具内部都有类似机制。

代价:摘要可能丢失关键细节,而且摘要的生成本身也要花 LLM 调用。

状态保持

SWE-agent 的 ACI 设计里专门强调了"当前打开的文件、当前浏览的行数"这类状态。把这些信息显式放在 prompt 里,可以让 Agent 不会"迷路"。

当前状态:
  打开的文件:src/auth/service.py
  当前浏览位置:第 245-310 行
  最近编辑:修改了 line 280-285 的 login() 方法

跨文件依赖的处理

修改一个函数往往会影响调用它的地方。Agent 需要识别这种依赖。

静态分析

用 LSP 或 tree-sitter 找出调用链:

def find_references(symbol_name, codebase_path):
    # 用 LSP 找到所有引用
    references = lsp_client.references(symbol_name)
    return [
        {"file": ref.file, "line": ref.line, "context": ref.surrounding_code}
        for ref in references
    ]

把这些引用信息放入 prompt,Agent 修改函数时就能同步更新调用点。

动态运行测试

修改之后跑测试,让测试来告诉 Agent 哪里坏了:

修改:src/auth/service.py 的 login() 方法

跑测试:pytest tests/test_auth.py
结果:
  tests/test_auth_handlers.py::test_login_endpoint FAILED
    - 错误:TypeError: login() missing 1 required argument: 'remember_me'

测试错误信息直接告诉了 Agent 漏改了哪些地方。

实际做法是两者结合:静态分析帮忙找候选影响点,动态测试验证。


缓存与增量更新

代码库不是静态的,频繁修改后索引会过期。处理方式:

文件级缓存失效:每个文件的索引带时间戳或哈希,文件修改时只重建该文件的索引,不重建整个库。

懒加载:不预先索引整个库,按需建立索引(用户查询到时才计算 embedding)。

多级缓存

  • L1:最近访问的文件(内存,几十个)
  • L2:项目的高频文件(磁盘,几百个)
  • L3:完整代码库索引(向量数据库)

每一级都有 hit 时跳过下一级。


不同规模的策略选择

代码库规模推荐策略
万行以内全部放入上下文,无需特殊设计
万到十万行文件树 + 关键词搜索 + 按需读取
十万到百万行上述 + 语义检索 + LSP 调用图
百万行以上完整索引系统 + 摘要 + 多级缓存

规模越大,预处理和工程投入越多,纯靠"把代码塞给 LLM"越不可行。


工程实践的几个观察

精确比全面重要:给 Agent 30 个相关文件,不如给 3 个真正相关的文件。前者容易让 Agent 注意力分散,后者反而效果好。

结构化优于自由文本:用明确的格式(文件名 + 行号 + 内容)比把代码段堆在一起更利于 Agent 处理。

让 Agent 自己控制:与其预先决定给它看什么,不如提供工具让 Agent 自己按需查询。SWE-agent 的设计就是这种思路——给工具,不给数据。

上下文管理是迭代过程:Agent 看完一些代码后,会发现需要再看另一些代码。这种动态查询比一次性"给齐所有相关材料"更接近真实开发过程。


小结

大代码库的上下文管理是 Code Agent 从 demo 走向实际可用的关键工程问题之一。它不是某个算法能解决的,而是几个组件的组合:

  • 多种检索方式(关键词、语义、AST、git)
  • 信息组织策略(完整 / 片段 / 摘要 / 签名)
  • 跨文件依赖处理(静态 + 动态)
  • 缓存与增量更新

模型的上下文窗口在变大,但代码库的规模也在变大,工程上的"按需检索 + 智能裁剪"在可预见的将来仍然是必要的。

版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:16. 大代码库的上下文管理:Agent 如何在百万行代码里找路

本文链接:https://www.sshipanoo.com/blog/ai/code-agent-harness/16-大代码库的上下文管理/

本文最后一次更新为 天前,文章中的某些内容可能已过时!