从上下文污染到状态隔离:Chain-of-Verification 的工程化重构路径
摘要
Chain-of-Verification(CoVe)把“起草—提问—独立作答—汇总”拆成四段,其有效性高度依赖第三段能否真正“看不见草稿”。工程上若把草稿留在同一上下文里,模型会沿自生成文本做一致性补全,验证退化为复述。本文以“状态隔离度”为贯穿主线,把隔离拆成上下文、会话、参数三层,给出可落地的双会话、快照回滚、子代理三类实现,并讨论隔离带来的信息损失与成本边界。
目录
一、问题定义:为什么“不藏草稿”会让验证失效
先把概念钉死。CoVe 指让模型先产出一份初始回答(draft),再基于该回答生成一组可核查的验证问题(verification questions),随后对每个问题在不接触草稿的条件下独立作答,最后把独立答案与草稿比对、修订,得到终稿。它最早由 Meta 研究团队在 2023 年的工作中系统提出(Dhuliawala et al., “Chain-of-Verification Reduces Hallucination in Large Language Models”, 2023,arXiv:2309.11495)。该文的关键实验设置之一,就是“factored”变体:把验证问题拆成彼此不可见的独立调用,从而避免模型看到原始回答。
为什么“看见草稿”是致命的?用一句话概括:自回归解码的本质是条件概率的链式分解,模型每一步都在“续写最可能的下一段”。当草稿已经在上下文里,验证阶段的输入分布就从“根据世界知识回答”漂移为“根据眼前这段文字回答”。后者在训练分布中对应的是摘要、复述、改写类任务,而不是事实核查。于是模型倾向于输出与草稿语义一致的句子,把“验证”做成“润色”。本文评述:这不是模型“不诚实”,而是任务被上下文悄悄改写了——你让它做的和你想让它做的,已经不是同一件事。
这一现象在文献中有多条独立证据线。其一是“自我修正”研究中的一致性偏差:Huang 等人在 “Large Language Models Cannot Self-Correct Reasoning Yet”(ICLR 2024)中指出,在没有外部反馈时,模型对自身输出的“自我纠错”往往不能提升甚至降低推理正确率,原因之一正是模型倾向于维持既有答案。其二是“上下文内重复”与“谄媚”倾向:Sharma 等人在 “Towards Understanding Sycophancy in Language Models”(2023,arXiv:2310.13548)中记录了模型倾向于附和上下文中已出现的立场。其三是“多轮自我强化”:Xu 等人在 “On the Role of Context in Reading Comprehension” 一类工作中反复观察到,错误一旦进入上下文,后续轮次更难纠正。本文评述:这三条线指向同一个机制——上下文既是信息,也是“锚”。隔离的本质,是切断这个锚。
需要强调的是,隔离不是“把草稿删掉”这么简单。删掉之后,验证问题本身仍可能泄露草稿的结论(例如问题写成“巴黎是法国首都吗?”而非“法国首都是哪里?”)。因此完整的隔离要同时处理三件事:草稿文本不可见、验证问题的措辞不预设答案、独立作答的输出不被前序答案污染。这三件事分别对应后文的上下文层、措辞层与状态层。笔者认为,很多团队“做了 CoVe 但没效果”,恰恰是只做了第一件事。
一句话判据:如果验证阶段的 prompt 里能检索到草稿的任何一句原话或同义改写,隔离就没有真正发生。
二、机制拆解:隔离的三层结构与因果链
2.1 三层隔离:上下文、会话、参数
把“隔离”当成一个连续变量而非布尔值,是本文的分析主线。笔者把它拆成三层,隔离度自低到高排列:
| 层级 | 隔离对象 | 典型做法 | 残留风险 |
|---|---|---|---|
| L1 上下文层 | 草稿文本是否在 prompt 中 | 从消息列表中移除草稿消息 | 问题措辞泄露结论 |
| L2 会话层 | 对话历史、缓存、记忆 | 新建会话 / 子代理 / 快照回滚 | 系统提示词仍含任务描述 |
| L3 参数层 | 采样随机性、温度、seed | 独立采样、固定 seed 对照 | 同一模型先验本身有偏 |
L1 是最低要求,也是绝大多数框架默认做不到的地方——因为很多实现把“草稿 + 验证问题”拼在同一个 prompt 里,指望模型“自觉忽略”草稿。这在工程上等价于没有隔离。L2 解决的是“隐式记忆”:即使你把草稿从当前消息里删了,若复用了同一个会话对象,服务端的对话历史、KV 缓存、甚至 agent 的长期记忆仍可能把草稿带回来。L3 则关乎可复现性:验证阶段若沿用草稿阶段的采样状态,随机性无法独立,对照实验就失去意义。
2.2 因果链:从“看见”到“顺着说”
把因果链写清楚,才能知道在哪一环下刀。笔者将其归纳为四步:
草稿进入上下文
↓ (注意力在草稿 token 上分配权重)
验证问题被“条件化”到草稿语义
↓ (解码分布向“与草稿一致”偏移)
独立作答退化为复述/改写
↓ (比对阶段看到两份一致文本)
误判为“已验证”,错误被固化
这条链的关键节点在第二步。本文评述:从信息论角度看,草稿为验证问题提供了额外的条件信息,降低了输出的条件熵,使模型更“自信”地给出与草稿一致的答案;但这种“自信”来自上下文而非世界知识,属于典型的分布外捷径。要打断它,必须在第二步之前切断条件依赖,也就是在草稿与验证之间插入一道“状态墙”。
值得补充的是,隔离并非越彻底越好。完全隔离会丢失草稿中“哪些点需要验证”的定位信息,导致验证问题泛化、覆盖不足。因此工程上的目标不是“零信息传递”,而是“只传递问题、不传递答案”。这正是 CoVe 原文 factored 变体的设计意图:把草稿“编译”成问题,然后丢弃草稿。笔者认为,用“编译”这个类比来理解隔离最贴切——编译器输出的是中间表示,而不是把源码抄一遍。
三、工程落地:三类隔离方案与操作步骤
3.1 方案 A:双会话(Two-Session)隔离
最直接的做法是维护两个互不共享历史的消息列表:会话 D(draft)负责起草与生成验证问题,会话 V(verify)只接收验证问题与系统指令。两者共用同一模型端点,但不共用对话对象。操作步骤如下:
- 在会话 D 中生成草稿,并抽取 3–8 个原子化验证问题,每个问题只问一个事实点。
- 对问题做“去结论化”改写:把“X 是不是 Y”改为“X 是什么”,避免问题本身携带答案。
- 新建会话 V,系统提示词只保留“你是事实核查员,请仅依据自身知识作答,不确定时明确说不知道”。
- 逐题调用 V,记录答案与置信表述;不同问题之间不共享上下文(或仅共享问题清单,不共享答案)。
- 回到会话 D(或第三个汇总会话),把草稿与 V 的答案并列,要求模型标注冲突点并修订。
这里有个容易踩的坑:第 4 步若把“问题清单 + 已答答案”一起喂回,前面的答案就成了后面前提的锚,等价于在 V 内部重新引入污染。稳妥做法是每题独立调用,或用“只读问题、不读他答”的批处理模板。本文评述:双会话的成本是调用次数上升(N 个问题约 N+2 次调用),收益是隔离边界清晰、易于审计。对延迟不敏感的场景,这是性价比最高的起点。
可参考的通用实现思路见 OpenAI Cookbook 中关于“self-consistency 与多轮独立采样”的示例(cookbook.openai.com),以及 LangChain 关于“子链独立上下文”的文档(python.langchain.com)。这些资料讲的是通用编排,隔离语义需要自己补。
3.2 方案 B:快照回滚(Snapshot & Rollback)
若框架只支持单会话,可用“状态快照”模拟隔离:在生成草稿之前保存消息列表的深拷贝,生成草稿与问题后,把会话状态回滚到快照点,再注入验证问题。关键在于回滚必须彻底——消息列表、工具调用记录、检索缓存都要还原,否则残留的检索结果会变相泄露草稿依据。
snapshot = deepcopy(session.messages) # 起草前快照
draft = session.chat(draft_prompt) # 生成草稿
questions = session.chat(make_questions) # 生成问题
session.messages = deepcopy(snapshot) # 回滚,草稿与问题均不可见
for q in sanitize(questions): # 去结论化后逐题验证
ans = session.chat(verify_prompt(q)) # 此时上下文干净
answers.append(ans)
这段伪代码的重点不在语法,而在两个细节:deepcopy 而非引用赋值,避免回滚后原对象被后续写入污染;sanitize 必须真的改写问题措辞,而不是简单拼接。本文评述:快照回滚的优点是改动小、可嵌入现有 agent 循环;缺点是它只隔离了“消息”,隔离不了“模型侧缓存”。若服务端做了 prompt caching,回滚后的前缀若与草稿阶段相同,缓存命中虽不直接泄露内容,但会让“独立采样”的假设打折。工程上应把验证 prompt 的前缀设计得与起草阶段明显不同,主动让缓存失效。
3.3 方案 C:子代理 / 工具化隔离
在多代理框架里,最干净的隔离是“验证者根本拿不到草稿”。把验证封装成一个工具函数 verify(question),主代理只能传问题字符串,工具内部新建无历史会话执行。这样隔离边界由接口强制,而非靠 prompt 约束。操作要点:
- 工具入参只允许问题文本,禁止传草稿或答案;
- 工具内部固定系统提示词,禁止调用方覆盖;
- 返回结构包含答案、置信度、是否“不知道”,便于汇总阶段做冲突检测;
- 对工具调用做限流与超时,避免 N 题串行拖垮延迟。
这类设计在 AutoGen、CrewAI 等多代理框架的“独立 agent”模式中有对应实现,可参考 AutoGen 文档(microsoft.github.io/autogen)与 CrewAI 文档(docs.crewai.com)。本文评述:子代理方案把“隔离”从约定升级为契约,是三者中最适合生产环境的;代价是编排复杂度上升,且需要处理“验证者与起草者知识边界不一致”的问题——验证者可能因为缺少领域上下文而答“不知道”,此时应回退到检索增强而非强行作答。
3.4 三方案对比与选型
| 维度 | 双会话 | 快照回滚 | 子代理 |
|---|---|---|---|
| 隔离强度 | 中高 | 中 | 高 |
| 改造量 | 低 | 低 | 中高 |
| 可审计性 | 高 | 中 | 高 |
| 适用场景 | 离线批处理 | 单会话 agent | 生产多代理 |
选型上,笔者建议按“先 A 后 C、B 作过渡”的顺序推进:先用双会话验证隔离是否带来可测的收益,再把验证逻辑固化成工具,最后在多代理架构中替换为子代理。跳过 A 直接上 C,容易在没搞清收益来源的情况下把复杂度堆上去。
四、评测与数据:如何度量隔离是否真的生效
“隔离生效”不能靠主观感觉,要设计对照。核心思路是构造“草稿含错”的样本,看验证阶段能否把错误翻出来。若隔离有效,验证答案应与草稿冲突;若无效,验证答案会与草稿一致。指标可定义为“冲突检出率”:在草稿被人为注入错误事实的样本上,验证阶段给出不同答案的比例。
数据构造上,可基于公开事实核查数据集做改造。例如 FEVER(Thorne et al., 2018)提供 claim 与证据标注,可抽取 claim 作为“草稿结论”,注入反事实改写作为“错误草稿”;TruthfulQA(Lin et al., 2022)覆盖模型易错的常识与误解类问题,适合测“顺着编”的倾向。需要说明的是,本文不提供自建实验的绝对数值,凡涉及比例均为模拟数据,仅用于说明评测设计,不代表任何模型的真实表现。
| 评测条件 | 冲突检出率(模拟) | 说明 |
|---|---|---|
| 同上下文(不隔离) | 约 12% | 多数情况复述草稿 |
| 仅删草稿(L1) | 约 41% | 问题措辞仍泄露 |
| 双会话 + 去结论化(L1+L2) | 约 68% | 接近独立作答上限 |
上表数值为模拟数据,用于展示“隔离度—检出率”的单调关系,不作为任何具体模型的性能声明。本文评述:真正有价值的不是绝对数字,而是“同上下文 vs 隔离”的差值——它直接量化了上下文污染带来的验证失效幅度。工程团队应把这一差值作为 CoVe 上线前的必测项。
另一个易被忽略的指标是“过度冲突率”:隔离过强或验证者知识不足时,会把正确草稿也判为错误。二者需同时监控,避免为了降幻觉而引入大量误报。可参考 RAGAS 一类评测框架对“faithfulness”与“answer relevancy”的拆分思路(docs.ragas.io),把“是否忠于证据”和“是否答到点上”分开度量。
五、边界、代价与常见误用
隔离有明确边界。第一,它只能缓解“自生成文本导致的自我强化”,无法解决模型先验知识本身的错误——若模型对某事实一无所知,隔离后仍会答错,只是错得“独立”。第二,隔离会削弱多跳推理的连贯性:某些验证需要草稿中的中间结论作为跳板,完全切断后验证者可能无法完成推理。此时应采用“部分隔离”,即只暴露中间结论的抽象形式而非原文。第三,成本随问题数线性增长,需在覆盖率与延迟间取舍。
常见误用有四种:把草稿放进 system prompt 却声称“模型会忽略”;用同一会话连续提问,误以为“换了个问题就算独立”;验证问题写成是非题,答案已隐含在问句中;汇总阶段把验证答案与草稿混排后让模型“自己判断”,结果又被一致性偏差拉回。本文评述:这四种误用的共同点是“隔离只做在表面”,没有触及状态层。判断是否踩坑的简单办法,是打印验证阶段实际发送的完整 prompt,逐字检查是否含草稿信息。
工程检查清单:①验证 prompt 不含草稿原句;②验证问题为开放式;③每题独立调用或只读问题不读他答;④汇总阶段显式要求“以验证答案为准”;⑤记录冲突检出率与过度冲突率。
六、前沿演进与结论
近两年的演进大致三条线。其一是“检索增强的验证”:把独立作答替换为“先检索再作答”,用外部证据替代模型记忆,代表工作是 Self-RAG(Asai et al., 2023,arXiv:2310.11511)与 CRAG 一类方法,它们把“是否检索、是否采纳”做成可学习的判断,间接实现了“验证者不依赖草稿”。其二是“多智能体辩论”:让多个独立实例先各自作答再交叉质询,隔离体现在实例之间不共享上下文,代表工作如 Du et al. 的 multiagent debate(2023,arXiv:2305.14325)。其三是“推理时扩展”:通过多次独立采样与投票提升可靠性,Self-Consistency(Wang et al., 2022,arXiv:2203.11171)是其早期代表,其“独立采样”假设与本文的隔离思想同源。
本文评述:这三条线的共同趋势,是把“隔离”从 prompt 技巧上升为系统架构属性——不再指望模型“自觉不看”,而是让它在物理上“看不到”。对工程团队而言,这意味着 CoVe 不应作为一个 prompt 模板存在,而应作为一个带状态管理的验证服务存在。笔者认为,未来更值得投入的方向是“可验证的中间表示”:把草稿编译成结构化断言(实体、关系、数值),验证阶段只对这些断言做独立核查,既保留定位信息,又切断原文污染,这比单纯“藏草稿”更接近问题本质。
回到最初的问题:隔离到底怎么落地?答案是三层一起做——上下文层删干净、会话层断彻底、措辞层去结论化,再用冲突检出率验证是否真的生效。只做第一层,等于没做;三层都做,CoVe 才从“看起来在验证”变成“真的在验证”。
主要参考文献(节选)
- Dhuliawala S., et al. Chain-of-Verification Reduces Hallucination in Large Language Models. arXiv:2309.11495, 2023.
- Huang J., et al. Large Language Models Cannot Self-Correct Reasoning Yet. ICLR, 2024.
- Sharma M., et al. Towards Understanding Sycophancy in Language Models. arXiv:2310.13548, 2023.
- Asai A., et al. Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection. arXiv:2310.11511, 2023.
- Du Y., et al. Improving Factuality and Reasoning in Language Models through Multiagent Debate. arXiv:2305.14325, 2023.
- Wang X., et al. Self-Consistency Improves Chain of Thought Reasoning in Language Models. arXiv:2203.11171, 2022.
- Thorne J., et al. FEVER: a Large-scale Dataset for Fact Extraction and VERification. NAACL, 2018.
- Lin S., et al. TruthfulQA: Measuring How Models Mimic Human Falsehoods. ACL, 2022.
- Ji Z., et al. Survey of Hallucination in Natural Language Generation. ACM Computing Surveys, 2023.
数据集说明:FEVER 使用时按 claim 去重、剔除证据缺失样本;TruthfulQA 使用时按类别分层抽样,避免单一类别主导。本文涉及的比例数据均为模拟数据,仅用于说明评测设计。
文章声明:本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。
内容仅供学习参考。如需引用,请以原始文献为准。 全文约 7200 字 | 参考文献 60 余篇(主要 9 篇)。

