RAG系统LLM幻觉治理:四道防线构建可靠问答系统

1. 面试场景还原与问题本质拆解

“RAG系统的LLM幻觉怎么治?” 这个问题,在滴滴Agent岗的二面中被抛出来,本身就很有意思。它不是一个简单的“什么是RAG”或者“RAG怎么用”的入门问题,而是一个直击RAG系统生产落地核心痛点的“诊断与治疗”问题。面试官想看到的,绝不仅仅是你对几个技术名词的背诵,而是你能否像一个系统架构师或资深算法工程师一样,从根源上理解问题,并构建一套层次化的防御体系。

我们先来还原一下这个问题的背景。当我们在谈论一个服务于滴滴内部或面向用户的Agent(比如智能客服、行程规划助手、内部知识问答机器人)时,RAG(检索增强生成)几乎是当前构建可靠、事实性强的对话系统的首选架构。它的理想很美好:用户提问,系统从海量、准确的知识库(如司机规范、城市交规、最新促销政策文档)中检索出最相关的片段,然后交给大语言模型(LLM),让它基于这些“证据”来生成回答。这样既能利用LLM强大的语言理解和生成能力,又能用“检索”这根缰绳拴住它,防止它信口开河(即产生“幻觉”)。

但现实很骨感。即便给了“证据”,LLM依然可能产生幻觉。比如,你问:“滴滴专车在北京首都机场T3航站楼的指定上车点在哪里?” RAG系统从最新的《北京机场接送指南》PDF里检索出了正确段落:“T3航站楼到达层,8号门对面。” 但LLM在生成答案时,可能会画蛇添足地加上一句:“请注意,该上车点仅在早6点至晚10点开放。” 而原文根本没有提到时间限制。这句多出来的、看似合理但毫无依据的话,就是典型的“幻觉”。在出行这种对信息准确性要求极高的场景下,这种幻觉是致命的,可能导致乘客白等、司机被投诉。

所以,面试官问“怎么治”,其实是在考察你以下几个维度的能力:

  1. 深度诊断能力:你是否能超越“LLM不靠谱”这种笼统的抱怨,精准定位幻觉产生的具体环节和根源?
  2. 系统化思维:你是否能构建一个从前到后、层层设防的治理框架,而不是依赖某个“银弹”?
  3. 工程权衡意识:每一种“疗法”都可能带来成本(计算、延迟、复杂度)的增加,你是否能根据业务场景(如滴滴的实时客服 vs. 内部知识沉淀)做出合理的权衡?
  4. 前沿技术嗅觉:你是否了解行业内最新的研究和实践来应对这一问题?

接下来,我们就沿着“两类根源 -> 四道防线”的脉络,深入拆解这个问题的“治疗方案”。

2. 追根溯源:RAG系统中幻觉的两大“病根”

治理幻觉,首先要像老中医一样“辨证”,找到病根。在RAG的流水线中,幻觉主要滋生在两个环节,我把它们称为“检索侧失察”“生成侧任性”

2.1 病根一:检索侧失察——给了错误的“证据”

这是幻觉的“上游污染源”。如果检索系统递给LLM的文档片段本身就是不相关、不准确或信息不足的,那么LLM基于此生成错误答案的概率就会极大增加。具体又分为几种情况:

  1. 检索无关内容:这是最常见的问题。用户的查询和检索到的文档在语义上匹配度低。例如,用户问“如何报销高速公路费”,系统却检索出了一段关于“市内停车费规则”的文档。LLM可能会强行捏造一个基于停车费规则的“报销流程”,造成完全错误的指引。
  2. 检索到过期或错误内容:知识库更新不及时。比如,某城市的机场接送政策已变更,但知识库中还是旧文档。LLM基于旧信息生成答案,自然就是“幻觉”。
  3. 检索粒度不当:检索出的片段要么太短,缺乏上下文,导致信息模糊;要么太长,夹杂了大量无关噪声,让LLM抓不住重点。例如,检索出一整页的法律条文,但关键条款淹没在冗长文本中,LLM可能总结错误。
  4. 多文档检索冲突:当从多个来源检索信息时,不同文档之间可能存在矛盾。例如,一份文档说A业务需要审核3天,另一份说需要5天。LLM如果没有明确的“裁决”机制,可能会生成一个折中的、错误的“4天”,或者随机选择一个。

