You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
你的 AI Agent 读不懂 50 万行代码——真正有效的方案是什么
每周都能在 Claude Code / Cursor / Aider 的 issue 里看到同一类问题:
根因永远是同一个:你的 agent 没有依赖图。 它一次读一个文件,看得见定义但看不见调用者,对"改了这里会 break 什么"完全没有概念。
"给它更多 context" 不 scale。50 万行代码 ≈ 200 万 token。就算你有 1M context window,也塞不下全部——而且就算塞下了,注意力在 200K 之后就开始衰减。
这篇文章讲的是:我们怎么用预计算的代码依赖图,在正确的时间注入正确的上下文。
问题,精确定义
失败模式不是"Agent 写烂代码",是**"局部正确、全局错误"**。函数编译通过,单测 pass,但另外 5 个模块用旧签名调它——全 break。
设计抉择:预计算图 vs 实时 Grep vs RAG
grep -r "函数名"核心洞察: 依赖关系是结构事实,不是语义相似度。"谁在调用这个函数?"的答案来自 AST 分析,不是 embedding 距离。RAG 在这里是用错了工具。
架构:4 层
Agent 实际看到什么
没有 Code Intelligence:
有 Code Intelligence:
注入发生在 agent 处理文件之前。它不是"问了才告诉你"——是"你还没开始想,依赖关系已经在面前了"。
E2E Flow:一次 Read 调用的完整生命周期
从 agent 发起
Read("session_unit.py")到 context 注入完成,经过这些步骤:关键设计点:
hook_builder.py— 条件是code_intel_enabled=True(默认开启)"approve"— 它只加信息,不阻止任何操作关键 Design Decisions
1. SQLite,不用图数据库
Neo4j、Dgraph 等对这个场景 overkill。代码图的特点是:读多写少(只有 commit 时写),20 万行项目只需 30-50MB。SQLite + WAL 模式给你:
决策:运维简单 > 理论最优。
2. tree-sitter + regex 兜底
tree-sitter 对 Python/TypeScript/Java/Go 提供精确 AST。但:
方案:tree-sitter 优先,失败回退到 regex。Regex 漏掉 ~15% 的边,但定义 100% 准确。不完美的图 > 没有图。
3. 注入 100 token,不是 1000
Agent 不需要完整依赖树。它需要:
上下文越多 = 注意力越稀释。我们测了 50/100/200/500 token 注入。100 是 sweet spot——够影响决策,不至于淹没原始信息。
4. 按文件原子更新
改了一个文件 → 只删除+重插这个文件的 nodes/edges。用
BEGIN IMMEDIATE事务保证读者永远看不到中间状态:5. 用 git SHA 判断新鲜度,不用 mtime
mtime 不可靠(build、touch、编辑器临时文件都会改 mtime)。git SHA 对比:
git rev-parse HEAD调用)多包 / Monorepo 场景
单 repo 的按项目图已经很有用。Monorepo 或多包场景的关键扩展是跨项目边:
当 agent 要改
validate_token()时:这就是价值爆发点。没有人记得住所有跨包依赖。grep 不可靠。只有 graph 能做到。
避坑指南
❌ 不要索引所有文件
跳过:测试文件、生成代码、vendored 依赖、node_modules。它们增加噪音(假 caller),膨胀 DB,且没有架构价值。
❌ 不要在每个 tool call 都注入
只对
Read和Grep注入——这是 agent 即将推理代码的时刻。对Write或Bash注入 = 白花延迟。❌ 不要用 embeddings 回答依赖问题
"哪些文件跟这个语义相似?" ≠ "哪些文件会因为我改了这里而 break。"
一个测试文件和它的实现,embedding 距离最近。它们确实有依赖关系,但那是
calls,不是is_similar_to。❌ 不要在图里存代码内容
诱惑:把函数体存进去做"上下文注入"。别。Agent 会自己 Read 文件——你在重复内容。图存的是关系(谁调谁,edges),不是内容(函数做什么)。
❌ 不要过度设计 parser
我们最初试图解析所有动态分发、metaclass、装饰器修改的签名。结果:实现时间 ×2,多了 10% 的边,索引慢了 30%。先出 85% 方案。那缺失的 15% 边在 blast radius 分析中极少 matter。
✅ 必须自动保鲜
如果图落后于 HEAD(过时了),它给的答案是错的——比没有图更危险。每次 session start 自动检查 freshness 是非协商的。小改动增量更新,大改动后台全量重建。
✅ 必须让用户看得见状态
我们的底栏显示:
🧠 11,682 | today——symbol 数 + 最后索引时间。用户一眼知道 Code Intelligence 是否在工作、是否新鲜。如果显示"3d ago" → 点 Reindex。生产数据
calls类型)git rev-parse HEAD对比什么时候需要这个
需要:
grep返回结果太多反而没用不需要:
复合效应
Code Intelligence 单独用是个不错的优化。和 DDD(领域驱动设计文档) + 自治 Pipeline 组合,变成结构性能力:
每一层让其他层更有效。图提供结构事实。DDD 提供判断上下文。Pipeline 提供执行纪律。组合起来:agent 把 codebase 当系统理解,不是当文件集合。
术语表
WITH RECURSIVE语法,允许一个查询引用自身——用来做图遍历。我们的blast_radius()用递归 CTE 从一个被修改的节点出发,沿着 edges 表逐层扩展,直到 depth=N,找到所有可能被影响的下游调用者。validate,瞬间返回所有包含这个词的函数/类/方法名。比LIKE '%validate%'快 100x+,因为走的是倒排索引。代码可用性
本文描述的代码图实现是 SwarmAI 核心引擎的一部分。关键文件:
parser.py— tree-sitter AST 提取 + 3 层名称解析graph_store.py— SQLite 图 + CTE 遍历 + FTS5 + 原子更新freshness.py— git SHA 保鲜检测code_intel_hook.py— PreToolUse 注入 (<50ms)codebase_map.py— session briefing 生成 (~100 tokens)模式可适配到任何支持 tool-use hooks 的 agent 框架。
扩展阅读(本系列其他 Discussion)
附录:graph_store 实际数据样本
以下是从生产环境
code_intel.db导出的真实记录,让你直观感受数据长什么样。code_nodes 表(符号定义)
code_edges 表(调用关系)
查询示例:blast_radius(递归 CTE)
这就是 agent 在你改
get_module_map()之前看到的信息——不是猜的,是图算出来的。你在大型代码库上用 AI Agent 的体验是什么?试过 graph-based 的方案吗?在评论区说说。
Beta Was this translation helpful? Give feedback.
All reactions