RAG(Retrieval-Augmented Generation,检索增强生成)是构建AI Agent和智能问答系统的核心技术。它让大语言模型能够访问外部知识库,突破训练数据的限制。本文详细介绍RAG的原理和实现方法。
为什么需要RAG 大语言模型的局限性 LLM(大语言模型)虽然强大,但存在几个关键问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ┌─────────────────────────────────────────────────────────┐ │ LLM的局限性 │ ├─────────────────────────────────────────────────────────┤ │ │ │ 1. 知识截止日期 │ │ 训练数据有截止时间,无法获取最新信息 │ │ "2024年奥运会在哪举办?" → 可能回答错误 │ │ │ │ 2. 幻觉问题(Hallucination) │ │ 可能编造不存在的事实 │ │ "张三的论文主要观点是什么?" → 可能瞎编 │ │ │ │ 3. 私有数据不可知 │ │ 无法访问企业内部文档、个人笔记等 │ │ "我们公司的报销流程是什么?" → 无法回答 │ │ │ │ 4. 上下文窗口限制 │ │ 无法一次性处理大量文档 │ │ │ └─────────────────────────────────────────────────────────┘
RAG的解决方案 RAG通过检索外部知识 来增强LLM的生成能力:
1 2 3 4 5 6 7 8 传统LLM: 用户问题 ──────────────────────▶ LLM ──▶ 回答(可能有幻觉) RAG增强: 用户问题 ──▶ 检索相关文档 ──▶ 文档+问题 ──▶ LLM ──▶ 回答(有据可依) │ ▼ 知识库/向量数据库
RAG工作流程 整体架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ┌─────────────────────────────────────────────────────────────────┐ │ RAG系统架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 离线索引阶段 │ │ │ │ │ │ │ │ 文档 ──▶ 分割 ──▶ 向量化 ──▶ 存储到向量数据库 │ │ │ │ │ │ │ │ │ │ │ │ PDF Chunk Embedding Milvus │ │ │ │ Word 256字 768/1536维 Pinecone │ │ │ │ HTML Chroma │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 在线查询阶段 │ │ │ │ │ │ │ │ 用户问题 ──▶ 向量化 ──▶ 相似度检索 ──▶ Top-K文档 │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ 构建Prompt ──▶ LLM ──▶ 回答 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘
两个阶段 阶段1:离线索引(Indexing)
加载文档(PDF、Word、网页等)
文本分割(切分成小块)
向量嵌入(文本转向量)
存储到向量数据库
阶段2:在线查询(Querying)
用户提问
问题向量化
相似度检索,获取相关文档
构建增强Prompt
LLM生成回答
核心组件详解 1. 文档加载(Document Loader) 将各种格式的文档加载为统一的文本格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from langchain_community.document_loaders import ( PyPDFLoader, TextLoader, UnstructuredWordDocumentLoader, WebBaseLoader ) pdf_loader = PyPDFLoader("document.pdf" ) pdf_docs = pdf_loader.load() text_loader = TextLoader("notes.txt" ) text_docs = text_loader.load() word_loader = UnstructuredWordDocumentLoader("report.docx" ) word_docs = word_loader.load() web_loader = WebBaseLoader("https://example.com/article" ) web_docs = web_loader.load()
2. 文本分割(Text Splitter) 将长文档切分成适合检索的小块:
1 2 3 4 5 6 7 8 9 from langchain.text_splitter import RecursiveCharacterTextSplittersplitter = RecursiveCharacterTextSplitter( chunk_size=500 , chunk_overlap=50 , separators=["\n\n" , "\n" , "。" , "," , " " , "" ] ) chunks = splitter.split_documents(documents)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 原始文档: ┌─────────────────────────────────────────────────────────────┐ │ 第一段内容...第一段内容...第一段内容... │ │ 第二段内容...第二段内容...第二段内容... │ │ 第三段内容...第三段内容...第三段内容... │ └─────────────────────────────────────────────────────────────┘ 分割后: ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Chunk 1 │ │ Chunk 2 │ │ Chunk 3 │ │ 第一段内容... │ │ ...第二段... │ │ ...第三段... │ │ (500字符) │◀─▶│ (500字符) │◀─▶│ (500字符) │ └──────────────┘ └──────────────┘ └──────────────┘ 重叠50字符
分割策略选择:
策略
适用场景
固定大小
通用场景
按段落
结构清晰的文档
按句子
需要精确匹配的场景
递归分割
推荐,智能选择分割点
语义分割
保持语义完整性
3. 向量嵌入(Embedding) 将文本转换为稠密向量,使语义相似的文本在向量空间中距离更近:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from langchain_openai import OpenAIEmbeddingsfrom langchain_community.embeddings import HuggingFaceEmbeddingsopenai_embeddings = OpenAIEmbeddings( model="text-embedding-3-small" ) local_embeddings = HuggingFaceEmbeddings( model_name="sentence-transformers/all-MiniLM-L6-v2" ) vector = openai_embeddings.embed_query("什么是机器学习?" ) print (len (vector))
1 2 3 4 5 文本 ──▶ Embedding模型 ──▶ 向量 "猫是一种宠物" ──▶ [0.12, -0.34, 0.56, ..., 0.78] (1536维) "狗是人类的朋友" ──▶ [0.15, -0.32, 0.58, ..., 0.75] 相似,距离近 "量子力学很难" ──▶ [-0.45, 0.67, -0.23, ..., 0.12] 不相似,距离远
常用Embedding模型:
模型
维度
特点
OpenAI text-embedding-3-small
1536
效果好,需付费
OpenAI text-embedding-3-large
3072
更精确,更贵
BGE-large-zh
1024
中文效果好,开源
all-MiniLM-L6-v2
384
轻量,免费
GTE-large
1024
阿里开源,效果好
4. 向量数据库(Vector Store) 存储向量并支持高效的相似度检索:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from langchain_community.vectorstores import Chroma, FAISS, Milvusvectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db" ) vectorstore = FAISS.from_documents( documents=chunks, embedding=embeddings ) vectorstore.save_local("./faiss_index" ) vectorstore = Milvus.from_documents( documents=chunks, embedding=embeddings, connection_args={"host" : "localhost" , "port" : "19530" } )
常用向量数据库对比:
数据库
特点
适用场景
Chroma
轻量、易用、支持持久化
开发测试、小规模应用
FAISS
Facebook开源、高性能
中等规模、单机部署
Milvus
分布式、云原生
大规模生产环境
Pinecone
全托管、开箱即用
快速上线、不想运维
Weaviate
支持混合搜索
需要关键词+向量混合
Qdrant
Rust实现、高性能
高性能需求
5. 检索器(Retriever) 根据查询获取相关文档:
1 2 3 4 5 6 7 8 9 10 retriever = vectorstore.as_retriever( search_type="similarity" , search_kwargs={"k" : 5 } ) docs = retriever.invoke("什么是RAG?" ) for doc in docs: print (doc.page_content[:100 ])
检索策略:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 retriever = vectorstore.as_retriever(search_type="similarity" ) retriever = vectorstore.as_retriever( search_type="mmr" , search_kwargs={"k" : 5 , "fetch_k" : 20 , "lambda_mult" : 0.5 } ) retriever = vectorstore.as_retriever( search_type="similarity_score_threshold" , search_kwargs={"score_threshold" : 0.7 } )
6. 生成器(LLM) 基于检索到的文档生成回答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from langchain_openai import ChatOpenAIfrom langchain.prompts import ChatPromptTemplatellm = ChatOpenAI(model="gpt-4o" , temperature=0 ) prompt = ChatPromptTemplate.from_template(""" 基于以下上下文回答问题。如果上下文中没有相关信息,请说"我没有找到相关信息"。 上下文: {context} 问题:{question} 回答: """ )
完整RAG实现 使用LangChain 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 from langchain_openai import OpenAIEmbeddings, ChatOpenAIfrom langchain_community.vectorstores import Chromafrom langchain_community.document_loaders import PyPDFLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain.chains import RetrievalQAfrom langchain.prompts import PromptTemplateloader = PyPDFLoader("knowledge_base.pdf" ) documents = loader.load() splitter = RecursiveCharacterTextSplitter( chunk_size=500 , chunk_overlap=50 ) chunks = splitter.split_documents(documents) embeddings = OpenAIEmbeddings() vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db" ) retriever = vectorstore.as_retriever(search_kwargs={"k" : 3 }) llm = ChatOpenAI(model="gpt-4o" , temperature=0 ) prompt_template = """ 你是一个专业的问答助手。请根据以下参考资料回答问题。 参考资料: {context} 问题:{question} 要求: 1. 只基于参考资料回答,不要编造 2. 如果资料中没有相关信息,请明确说明 3. 回答要简洁准确 回答: """ prompt = PromptTemplate( template=prompt_template, input_variables=["context" , "question" ] ) qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff" , retriever=retriever, return_source_documents=True , chain_type_kwargs={"prompt" : prompt} ) result = qa_chain.invoke({"query" : "RAG的主要优势是什么?" }) print (result["result" ])print ("来源文档:" , result["source_documents" ])
使用LCEL(LangChain Expression Language) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from langchain_core.runnables import RunnablePassthroughfrom langchain_core.output_parsers import StrOutputParserdef format_docs (docs ): return "\n\n" .join(doc.page_content for doc in docs) rag_chain = ( { "context" : retriever | format_docs, "question" : RunnablePassthrough() } | prompt | llm | StrOutputParser() ) answer = rag_chain.invoke("什么是向量数据库?" ) print (answer)for chunk in rag_chain.stream("RAG如何工作?" ): print (chunk, end="" , flush=True )
向量相似度计算 常用相似度度量 1 2 3 4 5 6 7 8 9 10 11 12 13 import numpy as npdef cosine_similarity (a, b ): """余弦相似度:最常用""" return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) def euclidean_distance (a, b ): """欧氏距离""" return np.linalg.norm(a - b) def dot_product (a, b ): """点积""" return np.dot(a, b)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 余弦相似度: A·B cos(θ) = ───────── |A|×|B| 范围:[-1, 1] 1 = 完全相同方向 0 = 正交(无关) -1 = 完全相反 ▲ B /│ / │ / │ /θ │ / │ ──────┼────▶ A │ cos(θ)越大,越相似
近似最近邻搜索(ANN) 精确搜索在大规模数据上太慢,向量数据库使用ANN算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 精确搜索:O(n) - 遍历所有向量 ANN搜索:O(log n) - 近似但快速 常用ANN算法: ┌─────────────────────────────────────────────────────────┐ │ │ │ HNSW(Hierarchical Navigable Small World) │ │ ├── 构建多层图结构 │ │ ├── 从顶层开始搜索,逐层下降 │ │ └── 查询速度快,内存占用大 │ │ │ │ IVF(Inverted File Index) │ │ ├── 聚类划分空间 │ │ ├── 只搜索相关聚类 │ │ └── 内存效率高 │ │ │ │ PQ(Product Quantization) │ │ ├── 向量压缩 │ │ ├── 减少内存占用 │ │ └── 适合超大规模数据 │ │ │ └─────────────────────────────────────────────────────────┘
RAG优化技巧 1. 分块策略优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from langchain.retrievers import ParentDocumentRetrieverfrom langchain.storage import InMemoryStoreparent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000 ) child_splitter = RecursiveCharacterTextSplitter(chunk_size=400 ) store = InMemoryStore() retriever = ParentDocumentRetriever( vectorstore=vectorstore, docstore=store, child_splitter=child_splitter, parent_splitter=parent_splitter )
1 2 3 4 5 6 7 8 9 10 11 12 13 父子分块: 原始文档 ┌─────────────────────────────────────────────┐ │ 父块(2000字符) │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ 子块1 │ │ 子块2 │ │ 子块3 │ │ │ │ 400字符 │ │ 400字符 │ │ 400字符 │ │ │ └─────────┘ └─────────┘ └─────────┘ │ └─────────────────────────────────────────────┘ 检索时:用子块的向量匹配 返回时:返回父块,保留完整上下文
2. 查询改写(Query Rewriting) 1 2 3 4 5 6 7 8 9 10 11 12 13 from langchain.retrievers import MultiQueryRetrievermulti_query_retriever = MultiQueryRetriever.from_llm( retriever=base_retriever, llm=llm )
3. 混合检索(Hybrid Search) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from langchain.retrievers import EnsembleRetrieverfrom langchain_community.retrievers import BM25Retrieverbm25_retriever = BM25Retriever.from_documents(chunks) bm25_retriever.k = 5 vector_retriever = vectorstore.as_retriever(search_kwargs={"k" : 5 }) ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, vector_retriever], weights=[0.3 , 0.7 ] )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 混合检索: 用户查询:"Python装饰器" BM25(关键词匹配): ├── 精确匹配"Python" ├── 精确匹配"装饰器" └── 适合专有名词 向量检索(语义匹配): ├── 理解"装饰器"的含义 ├── 找到相关概念(高阶函数、元编程) └── 适合语义相关内容 结合两者:精确 + 语义
4. 重排序(Re-ranking) 1 2 3 4 5 6 7 8 9 10 11 12 from langchain.retrievers import ContextualCompressionRetrieverfrom langchain.retrievers.document_compressors import CrossEncoderRerankerfrom langchain_community.cross_encoders import HuggingFaceCrossEncodermodel = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-base" ) compressor = CrossEncoderReranker(model=model, top_n=3 ) reranking_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=retriever )
1 2 3 4 5 6 7 8 9 10 11 12 13 重排序流程: 初始检索:Top 20 ┌────┬────┬────┬────┬────┬────┬────┬────┐ │ D1 │ D2 │ D3 │ D4 │ D5 │ D6 │...│D20 │ └────┴────┴────┴────┴────┴────┴────┴────┘ 粗排序(Bi-Encoder,快但不精确) 重排序:Top 3 ┌────┬────┬────┐ │ D3 │ D7 │ D1 │ └────┴────┴────┘ 精排序(Cross-Encoder,慢但精确)
5. 查询路由(Query Routing) 1 2 3 4 5 6 7 8 9 10 from langchain.chains.router import MultiPromptChainroutes = { "技术文档" : tech_retriever, "产品手册" : product_retriever, "FAQ" : faq_retriever }
高级RAG架构 Self-RAG(自我反思RAG) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ┌─────────────────────────────────────────────────────────────┐ │ Self-RAG │ │ │ │ 问题 ──▶ 是否需要检索? │ │ │ │ │ ┌────┴────┐ │ │ 需要 不需要 │ │ │ │ │ │ ▼ ▼ │ │ 检索文档 直接生成 │ │ │ │ │ ▼ │ │ 文档是否相关? ──▶ 不相关 ──▶ 丢弃 │ │ │ │ │ 相关 │ │ │ │ │ ▼ │ │ 生成回答 │ │ │ │ │ ▼ │ │ 回答是否有支撑? ──▶ 无支撑 ──▶ 重新生成 │ │ │ │ │ 有支撑 │ │ │ │ │ ▼ │ │ 输出回答 │ │ │ └─────────────────────────────────────────────────────────────┘
Corrective RAG(纠正式RAG) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def corrective_rag (question ): docs = retriever.invoke(question) relevant_docs = [] for doc in docs: relevance = evaluate_relevance(question, doc) if relevance == "relevant" : relevant_docs.append(doc) if not relevant_docs: web_results = web_search(question) relevant_docs = web_results return generate_answer(question, relevant_docs)
Graph RAG(图增强RAG) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 传统RAG:文档 → 向量 → 检索 Graph RAG:文档 → 实体和关系 → 知识图谱 → 图检索 ┌──────────────────────────────────────────────────────────┐ │ 知识图谱 │ │ │ │ [Python] ───创建者──▶ [Guido] │ │ │ │ │ │ 用于 国籍 │ │ │ │ │ │ ▼ ▼ │ │ [机器学习] ◀───包含─── [荷兰] │ │ │ │ │ 框架 │ │ │ │ │ ▼ │ │ [TensorFlow] │ │ │ └──────────────────────────────────────────────────────────┘ 优势: - 捕获实体关系 - 多跳推理 - 结构化知识
评估指标 检索评估 1 2 3 4 5 6 7 8 9 10 recall = len (retrieved & relevant) / len (relevant) precision = len (retrieved & relevant) / len (retrieved) mrr = 1 / rank_of_first_relevant
生成评估 1 2 3 4 5 6 7 8 9 10 11 12 from ragas import evaluatefrom ragas.metrics import faithfulness, answer_relevancy, context_precisionresult = evaluate( dataset, metrics=[faithfulness, answer_relevancy, context_precision] )
应用场景 1. 企业知识库问答 1 2 3 4 5 6 7 8 9 10 11 12 13 ┌─────────────────────────────────────────────┐ │ 企业文档: │ │ - 员工手册 │ │ - 产品文档 │ │ - 技术规范 │ │ - 会议记录 │ └─────────────────────────────────────────────┘ │ ▼ RAG系统 │ ▼ "新员工入职需要准备什么?" → 精准回答
2. 客服机器人 1 2 3 4 5 6 7 8 qa_bot = RetrievalQA.from_chain_type( llm=llm, retriever=product_docs_retriever, chain_type_kwargs={ "prompt" : customer_service_prompt } )
3. 代码助手 1 2 3 4 5 6 7 8 9 code_retriever = vectorstore.as_retriever( search_kwargs={ "k" : 5 , "filter" : {"file_type" : "code" } } )
4. 学术研究助手 1 2 3 4 5 6 7 8 9 paper_rag = RetrievalQA.from_chain_type( llm=llm, retriever=paper_retriever, return_source_documents=True )
总结 RAG核心流程 1 2 3 文档 → 分块 → 向量化 → 存储 ↓ 问题 → 向量化 → 检索 → 增强Prompt → LLM → 回答
关键组件选型
组件
推荐选择
Embedding
OpenAI / BGE / GTE
向量数据库
Chroma(开发) / Milvus(生产)
分块策略
递归分割 + 父子分块
检索策略
混合检索 + 重排序
LLM
GPT-4o / Claude
优化方向
分块优化 :父子分块、语义分块
检索优化 :混合检索、重排序、查询改写
生成优化 :Prompt工程、Self-RAG
评估优化 :使用RAGAs等框架持续评估
RAG是构建可靠AI应用的关键技术,它让LLM能够基于真实数据生成回答,大大减少幻觉问题,是企业级AI应用的基础架构。