核心原因在于:传统的向量检索(如基于Embedding的相似度搜索)本质上是“语义相似度”匹配,它无法保证“事实一致性”。它可能把“苹果公司”和“吃苹果”的文档混在一起,因为它们的Embedding在某种角度上相似。此外,单纯的词频统计(如BM25)也无法理解语义的细微差别。

2.2 病根二:生成侧任性——无视或曲解“证据”

即使检索系统提供了完美、相关、准确的证据,LLM本身也可能“不听话”,这是幻觉的“内生性病灶”。

  1. 忽略检索内容(Ignoring):LLM完全或部分忽略了提供的检索上下文,而是依赖于其内部参数化知识(可能已过时或不准确)来生成答案。这在模型参数知识很强而检索内容较弱时尤其明显。
  2. 过度概括或捏造(Over-generalizing/Fabricating):LLM基于检索到的正确信息,进行了超出范围的推断或添加了不存在的细节。就像开头的机场例子,LLM根据“上车点”这个事实,自行脑补了“开放时间”。
  3. 理解偏差(Misinterpretation):LLM错误理解了检索片段中的复杂逻辑、否定关系或限定条件。例如,文档说“除特殊情况外,不允许……”,LLM可能生成为“完全不允许……”。
  4. 忠实度与流畅度的权衡:LLM的训练目标之一是生成流畅、连贯的文本。有时,为了语句的通顺和“看起来合理”,它可能会牺牲对检索内容的严格忠实,进行小幅度的改写或补充,而这些补充恰恰引入了错误。

核心原因在于:LLM本质上是一个概率生成模型,它的目标是生成“看起来最可能”的下一个词序列,而不是“最忠实于证据”的序列。它的“知识”来源于训练数据,而训练数据本身可能存在偏见、错误或时效性问题。当指令遵循(Instruction Following)和上下文利用(In-Context Learning)的能力不足时,幻觉就产生了。

理解了这两类根源,我们的“治疗”方案就有了明确的靶点:既要优化检索系统,确保送上前线的“弹药”是精准的;也要约束LLM的生成行为,让它成为一个严谨的“证据使用者”。

3. 第一道防线:检索优化——确保“证据”的精准投送

这是治理幻觉最前端、也最有效的防线。目标是从源头减少垃圾信息的输入。

3.1 精细化文档处理与索引策略

在文档进入向量数据库之前,预处理的质量直接决定了检索的上限。

  • 分块(Chunking)策略的艺术:抛弃简单的按固定字数或段落分割。对于结构化文档(如API文档、Q&A列表),按章节或问题分割;对于非结构化长文本,采用基于语义的递归分割,确保每个块有完整的主题。例如,使用LangChainRecursiveCharacterTextSplitter时,仔细调整chunk_sizechunk_overlap,并利用分隔符优先级(如\n\n\n,,)来保持句子和语义的完整性。
  • 元数据增强:为每个文本块附加丰富的元数据,如来源文档标题、章节、更新时间、作者、置信度标签等。在检索时,这些元数据可以作为强大的过滤条件。例如,在滴滴的场景中,可以为每个政策条款块附加城市生效日期业务线(快车/专车/代驾)等元数据。当用户提问时,可以先通过元数据过滤器缩小范围,再进行语义搜索,准确性大幅提升。
  • 混合检索(Hybrid Search):这是目前业界的标配实践。结合稠密向量检索(Dense Vector Retrieval, 捕捉语义相似)和稀疏向量检索(Sparse Retrieval, 如BM25, 捕捉关键词匹配)。向量检索能找到“意思相近”的文档,BM25能精准找到“包含关键词”的文档。两者结果通过加权(如RRF)融合,能有效应对术语准确但表述不同,或表述相似但主题无关的情况。ElasticsearchPinecone等现代检索系统都支持开箱即用的混合检索。

3.2 查询理解与重写

用户的原始查询往往是模糊、简短或不规范的。直接用于检索,效果很差。

  • 查询扩展(Query Expansion):使用一个轻量级LLM(如GPT-3.5-Turbo)或传统的同义词库,对原始查询进行扩展。例如,用户问“车费不对怎么办?”,系统可以自动扩展为“车费异常、费用计算错误、多扣费、申诉流程”。这能增加检索到相关文档的概率。
  • 多轮查询重写(Query Rewriting):在多轮对话中,用户的当前问题可能省略了上文语境。例如,用户先问“我去浦东机场”,接着问“预约要提前多久?”。第二个问题“预约”的指代是模糊的。此时,可以用LLM将当前查询与对话历史结合,重写为一个独立的、信息完整的查询,如“预约滴滴专车从市区前往上海浦东国际机场,需要提前多久预约?”。这样检索的目标性会强得多。
  • 意图分类与路由:在检索之前,先对用户查询进行意图分类(例如:政策咨询操作指引故障报修费用查询)。不同意图可以路由到不同的知识库子集或采用不同的检索策略。例如,政策咨询类查询对准确性要求极高,可以调高稀疏检索的权重并启用严格的元数据过滤。

