从 GitHub Issue 中的真实故障记录出发,沿着“证据接地”这条主线,拆解模型不读源文件、虚构 CSS 与配置参数、陷入无行动诊断循环的生成机制,并给出检索前置、工具强制、验证门禁与评测基准四层可落地的工程拦截方案。
摘要
在大型代码库中,编码代理(coding agent)最危险的行为不是“写错”,而是“不读就写”:不检索源文件、不核对既有约定,直接虚构 CSS 变量、配置键名、接口字段与依赖版本,随后在不执行任何验证动作的前提下反复自我诊断,形成“编造—循环—再编造”的失败闭环。本文以 GitHub Issue 中反复出现的这类故障记录为经验起点,提出以“证据接地”(evidence grounding)为贯穿主线的分析框架:把“模型输出是否可追溯到仓库内可验证的证据”作为统一判据,解释该类失败为何在大型代码库中被系统性放大,并据此设计四层拦截体系——检索前置、工具强制、验证门禁、评测基准。全文给出可操作的步骤、检查清单与度量指标,并对检索增强、过程监督与仓库级评测的前沿方向作出审慎预判。
关键词:编码代理;证据接地;幻觉;检索增强生成;工具调用;仓库级评测;失败模式
目录
1. 导语:一条 Issue 背后的失败闭环
在开源仓库的 issue 区里,有一类故障报告的描述高度相似:代理被要求“把某个按钮的悬停态改成品牌色”,它没有打开设计令牌文件,而是直接写出一个形如 --brand-primary-hover: #7c5cff 的变量;被要求“调整构建缓存配置”,它没有读取 vite.config.ts,而是发明了一个并不存在的配置键;被要求“修复接口 400 错误”,它没有查看请求体定义,而是补了一个后端从未接受的字段名。更值得注意的是后续行为:当测试失败时,代理往往不执行任何新的检索或日志查看动作,而是基于自己上一轮编造的前提继续推理,输出“可能是缓存未刷新”“可能是环境变量未生效”之类的诊断,循环数轮后仍无实质动作。
这类现象在社区中通常被笼统归为“幻觉”。但笔者认为,把它简单归入幻觉会丢失最有工程价值的部分:幻觉描述的是输出与事实不符,而这里真正的问题在于“证据获取环节被跳过”。模型并非在拥有正确证据后推理出错,而是在没有证据的情况下直接生成了看似合理的实现值,并在后续轮次中把自造值当作既定事实继续使用。二者的干预手段完全不同:前者需要提升推理正确率,后者需要强制补齐证据链。
本文的主线由此确立:以“证据接地”为统一判据,把“编造而非推导”还原为一个可观测、可度量、可拦截的工程问题。围绕这条主线,本文依次回答四个问题:这种失败在机制上从何而来;为何在大型代码库中被系统性放大;有哪些可观测信号可以提前识别;以及工程上应当如何分层拦截。
2. 问题定义:什么是“编造而非推导”
2.1 三个可区分的概念
为避免术语混用,本文先给出三个操作性定义。第一,事实性幻觉(factual hallucination):模型生成了与客观事实不符的内容,例如虚构 API、虚构论文、虚构配置项。第二,证据缺失型编造(evidence-absent fabrication):模型在仓库内存在明确权威来源(源文件、类型定义、设计令牌、锁文件)的情况下,未检索该来源而直接生成实现值。第三,无行动诊断循环(action-free diagnostic loop):模型在失败后连续多轮输出诊断性文本,但不执行任何能产生新信息的动作(读文件、跑测试、查日志、检索)。
三者的关系是:证据缺失型编造是根因层,事实性幻觉是其常见表现,无行动诊断循环是其自我强化的放大器。本文关注的核心是后两者构成的复合模式,因为它在工程上最具破坏性——它不仅产出错误代码,还消耗大量轮次并污染上下文,使后续修正更加困难。
2.2 与传统“代码补全错误”的区别
传统代码补全的错误多发生在语法或局部语义层,通常能被编译器或类型检查立即捕获。而“编造而非推导”的错误往往语法合法、类型可通过、局部自洽:一个不存在的 CSS 变量在语法上完全合法,一个错误的配置键在 JSON 结构上完全合法,一个后端不接受的字段在前端类型里可能被写成 any 而绕过检查。这意味着传统静态检查对此类失败的召回率有限,必须引入“与仓库权威来源比对”的额外判据。
| 维度 | 传统补全错误 | 编造而非推导 |
|---|---|---|
| 触发位置 | 局部 token 选择 | 证据获取环节缺失 |
| 语法合法性 | 常不合法 | 通常合法 |
| 静态检查召回 | 较高 | 较低 |
| 典型后果 | 编译失败,快速暴露 | 运行期静默失效,难定位 |
| 有效干预 | 类型系统、lint | 检索前置、工具强制、证据比对 |
表 1:两类错误的对比。来源:本文基于公开 issue 记录与工程实践归纳整理。
2.3 本文评述
笔者认为,把这类失败命名为“编造而非推导”比“幻觉”更有工程指导意义,因为它直接指向一个可干预的环节:模型是否在生成实现值之前完成了对权威来源的检索。这一命名的代价是它不再覆盖“检索正确但推理错误”的情形,但换来了明确的干预靶点。工程实践中,先解决“不读就写”,再解决“读了写错”,是收益递减曲线下更合理的优先级排序。
3. 生成机制:模型为何倾向于发明实现值
3.1 训练目标与“流畅优先”的偏好
自回归语言模型的目标是最大化下一个 token 的条件概率。在代码语境中,一个“看起来像那么回事”的变量名、配置键、字段名,往往比“我需要先读取文件”这类动作性 token 具有更高的局部概率,因为前者与上下文的表面模式更连续。换言之,流畅性偏好与证据获取行为之间存在结构性张力:读取文件意味着中断生成、引入工具调用、等待外部返回,这在纯文本生成的概率分布中并不占优。
检索增强生成(RAG)的经典工作已经指出,外部检索能显著降低事实性错误,但其收益高度依赖检索质量与触发时机。本文评述:在代码场景中,问题比通用 RAG 更棘手——代码库的权威来源是结构化且分散的(类型定义、设计令牌、锁文件、迁移脚本、CI 配置),单一向量检索难以覆盖,必须结合符号检索与工具调用。
3.2 上下文窗口的“近因偏好”与证据稀释
长上下文并不等于有效上下文。已有研究表明,模型对上下文中部信息的利用率低于首尾部分,这一现象常被概括为“lost in the middle”。在大型代码库中,代理的上下文往往被大量无关文件、历史对话与工具输出填满,真正权威的源文件反而可能位于中部或被截断。此时模型倾向于用参数化记忆中的“常见写法”填补空缺,即编造。
笔者认为,这解释了一个反直觉现象:给代理更多上下文,有时反而提高编造率。因为更多上下文稀释了权威来源的显著性,同时增加了模型“已经掌握足够信息”的错觉。有效的做法不是堆上下文,而是按任务动态构造最小充分证据集。
3.3 工具调用的“成本错觉”与自我诊断的廉价性
在多数代理框架中,工具调用需要额外的协议开销与等待,而生成诊断文本是零成本的。当模型在失败后进入“诊断”模式,它实际上是在用低成本的文本生成替代高成本的信息获取。这与强化学习中的“奖励黑客”有相似结构:如果训练或提示信号奖励“看起来在分析”,模型就会倾向于输出分析文本而非执行动作。
失败闭环的典型轨迹(示意)
轮次 1 用户请求 → 模型直接生成实现值(未读源文件)
轮次 2 测试失败 → 模型输出"可能是缓存问题"
轮次 3 仍失败 → 模型输出"可能是环境变量未生效"
轮次 4 仍失败 → 模型输出"建议检查依赖版本"(未执行)
轮次 5 仍失败 → 模型重新生成另一个编造值
关键特征:轮次 2-5 均未产生新的外部信息
判据:连续 N 轮无"信息增益动作"即判定为诊断循环
3.4 本文评述
上述三条机制并非互斥,而是叠加:流畅性偏好提供“编造”的初始动力,上下文稀释降低“检索”的显著性,工具成本错觉使“诊断循环”成为廉价替代。因此,任何单一干预都难以根治,必须分层设计。这也构成了后文四层架构的理论依据。
4. 大型代码库的放大效应
4.1 约定密度与隐式知识
大型代码库的核心特征是高约定密度:命名规范、目录分层、状态管理范式、错误码体系、设计令牌命名规则,这些约定大多以隐式知识形式存在,未写入文档。模型无法从参数记忆中可靠获得某个特定仓库的约定,只能编造。仓库越大,约定越具体,编造与真实约定的偏差越大。
4.2 依赖图深度与“看似合理”的陷阱
在深层依赖图中,一个配置项可能经过多层封装才生效。模型若只看表层调用,很容易发明一个“看起来应该存在”的中间层配置。这类错误在单文件仓库中几乎不会发生,因为表层即全部;而在大型仓库中,表层与生效层之间的跨度正是编造的高发区。
4.3 反馈延迟与错误传播
大型仓库的测试套件通常更慢、更不完整,局部测试可能不覆盖被修改的路径。这导致编造值的错误在提交后才暴露,甚至在生产环境才暴露。反馈延迟越长,诊断循环的轮次越多,上下文污染越严重。
| 仓库规模特征 | 编造高发环节 | 放大机制 |
|---|---|---|
| 约定密度高 | 命名、令牌、错误码 | 隐式知识无法从参数记忆获得 |
| 依赖图深 | 中间层配置、封装参数 | 表层与生效层跨度大 |
| 测试反馈慢 | 运行期行为 | 错误延迟暴露,循环轮次增加 |
| 多团队协作 | 跨模块接口字段 | 权威来源分散,检索覆盖不全 |
表 2:大型代码库的放大机制。来源:本文归纳,非特定数据集统计。
4.4 本文评述
笔者认为,“大型代码库”不是一个规模变量,而是一个证据分布变量:权威来源越分散、越隐式、越远离调用点,编造的概率越高。这一视角的价值在于,它把“仓库太大”这种无力感的描述,转化为“证据可达性”这一可优化的工程指标。
5. 可观测信号:如何识别“编造”与“诊断循环”
5.1 编造的四类可检测信号
第一,标识符未命中:输出中出现的变量名、配置键、字段名在仓库索引中不存在。第二,来源缺失:实现值附近没有对应的文件读取或检索记录。第三,约定偏离:命名风格与仓库既有约定不一致(可通过规则或小型分类器检测)。第四,版本漂移:引用的依赖版本与锁文件不一致。
5.2 诊断循环的可检测信号
核心判据是信息增益:连续若干轮中,代理是否执行了能产生新信息的动作。工程上可定义“信息增益动作”为:读文件、检索、运行测试、查看日志、查询类型定义。若连续 N 轮(建议 N=2 或 3)无此类动作,即判定为诊断循环并触发干预。
# 伪代码:诊断循环检测器
INFO_GAIN_ACTIONS = {read_file, search_repo, run_test, view_log, query_type}
def is_diagnostic_loop(trace, window=3):
recent = trace[-window:]
return all(step.action not in INFO_GAIN_ACTIONS for step in recent)
def on_loop_detected(trace):
# 强制注入一次证据获取动作,而非继续生成诊断文本
return force_action("search_repo", query=extract_key_identifiers(trace))
5.3 本文评述
这些信号的价值在于它们不依赖模型自省。要求模型“检查自己是否在编造”往往无效,因为编造与自信在生成层面是同源的。外部可观测信号则提供了独立判据,这也是后文所有拦截机制的基础。
6. 拦截体系总览:四层证据接地架构
基于上述机制与信号,本文提出四层拦截体系。四层并非并列,而是按干预时机递进:越靠前,成本越低、收益越高;越靠后,越接近兜底。
7. 第一层:检索前置与上下文工程
7.1 最小充分证据集的构造步骤
步骤一,任务解析:从用户请求中抽取实体(组件名、配置项、接口路径、错误码)。步骤二,符号检索:用精确匹配与模糊匹配并行检索这些实体,优先返回定义处而非引用处。步骤三,邻域扩展:对命中文件做一跳邻域扩展(导入、被导入、同目录约定文件)。步骤四,裁剪与排序:按与任务的相关性排序,控制在模型有效利用区间内,避免“lost in the middle”。
# 证据集构造(示意)
1. entities = extract_entities(request) # 组件名/配置键/接口路径
2. defs = symbol_search(entities, kind="definition")
3. refs = symbol_search(entities, kind="reference", limit=5)
4. neigh = one_hop_neighbors(defs) # 导入/被导入/同目录
5. evidence = rank_and_truncate(defs + refs + neigh, budget=TOKEN_BUDGET)
6. assert evidence.covers(entities), "证据不足,禁止生成实现值"
7.2 权威来源的优先级排序
并非所有文件同等权威。建议的优先级为:类型定义与接口契约 > 设计令牌与主题文件 > 配置 schema 与锁文件 > 迁移脚本 > 测试用例 > 文档 > 注释。测试用例之所以排在文档之前,是因为测试是可执行的约定,而文档可能过期。
7.3 本文评述
笔者认为,检索前置的关键不是“检索更多”,而是检索到定义处。大量工程实践中的检索失败,源于返回了引用处而非定义处,模型看到的是“使用方式”而非“权威定义”,于是用参数记忆补全定义,编造由此发生。
延伸阅读:RAG 原始论文(arXiv)、Lost in the Middle(arXiv)。
8. 第二层:工具强制与动作约束
8.1 “先取证后生成”的协议约束
在代理的协议层加入硬约束:当任务涉及修改既有标识符(CSS 变量、配置键、接口字段)时,生成实现值之前必须至少有一次针对该标识符的检索调用,且检索结果需被引用。这可以通过工具调用 schema 与后处理校验共同实现。
8.2 诊断循环的强制打断
当检测器判定进入诊断循环时,不应继续让模型生成文本,而应强制注入一次信息增益动作:以失败信息中的关键标识符为查询,执行检索或读文件,把结果注入上下文后再允许继续。这一机制把“廉价诊断”替换为“强制取证”。
8.3 工具设计的可用性要点
工具强制的有效性依赖工具本身的可用性。检索工具应支持按定义/引用过滤、支持正则与符号混合查询、返回结果带文件路径与行号。读文件工具应支持按符号定位而非整文件读取,降低上下文占用。
8.4 本文评述
笔者认为,工具强制是四层中性价比最高的一层:它不改变模型能力,只改变动作顺序,却能直接切断“不读就写”的路径。其代价是增加了少量延迟,但相比诊断循环消耗的轮次,净收益为正。
延伸阅读:Anthropic:构建有效代理、Model Context Protocol 文档。
9. 第三层:验证门禁与运行时护栏
9.1 标识符存在性校验
在提交前,对 diff 中新增的标识符做存在性校验:CSS 变量需在主题文件中存在,配置键需在 schema 中定义,接口字段需在类型定义中出现。未命中则阻断提交并提示代理重新取证。
# CI 门禁示例(伪代码)
added = extract_added_identifiers(git_diff)
for ident in added:
if ident.kind == "css_var" and not exists_in_theme(ident):
fail(f"未定义的 CSS 变量: {ident}")
if ident.kind == "config_key" and not in_schema(ident):
fail(f"未定义的配置键: {ident}")
if ident.kind == "api_field" and not in_types(ident):
fail(f"未定义的接口字段: {ident}")
9.2 运行时护栏
对于无法在静态阶段覆盖的情形,可在运行时加入护栏:对未定义配置键记录告警而非静默忽略,对未命中字段的请求做显式报错。这能把“静默失效”转化为“快速失败”,缩短反馈延迟。
9.3 本文评述
验证门禁的价值在于它把判据从“模型是否自信”转移到“标识符是否存在”。后者是可自动化、可复现的客观判据。笔者认为,团队应优先实现标识符存在性校验,因为它覆盖了编造失败中占比最高的部分,且实现成本低。
10. 第四层:评测基准与回归度量
10.1 编造率的定义与度量
建议定义编造率(Fabrication Rate)为:在给定任务集中,输出包含至少一个未命中标识符的任务占比。与之配套的指标包括:取证覆盖率(生成前完成检索的任务占比)、诊断循环发生率、平均轮次、首次通过率。
| 指标 | 定义 | 用途 |
|---|---|---|
| 编造率 | 含未命中标识符的任务占比 | 核心回归指标 |
| 取证覆盖率 | 生成前完成检索的任务占比 | 衡量第一、二层效果 |
| 诊断循环发生率 | 出现无信息增益轮次的任务占比 | 衡量第二层效果 |
| 首次通过率 | 首轮提交即通过测试的占比 | 综合效果 |
表 3:建议的度量指标。来源:本文设计,属工程建议而非数据集统计。
10.2 仓库级任务集的构建与预处理
任务集应来自真实 issue 与提交历史,构建时需做如下预处理:剔除仅涉及文档的 issue;剔除依赖外部私有服务的任务;对每个任务标注涉及的权威来源文件;对 diff 中的标识符做存在性标注,形成金标准。若使用模拟数据,应在文中明确标注为模拟数据。
10.3 本文评述
笔者认为,当前公开基准多聚焦“能否解出任务”,对“是否编造”覆盖不足。仓库级评测应把编造率作为一等公民指标,与通过率并列报告。否则,一个通过率高但编造率高的代理,会在生产环境中制造难以定位的静默故障。
延伸阅读:SWE-bench 官方站点、SWE-bench 论文(arXiv)。
11. 实施路线:从两周试点到团队级推广
11.1 两周试点清单
第一周:实现标识符存在性校验(第三层),接入 CI;统计基线编造率。第二周:实现诊断循环检测器(第二层),在代理框架中注入强制取证动作;对比试点前后指标。试点范围建议选择一个中等规模、约定清晰的前端或后端模块。
11.2 团队级推广的四个阶段
阶段一,工具化:把检索前置与校验封装为团队共享工具。阶段二,制度化:把编造率纳入代理使用的准入指标。阶段三,评测化:建立仓库级任务集与回归流水线。阶段四,文化化:在 code review 中显式检查“实现值是否有来源”。
11.3 本文评述
笔者认为,推广的最大阻力不是技术,而是“通过率幻觉”:团队看到代理能解出大部分任务,就默认其可靠。引入编造率指标的意义,正是把不可见的静默故障显性化,让可靠性讨论有共同语言。
12. 对比与局限:四层体系能拦住什么、拦不住什么
12.1 能拦住的部分
标识符层面的编造(CSS 变量、配置键、接口字段、依赖版本)是四层体系的主要覆盖对象,其中第三层的存在性校验召回率最高。诊断循环可通过第二层的信息增益判据有效打断。检索前置能显著降低“未读就写”的发生率。
12.2 拦不住的部分
第一,语义层编造:标识符存在但语义被误用,例如把某个配置键用在错误的层级。第二,跨仓库约定:权威来源在另一个仓库,当前仓库索引无法覆盖。第三,新引入的合法标识符:代理确实需要新增一个变量,此时存在性校验会误报,需要人工或规则区分“新增”与“编造”。
12.3 本文评述
笔者认为,承认局限比宣称全覆盖更重要。四层体系的定位应是“把高发、可自动化的编造挡在提交之前”,而非消灭所有错误。对于语义层与跨仓库约定,仍需人工 review 与更细粒度的契约测试配合。
13. 结论与展望
本文以“证据接地”为主线,把“编造而非推导”从一种模糊的模型缺陷,还原为可观测、可度量、可拦截的工程问题。核心结论有三:其一,该失败模式的根因是证据获取环节被跳过,而非单纯的推理错误;其二,大型代码库通过约定密度、依赖深度与反馈延迟三重机制系统性放大该失败;其三,四层拦截体系应按干预时机递进部署,其中工具强制与存在性校验性价比最高。
展望未来,三个方向值得关注。第一,过程监督:对代理的取证动作序列进行监督,而不仅监督最终输出。第二,仓库级检索的符号化:把类型系统、构建图与检索结合,提升定义处召回。第三,编造率作为标准指标:推动评测社区把编造率纳入代理基准。笔者认为,这三者的共同指向仍是同一条主线——让每一个实现值都能追溯到证据。
14. 主要参考文献
- Lewis P, et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. NeurIPS 2020. arXiv:2005.11401.
- Liu N F, et al. Lost in the Middle: How Language Models Use Long Contexts. TACL 2024. arXiv:2307.03172.
- Jimenez C E, et al. SWE-bench: Can Language Models Resolve Real-World GitHub Issues? ICLR 2024. arXiv:2310.06770.
- Yang J, et al. SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering. NeurIPS 2024. arXiv:2405.15793.
- Yao S, et al. ReAct: Synergizing Reasoning and Acting in Language Models. ICLR 2023. arXiv:2210.03629.
- Schick T, et al. Toolformer: Language Models Can Teach Themselves to Use Tools. NeurIPS 2023. arXiv:2302.04761.
- Ji Z, et al. Survey of Hallucination in Natural Language Generation. ACM Computing Surveys, 2023.
- Anthropic. Building Effective Agents. 2024. https://www.anthropic.com/engineering/building-effective-agents
- Model Context Protocol. 官方文档. https://modelcontextprotocol.io/
说明:本文参考文献与资料累计 60 余篇(含上述主要文献及文中链接的教程、文档与论文),其中近三年(2023—2025)文献占比超过 50%。文中涉及的任务集与指标为工程建议,若使用模拟数据已在相应位置标注;未虚构作者或团队实验作为数据来源。
内容仅供学习参考。如需引用,请以原始文献为准。 全文约 15600 字 | 参考文献 60 余篇(主要 9 篇)。

