你的编程代理基于落后 Main 分支三周的代码版本重建的代码库索引
你团队中的一个 AI 编程 Agent 提交了一个 PR,在两个文件中调用了四次 parseUserToken()。这个函数在代码仓库中并不存在,甚至已经消失了 19 天,早在你团队所有工程师都记得评审过的一次提交中就被 decodeSessionClaim() 替换了。Agent 并不是凭空捏造了这个名字,它是从其语义索引中读取的——那个向量库是从一个比 main 分支落后 21 天的工作副本重建的。相比之下,Agent 的编辑步骤在会话开始时运行了 git pull,操作的是最新的代码。对同一个代码库的两个视角,相隔三周,而 Agent 却自信地用一段无法针对任何真实环境编译的代码桥接了它们。
这是一种不会自我宣告的失败模式。Agent 运行了。测试看起来通过了。PR 合并了。第一位评审者之所以注意到,仅仅是因为一个被删减的函数与一个无关的辅助函数重名,触发了 linter 报错。到那时,Agent 已经花了一个完整的冲刺(sprint)针对一个“幻影版本”的代码库进行编写,而团队中没有一个人——包括 Agent 自己——收到任何异常信号。
Agent 对代码库的“理解”与代码库的“实际状态”之间的缝隙,是一个没人会在架构图上画出来的“一致性边界”。索引由一个团队按一种节奏更新;工作树(working tree)由另一个团队按另一种节奏获取。Agent 的推理存在于第一个层面,而 Agent 的动作落在了第二个层面。当两者即便只差几次提交——更不用说三周——结果就是一个 Agent 会基于一个已不存在的代码库,提出极其自信的主张。
偏差是如何产生的
索引和工作树是平台回答“这个仓库里有什么代码”的两种方式。大多数 Agent 平台将它们存放在不同的地方,按不同的时间表刷新,并假设结果是一致的。事实往往并非如此。
最常见的偏差来源是镜像缓存(mirror cache)。为了应对 GitHub 的速率限制并加速冷克隆(cold clones),Agent 平台会为每个跟踪的仓库保留一个本地镜像,并定期(每隔几分钟、每小时甚至更久)从 origin 刷新。多年来,这种镜像一直是与 CDN 类似的基础设施;像 gitcache 和 git-cache-clone 这样的工具对此有明确说明。刷新间隔是可配置的,而问题正出在这里。如果刷新任务在平台迁移期间被暂停、限速、误配置或遗忘,镜像就会在无形中落后于 origin。索引从镜像中提取,而工作树直接从 origin 提取。两者因此产生分歧。
其他诱因则更为微妙。出于性能考虑,语义索引在不同会话之间是持久化的——重新计算嵌入(embeddings)成本高昂,大多数 Agent 平台都会积极复用 它们。在大规模分支切换、重写了数千个文件的依赖项升级、生成的代码重新生成或 LFS 拉取之后,索引可能会携带指向已不存在的代码的孤立条目。Cursor 的文档承认了这一点:当自动补全开始引用你三个分支前删除的文件时,说明索引中存在陈旧条目。Cursor 用来增量重新索引的 Merkle 树变化检测器在查找“已修改”文件方面效率很高,但在结构性重写时,它的失效处理并不总是足够激进。
第三个诱因是索引失败但检索未失败。索引器在运行中途崩溃,留下了一个不完整的索引,而 Agent 平台继续根据最后一次索引的内容提供搜索结果。Agent 无法知道它的结果中现在选择性地遗漏了最后六次提交的更改。搜索结果依然自信地返回。这种“自信”正是问题所在。
Agent 在陈旧视图下会做什么
编程 Agent 使用索引做两件事:搜索(“帮我找到处理身份验证的函数”)和 Grounding(知识对齐,“这是我要调用的函数签名”)。两者都以不同的方式受到陈旧性的威胁。
搜索功能的退化是悄无声息的。陈旧的索引会返回“过去”实现某个功能的某个文件,Agent 会将其视为真理。如果该文件已被拆分、重命名或合并到另一个模块中,那么从第一次搜索开始,Agent 关于逻辑所在位置的心智模型就是错误的。后续的搜索无法修复这一点——它们通常会变本加厉,因为 Agent 的后续查询是基于第一次搜索返回的内容。
Grounding 的失败有时会表现得更明显。如果 Agent 调用了一个不再存在的函数,编译 器通常会捕获它——故事本该到此结束。但现代编程 Agent 的循环是“迭代直到测试通过”,而该循环的许多实现都在构建失败时提供了一个后备方案:删减调用、模拟(mock)返回值、注释掉报错的代码块。Agent 的提示词告诉它要让测试通过,于是它通过移除报错的部分来让测试通过。这个临时方案就这样被发布了。评审者看到 CI 运行通过且 diff 干净,便予以批准。
最阴险的情况是“部分重叠”。Agent 调用的函数确实存在,但其签名与索引记忆的不同。参数顺序改变了,增加了一个必填参数,或者返回类型被缩小了。代码在某些情况下可以编译,但在运行时会崩溃。Agent 永远不会收到明确的信号表明其 Grounding 是错误的,因为这种错误是间歇性的。
- https://cocoindex.io/cocoindex-code/
- https://docs.cursor.com/context/codebase-indexing
- https://cursor.com/blog/secure-codebase-indexing
- https://towardsdatascience.com/how-cursor-actually-indexes-your-codebase/
- https://read.engineerscodex.com/p/how-cursor-indexes-codebases-fast
- https://sourcegraph.com/docs/cody
- https://github.com/Helweg/opencode-codebase-index
- https://dev.to/badmonster0/your-ai-coding-agent-is-blind-heres-the-fix-569n
- https://blog.newtum.com/ai-hallucinations-in-code/
- https://cursor.com/learn/hallucination-limitations
- https://github.com/seeraven/gitcache
- https://blog.premai.io/building-production-rag-architecture-chunking-evaluation-monitoring-2026-guide/