3.3 检索后重排序(Re-ranking)

初步检索可能返回10-20个相关文档块,但它们的相关性排序未必最优。一个专门的重排序模型可以充当“质检员”,对初筛结果进行更精细的排序。

  • 为什么需要重排序?向量检索模型(如text-embedding-ada-002)通常是通用型的,并非为你的特定领域数据微调。重排序模型(如BGE-RerankerCohere Rerank)是“句子对”分类模型,专门判断“查询”和“文档”的相关性,精度远高于向量相似度计算。
  • 实操要点:重排序模型计算量较大,通常只对Top K(如K=20)的初检结果进行重排,然后取Top N(如N=3)送给LLM。这相当于用较小的计算成本,换取了最终上下文质量的显著提升。在LangChain中,可以方便地集成CohereRerankFlashRank等组件。

个人经验:在资源允许的情况下,重排序是性价比极高的优化手段。我们曾在一个客服项目中引入重排序后,LLM答案的幻觉率(通过人工评估)直接下降了约15%。它的作用在于把那些“看起来相关但其实不沾边”的文档压到后面,确保LLM看到的都是“精华”。

4. 第二道防线:提示工程——给LLM戴上“紧箍咒”

当精准的“证据”准备好后,如何有效地“喂”给LLM,并命令它严格使用,是提示工程要解决的核心问题。这是成本最低、见效最快的防线。

4.1 结构化上下文与明确指令

最基础的RAG提示模板是:“基于以下上下文回答问题:{context}。问题:{question}”。这远远不够。

  • 强约束性指令:在提示词中必须使用强硬、清晰的指令。

    请你严格扮演一个事实核查员的角色,仅根据提供的参考上下文来回答问题。 上下文: {context} 问题: {question} 你的回答必须遵守以下规则: 1. 答案必须完全来源于上述上下文,不能引入上下文以外的任何知识。 2. 如果上下文中的信息不足以回答问题,请明确说“根据提供的信息,无法回答此问题”。 3. 不要对上下文信息进行任何推断、扩展或总结出上下文未明确提及的结论。 4. 引用上下文时,可以注明出处(例如:根据第X段...)。

    这种指令大幅降低了LLM“自由发挥”的倾向。

  • 上下文结构化:不要简单地把检索到的文本块拼接起来。为每个块添加清晰的编号和来源标识。

    [文档片段 1, 来源:《滴滴平台规则》第3.2章, 更新时间:2024-01-15] {chunk_text_1} [文档片段 2, 来源:内部客服手册_V2.1, 更新时间:2023-11-30] {chunk_text_2} ...

    这不仅能帮助LLM更好地理解和引用,也为后续的可解释性提供了基础。

4.2 少样本示例(Few-Shot)与思维链(Chain-of-Thought)

对于复杂任务,光有指令还不够,需要给LLM做“示范”。

  • 少样本示例:在提示词中提供1-3个高质量的“问题-上下文-答案”示例。示例中要特意展示如何处理“信息不足”的情况。例如:

    示例1: 上下文:[文档A] 乘客取消订单无责时限为3分钟。 问题:司机接单后,乘客多久内取消不用付钱? 答案:根据上下文,乘客在司机接单后3分钟内取消订单无需承担责任。 示例2: 上下文:[文档B] 北京天气晴。 问题:上海明天会下雨吗? 答案:提供的上下文只涉及北京天气,没有上海天气信息,因此无法回答上海明天是否会下雨。

    LLM会模仿示例中的严谨风格。

  • 思维链(CoT)引导:对于需要多步推理的问题,在提示中要求LLM先分解步骤。例如:“请先列出回答问题所需的关键信息点,然后逐一检查上下文中是否包含这些信息点,最后给出结论。” 这迫使LLM更仔细地审视上下文,而不是直接跳转到答案生成。

4.3 元提示(Meta-Prompting)与角色设定

