
1. 从“幻觉”到“落地”为什么我们需要RAG如果你最近在折腾大语言模型或者关注AI应用开发大概率已经对“幻觉”这个词深恶痛绝了。你满怀期待地问模型一个具体问题比如“我们公司去年Q3的销售冠军是谁”它可能会给你编造一个名字甚至附上一段看似合理的履历。这种一本正经地胡说八道就是大模型“幻觉”的典型表现。其根源在于大模型本质上是一个基于海量通用数据训练的概率模型它擅长生成“像那么回事”的文本但并不具备事实核查和实时信息获取的能力。它不知道你公司的内部数据也不知道昨天刚发生的新闻。为了解决这个问题业界最初想到的是“微调”。就像给一个通才做专项培训我们把特定领域的知识比如公司内部文档、产品手册喂给模型让它重新学习从而获得在该领域的“专家能力”。这个方法有效但成本高昂且不灵活。每次知识更新都需要重新训练模型耗时耗力并且容易导致“灾难性遗忘”——模型学会了新知识却可能忘掉了之前的一些通用能力。于是检索增强生成应运而生。它的核心思想非常直观我不去改变模型本身而是为模型配备一个“外挂大脑”。当用户提问时系统不是让模型凭空想象而是先从你的专属知识库文档、数据库、网页等中检索出与问题最相关的信息片段然后将这些“证据”和问题一起交给模型指令模型“请基于以下资料回答问题。” 这样一来模型的回答就有了事实依据极大地减少了幻觉提高了答案的准确性和可信度。RAG不是某个具体的工具而是一种架构范式。它巧妙地将信息检索IR领域成熟的技术与大语言模型LLM强大的理解和生成能力结合起来。如今从企业级知识库问答、智能客服到AI编程助手、法律文件分析RAG已经成为让大模型“落地”到具体业务场景中最主流、最实用的技术路径。它降低了大模型的应用门槛让我们能够以相对低的成本构建出“懂”我们私有知识的智能应用。2. RAG的核心工作流四步构建“外挂大脑”一个典型的RAG系统其工作流程可以清晰地划分为四个核心阶段知识切片、向量化、检索召回与重排、生成。理解这个流水线是掌握RAG的关键。2.1 知识切片把“厚书”拆成“便签”想象一下你有一本1000页的产品手册当用户问“如何重置设备密码”时你绝不会把整本手册扔给模型。你需要快速翻到“故障排除”章节下的“密码管理”小节。知识切片做的就是这件事——将长篇文档分解为语义上相对独立、大小合适的片段Chunks。为什么切片如此重要精度过大的片段会包含无关信息干扰模型聚焦过小的片段可能丢失关键上下文比如定义和例子被分开了。合适的切片能让检索更精准。效率向量数据库处理和检索短文本的速度更快成本更低。上下文长度限制LLM有上下文窗口限制如4K、8K、128K Token我们必须确保检索到的证据总和不超过这个限制。切片策略是RAG的“暗艺术”之一常见方法有固定长度重叠切片这是最基础的方法。设定一个固定长度如512个字符和重叠长度如50个字符。像滑动窗口一样切割文本。重叠部分保证了上下文连贯性避免一个句子被生生切断。这种方法简单但对文档结构不敏感。基于语义/句子的递归切片更智能的方法。它会尝试在完整的句子、段落或自然章节边界处进行切割。例如使用LangChain的RecursiveCharacterTextSplitter你可以指定分隔符优先级如\n\n,\n,., 工具会尽量在大的语义单元处切割不行再递归到更小的单元。基于文档结构的切片对于PDF、Markdown、HTML等结构化文档可以根据标题#,##、列表、表格等进行切割能更好地保留语义完整性。实操心得没有“一刀切”的最佳策略。对于技术文档按章节或子标题切分效果很好对于对话记录按说话人轮次切分更合理对于代码可能需要按函数或类来切分。通常需要结合业务数据特点进行实验和评估。2.2 向量化让计算机“理解”语义切片后的文本对人类是清晰的但对计算机只是一串字符。我们需要将其转化为计算机能“理解”并进行相似度比较的形式——向量或称嵌入Embedding。向量化模型如text-embedding-ada-002,bge-large-zh,m3e会将一段文本映射为一个高维空间中的点一个由数百或数千个数字组成的数组。这个空间的神奇之处在于语义相似的文本其向量在空间中的距离通常用余弦相似度衡量会很接近。例如“狗”和“宠物”的向量距离会比“狗”和“汽车”的向量距离近得多。这样当用户提问“如何照料我的宠物犬”系统将问题也转化为向量然后在向量空间中寻找与它最接近的那些知识片段向量这些片段很可能就包含了关于“狗”、“饲养”、“护理”等内容。向量模型的选择至关重要领域适配性通用模型如OpenAI的ada-002在多样任务上表现稳健。垂直领域模型如针对金融、法律、医疗训练的嵌入模型在特定领域效果更佳。语言处理中文优先选择优秀的中文嵌入模型如bge-large-zh、m3e。维度与性能向量维度越高通常表征能力越强但存储和计算成本也越高。需要在精度和效率间权衡。2.3 检索从“大海”到“精炼”检索阶段的目标是给定用户问题从海量知识片段中快速、准确地找出最相关的Top-K个片段。这个过程通常分为两步召回Recall和重排序Rerank。2.3.1 召回广撒网召回的目标是“宁可错杀不可放过”尽可能把所有可能相关的片段都找出来。最主流的方法是向量相似度检索。系统计算问题向量与所有知识片段向量的相似度分数如余弦相似度然后返回分数最高的N个例如20个片段。这就是常说的“向量检索”或“语义搜索”。除了纯向量检索还有关键词检索稀疏检索如BM25算法。它基于关键词匹配擅长处理实体、术语等精确匹配。对于“找包含‘API密钥错误代码403’的文档”这类问题BM25可能比向量检索更快更准。混合检索结合向量检索和关键词检索的结果。这是目前工业界的主流实践因为它能兼顾语义相似性和字面匹配提高召回结果的覆盖面和鲁棒性。例如可以先分别用两种方法各召回10个结果然后合并去重得到约15-20个候选片段。2.3.2 重排序精挑选召回阶段得到的候选片段在相关性上可能是粗糙的。重排序就像一个更精细的“裁判”对这批候选片段进行二次打分和排序选出最精华的Top-M个例如5个送给LLM。重排序器Reranker通常是一个比嵌入模型更强大、但计算成本也更高的交叉编码器模型如bge-reranker,Cohere rerank。它同时编码问题和候选片段直接计算它们之间的相关性分数这个分数比单纯的向量余弦相似度更能反映深层次的语义关联。踩坑实录很多初学者搭建的RAG系统效果不佳问题往往出在检索环节。他们只做了简单的向量检索召回了一堆“似是而非”的片段。例如用户问“Python中如何连接MySQL”向量检索可能召回大量关于“Python数据库连接”、“MySQL安装”、“SQL语法”的片段但重排序器能识别出“连接MySQL”这个具体任务将“使用pymysql或mysql-connector-python库”的片段排到最前面。跳过重排序你的RAG系统精度可能会大打折扣。2.4 生成基于证据的“创作”这是最后一步也是LLM大显身手的环节。我们将经过重排序筛选出的、最相关的知识片段作为上下文或证据连同用户的问题以及一个精心设计的提示词Prompt一起输入给LLM。一个典型的Prompt模板如下你是一个专业的助手请严格根据以下提供的上下文信息来回答问题。如果上下文中的信息不足以回答问题请直接说“根据已知信息无法回答该问题”不要编造信息。 上下文信息 {context} 问题{question} 请根据上下文回答LLM的任务是“阅读理解”“归纳总结”“流畅生成”。它需要理解上下文从中提取与问题相关的关键事实然后组织成通顺、准确的答案。这一步的关键在于提示词工程和上下文管理指令清晰明确要求模型“基于上下文”并设定拒绝回答的边界。上下文编排如何将多个检索到的片段有效地组织成一段连贯的上下文简单的拼接可能造成信息混乱。有时需要按相关性排序后拼接或加入分隔符如\n---\n。引用溯源对于企业级应用要求模型在答案中注明引用来源出自哪个片段的哪部分是刚需这增加了答案的可信度和可核查性。至此一个完整的RAG流程就走完了。从原始文档到精准答案RAG通过检索为LLM装上了“事实的锚点”。3. RAG的进阶架构与工程化挑战基础的RAG流程解决了“有无”问题但要构建一个生产级可用的、高性能、高可靠的RAG系统我们还需要面对一系列工程化挑战并引入更复杂的架构模式。3.1 超越基础高级RAG架构模式1. 递归检索与查询转换简单的一次检索可能不够。例如用户问“我们公司去年在AI方面的投入和今年比有什么变化”。这个复杂问题可以分解为“去年AI投入”和“今年AI投入”。高级RAG系统会先让LLM将原问题分解或重写为多个更易检索的子查询查询转换然后分别检索最后综合所有结果生成答案。这就是Agentic RAG的雏形——让LLM主动规划检索策略。2. 图增强RAG传统RAG将文档视为独立的片段忽略了片段之间丰富的关联关系如上下级、引用、共现。图增强RAGGraph RAG利用知识图谱来建模这些关系。在检索时不仅检索相关片段还检索其在图谱中的邻居节点从而获得更丰富、关联性更强的上下文。这对于回答需要多步推理或深度探索的问题特别有效。3. 自适应RAG与路由不是所有问题都需要检索。对于“你好”、“谢谢”这样的通用对话直接让LLM回答即可对于需要最新知识或私有知识的问题才走RAG流程。系统需要一个“路由”机制根据问题内容动态决定是否检索、以及检索哪些数据源。这通常通过一个分类器或轻量级LLM来实现。3.2 工程化落地的核心挑战1. 知识切片之痛如前所述切片策略极大影响效果。更大的挑战在于处理复杂内容表格简单的文本切片会破坏表格结构导致信息丢失。需要专门处理或将表格转换为描述性文本。长文档技术手册、学术论文。如何保持跨页、跨章节的上下文连贯性递归切片和重叠是基础有时需要结合章节标题进行层次化切片。代码仓库如何切分代码文件按函数、类、模块还需要考虑导入关系、注释的完整性。2. 检索质量瓶颈多模态检索如果知识库包含图片、图表如何实现“根据图片找相似图片”或“根据文字描述找图片”需要多模态嵌入模型。检索效率当知识库达到百万、千万级别时暴力计算相似度不可行。必须使用近似最近邻ANN索引如FAISS、HNSWpgvector支持、SCANN等来加速。这需要在精度和速度之间做trade-off。冷启动与稀疏性对于专业术语、新名词嵌入模型可能无法很好地表征。需要结合同义词扩展、术语库或进行领域自适应微调。3. 上下文管理与LLM的“注意力”LLM的上下文窗口是有限的。即使我们检索到了5个最相关的片段如果它们总长度超过了窗口限制我们也无法全部送入模型。这时需要策略压缩使用LLM对检索到的上下文进行摘要压缩保留核心信息。选择性注入只送入最相关的前几个片段或者采用“滑动窗口”方式分多次交互。长上下文模型虽然128K甚至更长上下文的模型出现了但成本高昂且模型对长上下文中部信息的“注意力”可能依然不足。4. 评估与迭代RAG没有“银弹”如何判断你的RAG系统是好是坏不能只靠人工抽查。需要建立评估体系检索评估命中率检索到的片段是否包含答案、平均排名答案片段的平均位置。生成评估忠实度答案是否严格基于提供的上下文有没有幻觉或添油加醋答案相关性答案是否直接回答了问题上下文相关性提供的上下文是否都与问题强相关 可以使用自动化评估框架如RAGAS、TruLens结合人工评估持续迭代切片策略、检索模型和提示词。4. 从零到一手把手构建一个简易RAG系统理论说了这么多我们来点实际的。下面我将以处理一份PDF格式的产品FAQ为例使用LangChain和Chroma向量数据库构建一个最简化的RAG问答系统。你会看到每个环节的具体代码和决策理由。4.1 环境准备与工具选型为什么选LangChain和ChromaLangChain它不是唯一的RAG框架但生态最丰富、文档最全、社区最活跃对于快速原型开发和学习来说是最佳选择。它封装了从文档加载、切片、向量化到检索、生成的完整链条。Chroma轻量级、开源、易用的向量数据库可以持久化到磁盘适合本地开发和中小型项目。生产环境可能会考虑Weaviate、Qdrant或PGVector与PostgreSQL集成。安装依赖pip install langchain langchain-community langchain-chroma pypdf sentence-transformerspypdf用于读取PDF文件。sentence-transformers我们使用开源的all-MiniLM-L6-v2模型进行向量化它体积小、速度快适合演示。4.2 第一步文档加载与切片假设我们有一个product_faq.pdf文件。from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. 加载文档 loader PyPDFLoader(./product_faq.pdf) documents loader.load() print(f加载了 {len(documents)} 页PDF文档) # 2. 文本切片 text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个片段的字符数目标 chunk_overlap50, # 片段间的重叠字符数 length_functionlen, separators[\n\n, \n, 。, , , , , , ] # 按中文习惯优先切分 ) chunks text_splitter.split_documents(documents) print(f将文档切分为 {len(chunks)} 个片段)关键参数解析chunk_size500对于FAQ问题答案通常较短500字符能容纳一个问答对及其上下文。chunk_overlap50重叠确保一个问题如果跨了自然段其信息不会被完全割裂。separators这里调整了分隔符优先级更符合中文文本的断句习惯。4.3 第二步向量化与存储from langchain_chroma import Chroma from langchain.embeddings.sentence_transformer import SentenceTransformerEmbeddings # 1. 初始化嵌入模型 embedding_function SentenceTransformerEmbeddings(model_nameall-MiniLM-L6-v2) # 注意首次运行会下载模型约80MB。 # 2. 创建向量数据库并存储片段 vectorstore Chroma.from_documents( documentschunks, embeddingembedding_function, persist_directory./chroma_db # 指定持久化目录 ) print(向量数据库已创建并持久化到 ./chroma_db)这里我们使用本地运行的sentence-transformers模型避免了调用API的费用和延迟。persist_directory参数让Chroma将向量索引保存到磁盘下次启动无需重新计算。4.4 第三步检索与问答链from langchain.chains import RetrievalQA from langchain.llms import Ollama # 假设使用本地运行的Ollama Llama3模型 # 也可以使用OpenAI: from langchain_openai import ChatOpenAI # 1. 初始化LLM # 使用Ollama本地模型 llm Ollama(modelllama3) # 如果使用OpenAI则 # from langchain_openai import ChatOpenAI # llm ChatOpenAI(modelgpt-3.5-turbo) # 2. 创建检索器 retriever vectorstore.as_retriever( search_typesimilarity, # 使用相似度搜索 search_kwargs{k: 4} # 返回最相关的4个片段 ) # 3. 创建问答链 qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 最简单的方式将所有检索到的上下文“塞”进Prompt retrieverretriever, return_source_documentsTrue, # 返回源文档便于溯源 chain_type_kwargs{ prompt: PROMPT # 可以传入自定义的Prompt模板见下文 } ) # 4. 自定义Prompt模板增强可控性 from langchain.prompts import PromptTemplate template 请严格根据以下上下文信息来回答问题。如果你不知道答案就说你不知道不要试图编造答案。 上下文 {context} 问题{question} 请用中文给出答案 PROMPT PromptTemplate( templatetemplate, input_variables[context, question] ) # 将自定义Prompt传入chain_type_kwargs qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, retrieverretriever, return_source_documentsTrue, chain_type_kwargs{prompt: PROMPT} )4.5 第四步运行与测试# 提问 question 产品支持哪些支付方式 result qa_chain.invoke({query: question}) print(f问题{question}) print(f答案{result[result]}) print(\n--- 来源片段 ---) for i, doc in enumerate(result[source_documents]): print(f\n片段 {i1} (页码{doc.metadata.get(page, N/A)}):) print(doc.page_content[:200] ...) # 打印前200字符运行这段代码你会得到基于PDF内容的答案并看到是哪些文本片段支撑了这个答案。这就是一个最基础的RAG应用。避坑指南在实际开发中你很快会遇到chain_typestuff的局限性。当检索到的上下文很长时会超出LLM的上下文窗口。此时需要更高级的chain_type如map_reduce先对每个片段单独生成答案再汇总、refine迭代式精炼答案或者使用LangChain的ContextualCompressionRetriever对上下文进行压缩。这标志着你的RAG系统从“玩具”走向“生产”的第一步。5. RAG的评估、优化与未来展望构建出第一个可运行的RAG管道只是起点。要让其真正产生价值持续的评估、迭代和优化必不可少。5.1 如何评估你的RAG系统你不能等到用户投诉才发现答案错了。建立一个自动化的评估流水线是关键。核心评估维度检索质量命中率对于一组有标准答案的问题检索到的Top-K个片段中包含正确答案的比例。平均倒数排名正确答案在检索结果列表中的排名的倒数平均值。值越高说明正确答案排得越靠前。生成质量忠实度答案是否完全源自提供的上下文可以用“答案-上下文”的N-gram重叠度或让另一个LLM来判断。答案相关性答案是否直接回答了问题同样可以用LLM进行评判。信息完整性答案是否涵盖了问题所问的所有方面工具推荐RAGASRAGAS是一个专门用于评估RAG系统的开源框架。你只需要提供“问题”、“检索到的上下文”、“生成的答案”以及“标准答案”可选它就能自动计算出一系列指标。# 示例性代码展示RAGAS的理念 from ragas.metrics import faithfulness, answer_relevance, context_relevance from ragas import evaluate # 假设你有测试数据集 dataset [ { question: 支付方式有哪些, answer: 支持支付宝、微信支付和信用卡。, # 模型生成的答案 contexts: [[片段1内容..., 片段2内容...]], # 检索到的上下文 ground_truth: 支付宝、微信支付、信用卡 # 标准答案 }, # ... 更多测试用例 ] results evaluate(dataset, metrics[faithfulness, answer_relevance]) print(results)通过定期在测试集上运行评估你可以量化每次代码或策略变更如更换嵌入模型、调整切片大小、增加重排序带来的效果是提升还是下降。5.2 常见问题与调优技巧问题1答案出现幻觉引用了不存在的上下文。检查Prompt是否足够强硬地限制了模型“必须基于上下文”尝试在Prompt中加入“如果上下文没有提到请回答‘我不知道’”。检查检索到的上下文是否真的与问题强相关可能是检索环节出了问题考虑引入重排序模型。检查LLM本身是否过于“健谈”可以尝试调整生成参数如降低temperature如设为0.1以减少随机性。问题2答案不完整遗漏了上下文中的部分信息。调整检索增加search_k参数让系统检索更多的候选片段例如从4个增加到8个。调整切片检查是否因为切片过大导致LLM忽略了片段后半部分的信息或者切片过小导致关键信息被割裂需要优化切片策略。使用更复杂的Chain将chain_type从stuff改为map_reduce让模型先对每个片段生成子答案再综合可能有助于捕捉分散的信息。问题3对于简单、通用的问题系统也去检索速度慢且答案生硬。实现路由机制在RAG管道前加一个“分类器”。可以用一个快速的文本分类模型或者用少量示例提示LLM来判断“用户的问题是关于私有知识/内部文档还是通用对话” 只有前者才触发检索。缓存对常见问题及其答案进行缓存可以极大提升响应速度。5.3 RAG的未来Agentic、自主与多模态RAG技术本身也在快速演进Agentic RAG未来的RAG系统将更像一个自主的“研究助理”。用户提出一个复杂问题RAG Agent可以自主规划是否需要拆解问题是否需要多轮检索是否需要联网搜索最新信息是否需要调用计算工具它将检索、推理、工具调用融为一体。全链路优化从更智能的文档解析和切片理解图表、公式到更高效的索引和检索算法支持过滤、混合查询再到更强大的重排序和上下文管理模型每一环都在被深度优化。多模态RAG知识库不再只是文本。RAG系统需要处理图片、音频、视频并实现跨模态的检索和生成。例如根据描述生成图像或根据图表回答问题。与微调的融合纯粹的RAG和纯粹的微调并非对立。一种混合模式是用领域数据对基础模型进行轻量级微调如LoRA使其更“懂行”再结合RAG提供具体事实。这样既能获得领域知识又能保持信息的实时性。对我而言RAG最吸引人的地方在于它的“朴素”和“有效”。它没有试图用暴力改造大模型而是用工程化的思维为模型搭建了一个高效、可管理、可更新的外部知识系统。这正符合软件工程中“组合优于继承”的思想。随着底层模型能力的持续进步和工程框架的日益成熟RAG将成为构建可靠、可信AI应用的基石技术。它的门槛会越来越低但要想做出真正好用、解决实际痛点的产品对数据、对业务、对用户体验的深度理解永远是开发者最核心的竞争力。