经过前面章节的系统学习,我们已经掌握了RAG系统的完整技术链条:从第2章对RAG技术诞生与发展的历史回顾,到第3章中RAG与微调、长上下文模型的对比分析,再到第4章的RAG完整工作流程全景,第5章的离线索引构建,以及第6章的在线检索技术。当检索阶段返回了与用户查询最相关的文档片段之后,RAG系统便进入了最后一个、也是直接面向用户的环节——生成阶段(Generation)。生成阶段的质量直接决定了用户对整个系统的信任度和满意度。
生成阶段的核心任务是将检索到的上下文信息与用户查询结合,通过大语言模型生成准确、完整、有据可查的回答。然而,这一看似简单的任务面临着诸多技术挑战:如何设计有效的提示词来引导模型充分利用检索结果?如何在有限的上下文窗口中合理组织大量检索信息?如何确保生成的回答忠实于检索到的上下文而非模型自身的参数化知识?如何让回答具有可追溯的引用来源?如何控制回答的格式和风格以满足不同场景的需求?
本章将系统性地解答这些问题。我们从提示工程在RAG中的应用出发(7.1节),深入探讨上下文拼接策略(7.2节),然后介绍引用溯源技术(7.3节)和幻觉抑制技术(7.4节),最后讨论回答格式与风格控制(7.5节)。这些技术共同构成了RAG生成阶段的核心方法论,是构建高质量、可信赖RAG系统的关键。作为本书第二部分“原理篇”的最后一章,本章还将在结尾对整个原理篇进行回顾总结,并为即将到来的第三部分“进阶篇”做好铺垫。
7.1 提示工程在RAG中的应用
提示工程(Prompt Engineering)是RAG生成阶段的核心技术之一。在RAG系统中,提示词不仅是用户与模型交互的接口,更是将检索到的外部知识与模型推理能力有效结合的关键桥梁。与传统的纯LLM对话不同,RAG系统的提示词需要同时处理三种信息:系统指令(定义模型的角色和行为规范)、检索上下文(提供事实依据)和用户问题(明确生成目标)。如何在这三者之间找到最优的平衡点,是RAG提示工程的核心课题。
Gao等人在RAG综述中明确指出,提示工程是RAG系统中与检索、生成并列的三大核心组件之一,对最终生成质量有着至关重要的影响。Lewis等人在原始RAG论文中也强调了提示词设计在引导模型利用检索文档方面的关键作用。本节将从RAG提示词的核心结构、专用模板设计、优化策略以及常见反模式四个方面,全面介绍提示工程在RAG中的应用。
7.1.1 RAG提示词的核心结构
一个典型的RAG提示词由三个核心部分组成:系统提示(System Prompt)、上下文(Context)和用户问题(User Question)。这三者在提示词中各司其职,共同引导模型生成高质量的回答。
系统提示是RAG提示词的“总指挥”,它定义了模型的角色、行为规范和输出要求。在RAG场景中,系统提示通常需要包含以下关键指令:第一,明确告知模型其角色是一个知识助手,必须基于提供的上下文信息回答问题;第二,要求模型在回答时严格区分上下文中的事实信息和自身的参数化知识;第三,规定模型在上下文信息不足时应明确表示无法回答,而非编造答案。一个精心设计的系统提示能够显著降低模型的幻觉倾向,提高回答的忠实度。
上下文部分承载了检索阶段返回的相关文档片段,是RAG系统区别于纯LLM对话的核心所在。上下文的质量和组织方式直接影响模型的生成效果。在将检索结果注入提示词时,需要注意以下几点:为每个文档片段添加明确的标识符(如序号或来源标题),以便模型在回答时进行引用;保持文档片段的原始文本,避免在注入时进行二次改写或摘要,因为改写可能引入信息偏差;控制上下文的总长度,确保其不超过模型的上下文窗口限制。
用户问题是提示词的驱动力,它明确了模型需要回答的具体内容。在RAG系统中,用户问题通常直接来自用户的输入,但也可以经过查询改写、查询扩展等预处理步骤进行优化。用户问题的表述方式对生成效果有显著影响——清晰、具体、明确的问题通常能够引导模型生成更精准的回答。
RAG提示词的核心结构示例
system_prompt = """你是一个专业的知识问答助手。
请严格基于以下【参考上下文】中的信息回答用户问题。
回答要求:
-
仅使用上下文中提供的信息,不得编造或猜测
-
如果上下文中没有足够信息回答问题,请明确说明
-
在回答中标注信息来源(如[1]、[2])"""
context = """【参考上下文】
[1] RAG(Retrieval-Augmented Generation)是一种结合了
信息检索和文本生成的AI技术框架..."""
user_question = "什么是RAG技术?它的核心组件有哪些?"
[1] Gao et al. Retrieval-Augmented Generation for Large Language Models: A Survey. 2024. arXiv:2312.10997
[2] Lewis et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. NeurIPS 2020. arXiv:2005.11401
7.1.2 RAG专用提示模板设计
在实际的RAG系统开发中,提示模板(Prompt Template)是将上述核心结构工程化、可复用化的关键工具。LangChain等主流RAG框架都提供了内置的提示模板机制,允许开发者定义可参数化的提示词模板,在运行时动态填充检索结果和用户问题。设计一个高质量的RAG提示模板需要考虑多个维度:上下文格式化方式、引用标注规则、回答风格要求和边界条件处理。
以下是一个基于LangChain Prompt Templates设计的RAG专用提示模板,它展示了如何将系统指令、上下文和用户问题有机整合在一起。该模板采用了分节标记的方式,使模型能够清晰地区分不同类型的信息。模板中的{context}和{question}是运行时动态填充的占位符,分别对应检索结果和用户问题。
from langchain_core.prompts import ChatPromptTemplate
rag_template = ChatPromptTemplate.from_messages([
("system", """你是一个严谨的知识问答助手。
请严格根据【参考资料】中的信息回答用户问题,遵循以下规则:
-
回答必须基于参考资料中的事实,不得使用自身知识补充
-
每个关键论断后用方括号标注来源编号,如[1][2]
-
如果参考资料不足以完整回答问题,明确说明哪些部分无法确认
-
先给出直接回答,再展开详细说明
-
使用Markdown格式组织回答结构
【参考资料】
{context}"""),
("human", "{question}")
])
在模板设计中,上下文格式化是一个容易被忽视但极其重要的环节。不同的上下文格式化方式会显著影响模型对信息的利用效率。常见的格式化策略包括:为每个文档片段添加序号标识(便于引用)、在片段之间使用分隔符(如空行或特殊标记)以避免信息混淆、在每个片段前添加来源元数据(如文档标题、作者、日期等)以帮助模型评估信息可信度。
此外,模板设计还需要考虑异常场景的处理。例如,当检索结果为空时,模板应引导模型生成“无法回答”的响应而非编造答案;当检索结果与用户问题相关性较低时,模板应引导模型指出上下文信息不足。这些边界条件的处理对于构建可靠的RAG系统至关重要。
[10] LangChain Documentation. Prompt Templates. python.langchain.com/docs/concep…
7.1.3 提示词优化策略
在基础的RAG提示词结构之上,多种高级提示策略可以进一步提升生成质量。其中,Few-shot提示(Few-shot Prompting)和思维链提示(Chain-of-Thought Prompting, CoT)是两种在RAG场景中尤为有效的优化策略。
Few-shot提示通过在提示词中提供若干高质量的问答示例,来引导模型理解期望的输出格式和回答风格。Brown等人在GPT-3论文中首次系统性地展示了Few-shot学习的能力,证明仅通过少量示例就能显著提升模型在特定任务上的表现。在RAG场景中,Few-shot示例需要展示以下关键行为:如何从上下文中提取相关信息、如何标注引用来源、如何处理上下文信息不足的情况、如何组织回答结构。一个包含2到3个精心设计的示例的Few-shot提示,通常能够显著提升模型输出的一致性和质量。
Few-shot RAG提示示例
few_shot_prompt = """你是一个专业的技术文档问答助手。
请基于参考资料回答问题,并在回答中标注来源。
【示例1】
参考资料: [1] Python 3.10引入了结构模式匹配(match-case语句)。
问题: Python 3.10有哪些新特性?
回答: Python 3.10引入了结构模式匹配特性[1]。
【示例2】
参考资料: [1] Redis支持多种数据结构,包括字符串、哈希、列表等。
问题: Redis支持哪些数据类型?
回答: Redis支持多种数据结构,包括字符串、哈希、列表等[1]。
现在请回答以下问题:
参考资料: {context}
问题: {question}"""
思维链提示(Chain-of-Thought Prompting)是Wei等人在2022年NeurIPS上提出的一种提示策略,其核心思想是引导模型在给出最终答案之前,先展示逐步推理的过程。在RAG场景中,CoT提示可以引导模型先分析检索到的上下文与用户问题的关联性,再逐步提取关键信息,最后综合生成回答。这种逐步推理的方式不仅能够提高回答的准确性,还能增强回答的可解释性。
将CoT与RAG结合的一种有效方式是在系统提示中明确要求模型的推理步骤。例如,可以要求模型先列出从上下文中找到的关键信息点,再基于这些信息点构建回答。这种显式的推理过程有助于模型更充分地利用检索结果,减少对参数化知识的依赖,从而降低幻觉风险。研究表明,CoT提示在需要多步推理的复杂问答任务上效果尤为显著,能够将准确率提升10%到30%。
[4] Wei et al. Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. NeurIPS 2022.
[9] Brown et al. Language Models are Few-Shot Learners. NeurIPS 2020.
7.1.4 常见提示词反模式与避免方法
在RAG系统的实际开发中,一些常见的提示词设计错误会显著降低生成质量。识别并避免这些反模式,是提升RAG系统可靠性的重要环节。
第一个常见的反模式是“上下文角色模糊”。当提示词中没有明确区分系统指令、检索上下文和用户问题时,模型可能无法正确识别哪些信息来自外部检索、哪些是用户的问题,从而在回答时混淆事实来源。避免方法是在提示词中使用清晰的分节标记(如“【系统指令】”、“【参考资料】”、“【用户问题】”),并在系统指令中明确要求模型仅基于参考资料回答问题。
第二个反模式是“过度约束”。有些开发者为了追求回答的精确性,在系统提示中添加了过多的约束条件(如严格的字数限制、格式要求、语气要求等),导致模型在生成时过度关注格式而忽略了内容的准确性。避免方法是将核心约束(如“基于上下文回答”)和辅助约束(如格式要求)区分优先级,核心约束必须严格遵守,辅助约束可以在不影响准确性的前提下灵活处理。
第三个反模式是“缺乏否定指令”。如果系统提示中没有明确告诉模型“不要做什么”,模型可能会在上下文信息不足时倾向于使用自身的参数化知识来“填补空白”,从而产生幻觉。避免方法是在系统提示中明确添加否定指令,如“不得使用参考资料之外的信息”、“如果参考资料不足以回答问题,请明确说明”。
第四个反模式是“忽略检索质量信号”。当检索结果的相关性分数较低时,说明检索到的文档可能与用户问题关联性不强。如果提示词中没有利用这一信号,模型可能会基于低质量上下文生成不可靠的回答。避免方法是在上下文注入时附带相关性信息,并在系统提示中指导模型根据相关性信息调整回答策略。
| 反模式类型 | 问题描述 | 影响 | 避免方法 |
|---|---|---|---|
| 上下文角色模糊 | 未明确区分指令、上下文和问题 | 模型混淆信息来源 | 使用清晰的分节标记 |
| 过度约束 | 添加过多格式和风格约束 | 模型关注格式而忽略准确性 | 区分核心约束和辅助约束 |
| 缺乏否定指令 | 未明确禁止模型使用外部知识 | 上下文不足时产生幻觉 | 添加明确的否定指令 |
| 忽略检索质量 | 未利用相关性分数信号 | 基于低质量上下文生成回答 | 注入相关性信息并调整策略 |
| 模板过于复杂 | 提示词过长、指令过多 | 模型注意力分散 | 精简提示词,聚焦核心指令 |
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
7.2 上下文拼接策略
上下文拼接(Context Assembly)是将检索阶段返回的多个文档片段组织成一个连贯的整体,并注入到模型提示词中的过程。这一过程看似简单,实则涉及多个关键决策:如何对检索结果进行排序和截断?当检索结果的总长度超过模型的上下文窗口时如何处理?应该采用哪种上下文注入方式?如何应对LLM对中间位置信息注意力衰减的“Lost in the Middle”问题?如何对冗余的上下文进行去重和压缩?这些决策直接影响模型对检索信息的利用效率,进而影响最终生成质量。
本节将从检索结果的排序与截断、上下文窗口管理、上下文注入方式对比、Lost in the Middle问题以及上下文去重与压缩五个方面,系统性地介绍上下文拼接的核心策略。
7.2.1 检索结果的排序与截断
检索结果的排序是上下文拼接的第一步。在大多数RAG系统中,检索阶段返回的结果已经按照相似度分数进行了降序排列。然而,在某些场景下,可能需要对初始排序进行进一步优化。例如,当使用混合检索(结合向量检索和关键词检索)时,需要将不同检索方式的结果进行融合排序(Reciprocal Rank Fusion, RRF);当使用重排序模型(Reranker)时,需要根据重排序分数重新排列结果。
截断(Truncation)是控制上下文长度的基本手段。当检索返回的结果数量较多时,需要选择保留哪些结果、丢弃哪些结果。最简单的截断策略是Top-K截断——直接保留相似度分数最高的K个结果。然而,这种策略可能会丢失一些虽然相似度分数略低但包含独特信息的文档。更智能的截断策略包括:基于多样性(Maximal Marginal Relevance, MMR)的截断,在相关性和多样性之间取得平衡;基于查询意图覆盖度的截断,确保保留的文档能够覆盖用户问题的各个方面。
在实际工程中,截断参数的选择需要综合考虑模型上下文窗口大小、检索结果的信息密度以及生成任务的需求。对于信息密集型任务(如事实问答),建议使用较小的K值(3到5个片段)以确保每个片段都能被模型充分关注;对于需要广泛背景信息的任务(如综述生成),可以使用较大的K值(10到20个片段)。此外,还应考虑每个片段的平均长度——如果片段较长,即使K值不大,总上下文长度也可能接近模型的上下文窗口限制。
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
7.2.2 上下文窗口管理
大语言模型的上下文窗口(Context Window)是有限的资源。虽然最新的模型(如GPT-4 Turbo支持128K tokens、Claude 3支持200K tokens)已经大幅扩展了上下文窗口,但在实际应用中,上下文窗口仍然需要合理分配给系统提示、检索上下文、对话历史和生成空间。当检索结果的总长度超过可用上下文空间时,需要采用有效的管理策略来确保最关键的信息不被丢弃。
上下文窗口管理的第一步是精确计算token数量。不同模型的tokenizer对同一文本的token化结果不同,因此需要使用目标模型对应的tokenizer进行计算。例如,OpenAI的cl100k_base tokenizer和Llama的SentencePiece tokenizer对中文文本的token化效率差异显著。在工程实践中,建议预留约20%的上下文窗口空间给系统提示和生成输出,将剩余的80%分配给检索上下文和对话历史。
上下文窗口管理示例
import tiktoken
def manage_context(retrieved_docs, model_context_limit,
system_prompt_tokens, max_output_tokens):
"""管理上下文窗口,确保不超出模型限制"""
available_tokens = (model_context_limit
- system_prompt_tokens
- max_output_tokens
- 100) # 预留buffer
tokenizer = tiktoken.encoding_for_model("gpt-4")
selected_docs = []
used_tokens = 0
for doc in retrieved_docs:
doc_tokens = len(tokenizer.encode(doc.content))
if used_tokens + doc_tokens <= available_tokens:
selected_docs.append(doc)
used_tokens += doc_tokens
else:
break
return selected_docs, used_tokens
当检索结果超过可用上下文空间时,可以采用以下策略进行处理。第一种策略是“硬截断”——直接按照token数量截断,保留前面的文档片段,丢弃超出的部分。这种策略简单高效,但可能会丢失排在后面的重要信息。第二种策略是“智能压缩”——对较长的文档片段进行摘要或提取关键句,在保留核心信息的同时减少token消耗。第三种策略是“分层注入”——将检索结果分为多个层次,优先注入高相关性片段,在多轮对话中逐步注入更多背景信息。第四种策略是“递归摘要”——当文档过长时,先对文档进行摘要,如果摘要仍然过长,再对摘要进行二次摘要,直到满足长度限制。
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
[2] Lewis et al. RAG. NeurIPS 2020. arXiv:2005.11401
7.2.3 上下文注入方式对比
上下文注入方式指的是将检索到的文档片段放置在提示词中的位置。常见的注入方式有三种:前缀注入(Prefix Injection)、中缀注入(Infix Injection)和后缀注入(Suffix Injection)。不同的注入方式会影响模型对上下文信息的注意力分配,进而影响生成质量。
前缀注入是将检索上下文放置在用户问题之前,这是RAG系统中最常用的注入方式。其提示词结构为:[系统提示] + [检索上下文] + [用户问题]。前缀注入的优势在于模型在处理用户问题时,已经“阅读”了所有上下文信息,可以基于完整的上下文进行回答。大多数RAG框架(如LangChain的load_qa_chain)默认采用前缀注入方式。
后缀注入是将检索上下文放置在用户问题之后,提示词结构为:[系统提示] + [用户问题] + [检索上下文]。这种方式的理论依据是“近因效应”——模型在生成回答时,对最近处理的信息(即后缀注入的上下文)有更高的注意力。一些研究表明,对于需要模型“查找”特定信息的任务,后缀注入可能表现更好。
中缀注入是将检索上下文嵌入到用户问题或系统提示的中间位置。这种方式在特定场景下有独特优势——例如,在多步推理任务中,可以将不同步骤的上下文分别注入到对应的推理阶段之间。然而,中缀注入的实现复杂度较高,且可能打断提示词的逻辑连贯性,因此使用相对较少。
| 注入方式 | 提示词结构 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| 前缀注入 | 系统提示+上下文+问题 | 模型先阅读上下文再回答 | 上下文可能被部分遗忘 | 通用问答,最常用方式 |
| 后缀注入 | 系统提示+问题+上下文 | 近因效应,上下文注意力高 | 模型需先理解问题再查找 | 信息查找类任务 |
| 中缀注入 | 上下文嵌入问题中间 | 可分步注入不同上下文 | 实现复杂,逻辑连贯性差 | 多步推理任务 |
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
7.2.4 Lost in the Middle问题
Lost in the Middle是Liu等人在2023年提出的一个重要发现(arXiv:2307.03172),揭示了大型语言模型在处理长上下文时存在的一个系统性偏差:模型对上下文开头和结尾位置的信息注意力较高,而对中间位置的信息注意力显著衰减。这一发现对RAG系统的上下文拼接策略有着深远的影响。
Liu等人的实验设计非常严谨。研究者将事实性陈述放置在上下文的不同位置(开头、中间、结尾),然后测试模型能否正确利用这些信息回答问题。实验涵盖了多种开源和闭源模型,包括GPT-3.5、GPT-4、Llama系列、Claude等。结果显示,几乎所有测试模型都表现出U形的注意力分布——对开头和结尾信息的利用率明显高于中间信息。当上下文长度增加时,这种效应更加显著。例如,在一个包含20个文档片段的上下文中,模型对位于第10到第15位(中间位置)的信息的利用率可能比开头和结尾低20%到50%。
Lost in the Middle现象对RAG系统的上下文拼接策略提出了明确的指导建议。首先,应该将最重要的、与用户查询最相关的文档片段放置在上下文的开头或结尾位置,而非简单地按照相似度分数从高到低排列。一种实用的策略是“首尾放置法”——将相似度最高的片段放在开头,次高的片段放在结尾,其余片段放在中间。其次,应控制上下文中文档片段的数量,避免因片段过多而加剧中间位置的注意力衰减。实验表明,当上下文中的片段数量超过10到15个时,Lost in the Middle效应会变得非常显著。
此外,Lost in the Middle问题也与模型的上下文窗口大小密切相关。对于上下文窗口较小的模型(如4K或8K tokens),中间位置的信息更容易被“淹没”;而对于支持128K或更长上下文的模型,虽然理论上可以容纳更多信息,但Lost in the Middle效应仍然存在,只是衰减程度可能有所缓解。因此,无论使用何种模型,都应在上下文拼接时考虑信息位置对模型注意力的影响。
[3] Liu et al. Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172, 2023.
7.2.5 上下文去重与压缩
在实际的RAG系统中,检索阶段返回的结果经常包含重复或高度相似的内容。这种情况在知识库中存在大量重复文档、或者不同文档包含相同信息的场景中尤为常见。如果不去重就直接将所有检索结果注入提示词,不仅会浪费宝贵的上下文窗口空间,还可能导致模型对重复信息过度关注,而忽略了其他重要但出现次数较少的信息。
上下文去重(Context Deduplication)可以在多个粒度上进行。最粗粒度的去重是基于文档ID的去重——如果多个检索结果来自同一文档,只保留相似度最高的那个。中等粒度的去重是基于文本相似度的去重——计算文档片段之间的相似度,如果两个片段的相似度超过某个阈值(如余弦相似度大于0.9),则合并或去除重复的片段。最细粒度的去重是基于语义的去重——使用语义模型识别不同表述方式的相同含义,进行语义级别的去重。
上下文去重示例(基于文本相似度)
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
def deduplicate_contexts(docs, embeddings, threshold=0.9):
"""基于余弦相似度去重"""
unique_indices = []
for i in range(len(docs)):
is_duplicate = False
for j in unique_indices:
sim = cosine_similarity(
[embeddings[i]], [embeddings[j]])[0][0]
if sim > threshold:
is_duplicate = True
break
if not is_duplicate:
unique_indices.append(i)
return [docs[i] for i in unique_indices]
上下文压缩(Context Compression)是另一种优化上下文利用效率的技术。与去重不同,压缩的目标是在保留关键信息的前提下减少文本长度。Han等人提出的LLMLingua(arXiv:2310.05736)是一种基于LLM的上下文压缩方法,它利用语言模型计算每个token对上下文整体语义的贡献度(即“困惑度”),然后去除贡献度低的冗余token。实验表明,LLMLingua可以在保留约95%关键信息的同时,将上下文长度压缩50%到70%,显著提升了RAG系统的效率。
除了LLMLingua之外,还有其他上下文压缩方法值得考虑。基于提取式摘要的压缩方法(如TextRank、LexRank)通过识别文本中的关键句子来构建压缩后的上下文;基于生成式摘要的压缩方法使用较小的语言模型对长文档进行摘要,但可能引入摘要模型的幻觉风险;基于关键词提取的方法(如RAKE、YAKE)仅保留与查询最相关的关键词和短语,适用于需要极致压缩的场景。选择哪种压缩方法取决于具体的任务需求和对信息保真度的要求。
[6] Han et al. LLMLingua: Compressing Prompts for Accelerated Inference of Large Language Models. arXiv:2310.05736, 2023.
7.3 引用溯源:让回答有据可查
引用溯源(Citation / Attribution)是RAG系统区别于纯LLM对话的一个核心特征,也是构建可信赖AI系统的关键能力。在传统LLM对话中,模型的回答基于其参数化知识,用户无法验证回答的准确性和来源。而RAG系统通过引用溯源机制,让模型的每个关键论断都能追溯到具体的检索文档,使回答变得透明、可验证、可审计。这不仅增强了用户对系统的信任,也为回答质量的自动化评估提供了基础。
本节将从引用溯源的价值与实现原理、引用标注的常见格式、引用准确性保障机制以及企业场景中的最佳实践四个方面,全面介绍RAG系统中的引用溯源技术。
7.3.1 引用溯源的价值与实现原理
引用溯源在RAG系统中具有多重价值。首先,从用户信任的角度来看,引用来源使回答从“黑箱”变为“白箱”——用户可以点击引用链接查看原始文档,验证回答的准确性。这种透明度是构建用户信任的基础,尤其在医疗、法律、金融等高敏感领域,引用溯源几乎是RAG系统的必备功能。其次,从系统质量的角度来看,引用溯源为回答质量的自动化评估提供了可能——通过比对生成回答中的引用与原始文档内容的一致性,可以自动检测幻觉和错误。第三,从合规的角度来看,许多行业法规(如金融领域的MiFID II、医疗领域的HIPAA)要求AI系统的输出必须提供信息来源,引用溯源是满足这些合规要求的技术基础。
引用溯源的实现原理可以分为三个阶段。第一阶段是“来源标记”——在检索结果注入提示词时,为每个文档片段添加唯一的标识符(如[1]、[2]、[3]或[source_1]、[source_2]),并在标识符后附上文档的元数据(如标题、URL、页码等)。第二阶段是“引用生成”——在系统提示中要求模型在回答的关键论断后标注对应的来源标识符。模型的引用行为通过提示词中的指令和Few-shot示例来引导。第三阶段是“引用验证”——在模型生成回答后,系统自动检查回答中的每个引用是否确实对应于上下文中的相关内容,过滤掉无效或错误的引用。
在技术实现上,引用溯源面临的一个核心挑战是模型引用行为的可靠性。研究表明,LLM有时会生成“虚假引用”——在回答中标注了[1]、[2]等引用标记,但这些引用并不对应上下文中的实际内容,或者引用编号与文档片段的对应关系错误。这种虚假引用比完全不提供引用更具误导性,因为用户可能误以为回答已经被验证过。因此,构建可靠的引用验证机制是引用溯源系统的关键组成部分。
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
[7] Es et al. RAGAS: Automated Evaluation of Retrieval Augmented Generation. arXiv:2309.15217, 2023.
7.3.2 引用标注的常见格式
引用标注的格式设计需要在信息完整性和用户体验之间取得平衡。不同的应用场景适合不同的引用格式,以下是几种常见的引用标注格式及其适用场景。
方括号引用(Bracket Citation)是最常见的引用格式,在回答中的关键论断后使用方括号标注来源编号,如“RAG技术由Lewis等人于2020年提出[1]”。这种格式简洁直观,用户可以通过编号在上下文中快速定位来源。方括号引用特别适合技术文档问答、学术论文问答等场景。为了增强用户体验,可以在回答末尾附上引用来源的详细信息列表,形成类似学术论文的参考文献格式。
脚注式引用(Footnote Citation)将引用来源信息以脚注的形式展示在回答的底部,回答正文中使用上标数字标记引用位置。这种格式保持了正文的简洁性,同时提供了完整的来源信息。脚注式引用适合需要展示丰富来源元数据(如文档标题、作者、发布日期、URL等)的场景。在Web应用中,脚注可以设计为可点击的链接,直接跳转到原始文档。
内联引用(Inline Citation)将来源信息直接嵌入到回答文本中,如“根据《RAG技术白皮书》的描述,RAG技术由Lewis等人于2020年提出”。这种格式阅读体验最自然,不需要用户跳转查看引用信息,但可能会使回答文本变得冗长。内联引用适合来源较少、每个来源需要详细说明的场景。
悬浮引用(Hover Citation)是一种交互式的引用格式,在Web应用中较为常见。回答中的引用标记(如[1])在用户鼠标悬浮时显示来源的摘要信息,点击后跳转到原始文档。这种格式兼顾了正文的简洁性和信息的完整性,但仅适用于支持交互操作的应用场景。
| 引用格式 | 表示方式 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| 方括号引用 | 论断[1][2] | 简洁直观 | 需查看引用列表 | 技术文档问答 |
| 脚注式引用 | 论断¹(底部详情) | 正文简洁,信息完整 | 需滚动查看脚注 | 学术研究问答 |
| 内联引用 | 根据《来源》论断 | 阅读自然流畅 | 正文可能冗长 | 来源较少的场景 |
| 悬浮引用 | 论断[1](悬浮详情) | 交互体验好 | 仅限Web应用 | 交互式Web应用 |
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
7.3.3 引用准确性保障机制
引用准确性是引用溯源系统的生命线。一个标注了错误引用的回答,比没有引用的回答更具危害性——因为它给用户造成了“信息已验证”的虚假安全感。因此,构建多层次的引用准确性保障机制是RAG系统工程化的关键任务。
第一层保障是提示词层面的引导。通过精心设计的系统提示和Few-shot示例,引导模型正确理解引用规则。提示词中应明确说明:每个引用编号必须对应上下文中真实存在的文档片段;引用应标注在具体论断之后,而非笼统地标注在段落末尾;如果某个论断无法在上下文中找到直接依据,不应标注引用。Few-shot示例应展示正确和错误的引用方式,帮助模型建立对引用行为的准确理解。
第二层保障是后处理验证。在模型生成回答后,系统自动执行引用验证流程。验证步骤包括:提取回答中的所有引用标记;对于每个引用标记,检查其编号是否在有效范围内(即是否对应上下文中的某个文档片段);检查引用标记所在论断的内容是否与对应文档片段的内容一致。如果发现无效引用或不一致引用,系统可以自动移除或标记这些引用,并向用户发出警告。
引用验证示例(基于NLI)
from transformers import pipeline
nli = pipeline("text-classification",
model="cross-encoder/nli-deberta-v3-base")
def verify_citation(claim, source_text):
"""验证论断是否被来源文本所支持"""
result = nli(f"{claim} [SEP] {source_text}")
label = result[0]["label"]
score = result[0]["score"]
return label == "entailment", score
第三层保障是基于NLI(Natural Language Inference)的引用验证。使用专门训练的NLI模型来判断回答中的每个论断是否能够被对应的引用文档所“蕴含”(Entailment)。如果NLI模型判断为“矛盾”(Contradiction)或“中立”(Neutral),则说明引用可能不准确。这种基于NLI的验证方法比简单的字符串匹配更加鲁棒,能够捕捉语义层面的不一致性。
[7] Es et al. RAGAS: Automated Evaluation of Retrieval Augmented Generation. arXiv:2309.15217, 2023.
7.3.4 引用溯源在企业场景中的最佳实践
在企业级RAG应用中,引用溯源需要考虑更多实际因素,包括数据安全、权限控制、审计合规等。以下是企业场景中引用溯源的若干最佳实践。
第一,引用来源应包含完整的文档元数据。在企业知识管理场景中,仅提供引用编号是不够的。每个引用来源应至少包含以下元数据:文档标题、文档版本、作者/部门、最后更新日期、文档分类/密级标签、以及文档的永久链接。这些元数据不仅帮助用户验证信息来源,也为文档权限控制提供了基础——当用户点击引用链接时,系统可以检查用户是否有权限访问该文档。
第二,引用溯源应与文档权限体系集成。在企业环境中,不同文档可能有不同的访问权限。引用溯源系统需要确保:检索阶段只返回用户有权限访问的文档;引用来源的链接在用户无权限时应给出适当的提示(如“您没有权限访问此文档,请联系xxx申请权限”),而非直接暴露文档内容。这种权限集成是引用溯源系统安全性的重要保障。
第三,引用溯源应支持审计追踪。在金融、医疗等受监管行业中,RAG系统的输出可能需要被审计。引用溯源系统应记录每次回答的完整引用链路——包括用户问题、使用的检索上下文、生成的回答以及每个引用的来源文档和验证结果。这些审计日志不仅用于合规检查,也用于系统质量的持续监控和改进。
第四,引用溯源应提供引用质量的可视化监控。通过构建引用质量仪表盘,实时监控引用准确率、引用覆盖率、虚假引用率等关键指标。当引用质量指标低于预设阈值时,系统应自动发出告警,提示运维人员检查检索质量或调整提示词配置。这种持续监控机制是确保引用溯源系统长期可靠运行的必要手段。
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
[7] Es et al. RAGAS. arXiv:2309.15217, 2023.
7.4 幻觉抑制技术
幻觉(Hallucination)是大语言模型面临的核心挑战之一,也是RAG系统必须重点解决的问题。Ji等人在关于LLM幻觉的综述论文(arXiv:2311.05232)中将幻觉定义为“模型生成的内容与源内容不一致或无法被源内容所支持的现象”。在RAG系统中,幻觉表现为模型生成的回答包含了检索上下文中不存在的信息,或者回答与上下文中的信息相矛盾。幻觉的存在严重损害了RAG系统的可信度和实用性,因此幻觉抑制是RAG生成阶段最重要的技术挑战之一。
本节将从RAG中幻觉的类型与成因、基于提示的幻觉抑制、基于验证的幻觉抑制、RAG Triad评估框架以及幻觉检测与缓解的综合策略五个方面,系统性地介绍RAG系统中的幻觉抑制技术。
7.4.1 RAG中幻觉的类型与成因
理解幻觉的类型和成因是有效抑制幻觉的前提。在RAG系统中,幻觉可以分为两大类:内在幻觉(Intrinsic Hallucination)和外在幻觉(Extrinsic Hallucination)。这一分类框架由Ji等人在其综述中明确提出,已被学术界广泛采用。
内在幻觉是指模型生成的回答与检索上下文中的信息相矛盾。例如,上下文中明确说明“RAG技术由Lewis等人于2020年提出”,但模型回答“RAG技术由Meta团队于2021年发布”。这种幻觉通常源于模型对其参数化知识的过度依赖——当模型的预训练知识与检索到的上下文信息不一致时,模型可能“选择”相信自己更熟悉的参数化知识,而非上下文中的事实。
外在幻觉是指模型生成的回答虽然与上下文不矛盾,但包含了上下文中不存在的额外信息。例如,上下文中只提到了“RAG技术的两个核心组件是检索器和生成器”,但模型在回答中额外补充了“RAG技术还包含一个重排序组件”——这个信息可能来自模型的参数化知识,虽然本身可能是正确的,但并不在提供的上下文中。在严格的RAG场景中,外在幻觉同样是不被允许的,因为RAG系统的核心承诺是“仅基于检索到的上下文回答问题”。
RAG系统中幻觉的成因是多方面的。从检索层面来看,如果检索结果本身就不相关或信息不足,模型被迫依赖参数化知识来“填补空白”,这是最常见的幻觉成因。从上下文拼接层面来看,如果上下文组织混乱、信息冗余或存在矛盾,模型可能无法正确理解和利用上下文信息。从生成层面来看,模型的解码策略(如温度参数设置过高)和训练数据中的偏见都可能导致幻觉。从提示工程层面来看,如果系统提示没有明确要求模型“仅基于上下文回答”,模型可能默认使用混合策略,将上下文信息与参数化知识混合使用。
| 幻觉类型 | 定义 | 示例 | 主要成因 |
|---|---|---|---|
| 内在幻觉 | 回答与上下文信息矛盾 | 上下文说2020年,回答说2021年 | 模型过度依赖参数化知识 |
| 外在幻觉 | 回答包含上下文中不存在的信息 | 补充了上下文未提及的细节 | 模型用参数化知识填补空白 |
| 引用幻觉 | 引用标记与实际来源不对应 | 标注[1]但内容来自[2] | 模型引用行为不可靠 |
| 事实幻觉 | 回答中的事实性陈述错误 | 错误的数字、日期、名称 | 检索质量不足或模型推理错误 |
[8] Ji et al. Survey on Hallucination in Natural Language Generation. ACM Computing Surveys, 2023. arXiv:2311.05232
7.4.2 基于提示的幻觉抑制
基于提示的幻觉抑制(Prompt-based Hallucination Mitigation)通过精心设计提示词来引导模型减少幻觉行为。这种方法不需要额外的模型或计算资源,实现成本较低,是RAG系统中最基础的幻觉抑制手段。
CoVe(Chain-of-Verification)是Li等人在2023年提出的一种基于提示的幻觉抑制方法(arXiv:2309.11495)。CoVe的核心思想是让模型在生成初始回答后,自动进行自我验证。具体流程分为四个步骤:第一步,模型基于上下文生成初始回答(Baseline Response);第二步,模型从初始回答中提取需要验证的关键事实性陈述,生成一组验证问题;第三步,模型针对每个验证问题,重新查阅上下文并独立生成验证答案;第四步,模型比对初始回答和验证答案,修正不一致之处,生成最终回答。实验表明,CoVe在多个事实性问答基准上能够将幻觉率降低30%到50%。
CoVe (Chain-of-Verification) 实现示例
def cove_pipeline(question, context, llm):
"""CoVe幻觉抑制流程"""
# Step 1: 生成初始回答
baseline = llm.generate(
f"基于以下上下文回答问题。\n上下文:{context}\n问题:{question}"
)
# Step 2: 提取验证问题
verification_qs = llm.generate(
f"从以下回答中提取需要验证的事实性陈述,
并为每个陈述生成一个验证问题。\n回答:{baseline}"
)
# Step 3: 独立验证每个问题
verifications = []
for vq in verification_qs:
v_answer = llm.generate(
f"仅基于以下上下文回答验证问题。\n上下文:{context}\n问题:{vq}"
)
verifications.append(v_answer)
# Step 4: 修正并生成最终回答
final = llm.generate(
f"基于初始回答和验证结果,修正不一致之处。\n
初始回答:{baseline}\n验证结果:{verifications}"
)
return final
Self-Ask是另一种有效的基于提示的幻觉抑制策略。与CoVe的事后验证不同,Self-Ask采用事前分解的方式——在生成最终回答之前,模型先将复杂问题分解为一组简单的子问题,逐一回答每个子问题,最后综合子问题的答案生成最终回答。这种分解式推理的优势在于:每个子问题的范围更小、更明确,模型更容易基于上下文准确回答;如果某个子问题无法从上下文中找到答案,模型可以明确标记,而非强行编造。Self-Ask特别适合需要多步推理的复杂问答场景。
除了CoVe和Self-Ask之外,还有多种基于提示的幻觉抑制策略。例如,“角色扮演”策略——在系统提示中赋予模型“严谨的研究员”角色,要求其对每个论断提供证据;“自反提示”策略——在提示中要求模型先评估上下文信息是否足够回答问题,如果不足则直接表示无法回答;“分步生成”策略——要求模型先列出从上下文中提取的关键事实,再基于这些事实构建回答,而非直接生成最终回答。这些策略可以组合使用,形成多层次的幻觉抑制防线。
[5] Li et al. Chain-of-Verification Reduces Hallucination in Large Language Models. arXiv:2309.11495, 2023.
7.4.3 基于验证的幻觉抑制
基于验证的幻觉抑制(Verification-based Hallucination Mitigation)通过引入额外的验证机制来检测和纠正模型生成中的幻觉。与基于提示的方法不同,基于验证的方法通常需要额外的计算资源(如调用额外的模型或执行NLI推理),但能够提供更可靠的幻觉检测和纠正能力。
LLMLingua是Han等人在2023年提出的一种基于LLM的提示压缩方法(arXiv:2310.05736),虽然其主要目标是提升推理效率,但在幻觉抑制方面也有独特价值。LLMLingua通过计算上下文中每个token的困惑度(Perplexity),识别并去除对上下文语义贡献较低的冗余token。这种压缩过程不仅减少了上下文长度,还间接提升了生成质量——因为更精简的上下文意味着更少的干扰信息,模型更容易从中提取准确的事实。
事实核查(Fact-Checking)是一种更为直接的幻觉抑制方法。在模型生成回答后,系统将回答中的关键事实性陈述提取出来,逐一与检索上下文进行比对验证。验证可以通过多种方式实现:基于字符串匹配的精确验证(检查关键实体、数字、日期是否一致)、基于语义相似度的模糊验证(使用NLI模型判断陈述是否被上下文所蕴含)、以及基于外部知识库的交叉验证(通过搜索引擎或知识图谱验证陈述的真实性)。对于未通过验证的陈述,系统可以将其标记为“未验证”或直接移除。
基于NLI的幻觉检测示例
from transformers import pipeline
nli_classifier = pipeline(
"text-classification",
model="cross-encoder/nli-deberta-v3-base",
return_all_scores=True
)
def detect_hallucination(claim, context):
"""使用NLI模型检测幻觉"""
scores = nli_classifier(f"{claim} [SEP] {context}")[0]
entailment_score = next(
s["score"] for s in scores if s["label"] == "entailment"
)
contradiction_score = next(
s["score"] for s in scores if s["label"] == "contradiction"
)
if contradiction_score > 0.7:
return "hallucination", contradiction_score
elif entailment_score > 0.7:
return "factual", entailment_score
else:
return "uncertain", entailment_score
NLI-based验证是事实核查中最为常用的技术手段。NLI(Natural Language Inference,自然语言推理)模型接收一对文本——前提(Premise,即检索上下文)和假设(Hypothesis,即模型生成的陈述)——输出三种关系之一:蕴含(Entailment,假设能够从前提中推导出来)、矛盾(Contradiction,假设与前提矛盾)或中立(Neutral,前提既不支持也不反驳假设)。在幻觉检测中,如果NLI模型判断为“矛盾”,则可以高置信度地认为该陈述是幻觉;如果判断为“中立”,则需要进一步的人工审核或保守处理。
[6] Han et al. LLMLingua. arXiv:2310.05736, 2023.
[8] Ji et al. Survey on Hallucination in LLMs. arXiv:2311.05232, 2023.
7.4.4 RAG Triad评估:Faithfulness、Answer Relevance、Context Relevance
RAG Triad是评估RAG系统质量的三个核心维度,由Es等人在RAGAS框架(arXiv:2309.15217)中明确提出。这三个维度分别从不同角度衡量RAG系统的表现,共同构成了一个全面的评估体系。理解RAG Triad不仅有助于评估系统质量,也为幻觉抑制提供了明确的目标和方向。
第一个维度是忠实度(Faithfulness),衡量的是生成的回答对检索上下文的忠实程度。具体来说,忠实度评估回答中的每个论断是否都能被检索上下文所支持。忠实度是幻觉抑制的核心指标——高忠实度意味着低幻觉率。RAGAS框架通过将回答分解为一组原子性陈述(Atomic Statements),然后逐一检查每个陈述是否能够从上下文中推导出来,来计算忠实度分数。忠实度分数的范围为[0, 1],1表示回答完全忠实于上下文,0表示回答完全无法被上下文所支持。
第二个维度是答案相关性(Answer Relevance),衡量的是生成的回答与用户问题的相关程度。一个高相关性的回答应该直接针对用户的问题,提供有用的信息,而不应包含与问题无关的内容。答案相关性低并不一定意味着存在幻觉——回答可能完全忠实于上下文,但回答的是用户没有问的问题。RAGAS框架通过计算“反向问题”(Reverse Question)来评估答案相关性:基于生成的回答,生成一个对应的问题,然后计算这个反向问题与原始用户问题的相似度。相似度越高,说明回答越切题。
第三个维度是上下文相关性(Context Relevance),衡量的是检索到的上下文与用户问题的相关程度。上下文相关性是检索质量的直接反映——如果检索到的上下文与用户问题不相关,即使生成模型再强大,也无法产生高质量的回答。RAGAS框架通过计算上下文中与问题相关的句子比例来评估上下文相关性。具体做法是:将上下文分解为句子,逐一判断每个句子是否与问题相关,计算相关句子的比例作为上下文相关性分数。
| 评估维度 | 衡量对象 | 计算方法 | 理想值 | 与幻觉的关系 |
|---|---|---|---|---|
| 忠实度 (Faithfulness) | 回答 vs 上下文 | 回答中可被上下文支持的陈述比例 | 接近1.0 | 直接衡量幻觉率 |
| 答案相关性 | 回答 vs 问题 | 反向问题与原始问题的相似度 | 接近1.0 | 间接相关 |
| 上下文相关性 | 上下文 vs 问题 | 上下文中与问题相关的句子比例 | 接近1.0 | 低相关性是幻觉的主要诱因 |
RAGAS评估示例
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_relevancy
)
eval_result = evaluate(
dataset=test_dataset,
metrics=[faithfulness, answer_relevancy, context_relevancy]
)
print(f"忠实度: {eval_result['faithfulness']:.3f}")
print(f"答案相关性: {eval_result['answer_relevancy']:.3f}")
print(f"上下文相关性: {eval_result['context_relevancy']:.3f}")
RAG Triad的三个维度之间存在密切的关联关系。上下文相关性是基础——如果检索到的上下文质量不高,后续的生成质量必然受到影响。忠实度是核心——它直接反映了RAG系统的核心承诺是否被兑现。答案相关性是目标——它确保系统真正解决了用户的问题。在实际系统中,这三个维度应该被同时监控,任何一个维度的显著下降都应触发告警和调查。
[7] Es et al. RAGAS: Automated Evaluation of Retrieval Augmented Generation. arXiv:2309.15217, 2023.
7.4.5 幻觉检测与缓解的综合策略
在实际的RAG系统中,幻觉抑制不应依赖单一技术,而应构建多层次的防御体系。本节综合前面介绍的各种技术,提出一个系统性的幻觉检测与缓解框架。
第一层防御是“预防层”,在生成阶段之前就减少幻觉产生的可能性。预防层的核心措施包括:提升检索质量(使用混合检索、重排序等技术确保检索结果的相关性)、优化上下文拼接(控制上下文长度、避免信息冗余、考虑Lost in the Middle效应)、以及精心设计提示词(明确要求仅基于上下文回答、添加否定指令、提供Few-shot示例)。预防层的目标是从源头上减少幻觉的产生,是最经济有效的防线。
第二层防御是“检测层”,在模型生成回答后,自动检测可能存在的幻觉。检测层的核心措施包括:基于NLI的事实核查(检查回答中的陈述是否被上下文所支持)、引用验证(检查引用标记与来源文档的一致性)、以及基于RAGAS框架的自动化评估(计算忠实度、答案相关性和上下文相关性分数)。检测层的目标是识别出回答中可能存在的幻觉,为后续的纠正提供依据。
第三层防御是“纠正层”,对检测到的幻觉进行自动或半自动的纠正。纠正层的核心措施包括:CoVe自我验证(让模型重新审视并修正自己的回答)、基于检索的重新生成(对检测到幻觉的部分重新检索并生成)、以及人工审核(将高风险回答提交给人工审核员进行最终确认)。纠正层的目标是在幻觉发生后尽可能修复,减少其对用户的影响。
第四层防御是“监控层”,对整个系统的幻觉率进行持续监控和趋势分析。监控层的核心措施包括:构建幻觉率仪表盘(实时展示各维度的幻觉指标)、建立幻觉案例库(收集和分析典型的幻觉案例)、以及设置告警阈值(当幻觉率超过预设阈值时自动告警)。监控层的目标是发现系统性问题,驱动系统质量的持续改进。
这四层防御形成了一个从预防到监控的完整闭环。在实际部署中,建议从预防层开始逐步建设,随着系统的成熟度提升,逐步引入检测层、纠正层和监控层。每一层的建设都应基于数据驱动的方法——通过分析实际的幻觉案例来确定优先级最高的改进方向。
[5] Li et al. CoVe. arXiv:2309.11495, 2023.
[6] Han et al. LLMLingua. arXiv:2310.05736, 2023.
[7] Es et al. RAGAS. arXiv:2309.15217, 2023.
[8] Ji et al. Survey on Hallucination in LLMs. arXiv:2311.05232, 2023.
7.5 回答格式与风格控制
在确保回答的事实准确性和可靠性之后,回答的格式与风格控制是提升用户体验的另一个重要维度。不同的应用场景对回答的格式和风格有不同的要求——技术文档问答可能需要结构化的Markdown格式,客服场景可能需要简洁友好的口语化风格,报告生成场景可能需要正式的商务语气,国际化应用可能需要多语言支持。本节将从结构化输出、回答长度控制、语气与风格定制、多语言支持以及流式输出五个方面,介绍RAG系统中的回答格式与风格控制技术。
7.5.1 结构化输出
结构化输出(Structured Output)是指控制模型生成特定格式的回答,如JSON、Markdown、表格、列表等。结构化输出在RAG系统的工程化应用中非常重要,因为后续的处理流程(如前端渲染、数据存储、API集成等)通常需要特定格式的输入。
JSON格式输出是API集成场景中最常用的结构化输出格式。通过在系统提示中明确要求模型以JSON格式回答,并提供JSON Schema示例,可以有效地引导模型生成符合预期的JSON结构。现代LLM(如GPT-4、Claude 3)对JSON格式的遵循度较高,但为了确保稳定性,建议在生成后添加JSON格式验证和自动修复逻辑。
JSON结构化输出示例
json_prompt = """基于参考资料回答问题,并以JSON格式输出。
JSON格式要求:
{
"answer": "直接回答(字符串)",
"details": "详细说明(字符串)",
"sources": ["来源1", "来源2"],
"confidence": 0.0-1.0之间的置信度
}
参考资料:{context}
问题:{question}"""
Markdown格式输出是面向终端用户场景中最常用的格式。Markdown支持标题、列表、表格、代码块、粗体、斜体等丰富的排版元素,能够生成结构清晰、易于阅读的回答。在RAG系统中,可以通过系统提示要求模型使用Markdown格式组织回答,例如使用二级标题划分回答的各个部分、使用列表列举要点、使用表格对比不同方案等。
表格格式输出在需要对比分析的场景中特别有用。例如,当用户询问“RAG和微调各有什么优缺点”时,以表格形式呈现对比结果比纯文本更加清晰直观。然而,LLM生成表格的稳定性不如纯文本,有时会出现列数不一致、格式错乱等问题。建议在提示词中提供表格格式的明确示例,并在生成后添加表格格式验证逻辑。
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
[10] LangChain Documentation. Structured Output. python.langchain.com/docs/how_to…
7.5.2 回答长度与详细程度控制
回答长度的控制是RAG系统用户体验优化的重要环节。回答过短可能导致信息不完整,用户需要多次追问才能获得满意的答案;回答过长则可能包含冗余信息,增加用户的阅读负担。理想的回答长度取决于用户问题的复杂度、应用场景的特点以及用户的偏好。
在提示词层面,可以通过以下方式控制回答长度。第一,直接指定长度范围——在系统提示中明确要求“回答控制在200字以内”或“回答不少于500字”。第二,指定详细程度——使用“简洁回答”、“详细说明”、“分步骤解释”等指令来间接控制长度。第三,指定结构——通过要求“先给出结论,再展开说明”或“分三点回答”来控制回答的结构和长度。实践表明,指定结构比直接指定字数更有效,因为模型对字数的控制精度有限,但对结构的遵循度较高。
在工程实现层面,可以采用以下策略来优化回答长度。第一种策略是“分段生成”——先生成简洁的核心回答,如果用户需要更多信息,再通过后续追问展开详细说明。这种策略在客服场景中特别有效,能够快速响应用户的核心需求。第二种策略是“自适应长度”——根据查询的复杂度和检索结果的信息量动态调整回答长度。对于简单的查询(如“什么是RAG”),生成简洁的定义式回答;对于复杂的查询(如“比较RAG和微调的优缺点”),生成详细的分析式回答。第三种策略是“后处理截断”——在模型生成回答后,如果回答过长,使用摘要模型或规则方法进行截断,保留最核心的信息。
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
7.5.3 语气与风格定制
语气与风格定制使RAG系统能够适应不同的应用场景和用户群体。一个面向专业工程师的技术问答系统和一个面向普通消费者的产品咨询系统,在语气和风格上应该有显著差异。
语气定制示例
专业严谨型
professional_tone = """你是一位资深的技术顾问。
请使用专业、准确、简洁的语言回答问题。
避免使用口语化表达和不确定的措辞。"""
友好亲切型
friendly_tone = """你是一位乐于助人的助手。
请用通俗易懂的语言回答问题,就像和朋友聊天一样。
可以使用类比和日常生活中的例子来解释复杂概念。"""
教学解释型
educational_tone = """你是一位耐心的老师。
请用循序渐进的方式解释概念,从简单到复杂。
多使用具体的例子和类比帮助理解。"""
风格定制不仅包括语气,还包括回答的组织方式、术语使用偏好、以及视觉呈现形式。例如,在面向非技术用户的场景中,应避免使用过多的技术术语,或在使用术语时提供通俗的解释;在面向技术用户的场景中,可以使用精确的技术术语,并提供代码示例和技术细节。风格定制的核心原则是“以用户为中心”——根据目标用户群体的特征和需求来定制回答的风格。
在企业级RAG系统中,风格定制通常需要支持多租户场景——不同的企业客户或不同的业务部门可能有不同的风格要求。实现多租户风格定制的一种有效方式是构建“风格配置文件”(Style Profile),将语气、术语偏好、格式要求等风格参数抽象为可配置的项,在运行时根据租户身份动态加载对应的风格配置。
[10] LangChain Documentation. Prompt Templates. python.langchain.com/docs/concep…
7.5.4 多语言回答支持
多语言回答支持是全球化RAG系统的重要能力。用户可能使用中文提问,但知识库中的文档主要是英文的;或者用户使用英文提问,但希望获得中文回答。RAG系统需要能够处理这种跨语言的问答需求。
多语言RAG面临的核心挑战是“查询语言”与“文档语言”之间的不一致。当用户使用中文提问但检索到的文档是英文时,模型需要具备跨语言理解和生成能力。现代多语言LLM(如GPT-4、Claude 3、Qwen系列)通常具备较强的跨语言能力,能够阅读英文文档并用中文回答问题。然而,跨语言场景下的生成质量通常略低于同语言场景,因为模型需要在理解英文文档和生成中文回答之间进行“语言切换”,这增加了认知负担。
在提示词层面,可以通过以下方式优化多语言回答质量。第一,明确指定输出语言——在系统提示中明确要求“请使用中文回答”,避免模型自动选择语言。第二,提供多语言Few-shot示例——如果期望模型以特定语言回答,在Few-shot示例中展示该语言的问答对。第三,利用翻译增强——对于关键术语和概念,在上下文中同时提供原文和翻译,帮助模型更准确地理解专业术语的含义。
在工程实现层面,多语言RAG系统还需要考虑以下问题:嵌入模型的多语言支持(确保查询向量和文档向量在同一个多语言向量空间中)、分词器对多语言文本的处理效率(不同语言的token化效率差异可能导致上下文窗口利用不均衡)、以及多语言评估指标(确保评估数据集覆盖目标语言)。Gao等人在RAG综述中将多语言RAG列为重要的研究方向之一,指出多语言场景下的检索质量和生成质量仍有较大的提升空间。
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
7.5.5 流式输出与用户体验
流式输出(Streaming Output)是提升RAG系统用户体验的重要技术手段。在传统的非流式输出模式中,用户需要等待模型完成整个回答的生成后才能看到结果,对于较长的回答,等待时间可能达到数秒甚至数十秒,严重影响用户体验。流式输出通过将模型的生成过程实时推送给用户,使用户能够逐字或逐句地看到回答内容,显著降低了感知等待时间。
流式输出的技术实现基于LLM的token-by-token生成特性。在标准的LLM API调用中,模型逐个生成token,但API默认会等待所有token生成完毕后一次性返回。流式输出通过开启API的streaming选项(如OpenAI API的stream=True参数),使每个token生成后立即推送给客户端。在Web应用中,通常使用Server-Sent Events(SSE)或WebSocket协议来实现服务端到客户端的实时推送。
流式输出示例(使用OpenAI API)
from openai import OpenAI
client = OpenAI()
def stream_rag_response(question, context):
"""流式生成RAG回答"""
prompt = f"基于以下上下文回答问题。\n上下文:{context}\n问题:{question}"
stream = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content is not None:
yield chunk.choices[0].delta.content
在RAG系统中,流式输出面临一个独特的挑战:检索阶段的延迟。在非流式模式中,检索和生成是串行执行的——先完成检索,再开始生成。但在流式模式中,用户期望在提交问题后立即看到输出,而检索阶段可能需要数百毫秒到数秒的时间。为了解决这一问题,可以采用以下策略:第一,显示检索状态——在检索进行中时,向用户展示“正在搜索相关知识...”等状态提示;第二,预生成开头——在检索完成后、正式生成之前,先快速生成一个简短的开头(如“根据检索到的信息,...”),让用户感受到系统已经开始响应;第三,异步检索——对于多轮对话场景,可以在用户输入时就开始预检索,进一步减少感知延迟。
流式输出还需要考虑引用溯源的兼容性问题。在流式模式下,引用标记(如[1]、[2])是逐token生成的,用户可能在看到引用标记时还不知道对应的来源信息。解决这一问题的方法包括:在回答末尾流式追加引用来源列表;在Web应用中,将引用标记渲染为可悬浮的交互元素,悬浮时显示来源信息;在回答开头预先展示引用来源的摘要列表。这些方法确保了流式输出模式下引用溯源的可用性。
[1] Gao et al. RAG Survey. 2024. arXiv:2312.10997
[10] LangChain Documentation. Streaming. python.langchain.com/docs/how_to…
本章小结
本章系统性地介绍了RAG生成阶段的核心技术体系。我们从提示工程在RAG中的应用出发(7.1节),探讨了RAG提示词的核心结构、专用模板设计、优化策略以及常见反模式。然后深入分析了上下文拼接策略(7.2节),包括检索结果的排序与截断、上下文窗口管理、注入方式对比、Lost in the Middle问题以及上下文去重与压缩。在引用溯源部分(7.3节),我们介绍了引用溯源的价值与实现原理、常见引用格式、准确性保障机制以及企业场景中的最佳实践。幻觉抑制技术(7.4节)是本章的重点,我们全面介绍了RAG中幻觉的类型与成因、基于提示和基于验证的幻觉抑制方法、RAG Triad评估框架以及综合性的幻觉检测与缓解策略。最后,在回答格式与风格控制部分(7.5节),我们讨论了结构化输出、长度控制、语气定制、多语言支持和流式输出等用户体验优化技术。
至此,本书第二部分“原理篇”的全部六章内容已经呈现完毕。让我们回顾一下原理篇的知识脉络:第1章从大语言模型的固有局限性出发,引出了RAG技术的必要性;第2章回顾了RAG技术的诞生与发展历程,建立了完整的历史认知;第3章将RAG与微调、长上下文模型进行了系统对比,帮助读者在不同技术路线之间做出正确选择;第4章描绘了RAG完整工作流程的全景图,建立了从数据到回答的端到端认知框架;第5章深入离线索引阶段,详细介绍了数据采集、清洗、分块和向量化的技术细节;第6章聚焦在线检索阶段,系统介绍了从基础Top-K检索到前沿ColPali范式的完整检索技术图谱;第7章(本章)则完成了RAG流程的最后一环——生成阶段,涵盖了提示工程、上下文拼接、引用溯源、幻觉抑制和格式控制等核心技术。
原理篇的六章内容构成了一个从动机到方法、从理论到实践的完整知识体系。读者通过原理篇的学习,应该已经建立了对RAG技术的系统性理解,具备了设计和实现基础RAG系统的能力。然而,RAG技术的世界远比原理篇所覆盖的内容更加广阔和深入。在即将到来的第三部分“进阶篇”中,我们将探索更加前沿和高级的RAG技术——包括高级RAG架构(如多跳推理、自适应检索)、RAG系统的评估与优化方法论、多模态RAG(结合图像、音频、视频等多模态信息)、Agent化的RAG系统(让RAG系统具备自主规划和工具使用能力),以及RAG在企业级场景中的工程化实践。这些进阶内容将帮助读者从“会用RAG”提升到“精通RAG”,构建真正生产级的高质量RAG系统。
参考文献
[1] Gao, Y., Xiong, Y., et al. Retrieval-Augmented Generation for Large Language Models: A Survey. arXiv:2312.10997, 2023.
[2] Lewis, P., Perez, E., Piktus, A., et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. NeurIPS 2020. arXiv:2005.11401.
[3] Liu, N.F., Lin, K., Hewitt, J., et al. Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172, 2023.
[4] Wei, J., Wang, X., Schuurmans, D., et al. Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. NeurIPS 2022.
[5] Li, C., Zhang, Z., Mulcaire, O., et al. Chain-of-Verification Reduces Hallucination in Large Language Models. arXiv:2309.11495, 2023.
[6] Han, S., Schoelkopf, H., Zhao, Y., et al. LLMLingua: Compressing Prompts for Accelerated Inference of Large Language Models. arXiv:2310.05736, 2023.
[7] Es, S., James, J., Espinosa-Anke, L., Schockaert, S. RAGAS: Automated Evaluation of Retrieval Augmented Generation. arXiv:2309.15217, 2023.
[8] Ji, Z., Lee, N., Frieske, R., et al. Survey of Hallucination in Natural Language Generation. ACM Computing Surveys, 2023. arXiv:2311.05232.
[9] Brown, T.B., Mann, B., Ryder, N., et al. Language Models are Few-Shot Learners. NeurIPS 2020.
[10] LangChain Documentation. Prompt Templates & Structured Output. python.langchain.com/docs/concep…