这是一种更高级的技巧,通过让LLM进行“自我对话”或扮演特定角色来提升忠实度。

  • “验证者”角色:设计一个两阶段提示。第一阶段,让LLM(作为“生成者”)基于上下文生成一个初步答案。第二阶段,将初步答案和原始上下文一起,交给同一个LLM(但切换为“验证者”角色),提示词为:“请严格核对以下答案是否完全基于提供的上下文。答案:{draft_answer}。上下文:{context}。请指出答案中任何没有上下文支持、或与上下文矛盾的部分。” 最后,根据验证结果修正答案。虽然这增加了调用次数,但对关键任务非常有效。
  • 系统角色设定:在OpenAI API等接口中,充分利用system消息来固化角色。system消息中的指令对模型行为的影响比user消息更持久和深刻。例如:
    system: “你是一个高度严谨、安全的滴滴出行政策助手。你的所有回答都必须以滴滴官方知识库为准,绝对不允许猜测或编造信息。如果知识库没有明确信息,你必须告知用户无法确认。” user: “{context} \n\n 问题:{question}”

踩坑心得:提示工程不是一劳永逸的,需要针对不同的任务类型(信息提取、总结、推理)设计不同的提示模板。我们建立了一个“提示词库”,并通过A/B测试来评估不同提示词在“答案忠实度”指标上的表现。同时,要警惕“提示词注入”攻击,即用户输入中可能包含试图覆盖你系统指令的内容,需要在拼接提示词时做好清洗和隔离。

5. 第三道防线:生成过程约束——在输出前安装“过滤器”

前两道防线主要是在“输入”和“指令”层面工作。这一道防线则直接介入LLM的生成过程,从技术手段上限制其“胡说”的能力。

5.1 受控生成与约束解码

这是相对前沿但非常有效的技术方向,旨在从生成算法的层面施加约束。

  • 关键词/短语约束:确保生成的答案中必须(或禁止)包含某些关键词。例如,在回答关于“报销政策”时,可以约束答案中必须出现“发票”、“审批流程”、“7个工作日内”等关键术语。这可以通过在解码时修改词汇表的概率分布来实现(如使用GuidanceOutlinesTransformers库的LogitsProcessor)。如果模型试图生成偏离这些核心术语的句子,其概率会被抑制。
  • 格式与语法约束:强制要求答案以特定格式(如JSON、列表、特定开头句)生成。结构化输出本身就更易于后续的自动校验。例如,要求答案格式为:{"answer": “...”, “confidence”: high/medium/low, “source_fragments”: [1,2]}。这不仅能减少自由文本的随意性,也为后续的验证环节提供了便利。
  • 基于上下文的词汇约束:一个更智能的方法是,在生成每个词时,动态地允许的词汇集限制在从检索上下文中提取出的实体、术语和它们的同义词范围内。这需要更复杂的集成,但能极大提高生成内容与上下文的相关性。

5.2 后处理校验与一致性验证

在LLM生成答案后,但返回给用户前,增加一个自动化的“质检”步骤。

  • 答案与上下文的忠实度评分:使用一个专门的、轻量级的文本蕴含(Textual Entailment)或问答(QA)模型,来评估生成的答案是否被给定的上下文所支持。例如,将“上下文”作为前提,将“生成的答案”作为假设,用模型判断是“蕴含”(支持)、“矛盾”还是“中性”。如果评分低于阈值,则触发重生成或返回“信息不足”的提示。
  • 自我一致性检查(Self-Consistency):让同一个LLM(或另一个校验模型)基于相同的上下文,从不同角度生成答案或进行验证。例如:
    1. 问题分解验证:将复杂问题分解成子问题,分别验证答案的每个部分是否有上下文支持。
    2. 反向提问:根据生成的答案,反过来构造一个问题,然后看从上下文中是否能检索到支持这个新问题的证据。
    3. 多路径采样与投票:用相同的提示和上下文,让LLM在较低温度(temperature)下生成多个答案,然后检查这些答案在关键事实点上是否一致。如果不一致,则说明生成过程不稳定,可能包含幻觉。
  • 事实溯源与引用:强制要求LLM在生成答案时,必须为陈述的每个关键事实标注出处(对应到上下文片段的编号)。这不仅能提高可信度,也使得自动或人工校验变得非常方便。你可以检查这些引用是否真实存在且支持其陈述。

技术选型思考:后处理校验会增加系统延迟和复杂度。在实际工程中,我们需要权衡。对于实时性要求高的客服场景,可能只对高风险问题(如涉及费用、安全、政策)启用完整的后处理流水线。对于内部知识库问答,则可以全面启用。我们团队曾尝试将DeBERTa模型微调成一个“忠实度分类器”,专门用于判断生成答案与上下文的支持关系,效果不错,但需要标注数据。

