RAG 检索增强生成: 从原理到工程实践

Lisa Tan | 2026-09-02T01:08:43 | Python, AI

全面解析 RAG 的架构和核心组件,涵盖文档切片策略、Embedding 模型选择、向量检索优化和 Prompt 工程技巧。

# RAG 检索增强生成: 从原理到工程实践 ## 什么是 RAG Retrieval-Augmented Generation(检索增强生成)通过在 LLM 回答问题前先检索相关文档,将外部知识注入到生成过程中。它解决了 LLM 的两大痛点: - 知识过时(训练数据截止日期) - 幻觉(编造不存在的信息) ## RAG 架构 ``` 用户问题 | v Query 处理(改写/扩展) | v 向量检索 --> 向量数据库 | (预先索引的文档) v 相关文档片段 | v Prompt 组装(问题 + 上下文) | v LLM 生成回答 | v 后处理(引用来源/置信度过滤) ``` ## 离线阶段:文档处理与索引 ### 文档加载 ```python from langchain_community.document_loaders import ( PyPDFLoader, TextLoader, UnstructuredMarkdownLoader, ) # 加载不同格式 pdf_docs = PyPDFLoader("report.pdf").load() md_docs = UnstructuredMarkdownLoader("guide.md").load() ``` ### 文档切片 切片策略直接影响检索质量: ```python from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每个片段的目标大小 chunk_overlap=100, # 片段间的重叠 separators=["\n\n", "\n", "。", ".", " ", ""], length_function=len, ) chunks = splitter.split_documents(pdf_docs) print("Total chunks:", len(chunks)) ``` 切片大小选择: - **小片段(200-500 字)**:检索精确,但可能丢失上下文 - **大片段(1000-2000 字)**:上下文丰富,但检索噪声多 - **推荐**:500-800 字,100-200 字重叠 ### 向量化与存储 ```python from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db", collection_metadata={"hnsw:space": "cosine"}, ) ``` ## 在线阶段:检索与生成 ### 基础 RAG 链 ```python from langchain_openai import ChatOpenAI from langchain.chains import RetrievalQA llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=vectorstore.as_retriever( search_type="similarity", search_kwargs={"k": 5}, ), return_source_documents=True, ) result = qa_chain.invoke({"query": "What is the refund policy?"}) print(result["result"]) for doc in result["source_documents"]: print(" Source:", doc.metadata.get("source")) ``` ### 高级检索策略 #### 1. 混合检索(向量 + 关键词) ```python from langchain.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever bm25_retriever = BM25Retriever.from_documents(chunks) bm25_retriever.k = 5 vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5}) ensemble = EnsembleRetriever( retrievers=[bm25_retriever, vector_retriever], weights=[0.4, 0.6], ) ``` #### 2. 查询改写 ```python from langchain.prompts import PromptTemplate rewrite_prompt = PromptTemplate.from_template( "Rewrite the following question to be more specific and " "search-friendly. Only output the rewritten question.\n\n" "Original: {question}\nRewritten:" ) rewritten = llm.invoke(rewrite_prompt.format( question="How do returns work?")) # -> "What is the product return and refund process and policy?" ``` #### 3. 多查询检索 ```python from langchain.retrievers.multi_query import MultiQueryRetriever multi_retriever = MultiQueryRetriever.from_llm( retriever=vector_retriever, llm=llm, ) # 自动生成多个角度的查询,合并结果 ``` ## Prompt 工程 ```python RAG_PROMPT = """Based on the following context, answer the question. If the context does not contain enough information, say so honestly. Context: {context} Question: {question} Instructions: 1. Only use information from the provided context 2. If citing specific facts, mention which source 3. If the context is insufficient, say "I don't have enough information to answer this accurately" 4. Be concise but thorough Answer:""" ``` ## 评估指标 ```python # 检索质量 def recall_at_k(relevant_docs, retrieved_docs, k): retrieved_set = set(d.page_content for d in retrieved_docs[:k]) relevant_set = set(d.page_content for d in relevant_docs) return len(retrieved_set & relevant_set) / len(relevant_set) # 生成质量(使用 LLM 评估) eval_prompt = ( "Rate the following answer on a scale of 1-5 for accuracy " "and completeness, given the reference context.\n\n" "Context: {context}\nAnswer: {answer}\nScore:" ) ``` ## 常见问题与优化 ### 问题 1: 检索到的内容不相关 - 优化切片策略(更小的片段,更多重叠) - 使用混合检索(BM25 + 向量) - 添加元数据过滤 ### 问题 2: 回答包含幻觉 - Prompt 中明确要求"只使用提供的上下文" - 降低 temperature 到 0 - 添加来源引用要求 ### 问题 3: 上下文窗口不够 - 使用 map-reduce 链处理大量文档 - 先用 LLM 摘要再组装 - 选择更大上下文窗口的模型 ### 问题 4: 多语言支持 - 使用多语言 Embedding 模型 - 查询和文档统一语言处理 - 考虑翻译后再检索 ## 生产部署建议 1. **缓存**:对热门查询缓存检索结果和生成回答 2. **监控**:记录检索延迟、命中率、用户反馈 3. **增量更新**:新文档实时索引,旧文档定期重建 4. **安全**:输入过滤防止 Prompt 注入 5. **成本控制**:Embedding 调用做批量处理,减少 API 调用 ## 总结 RAG 是当前最实用的 LLM 应用模式。核心在于三个环节的质量:文档切片决定知识粒度,检索策略决定相关性,Prompt 设计决定生成质量。从简单的向量检索开始,逐步引入混合检索、查询改写等优化手段,就能构建出高质量的知识问答系统。

← Back to Blog