很多人以为大模型幻觉是个玄学问题,觉得模型偶尔一本正经地胡说八道就是“AI不可靠”的铁证。但我在实际做知识库问答、客服机器人这类项目的时候发现,绝大多数幻觉并不是模型“智力不够”,而是你根本没把话说清楚。模型不知道你手里有什么资料、不知道你期望它站在什么立场回答、不知道哪些话可以编哪些话绝对不能编——于是它只能靠训练时留下的模糊记忆去“猜”,猜错了就成了幻觉。
这篇文章我想分享的是上下文工程的玩法:怎么通过提示词结构、检索增强、推理链路约束、采样策略和工具校验等手段,把幻觉从“时常发生”压到“偶发甚至不发生”。这些方法适合正在做对话应用、文档问答、数据助手的开发者和产品经理,也适合被客服AI坑过、想搞清楚问题根源的业务同学。我不会讲太多理论,重点是真实验证过的做法和容易踩的坑。
1. 幻觉从哪里来:先明白大模型为什么会“编”
幻觉的根源,得从大模型的工作方式说起。你在对话框里敲一句话,模型并不是“理解”了你的问题再去查资料,它做的事情本质上是接龙:根据已有的所有token,预测下一个最可能出现的词。训练阶段它读了海量文本,把规律压缩进了几千亿个参数里。这带来一个问题——参数化记忆不是数据库,它存的是“大概率的语言模式”,而不是“精确的事实条目”。
类比一下:模型像一个记忆力很好的同学,但不是每个细节都背下来了。你问它课本里写得清清楚楚的东西,它能答出来;你问它课本里只有半句话的内容,它会根据自己的理解“补全”成一个完整的答案。这个“补全”过程就是幻觉产生的瞬间。我做过一次内部测试,问一个文档问答机器人某设备的故障代码含义,文档里其实只提到了代码前缀的编码规则,但模型直接编出了“代码E-204代表液压系统压力异常”这种详细信息,听起来非常专业,数据来源却完全不存在。
还有个容易被忽略的因素是上下文窗口的注意力稀释。模型理解问题时,要在这几千个token里分配注意力。如果上下文里塞了太多无关内容,关键信息会被挤到边缘,模型抓不住重点,就开始依赖参数记忆做默认输出。这解释了为什么同样的模型,在提示词写得清楚、上下文干净的时候表现很好,一旦提示词含糊、上下文混乱,幻觉率立刻上升。
所以想治幻觉,第一步不是换更大的模型,而是减少模型“猜”的空间。上下文工程的核心思路就一句话:让模型手头的信息足够多、足够相关、足够明确,它就没有必要去编。下面的六种技术,本质上都是在往这个方向使劲。
2. 技术一:角色锚定——用系统提示词把模型的“答题范围”框死
系统提示词是最基础也最容易被浪费的手段。很多人的做法就是写一句“你是一个乐于助人的AI助手”,这等于没写。角色锚定的真正目的,是用提示词划定模型的回答立场、事实来源、表达风格和禁答边界。它不是在“调教”模型的性格,而是在压缩模型生成答案时的概率空间。
我在一个客服项目中吃过亏。最初给系统提示词设置的角色是“友好客服”,结果用户问产品参数时,模型会编出N种实际不存在的规格。后来我们把角色描述改成了这样:
你是一个售后支持助手。你的知识只能来自下方提供的产品知识库,回答问题时: 1. 先对照资料,资料里有明确写法的才能回答; 2. 资料里没有的信息,必须明确说明“资料中未记录”; 3. 不使用资料之外的经验或常识做推断; 4. 回答尽量引用资料原文中的关键表述。同样的模型,改完提示词之后,编造参数的情况基本消失。原因不难理解:提示词里的“只能来自”“必须说明未记录”给模型建立了一个行为约束环境,模型在生成每个token时,偏离资料的路径会被自己产出的上一段话“锁死”——它一旦开始说“资料中未记录”,后续就没法继续编细节,否则会自相矛盾。模型为了保持一致性,只能收敛到安全回答。
角色锚定过程中有两个参数建议配合调整。一个是温度,日常问答我建议冷却到0.2以下,温度越低,生成越保守,发散越少。另一个是top_p,控制在0.9上下即可,太低了会机械复读,影响回答自然度。实测下来,偏事实型问答场景,低温几乎是必备条件。
这里要特别提醒一个反直觉的点:角色锚定不是越严厉越好。如果你把约束写成“绝对禁止回答任何超出资料之外的内容”,模型有时候会连资料里明确有的信息都不敢答,表现为过度拒绝。我后来把措辞调成了“资料中有明确答案的必须回答,没有的要明说”,这样既避免了幻觉,也保住了回答率。边界感是调出来的,不是一次写死的。
3. 技术二:知识外挂——把事实从模型参数里搬到上下文里
光靠提示词约束还不够,模型脑袋里没有的资料就是没有。想让模型回答你的私有数据、最新数据、小众领域数据,必须把它们塞进上下文里。这就是检索增强生成(RAG)的基本逻辑:先根据问题去资料库里检索相关内容,把检索结果塞进提示词,再让模型基于这些内容生成答案。
RAG落地时最大的坑不在生成环节,而在检索环节。检索回来的材料质量不行,模型再聪明也白搭。这里有两个关键问题要处理好:
一个是切块粒度。我试过用整篇文章作为检索单位,结果就是检索回来的内容太泛,模型找不到具体答案;也试过把每句话切成一块,结果上下文碎片化,模型理解不了前后关系。实践下来,按章节或按语义段落切块,每块控制在300-500字左右,效果最稳。如果文档本身没有清晰章节结构,可以先用小模型做一层标题识别,再按标题归属切块。
另一个是检索方式的选择。关键词检索(稀疏检索)适合专业术语多、词汇匹配严格的文档;向量检索(稠密检索)适合语义相近但用词不同的情况。我做过一个设备维修手册问答系统,里面充斥着各种缩写和型号代码,纯向量检索效果很差,因为“E-204”这种代码在向量空间里跟“液压系统压力异常”并不邻近。后来改用关键词和向量双路召回、结果做重排,召回准确率才上来。
检索到内容之后,生成环节也有讲究。我个人习惯在提示词里增加一段“引用标注”的要求,让模型在回答中直接指出依据了哪份资料或哪个章节。这不是为了好看,而是给校验环节留抓手——我可以在回答生成后程序化地检查它引用的来源是否真的包含对应内容,不含就直接拒答。这种“生成后校验”的做法,能有效截住最后一道漏网的幻觉。
另外一个值得注意的点是资料冲突。同一个问题在不同文档里可能表述不一致,这也是幻觉的重灾区。我的处理方式是提示词里写明“如果检索内容相互冲突,请分别罗列不同说法,并标注各自来源,不要自行合并成一个答案”。这样模型就不会试图“融会贯通”出一个第三方案,幻觉空间自然被压缩。
4. 技术三:思维链与分步验证——逼着模型先打草稿再回答
模型直接输出最终结论容易错,尤其是涉及多步推理的问题。比如一个订单折扣规则:满1000打九折,超过5000的部分打八折,同时会员再减50。你要是让模型一步算出结果,它很容易把某个条件忽略掉。但这个结果如果让模型分步算,每一步都摊开来看,错误率会明显下降。
这就是思维链(CoT)的用处。它不是简单地加一句“请一步步思考”,关键是要给模型一个可操作的思考框架。我在实践中常用的做法是定义“三栏式”输出:
请严格按照以下步骤处理: 1. 列出已知条件(原样引用用户给出的所有数字和规则); 2. 分步骤计算(每一步写明依据了哪个条件); 3. 汇总结论(必须和前两步一致,不一致时说明原因)。表面上看这只是多写了几行字,实际效果差别很大。我给模型明确“从条件到结论”的展开路径之后,计算类问题的错误率大概能降低一半以上。原因在于,模型在逐步推理时,每一步产出的文本变成了后续决策的上下文约束。第一步写下了“折扣分三段:1000以内不打折,1000-5000打九折,5000以上八折”,到第三步它就不太可能得出一个无视分段的结论,否则前后文就对不上了。
思维链还有个进阶用法,叫“自校验”。让模型在得出答案之后,再拿原问题中的条件逐一对照检查一遍,把检查结果写出来。这段检查过程不一定给用户看,但能让模型在生成答案时多一次纠错机会。我用过一个模拟项目做验证:把校验作为提示词内置的一步,“最后检查你的答案是否每个条件都被使用,没被使用的条件说明你漏了什么”,这比单纯让模型“请再检查一遍”有效得多,因为“逐一对照条件”这个动作本身就把工作拆成了具体步骤,模型不太容易偷懒直接说“答案正确”。
当然,思维链也不是万能药。越是开放性的主观问题,比如“写一段营销文案”,分步思考反而会让回答变得僵硬。它更适合有明确条件、有推导规则的场景。我现在的习惯是:遇到逻辑推理、计算、规则判定的问题,拆分步骤;遇到创意生成、语义概括的问题,反而让它直接输出。
5. 技术四:多路采样与自我一致性——用概率对抗概率
一个很容易被忽略的事实是:大模型生成答案本身是带有随机性的。同样的输入,温度不为0时每次输出都不一样。既然幻觉一部分来自随机采样时的“发散”,那反过来用采样次数去收敛答案,就成了一个蛮巧妙的思路。
自我一致性(self-consistency)方法的套路并不复杂:同一个问题,在较高温度(比如0.7)下独立采样多个答案,然后对这些答案做聚类,出现次数最多的那个类别作为最终答案。原理上是说,如果答案是符合事实、有依据的,那么多次采样大概率会得到相似的表述;如果答案是幻觉,那么不同次采样可能会编出不同的细节,聚类之后形不成稳定多数。这样用“多数投票”天然淘汰掉发散的那一批。
我在一个内部问答机器人上实测过这个方案,主要是回答平台规则类问题。每组问题采样5次,聚类投票后,事实性错误的比例比单次回答下降了一半还多。注意聚类不能简单比对字符串完全相等,因为模型每次措辞会有变化,直接比字符串会认为两个正确答案不同。实践时建议先做语义相似度聚类,或者干脆在提示词里要求“每次回答尽量用相同格式和相同的关键数字开头”,这样便于程序做分组。
多路采样的代价是成本和延迟。一次采样变成五次,推理成本直接乘以五。所以我的建议是别无脑做全量采样,先把问题做个分类:那些能从检索结果里直接找到原话的事实型问题,单次回答就够了;真正需要多路采样的是那些推理链条长、条件容易遗漏的问题。还可以加一个置信度预检,先让模型生成答案并附带“确定程度”,只有低于阈值的才触发二次采样。这样既保住了效果,成本也控制得住。
6. 技术五:工具调用与外部校验——让AI当调度员,别让它当背书者
上下文工程做到这一步,很多人会发现一个瓶颈:再怎么优化提示词和检索,模型本质上还是在“预测文本”,它不会查数据库,不会算实时汇率,不会核验库存。那么当答案必须依赖外部动态数据时,正确做法不是把数据全塞进上下文,而是让模型学会“把问题交给工具去解决”。
这里的核心玩法是工具调用(function calling)。思路是给模型声明一个工具清单,每个工具包含名称、参数、功能描述。模型生成回答之前,会先判断“这个问题需要调哪个工具”,然后输出一个结构化的调用请求,系统拿到请求之后真正执行查询,把结果回填给模型,模型再基于这个真实结果生成最终回答。
我之前给某运维团队做过一个设备故障排查机器人。设备故障码含义存在数据库里,但经常更新。如果让模型凭记忆回答,必错。后来实现了一个“查询故障码”工具,模型觉得问题涉及故障码时,会自动生成工具调用请求,程序去数据库查完再返回给模型。从那以后,故障码类问题再没出现过编造。
写工具调用时有一个细节容易踩坑:工具描述一定要写得具体,模型才能准确决定何时调用。比如工具描述写“查询设备信息”就太糊了,模型不知道该在什么场景用;写成“当用户提到设备型号或故障码时,查询该设备的官方信息,返回内容包括型号、规格、故障码含义”就清晰得多。模型判断调用时机越准确,就越不会胡答。
另一个关键点是结果回填后,要明确告诉模型“以下内容来自系统查询结果,回答时只能使用其中信息,且要标注信息来源”。这一步相当于把工具返回的数据当作新的上下文锚点。实测中,如果省略这句话,模型有时候会把工具返回的真实数据和它自己的参数记忆混在一起,产生“一会儿真一会儿假”的回答,更危险。
外部校验还能跟前面的知识外挂形成双保险。我的做法是:涉及数字、日期、型号、价格的回答,生成后额外做一遍正则或规则校验,比如回答里出现的金额与工具返回的数据不一致就直接拦截。规则校验虽然简单粗暴,但它是最后一道兜底,效果最直接。
7. 技术六:边界治理——教会模型“不知道”比“知道”更重要
很多幻觉其实是模型被逼出来的。用户的每个问题都希望得到答案,模型也被训练成“尽量满足用户”的形态,于是面对不确定的内容,它倾向于编一个合理的说法,而不是承认不会。想治这一类的幻觉,必须主动给模型开一扇“拒绝之窗”。
我总结了一个三层兜底策略,比较实用:
第一层是知识边界声明。在系统提示词里明确告知模型“你有知识截止时间和领域边界,接不住的问题要直说”。注意措辞要具体,否则模型不知道什么算“超出边界”。我常用的一句话是:“如果问题无法用提供的资料或工具确认,请直接回答‘我目前无法确认这个问题’,不要推测。”
第二层是置信度分级。让模型在回答末尾附加信息确定性标记,比如“(答案来源于资料X)”“(基于当前知识,建议人工复核)”。表面上看只是加个标签,实际作用是把“模型对自己的把握程度”暴露出来,方便用户和下游系统做风险判断。我的习惯是要求它对间接推断的内容主动使用“据现有信息推测”这类限定语,这能显著减少回答中斩钉截铁的错觉。
第三层是拒答模板。给模型几个固定的拒答表达方式,让它遇到不能答的问题时不要即兴发挥。比如:“这个问题我目前无法确认,请提供更多信息或联系人工支持。”模板化拒答的好处是可控,防止模型用很多种花式话术说“不知道”——那些花式话术有时候本身就是一种隐性幻觉,绕来绕去反而让用户觉得它知道。
边界治理中最难的一类情况是“高置信错误”。模型对某些幻觉答案非常自信,让它自评置信度,它会给自己打高分。我这里有一个不算完美但实用的折中方案:在提示词里要求模型“区分事实断言与推断”,对不直接基于资料、工具的陈述,强制附带来源或不使用绝对化表述。至少可以把最危险的那类“无来源断言”降低到可控范围。经过三轮优化之后,我负责的项目里模型回答的绝对化语气出现频率明显下降,用户反馈“AI变得谨慎了”,这种谨慎其实是好事。
8. 组合起来才能落地:场景化搭配与质量评测
单独看上面六大技术,每一招都不复杂,但真正有难度的在于组合使用。不同场景,六种技术的权重完全不同。
比如纯闲聊机器人,角色锚定和边界治理最重要,知识外挂可以完全不做;文档问答系统,知识外挂是核心,思维链辅助处理逻辑推理类问题;数据助手,工具调用是必须项,多路采样用来兜底高风险数值问题。我搭的一个典型知识库问答链路是这样跑的:用户提问,先做意图识别,判断是事实查询、推理计算还是拒答场景;事实查询走“检索+引用校验”,推理计算走“思维链+多路采样”,涉及时效数据走“工具调用+外部校验”;以上全部叠加系统提示词里的角色锚定和边界治理。这样一套组合下来,单一技术的缺陷会被其他技术补上,幻觉率才真正压得住。
组合搭建之后,怎么证明效果变好了,这是很多人会忽略的问题。我推荐的做法是建一个幻觉评测集:每年或每季度积累100-200条容易诱发幻觉的典型问题,逐条标注标准答案和“可接受回答的边界”,然后分批跑你的系统,统计三个指标:错误率(答错的占比)、编造率(回答中出现资料或工具中不存在的信息的占比)、拒答率(该答但拒绝回答的占比)。跑评测之前,先确定一个基线版本,每次改动提示词或检索逻辑,都拿同样数据集跑一遍,对照指标变化。我在某模拟项目中就是因为坚持做了这个评测集,才发现曾经以为有效的“加强拒答指令”其实只是把编造率压低了,但拒答率飙升,整体体验反而变差了。
评测完之后还要会拆解bad case。幻觉问题排查时,我习惯按照检索缺失、检索不相关、生成忽略检索、生成混合记忆四类归因。检索缺失,问题出在切块或者索引建得不好;检索不相关,问题出在召回策略;检索到内容但生成没有用上,说明提示词的引用约束不够;生成把检索内容和自身记忆混在一起,优先加“只能基于提供的资料”的强约束。归因对了再动手改,比乱调参数高效得多。
9. 收个尾:一些可能对你有用的实操体会
最后分享几个我兜了很多圈才总结出来的小经验。
第一,提示词不是一口气写出来的。每次改动只动一个变量,然后跑评测集看指标。同时改三个地方,效果变差了都不知道是哪一处的锅。第二,别迷信模型厂商宣传的“幻觉率降低XX%”,换一个领域、换一套术语,效果可能天差地别。幻觉控制没有银弹,必须基于自己的数据反复调试。第三,上下文工程每一层都在为模型提供“锚点”——角色是行为锚点,资料是事实锚点,工具是数据锚点,校验是质量锚点。锚点越密,幻觉的空间就越小,这句话是我这几年做相关项目最深的体会。
如果你正准备改造一个经常胡说八道的AI应用,我的建议是别急着从头重做,先挑一个你最头疼的坏案例,从六个维度逐个排查是哪一环缺失导致的,补上那一环再看效果。很多时候治好一个案例的方法,能治好一整类问题。