三层防线:将大模型Agent幻觉率从20%压到3%的实战方案 📅 发布时间:2026/9/17 19:24:36 👁 浏览次数: 去年接手一个客服Agent项目的时候我差点被线上事故逼疯。知识库文档齐全API也全都接好了可是上线第一周就被业务方抓了个正着用户问“退货怎么免运费”Agent一本正经地编了一套售后政策说得有鼻子有眼结果和运营给的真实规则完全对不上。当时我的第一反应和大多数人一样把System Prompt里的“你必须基于知识库回答”加粗再强调了三遍毫无用处。后来又试过换大参数模型、调低temperature、给工具增加更严格的描述……前前后后折腾了接近三个月才慢慢把幻觉率从实测的20%以上压到3%以内。这篇内容不是什么概念科普而是我在真实项目里踩坑踩出来的三层方案边界划定、RAG链路优化、工具验证。这个组合也确实是近两年大模型面试里反复出现的高频考法2026年只会问得更深。如果你正在做Agent开发或者准备大模型方向的技术面试这篇内容可以直接当项目复盘来读。1. 内容整体设计与思路拆解1.1 先搞清楚Agent语境下的“幻觉”到底是什么很多人一提到幻觉就想到“模型胡编乱造”这在纯对话场景里是对的但在Agent场景里不够准确。Agent的幻觉至少有四种表现形态每种形态的修复手段完全不同。第一种是事实性幻觉模型输出与真实情况不符典型如客服Agent编造售后政策。第二种是能力幻觉模型以为自己接了某个工具、查了某个数据库实际上根本没有调用或者调用了但解析结果失败它也不报错直接顺着语气继续编。第三种是上下文归因错误模型确实检索到了正确的知识片段但回答时把另一个用户、另一份文档的信息张冠李戴。第四种是指令偏离你要求它“只能基于检索结果回答”它却在开场白里把自带的通用知识倒了出来。这里有个很关键的经验很多人排查幻觉时只盯着模型本身觉得“换个更强的模型就解决了”但实际项目里能力幻觉和上下文归因错误往往占了大头这两类问题恰恰不是模型能力能解决的而是流程设计问题。1.2 为什么单靠提示词约束治标不治本我在项目初期疯狂堆Prompt把能想到的约束都写进去比如“没有依据可以说不知道”“不要编造任何信息”效果确实有但极不稳定。原因在于LLM的本质是概率模型同样的输入换一种问法、换一次采样输出就可能偏离约束框架。更麻烦的是当上下文里有足够多“看似相关”的错误信息时模型会优先迎合上下文语境而不是遵守写在System Prompt里那条“不要编造”的规则——这在心理学上有点像从众效应模型比我们想象的更倾向于顺着上下文说话。另外提示词约束只能限制表达阻止不了推理路径。Agent本身就会决定“我要不要调用工具”“我要不要查询知识库”“我要不要再生成一步”。要让幻觉真正可控必须把约束从提示词层面下沉到系统流程层面让每一道环节都能独立拦截错误。1.3 三层方案的总体分工从“入口”到“出口”全程设卡这套三层方案的设计逻辑本质是把Agent的生产过程拆成“输入决策—知识获取—输出验证”三段每一段设一道关卡。边界划定负责的是入口决策哪些问题可以进哪些问题必须挡在外面Agent能执行哪些操作不能执行哪些操作在源头压缩幻觉的触发面。RAG链路优化负责的是知识获取确保模型拿到的是高质量、高相关的可溯源知识片段而不是一堆模棱两可的相似文本。工具验证负责的是出口质检对模型最终生成的关键结论做核对发现编造就触发纠错循环。说白了一句话边界划定让模型“不敢乱说”RAG链路优化让模型“有据可说”工具验证让模型“说了能验”。三层之间不是独立叠加的关系而是串成一条闭环——边界挡不住的、RAG没兜住的、模型自由发挥的最后由验证节点用工具结果强制校准。2. 第一层防线边界划定从源头压缩幻觉触发面2.1 意图边界的两种类型能力边界与知识边界边界划定不是简单写一句“你不能回答XX问题”就完事的它至少要拆成两层。能力边界定义的是Agent能执行什么动作。举个实际例子我的客服Agent系统里同时接了订单查询、物流查询、售后申请、商品推荐四个工具。没做能力边界之前用户问“帮我修改收货地址”Agent会直接说“已将您的地址修改为XX”但实际上系统里根本没有改地址这个工具。这就是典型的能力幻觉——模型知道自己“应该能做”但它不知道“当前系统没有这个能力”。处理方式是在意图路由层加一个“工具能力清单校验器”Agent的任何回复倾向都必须先过一遍工具清单如果目标动作不在清单里不允许直接输出操作完成类话术。知识边界定义的是Agent能回答哪些领域的问题。我的做法是维护一个“知识域白名单黑名单”结构白名单是售后政策、订单规则、物流时效这些业务域黑名单是健康建议、法律意见、投资推荐这些风险域。白名单之外的问题尽量走拒答流程黑名单直接触发固定提示语“该问题不在我的回答范围内”。2.2 系统提示词里的“结构性边界”写法先给一段我在LangGraph项目里实践过的System Prompt关键段落跟网上流传的模板有很大区别每一句都对应一种场景问题。你是一名电商平台客服助手。你的全部知识来源是系统提供的检索结果和工具返回结果。 - 如果检索结果为空或者检索结果与用户问题无关你必须明确回答“当前知识库中没有找到相关信息”。 - 只有工具调用成功并且返回有效结果时你才可以使用工具返回的数据进行回答。 - 当用户询问订单、售后、物流等具体业务操作时你只能描述“系统可以做什么”不得描述“用户的操作结果”。 - 禁止输出任何关于价格变动、库存数量、活动时效的具体数据除非这些数据来自工具返回结果。 - 回答必须附带信息来源标签格式为【来源文档ID或工具名】。这里最重要的不是“不要编造”这种抽象句而是第三句和第五句。“只能描述系统可以做什么不得描述用户的操作结果”这句话直接堵死了大量能力幻觉的表述空间而“必须附带信息来源标签”则给后续验证层铺好了路。2.3 置信度阈值让模型主动承认“不知道”我见过太多人忽略了Agent的“拒答机制”。模型其实在一定程度上知道自己有多大把握只是我们默认让它“尽力回答”而已。在调用接口时打开logprobs参数拿到每个token的生成概率然后用一个简单的公式计算整句的平均置信度[ confidence \frac{1}{N} \sum_{i1}^{N} logprob_i ]实际项目里我会同时算两个指标语句级平均logprob和首token的累积概率。如果整句平均置信度低于阈值我们调下来大概在-0.8到-0.5之间不同模型差异很大就强制走“不确定答案”分支转人工或者输出预设话术。但要注意置信度阈值不能一刀切。边界问题比如“你们发不发XX省”可以调低阈值尽量给答案事实性问题比如“具体哪天开始活动”必须调高阈值宁可拒答也不要编。我们初期阈值调得太激进结果一大堆本该正常回答的问题被打回可用率跌了一大截。后来改成按问题类型动态调阈值才平衡了可用率和幻觉率。2.4 LangGraph路由节点的工程实现边界划定在LangGraph里的落地方式核心是一个Router节点。我这里给一个简化但真实可跑的示意# route_node.py from typing import Literal from langchain_core.output_parsers import JsonOutputParser from langchain_openai import ChatOpenAI BOUNDARY_PROMPT 判断用户问题是否属于系统可回答范围。只能输出JSON格式 {{can_answer: true/false, reason: y/n, intent: order/aftersale/logistics/other}} 规则 1. 涉及订单、售后、物流等业务问题且系统工具可支持can_answertrue 2. 涉及健康、法律、投资建议can_answerfalse 3. 当用户询问结果而系统工具不支持该操作can_answerfalse 用户问题{question} class BoundaryRouter: def __init__(self, llm: ChatOpenAI): self.llm llm.with_config({temperature: 0}) self.parser JsonOutputParser() def __call__(self, state: dict) - dict: question state[question] prompt BOUNDARY_PROMPT.format(questionquestion) resp self.llm.invoke(prompt) parsed self.parser.parse(resp.content) if not parsed[can_answer]: return {route: reject, reason: parsed[reason]} return {route: parsed[intent], intent: parsed[intent]}这里有个细节边界判定用的LLM调用temperature必须设成0能选则选带结构化输出的模型减少解析失败的概率。边界路由是入口闸门稳定性优先级最高不需要让它有太多“创造性”。3. 第二层防线RAG链路优化让答案从可信知识里来3.1 文档切块大多数检索问题的根源RAG链路里最容易忽略的就是文档解析和切块。我记得有一次排查了半天发现用户问“退货周期”时Agent回答完全错误追根溯源是因为原始文档里那一段Policy被切成了两块一块只讲“退货条件”另一块只讲“退款时长”向量检索时单独召回哪块都不够完整模型只能靠自己的常识脑补。所以我把文档切块策略调整成“语义段落优先、重叠窗口兜底”。什么叫语义段落优先先按Markdown标题、表格结构、换行规律做一次粗切然后用句子嵌入做二次合并让语义完整的段落尽量不被拆散。重叠窗口则保证切在中间的关键句两边都有上下文。常用的参数是chunk_size512、chunk_overlap64但这个数值必须根据你的文档结构调不能无脑套。3.2 混合检索向量不是万能的刚开始我只用了embedding向量检索用bge-large-zh结果发现精确匹配能力很差。比如用户搜“七天无理由”文档里写的是“7天无理由退货”如果向量相似度阈值卡得不够低这个精确词条根本召回不到。换成BM25向量的混合检索之后情况立刻改观。混合检索的工程实现不复杂核心是把两者的得分做一个归一化加权[ score \alpha \cdot sigmoid(BM25_{score}) (1-\alpha) \cdot cosine_sim(embedding) ]\alpha是我们上线前反复调的一个超参0.4-0.6之间效果最好。需要注意的是BM25跑在中文文档上分词器很关键别用默认的按字切分至少配一个jieba或者HanLP的词表否则召回质量会被拉下一大截。除了混合检索我还会对查询做“改写”再检索。Agent收到的原始问题往往是口语化的比如“你们现在退换货到底怎么个流程啊”直接拿去检索效果很差。我用一个轻量LLM把问题改写成一到三个“适合匹配的查询语句”再分别检索合并结果。注意改写用的模型不需要很强小模型足够关键是改写后不能丢失原意。3.3 重排序十路召回不如一路精排多路召回有一个副作用召回的候选片段数量很多而上下文窗口有限。只按向量相似度取top-3的话很可能前三名全是表达相似但实质内容重复的片段真正的关键信息排在第四位。我的做法是在一个bge-reranker交叉编码器上做重排序。粗排阶段混合检索先召回大概20-30个候选片段精排阶段用cross-encoder把每个候选片段和原始用户问题一起过一遍输出相关性得分再按得分取top-3到top-5。这个精排模型的效果在业务数据上评测过MRR10比单纯向量检索提升了将近15个百分点。这里有个容易被忽略的细节如果不做重排序只用向量top-kRAG的幻觉率会明显偏高。原因是向量检索召回的是“语义相似”不是“问题相关”很多捎带相似的文本片段会误导生成模型偏离真实答案。3.4 上下文构建与引用溯源让模型“抄作业”而不是“写作文”检索到的片段不是一股脑全塞给模型就算完事上下文构建阶段同样影响幻觉率。我有一个经验口诀排在前面的内容权重高放在后面的内容容易被忽略、也容易被误解。我的上下文模板大概是这样的顺序先放强相关答案片段再放补充说明片段最后放可选的对比信息。并且在每个片段前面加一行元信息标注比如【来源售后政策V3_第12页】。Prompt里明确要求“回答中的事实描述必须严格对应【来源】标注的片段内容”。实际操作中我发现很多模型并不是“看不到”正确答案而是看到一个泛泛相关的片段就开始自由发挥。解决这个问题的关键是上下文注入方式的强约束把检索片段和回答格式同时放在Prompt里并且用few-shot示例展示“当检索片段包含A时回答必须只涉及A不得扩展到B”的对应关系效果比单纯加一句“请基于上下文回答”好得多。3.5 从“检索到了”到“真正用上了”RAG链路最气人的一种情况检索结果是正确的但模型最终还是给了错误回答。这通常说明两个问题一是上下文里正确信息与错误信息混杂模型被“带跑”了二是Prompt结构没有把检索片段的重要性抬高到模型“非用不可”的程度。我配合cutoff做了一个更严格的策略如果检索结果里出现了与用户问题明显矛盾的内容系统在构建上下文时会自动打上“注意该信息与当前问题可能不完全一致请以包含【可信】标记的片段为准”。这个策略上线后因“检索对但答错”导致的幻觉直接下降了一半左右。此外每个片段必须保留原始文档的元数据。这样不仅方便生成引文排查问题时也能跟着Source ID一路追回原始文档效率高很多。4. 第三层防线工具验证给Agent装上“事实质检员”4.1 工具调用的前提让结果可校验很多人设计Agent工具时只关心“如何让模型调用成功”不关心“调用成功之后怎么校验结果”。但工具验证这层做好的前提恰恰是工具的返回结果要结构化、可校验。我要求所有业务API返回统一格式{ code: 0, data: { order_id: A123456, status: 已签收, valid: true }, source: order_api, timestamp: 1710000000 }注意这里必须额外返回一个valid字段表示接口对“数据是否真实存在”的判断。比如订单查询接口查到单号不存在时就不能硬给一个默认值而是validfalse。这样后续验证节点才能判断模型是在“陈述事实”还是“编造事实”。4.2 验证节点的核心逻辑关键结论必须通过工具结果比对验证节点放在LLM生成完整回答之后、最终输出给用户之前。它的核心工作是从模型回答中抽取出关键断言然后与工具返回结果或检索片段逐条比对。实际工程里我不指望模型输出规范的JSON断言列表那样开发成本太高我更推荐用独立的验证LLM来做“事实核查”。验证LLM接收三份输入用户问题、模型回答、工具/检索结果然后输出格式化的核查结论# verify_node.py VERIFY_PROMPT 请核查模型回答中的事实性结论是否与给定的工具/检索结果一致。 输出JSON格式 {{claims: [{{claim: 退货周期为7天, status: supported/unsupported/unknown}}], overall: pass/fail/need_recheck}} 规则 - 回答中的每个数字、日期、订单状态、政策条目都应独立核查 - 如果工具结果中没有对应信息标记为unknown不影响整体pass - 如果工具结果与回答冲突标记为unsupportedoverallfail def verify_outputs(user_question: str, llm_answer: str, evidence: list): verifier ChatOpenAI(modelgpt-4o, temperature0) prompt VERIFY_PROMPT.format( questionuser_question, answerllm_answer, evidencejson.dumps(evidence, ensure_asciiFalse) ) result verifier.invoke(prompt) parsed result_parser.parse(result.content) return parsed我最看重的是状态为unsupported的断言。只要有一个结论与证据冲突这轮回答就走fail分支触发纠错循环。4.3 纠错循环验证失败后怎么办验证失败后最忌讳直接重新让Agent“再回答一次”。如果模型上一轮就是在错误证据基础上瞎编的重来一遍大概率还是错。我的做法是把验证结果作为新信息回传给Agent强制进入“反思模式”。反思模式是这样的路径把验证结果中状态为unsupported的断言提取出来回传给生成节点并提示“以下结论与证据不符请检查你的推理逻辑重新作答或说明无法确认”如果这轮修正后仍然fail则放弃自动回答转人工或输出兜底话术。这里的关键是要把验证结果当作“工具调用结果”一样对待它是可以被模型用来修正答案的额外上下文。不少Agent实现会把验证失败直接当作异常退出这太粗糙了既浪费了模型的信息整合能力又给了用户一个很差的体验。4.4 深度技巧投票式验证与自洽性检查最激进也最可靠的一种降幻觉手段是让同样的问题跑多次不同的采样得到多份答案再对这些答案做一致性投票。投票一致的问题答案可信度极高投票分裂的问题大概率是模型在边缘性编造。这个做法成本不低我通常只对高价值会话开启比如涉及金额计算、订单修改、售后赔偿的对话。另一种成本更低的替代方案是“重复反问”让模型以另一个角度重新复述一遍自己的回答比如“如果客户问你的是关于XX的售后期限你会怎么答”。如果两次回答的核心事实对不上说明模型是在自由发挥。自洽性检查对特定类型的幻觉尤其有效——模型在单次推理中会把一个不存在的细节讲得活灵活现但换个角度再问它自己就露馅了因为压根没有真实记忆可以兜底。5. 三层的联合调优与落地模型、阈值、评测一起转5.1 三层方案的协同机制三层方案不是各管各的它们在实际运行中需要联动。边界划定层对一个问题的判定结果会直接影响RAG链路是否要执行而工具验证层如果频繁fail反过来说明边界层放行标准太松或者RAG的知识覆盖有缺口。所以我在项目里加了一个“拦截原因回传”的统计模块。每次验证层fail都会把fail原因归类成三类无证据支撑、与证据冲突、工具调用失败。如果某一类占比持续升高就说明对应的那层防线需要调整。比如“与证据冲突”高发优先查RAG重排序阈值“无证据支撑”高发优先查边界层的拒答阈值。5.2 幻觉率评测用数据说话评测Agent幻觉水平不能只看几个case必须有可以量化的指标。我搭建的幻觉评测集包含三个子集正常问题集200条、边界问题集50条故意越权或超域、带干扰的检索问题集100条在上下文中塞入相似但不相关的片段。每次版本迭代后在评测集上跑四个指标指标定义我压到的目标线幻觉率FAHR回答中含unsupported断言的比例 3%拒答准确率边界问题中正确拒答的比例 90%可得答案率正常问题中不经转人工即可回答的比例 85%验证召回率故意注入错误时验证层能识别的比例 95%我特别提醒一句不要在还没有评测集的时候就开始调参否则很容易陷入“改一个case好一个case、换一个case又坏一个case”的泥潭。先花两三天把评测集建起来后面调优三天就能顶之前三周。5.3 全链路追踪没Trace就等于瞎调面对幻觉问题最容易犯的一个错误是看了一个case就急着改参数。正确的做法是先把这次回答的完整链路拉出来看边界判定结果是什么、检索召回了哪些片段、重排序后的top-k是什么、Prompt怎么构建的、模型输出的原始logprob是多少、验证层的核查结论是什么。我用Langfuse做全链路Trace每个session记录以下字段boundary_route边界路由结果retrieved_chunk_ids召回片段ID列表rerank_scores精排分数constructed_context最终注入上下文的摘要verification_result验证结论排查一个坏case时顺着Trace走一遍基本就能定位到问题发生在哪一层。这个习惯帮我省了太多时间强烈建议所有做Agent的团队都引入。5.4 工程落地时的三个取舍第一验证层的模型可以选便宜的小模型不一定非要和生成模型同级。验证任务相对简单结构化输出做得好就行省钱省延迟。第二纠错循环最多两轮再多轮次提升已经不大反而延迟翻倍。第三对时效要求极高的场景可以在验证节点前加一个“高风险问题判定器”只有判定为高风险涉及金额、政策、操作结果才做工具验证普通闲聊直接放行这样能把验证带来的额外延迟控制在可接受范围内。6. 常见问题与排查技巧实录6.1 高频问题速查表现象可能的根因排查路径解决方案Agent对检索到的错误上下文照单全收检索精排不够错误片段混入top-k看Trace中rerank_scores检查排名第一的片段是否与问题真正相关提高精排阈值或增加业务关键词过滤检索结果正确但回答仍然自由发挥生成阶段Prompt没有区分“可引用上下文”和“通用信息”检查构造后的上下文确认正确答案是否排在最前面重构Prompt结构加入few-shot约束示例验证层频繁把正确回答判为不一致验证LLM理解不了业务字段或单位换算查看failcase比对验证LLM输入的evidence是否完整补充验证模型的上下文或对上“单号状态”这类字段做规则校验工具调用失败后Agent不报错反而继续编缺少“工具状态”检查节点查看工具返回值是否正确解析、异常分支是否被捕获在工具节点外增加结果合法性检查非法返回强制走fail分支拒答过多可用率下降边界层阈值过于激进看拒答case分布检查是否把正常业务问题挡在门外按问题类型分开设阈值风险问题从严、事实问题从宽6.2 几个容易踩的坑我在这个项目里最大的一个教训是不要把temperature调到0当作万能药。temperature为0确实能减少一部分随机性但也会让模型更容易陷入重复的、格式固定的错误模式而且面对检索到的错误上下文时反而更“自信”地忠实复述错误信息。解决幻觉不能只靠采样参数流程校验是根本。另一个坑是切块时把表格拆碎了。业务文档里大量出现表格比如运费规则表、优惠叠加表。直接按纯文本切块会让表格内容失去列头信息检索出来的片段根本没法读。我的做法是先做表格识别每个表格作为一个独立chunk保留原始Markdown格式再单独走一遍“表格摘要生成”检索时优先返回表格摘要加原表效果提升很明显。还有一个容易被忽略的点多轮对话的上下文累积会持续放大幻觉。用户问一句“那如果是Plus会员呢”模型会把上一轮的错误信息当成前提继续演进。所以每次用户发新消息时我建议先做一轮“历史关键信息提取”只把与当前问题相关的历史结论传给Agent防止错误结论被滚雪球式放大。6.3 兜底策略当所有办法都失效时三层方案都到位后仍然会有极少数case漏过去。我保留了一个最终兜底策略——高危操作强制二次确认。只要回答涉及订单状态修改、退款金额、赔偿承诺等高风险意图Agent在输出最终话术前必须追加一个“确认步骤”让用户确认“是否要执行操作”而不是直接宣告结果。这个兜底不解决技术层面的幻觉但把幻觉造成的业务影响直接降到了最低属于成本极小、收益极大的设计。7. 面试视角怎么把三层方案讲成高分项目7.1 面试官大概率会追问的四个问题第一个追问是“边界划定和提示词约束的区别在哪”回答要点是提示词约束是软的模型可能违反边界划定是流程层的硬约束通过意图路由、工具清单元数据、置信度阈值实现系统级拦截不依赖模型“听话”。第二个追问是“RAG检索到的上下文本身是错的怎么办”回答思路是承认单靠RAG无法彻底保证正确性所以需要重排序提高相关性、在生成阶段做强引用约束、再由工具验证层兜底。三层合起来不是杜绝检索错而是尽量不让检索错变成最终输出错。第三个追问是“验证层会不会成为性能瓶颈”回答思路是验证层可以用更轻量的模型、只针对高风险回答开启、多轮纠错限制在两次以内再配合流式输出做到用户感知不到额外延迟。第四个追问是“这个方案在什么场景下会失效”这是一个很能拿分的问题。诚实回答当知识本身不在检索库和工具能力范围内时三层方案只能拒答或转人工做不到无中生有地正确当外部知识实时变化时如果知识库没更新验证也会基于过期信息通过。所以要配合知识库的更新机制和时效性管理。7.2 项目讲述的STAR结构建议如果要把这个项目讲成面试素材我建议按“问题背景—方案设计—落地结果—取舍总结”来组织。问题背景强调幻觉造成的业务影响比如客服满意度下降、人工转接率上升方案设计按三层展开每一层都给出具体的技术选型和参数落地结果用幻觉率、拒答准确率、可得答案率这几个指标量化取舍总结讲清楚哪些场景不适合这套方案、未来还能怎么演进。这套讲法之所以有效是因为面试官想听的往往不是“我用了LangChain”而是“我为什么这么设计、遇到了什么问题、怎么用数据衡量效果、如何取舍”。工程能力就是在这些决策里体现出来的。7.3 一句话版本给忙碌的面试者如果时间紧迫至少记住这段话Agent幻觉的本质是模型在概率推理中偏离了事实基线单靠提示词解决不了需要从边界入口、知识链路、输出验证三个层面共同设防。边界划定管住“能不能说”RAG链路优化管住“有没有依据说”工具验证管住“说出来的是不是真的”。我接手这个客服Agent项目三个月最大的体会是幻觉不是模型缺陷很大程度上是工程缺陷。把边界、检索、验证三层流程做扎实即使模型不是最强的那一版也能把幻觉率稳定压到极低水平。反过来模型再强如果入口不设防、知识链路混乱、输出没有质检还是会不断翻车。最后再分享一个小技巧排查这类问题时永远先看Trace再动参数。数据驱动的调优远比自己脑补到底哪层出错来得靠谱。