ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

防止LLM“传话游戏”:大模型信息保真设计与校验实践

防止LLM“传话游戏”:大模型信息保真设计与校验实践 1. 背景当 LLM 开始传话想法是怎么一步步走样的做 LLM 应用的同学大概率都经历过这样的时刻评审会上产品经理信誓旦旦地说这个方案就是我们最初要的东西你回头翻一下对话记录发现模型已经在三轮改写里把给老用户发券悄悄改成了给所有用户推广告。这不是哪个人记错了而是你的原始想法在 LLM 链路里被玩了一轮传话游戏——每一步看起来都合理最后拼起来却完全不是当初那个意思。传话游戏Telephone Game的规则很简单一群人排成一列第一个人想一句话悄悄传给下一个人依次小声复述传到最后公布的话往往和最初的话相差十万八千里。LLM 应用链路的可怕之处在于它天然就是一台高速传话机Agent A 总结一段内容传给 Agent BAgent B 再提炼成摘要传给 Agent C中途还穿插着帮我把这段改得更精炼按你的理解扩展一下这类指令。每一轮模型都在做有损压缩和再理解丢掉的细节不会自己回来新增的错误却会逐级放大。更麻烦的是很多人会把模型输出的结果直接当成我的想法。需求文档经过一次概括、二次润色、三次转成技术方案原始诉求已经被层层包裹再想追溯就非常困难。本文将围绕Dont Let LLMs Play Telephone with Your Ideas这条主线先拆解 LLM 链路中信息失真的典型场景和底层原因再给出从提示词设计、结构化输出、引用校验到自动化检查的完整保真方法最后用一套可直接运行的 Python 管线示例演示落地过程。无论你是在做多智能体应用、RAG 问答、自动化报告还是简单的想法→方案生成工具这套思路都能帮上忙。2. LLM 信息失真的典型场景2.1 多智能体协作中的消息接力失真多智能体是传话游戏的重灾区。假设你有三个 Agent需求分析 Agent、方案设计 Agent、代码生成 Agent。需求分析 Agent 读完一段用户描述先用自然语言输出用户需要一个支持团队协作的简单笔记工具。方案设计 Agent 拿到这段描述开始补充它应该支持多人编辑、实时同步、权限管理。代码生成 Agent 再往下走可能就直接开始设计数据库表结构了。整个过程看起来每一步都很合理但原始需求里可能根本没有多人编辑实时同步。这些内容是中间 Agent 基于自己的先验知识补进去的。补进去不算错问题是链路里没有人区分原文说了什么和模型猜了什么。一次补充可能无伤大雅三次、五次接力之后模型已经在替你做产品决策了。2.2 多轮迭代改写中的语义漂移生成式应用最常见的交互模式是生成→反馈→再生成。用户让模型写一段产品介绍模型写完初稿用户说再口语化一点模型改完用户又说顺便突出一下价格优势——这时候价格优势可能根本不是用户最初想要的卖点只是模型顺着上下文接上了话。这种逐轮微调造成的语义漂移叫 semantic drift。单轮变化往往很小人眼很难察觉但累积起来非常可怕。真实的项目里我见过一个案例最初的 prompt 是总结这篇论文的局限性经过五六轮展开举例说明意义之后输出变成了一篇像模像样的研究意义综述最初要的局限性分析几乎消失。根因是所有轮次都只参考上一轮输出而没有把第一轮的目标重新锚定在上下文里。2.3 记忆压缩与摘要级联丢失长对话、长文档处理场景里上下文窗口装不下全部内容时常见的做法是做摘要或记忆压缩先把 10000 字压缩成 1000 字再压缩成 200 字最后模型基于这 200 字回答用户问题。摘要每次都是有损压缩这在信息论层面就无可避免。压缩会倾向于保留整体感觉丢掉精确数字边界条件反例。比如原始文档里写着该方案在数据量小于 10 万条时可用压缩后变成该方案适用于中小规模数据。下一次再压缩中小规模可能变成大多数情况。当最终结论被用到决策里时错误的边界条件会带来完全超出预期的后果。这就是典型的摘要级联信息丢失。2.4 从需求到代码、从想法到文档的映射失真LLM 经常被用来做翻译把一段模糊的产品想法翻译成需求文档把需求文档翻译成技术方案把技术方案翻译成代码。一次翻译可以接受因为翻译总是要加信息、减信息、调整结构。但现实中这些翻译往往是流水线式的中间产物会不断被下一环节当作唯一事实来源。举个例子用户说我想做一个记录灵感的应用要快不要复杂。模型把它翻译成需求文档MVP 包含文本记录、标签、搜索、Markdown 支持。下一层翻译成技术选型时模型直接锁定了某个数据库、某个前端框架。用户那句不要复杂在整个映射链路上早就被稀释了最后交付的东西可能恰恰是最复杂的那一种。映射失真比摘要失真更隐蔽因为它是合理推导不是明显遗漏。3. 失真的底层原因3.1 概率生成不承诺保真大语言模型的本质是根据上下文预测下一个 token 的概率分布。它没有内置的信息守恒约束也没有这句话必须等价于上一句的强制机制。模型在生成时优化的是流畅性和相关性不是语义等价性。因此无论你希望它忠实原文还是准确总结都必须在外部显式地施加约束仅靠一句请忠实原文是不够的。3.2 摘要天然是有损压缩从信息论角度看把长文本压缩为短文本必然丢弃信息。压缩算法决定保留什么、丢掉什么而 LLM 的保留偏好由训练数据决定不一定符合你的业务优先级。你关心的数字、限定词、例外情况在模型眼里可能不如一个漂亮的概括重要。这也是为什么摘要级联场景必须引入外部校验而不能信任模型自己压缩自己。3.3 上下文窗口与截断上下文窗口是硬约束。一旦超长常见做法是截断、滑动窗口、向量检索。截断直接丢掉尾部内容滑动窗口把早期内容挤出上下文向量检索虽然能召回相关片段但召回本身就存在误差。原始想法如果落在没有被召回的区域模型就会直接在信息缺失的情况下继续生成——并且它不会主动告诉你我不知道这里有内容。3.4 链路缺少审计指针这是最核心的工程原因。很多 LLM 管线的数据流是输入文本 → 模型输出 → 下一轮模型的输入。每一轮的输出只保留内容不保留这个内容对应原文的哪一部分。没有引用、没有行号、没有来源标识后续环节就没有办法判断输出是否忠实。这种结构性的缺失让交叉核对变得不可能。要解决传话游戏问题第一步不是优化 prompt而是在系统里建立原始文本到衍生内容的显式引用关系。4. 保真设计的四个原则4.1 原始想法是唯一的源头事实工程上要把原始输入当作 source of truth事实源所有 LLM 输出都只是 derived artifact派生产物。原始文本存入后不可被模型输出覆盖衍生内容必须单独存储并带有来源标识。这就像 Git主干分支永远保留原始内容任何修改都开新分支确认无误后才允许合回主干。代码实现上哪怕只是把原始文本单独存一份、加个只读保护都能避免大量越改越远的问题。4.2 用结构化输出定义传输协议如果模型输出的是一大段自由文本下一环要从中理解含义就必然发生二次解读。更稳妥的做法是给模型定义一个严格的输出结构比如 JSON Schema让每一类信息落在固定的字段里。这样下一环可以直接读取字段而不是再次总结。结构化输出相当于给模型之间规定了接口协议字段名、类型、约束都写清楚解读自由度被大幅压缩。4.3 要求模型逐字引用原文在涉及关键信息时不要允许模型用自己的话转述而是要求它把原文片段原样引用出来。比如规定输出中的 quote 字段必须与输入文本逐字一致不允许改写。逐字引用把理解和复述分开了模型可以基于引用写解读但引用本身是死的、可校验的。程序上可以用一个简单的字符串匹配来检查模型是否真的引用了原文一旦发现引用不存在立刻告警而不是放它流到下一环。4.4 用相似度检查做自动防线人工审核永远不够快也不够稳定。在管线中加入自动化保真度检查至少有两层第一层是引用级校验检查 key point 的 quote 是否逐字存在于原文第二层是整体语义校验用文本相似度或 embedding 向量相似度衡量最终方案与原始想法的接近程度。相似度指标不完美但它的价值在于标出异常让开发者知道这一步的输出需要人工看一下而不是静默放行。5. 实战案例搭建一条想法保真管线下面我们实现一个完整的 Python 示例名为 IdeaKeeper。核心思路是输入一段原始想法 → 模型生成结构化分析必须逐字引用原文→ 程序校验引用是否真实存在 → 模型基于原始文本 结构化分析生成扩展方案 → 计算方案与原始想法的相似度并输出报告。整个管线刻意把原始文本一路携带到最后避免只看上一轮摘要的传话问题。5.1 项目结构与依赖示例采用 Python 3.10依赖库如下openai pydantic numpy python-dotenv实际部署时建议锁定版本例如openai1.30.0、pydantic2.0.0。项目结构idea_keeper/ ├── main.py # 主流程 ├── schemas.py # 结构化输出定义 ├── prompts.py # 提示词模板 ├── fidelity_check.py # 保真校验模块 └── requirements.txt5.2 定义结构化输出 Schema先用 Pydantic 定义模型必须输出的 JSON 结构。关键设计是quote字段必须逐字引用原文fidelity_risk字段强制模型说明这一步可能发生什么失真。# 文件路径idea_keeper/schemas.py from typing import List from pydantic import BaseModel, Field class KeyPoint(BaseModel): 一个关键要点必须包含原文逐字引用 quote: str Field( ..., description原文逐字引用必须与输入内容完全一致不能改写, ) interpretation: str Field( ..., description基于引用内容的理解与解读, ) fidelity_risk: str Field( ..., description这一步可能发生的失真风险说明, ) class IdeaAnalysis(BaseModel): 结构化分析结果 title: str Field(..., description给原始想法起的简短标题) key_points: List[KeyPoint] Field( ..., description关键要点列表每个要点必须引用原文, ) constraints: List[str] Field( ..., description后续处理必须遵守的保真约束, )为什么要给quote单独设字段因为如果你不规定引用的位置模型就会把引用和解读混在一起输出程序无法区分哪些是原文、哪些是模型加的内容。结构化字段等于给信息划分了明确的抽屉下一环节取用起来才不会乱。5.3 编写提示词模板提示词里最关键的规则是必须逐字引用、不得改写、不得补充原文没有的事实、歧义必须写进风险字段而不是自行消除。# 文件路径idea_keeper/prompts.py ANALYSIS_SYSTEM_PROMPT 你是一名需求分析助手。你的任务是分析用户提供的原始想法但必须严格遵守以下规则 1. 不得改写、概括或重述原始想法所有关键信息必须逐字引用原文。 2. 输出必须符合 JSON 结构字段含义以 schema 为准。 3. 如果原文存在歧义请把歧义写入 fidelity_risk而不是自行消除歧义。 4. 不要补充原文中没有的事实。 原始想法如下 {raw_idea} .strip() EXPANSION_SYSTEM_PROMPT 你是一名方案设计助手。你拿到的是已经过结构化的分析结果以及分析时引用的原始文本。 规则 1. 方案中的所有核心结论必须能追溯到原始文本或分析结果。 2. 涉及原始需求时必须引用原文不能用自己的话替代。 3. 如果原始文本没有提供的信息不允许编造只能标记为待确认。 4. 输出的扩展方案结构如下目标、假设、步骤、风险、待确认项。 原始文本 {raw_idea} 分析结果JSON {analysis_json} .strip()注意第二阶段 prompt 里同时放入了原始文本和结构化分析。这是故意为之扩展方案可以基于分析进行延伸但原始文本始终在场模型随时可以回看原始表达。这就是对抗传话游戏的核心手段——不要让消息只以上一个人的版本形式传递。5.4 实现保真校验校验逻辑需要完成两件事第一检查模型输出的引用是否真的逐字存在于原文第二提供一个相似度兜底指标供管线在缺少 embedding 接口时也能做快速检查。# 文件路径idea_keeper/fidelity_check.py from difflib import SequenceMatcher from typing import List def check_quotes_exist(raw_idea: str, quotes: List[str]) - List[dict]: 程序化校验每个 quote 是否逐字出现在原文中 results [] for quote in quotes: results.append({ quote: quote, exists: quote in raw_idea, similarity: SequenceMatcher(None, quote, raw_idea).ratio(), }) return results def text_similarity(text_a: str, text_b: str) - float: 基于字符 n-gram 的相似度作为无 embeddings 时的兜底指标 def ngrams(text: str, n: int 3) - set: text text.strip() if len(text) n: return {text} return {text[i:i n] for i in range(len(text) - n 1)} if not text_a or not text_b: return 0.0 grams_a, grams_b ngrams(text_a), ngrams(text_b) if not grams_a or not grams_b: return 0.0 inter len(grams_a grams_b) return inter / max(len(grams_a), len(grams_b)) def semantic_similarity(client, text_a: str, text_b: str, embed_model: str) - float: 使用 embeddings 计算语义相似度接口以实际环境为准 import numpy as np resp client.embeddings.create( modelembed_model, input[text_a, text_b], ) vec_a np.array(resp.data[0].embedding) vec_b np.array(resp.data[1].embedding) return float(vec_a vec_b / (np.linalg.norm(vec_a) * np.linalg.norm(vec_b)))check_quotes_exist是整个管线里最硬的防线。它不管模型解释得好不好只检查引用是否真实。如果模型没有逐字引用说明它要么没遵守规则要么原文信息已经丢失这时候必须停下来处理不能再往下一步传。5.5 编写主流程主流程把上面三部分串起来调用模型分析、校验引用、生成扩展方案、输出保真报告。# 文件路径idea_keeper/main.py import json import sys from openai import OpenAI from schemas import IdeaAnalysis from prompts import ANALYSIS_SYSTEM_PROMPT, EXPANSION_SYSTEM_PROMPT from fidelity_check import check_quotes_exist, text_similarity # 模型名称与 API 地址请按实际部署环境修改以下仅为示例写法 DEFAULT_MODEL your-model-name def call_llm(client: OpenAI, system_prompt: str, user_prompt: str, model: str DEFAULT_MODEL) - str: 调用 chat completions 接口并返回文本内容 resp client.chat.completions.create( modelmodel, messages[ {role: system, content: system_prompt}, {role: user, content: user_prompt}, ], temperature0.2, ) return resp.choices[0].message.content def extract_json(text: str) - dict: 从模型输出中提取 JSON 对象 start text.find({) end text.rfind(}) 1 if start -1 or end 0: raise ValueError(模型输出中未找到 JSON 对象) return json.loads(text[start:end]) def parse_analysis(client: OpenAI, raw_idea: str, model: str) - IdeaAnalysis: 第一步把原始想法结构化为带引用的分析结果 user_prompt 请分析下面这段原始想法并按照 JSON 结构输出\n\n raw_idea output call_llm(client, ANALYSIS_SYSTEM_PROMPT, user_prompt, model) return IdeaAnalysis.model_validate(extract_json(output)) def generate_plan(client: OpenAI, raw_idea: str, analysis: IdeaAnalysis, model: str) - str: 第二步基于原始文本 结构化分析生成扩展方案 user_prompt ( 请基于以下材料生成扩展方案\n\n原始文本\n raw_idea \n\n分析结果JSON\n analysis.model_dump_json(indent2) ) return call_llm(client, EXPANSION_SYSTEM_PROMPT, user_prompt, model) def run_pipeline(client: OpenAI, raw_idea: str, model: str DEFAULT_MODEL) - None: 完整保真管线入口 print( * 20, 第一步结构化分析, * 20) analysis parse_analysis(client, raw_idea, model) print(json.dumps(json.loads(analysis.model_dump_json()), ensure_asciiFalse, indent2)) print(\n, * 20, 引用校验, * 20) quotes [kp.quote for kp in analysis.key_points] for item in check_quotes_exist(raw_idea, quotes): print(f引用存在: {item[exists]} | 相似度: {item[similarity]:.3f} | {item[quote][:50]}) print(\n, * 20, 第二步扩展方案, * 20) plan generate_plan(client, raw_idea, analysis, model) print(plan[:2000]) sim text_similarity(raw_idea, plan) print(\n, * 20, 保真度检查, * 20) print(f方案与原始想法的字符级相似度参考值{sim:.3f}) if sim 0.15: print(警告相似度过低建议人工核对方案是否偏离原始想法。) else: print(提示当前指标在可接受范围建议结合语义检查进一步确认。) if __name__ __main__: client OpenAI() # 从环境变量读取 API Key 与 Base URL raw_idea sys.argv[1] if len(sys.argv) 1 else ( 我想做一个只面向团队内部的知识管理工具 强调想法不被改写所有 AI 输出必须可追溯并且不能自动覆盖人工记录。 ) run_pipeline(client, raw_idea)代码里的extract_json是兜底解析因为部分模型即使收到 JSON 指令也会在答案前后加说明文字。取第一个{到最后一个}之间的内容再交给 Pydantic 校验是工程上比较稳妥的容错方式。5.6 运行与结果说明在配置好 API Key 后直接执行cd idea_keeper python main.py预期输出分四段结构化分析一段合法的 JSON包含 title、key_points、constraints其中每个 key_point 的 quote 字段都能在原始文本中找到。引用校验程序逐条打印引用存在: True如果出现False说明模型没有忠实引用需要调整模型或提示词。扩展方案结构化文本包含目标、假设、步骤、风险、待确认项。保真度检查给出相似度参考值低于阈值时明确告警。这套管线的设计意图不是让模型输出完美而是让失真变得可发现、可追踪。它不能阻止所有信息丢失但可以在丢得过多时拦住流程把问题暴露在人的面前。6. 常见问题与排查思路问题现象常见原因解决思路模型输出大量改写核心要点丢失提示词没有强制引用原文在提示词中要求逐字引用并用代码校验结构化 JSON 解析失败模型输出夹杂说明文字或字段名漂移增加 JSON 提取兜底或启用模型自带的 JSON 模式多轮迭代后方案偏离原始需求每一轮都把上一轮输出当输入每轮携带原始文本并用引用指针做对齐长文本被截断尾部需求丢失上下文窗口超限分块 检索保留结构索引避免直接截断相似度指标很低但人工看起来正常字符级指标无法识别同义改写改用 embedding 语义相似度并配合人工抽检人工记录被 AI 输出覆盖系统把派生内容写回原始库原始表只读派生内容单独存储并记录来源这里单说两个高频问题。第一个是我明明在 prompt 里写了忠实原文为什么还是乱改。原因是忠实原文没有定义什么是原文、什么是解读、用什么格式输出。只有把逐字引用变成一个独立字段并写代码去校验约束才真正生效。第二个是相似度指标到底怎么定阈值。字符级相似度和语义相似度的分布差异很大不要直接套网上抄来的阈值。正确做法是先跑一批历史数据画出分数分布再选取能让明显偏离都落在阈值之外的数值并且保留人工抽检通道。7. 最佳实践与工程建议如果要把这套保真思路放进更大的生产系统下面这些原则非常值得参考。第一把原始输入当成 Git 主干。原始想法、原始需求、原始用户反馈都应该以不可变的方式存储任何 LLM 生成内容都是分支。用户确认前的所有 AI 产物不得写回原始表。实现上可以给原始表加只读账号或者用消息日志保存每次变更历史。第二为每个模型输出记录出处元信息provenance。至少包含原始输入哈希、模型名称与版本、prompt 版本号、温度参数、生成时间、保真度检查分数。这样一旦线上出现方案跑偏你能快速回放管线定位是哪一环开始失真的。哈希值可以用 sha256 对原文计算存一个短前缀即可。第三用 Git 管理提示词版本。提示词是代码的一部分修改后要能回滚。建议每次改动都 commit并把 prompt 版本号写进调用日志。线上出问题时查最近一次 prompt 变更往往是最高效的排查入口。第四定义好上下文中的引用协议。多智能体协作时Agent 之间传递的消息不要只有重述后的文本至少要包含原文片段 来源定位。比如传一段结论时附带source: doc_idchapter_3让下游 Agent 有据可查。第五把保真校验做成流水线关卡。引用检查、相似度检查、schema 校验可以写成一个独立的校验函数在 CI/CD 或应用启动前置步骤里执行。凡是不达标的输出不允许进入下游环节必须打回重跑或转人工。第六日志要能回答这段内容来自哪里。每条日志记录输入摘要、输出摘要、校验分数、异常标记。不要只记错误正常通过的结果也要保留样本后续优化阈值全靠这些历史数据。第七高风险动作必须人工确认。涉及删除、覆盖、发消息、改配置等操作时不要信任模型自己判断。可以把模型输出做成建议草案由人工点击确认后再执行并记录操作人、操作时间、审核意见。第八注意隐私与最小权限。原始想法可能包含敏感信息传给外部模型前要做脱敏或权限校验。调用日志也需要控制访问范围避免敏感内容二次泄露。8. 别忘了给想法留一条主干道回到标题那句话不要让你的想法在 LLM 里玩传话游戏。技术上的解法和传话游戏的破解方法其实很像——让每个人都能看到最初的那句话而不是只听上一个人的转述。具体到工程实现就是原始文本始终在场、输出结构固定成契约、引用可以被程序校验、失真能被自动发现。在下一个 LLM 项目里你可以先从最小动作开始把原始输入单独存一份只读文件让所有下游环节的 prompt 都带上原文 派生结果再写一个check_quotes_exist这样的校验函数拦住明显的偏离。随着管线复杂度增加再逐步补充 provenance 元数据、语义相似度、人工复核关卡。你会发现多数方案跑偏问题不是模型能力不够而是数据流设计里从没给原始想法留过一条不可改写的主干道。把这条主干道补齐LLM 做得再快也只是在它自己的分支上做贡献而不是替你重写想法。
返回列表