最近有一篇讲代码库RAG的技术文章在圈内传得很热,标题就叫“代码库的检索增强生成比看上去更难”。文章花了很大篇幅拆解,为什么朴素的向量检索一碰到真实代码仓库就频繁翻车。它的几个核心建议都非常到位:一定要预先过滤掉 node_modules 这类噪音目录;做文本切块时,必须基于抽象语法树的边界,而不是数 token 长度;文件路径这件事要当成一等元数据来保留;遇到架构级别的问题,应该用图遍历去表达依赖关系,而不是蛮力地找最近邻居。这些判断全对——如果你正在做面向代码的 RAG 系统,这篇值得全文精读。
但真正让我想顺着往下说的,是那篇文章最后落脚的结论。作者写道:“开发者信任取决于检索质量、诚实的不确定性承认,以及可验证的来源——而不仅仅是算法层面的精密度。”前面所有的那些技巧,无论是更聪明的分块策略、更具表征力的嵌入模型,还是仓库地图,最终都在帮模型更大概率地给出正确回答。可一旦让大语言模型动手去写代码,“更大概率正确”和“它刚刚告诉我的改动到底做没做”根本就是两种完全不同的问题。
RAG 只是通往信任鸿沟的上游一环。优秀的检索流程能塞给模型更贴切的上下文,然后模型据此生成一份代码修改,并且理直气壮地报告:“我新增了 /v1/refund 路由,把 MAX_RETRIES 设成了 5,只碰了解析器,所有测试都通过了。”请注意,这每一句都是针对当前仓库状态的、事实层面的断言。而真正让人坐立不安的测量数据来自 SWE-bench 的基准评测:在 100 次用真实 GitHub issue 驱动的 SWE-agent 实际运行中,有 30% 的尝试尽管最终没能通过测试套件,模型依然坚称自己已经修复了问题——“该方法已成功添加”“问题已被解决”。这是对照真实落地记录拿到的结论,由同一个评测流程中的 LLM 裁判员判定,结果可重复。
更强的检索能力无法弥合这种裂缝。哪怕模型拿到了完美的上下文,它照样可能夸大自己实际做过的事情。幻觉的形式在这里发生了迁移:以前是“对代码库本身给出了错误事实”,现在变成了“对自己做出的更改给出了错误描述”。后一种情况更加隐蔽,因为它听起来像是一份状态更新,而不是一种猜测。开发者很容易被这种工单式的回复麻痹,顺手就合入了本来就没修好的代码。
这就带出了作者末尾抛出的三个关键词——诚实的不确定性、可验证的来源、不凭空推测——它们恰好也是我们在 truth 这个系统里从头搭建的约束条件。但我想强调的是,这些东西没法通过把 RAG 流水线做得更精致来获得,因为它们压根儿不属于检索的性质。它们属于验证的性质。诚实的不确定性不是“模型会兜圈子式地加个限定语”,而是一套系统可以明确拒绝一个没有可测量依据的判断,并且认真执行这个拒绝。一个会虚晃一枪的验证器,比没有验证器还要糟糕。可验证的来源不是“给你贴一个检索块的引用”,而是一种基于直接读取到的、二进制式事实做出的裁定。这些事实可能是一条 git diff 里某个文件存在或不存在,可能是抽象语法树上的某个符号确实找得到或者找不到,可能是一个精确的常数值,也可能是一条命令的退出代码。引用必须落到文件和行号,直接可复验。不推测也不只是一句模型可以无视的提示语,它应当成为一道硬性的架构规则:裁决由固定规则的引擎来做出,而不是靠模型。智能体没有机会靠花言巧语把裁决掰成另一个答案。
所以 truth 的路线恰好是 RAG 的逆向操作。RAG 负责检索上下文,帮助模型产生回答;truth 负责检索证据,用来核查模型到底宣称了什么——对照真实的代码,对照实际跑出来的工作树,对照无法抵赖的二进制结果。一旦你不再把验证当成检索的附属品,而是把它当作和生成并列的独立系统来设计,开发者对代码修改的信任才有机会真正落地。
热门跟贴