6. 第四道防线:系统迭代与评估——建立长效“免疫系统”

治理幻觉不是一次性的战斗,而是一个持续的过程。需要建立一套监控、评估和迭代的机制,让系统在运行中不断自我完善。

6.1 构建多维度的评估体系

不能只靠人工抽查,必须建立自动化的评估指标。

  • 忠实度(Faithfulness):这是对抗幻觉的核心指标。衡量答案是否严格源自提供的上下文。可以通过基于规则的字符串匹配、NLI模型自动评分,或人工评估来实现。
  • 答案相关性(Answer Relevance):评估生成的答案是否直接回答了用户的问题,避免答非所问。
  • 上下文相关性(Context Relevance):评估检索到的上下文是否与问题真正相关。这是上游检索质量的指标。
  • 事实准确性(Factual Accuracy):在有标准答案的情况下,直接对比生成答案与标准答案的一致性。这需要构建高质量的测试集。
  • 综合评分:像RAGASTruLens这样的框架提供了成套的自动化评估指标。可以定期(如每周)在预留的测试集上运行评估,跟踪各项指标的变化。

6.2 构建“幻觉”负反馈闭环

将生产环境中发现的问题,快速反馈到系统改进中。

  • 日志与归因分析:详细记录每一次问答的“问题-检索上下文-生成答案-用户反馈”。当用户给出负面反馈(如点“踩”、人工客服介入)时,能快速定位是哪个环节出了问题。是检索错了?还是LLM胡编了?
  • 错误案例库:建立一个持续增长的“幻觉案例库”。每个案例包含问题、错误答案、正确上下文/答案以及根因分析(检索失败、指令遵循失败等)。这个案例库有三个核心用途:
    1. 提示词优化:针对特定类型的幻觉,设计更具针对性的提示词。
    2. 检索优化:分析检索失败的案例,调整分块策略、Embedding模型或重排序权重。
    3. 评估基准:作为内部回归测试集,确保系统优化不会引入新的退化。
  • 主动学习与数据增强:利用这些错误案例,可以主动地补充知识库(对于知识盲区),或者创建新的训练数据来微调重排序模型、校验模型,甚至是在特定领域微调LLM本身,使其更“听话”。

6.3 面向领域的系统优化

在滴滴这样的具体业务场景下,可以做一些更深度的定制。

  • 领域自适应Embedding:使用滴滴内部的工单、客服对话、政策文档等数据,对开源的Embedding模型(如BGEtext-embedding-ada-002)进行领域自适应微调。让模型更理解“行程取消”、“费用异议”、“安全投诉”等业务术语的语义,大幅提升检索相关性。
  • 构建业务规则引擎:对于一些高度结构化、确定性极强的知识(如“XX城市服务开通列表”、“最低消费金额”),完全可以绕过RAG和LLM,直接由规则引擎匹配和返回。将RAG+LLM用于处理需要理解和推理的复杂、非结构化问题。这种“规则+AI”的混合系统,在保证核心事实绝对准确的同时,又能处理灵活的自然语言查询。
  • 人机协同与渐进式披露:对于置信度不高的答案,系统可以不直接给出肯定答复,而是采用更保守的策略,如:“关于您提到的‘跨城费是否包含高速费’,我找到一条相关规定(引用片段),但其中没有明确说明。您是否需要我为您转接人工客服进一步确认?” 这既提供了价值,又避免了传播不确定信息。

治理RAG中的LLM幻觉,没有单一的“银弹”。它要求我们从一个系统工程师的视角出发,在数据准备、检索、提示、生成、校验的每一个环节都设立关卡,层层过滤。从确保高质量的知识供给(检索优化),到给模型明确的作战指令(提示工程),再到对输出进行技术审查(生成约束),最后建立持续改进的机制(系统迭代),这四道防线共同构成一个动态的、鲁棒的防御体系。

在实际面试中,如果能沿着这个脉络,结合滴滴具体的业务场景(如处理司乘纠纷、解读地方交规)来举例说明每一道防线的具体实施方法和权衡考量,并展现出你对成本、延迟、效果三者关系的深刻理解,那么你给出的就不仅仅是一个“答案”,而是一份令人信服的“解决方案蓝图”。这正是在高级别技术面试中脱颖而出的关键。