1504 字
4 分钟
让AI拥有知识:RAG检索增强生成详解
小张是某公司的IT,老板让他做个AI客服,能回答公司内部问题。
他直接把公司文档喂给ChatGPT,问:“公司的报销流程是什么?”
ChatGPT回答:“根据一般公司的报销流程…”
小张傻眼了:公司有自己的报销流程,ChatGPT根本不知道。
这就是LLM的知识局限:它只知道训练数据里的内容,不知道你的私有信息。
怎么办?RAG(检索增强生成)就是解决方案——让AI像开卷考试一样,能查阅你的知识库。
本文要点
- 为什么需要RAG
- RAG的工作原理(分块→向量化→检索→生成)
- 向量数据库和Embedding模型选择
- 六个优化技巧提升效果
- 实战代码示例
- RAG vs 微调怎么选
- GraphRAG简介
一、为什么需要RAG?
1.1 LLM的三大知识局限
1. 知识截止日期 - GPT-4的知识截止于2023年10月 - 之后发生的事,它不知道 - Claude的知识也有截止日期
2. 私有知识缺失 - 企业内部文档、产品手册 - 个人笔记、专属资料 - 未公开的研究成果 - 它全不知道
3. 幻觉问题 - 不知道的它可能编 - 编得还挺像真的 - 关键场景风险大1.2 RAG的解决思路
不让模型”记住”,而是让它”查询”。
就像考试:
- 闭卷考(纯LLM):靠记忆,可能忘记或记错
- 开卷考(RAG):能查阅资料,答案更准确
flowchart TD
N0["用户问题"]
N1["LLM"]
N0 --> N1
N1["LLM"]
N2["回答(可能编造)"]
N1 --> N2
N0["用户问题"]
N3["检索知识库"]
N0 --> N3
N3["检索知识库"]
N4["LLM+知识"]
N3 --> N4
N4["LLM+知识"]
N5["准确回答"]
N4 --> N5
1.3 RAG的优势
知识可更新:更新文档即可,无需重新训练可追溯来源:知道答案来自哪个文档成本低:不需要微调,只需存储和检索幻觉减少:基于真实文档回答适合企业:私有数据不出本地二、RAG是怎么工作的?
2.1 整体流程
┌─────────────────────────────────────────────────────────────┐│ RAG工作流程 │├─────────────────────────────────────────────────────────────┤│ ││ 【离线阶段:建知识库】 ││ ││ 文档 → 分块 → 向量化 → 存入向量数据库 ││ ││ ││ 【在线阶段:回答问题】 ││ ││ 问题 → 向量化 → 检索相关文档 → 问题+文档喂给LLM → 回答 ││ │└─────────────────────────────────────────────────────────────┘2.2 步骤详解
第一步:文档分块(Chunking)
把长文档切成小块,因为:
- 模型上下文窗口有限
- 检索精度更高
- 更灵活的组合
分块策略:
1. 固定长度分块 - 每块512/1024 Token - 简单粗暴 - 可能切断语义
2. 语义分块 - 按段落、章节分 - 语义完整 - 块大小不均
3. 滑动窗口分块 - 块之间有重叠(10-20%) - 减少边界信息丢失 - 推荐方案
最佳实践:- 块大小:512-1024 Token- 重叠:10-20%- 保留元数据(来源、标题、页码)第二步:向量化(Embedding)
把文本变成向量,让语义相似的文本距离更近:
flowchart TD
N0[""猫是宠物""]
N1["[0.2, 0.8, 0.1, ...]"]
N0 --> N1
N2[""狗是宠物""]
N3["[0.25, 0.75, 0.12, ...] ← 和"猫"距离近"]
N2 --> N3
N4[""汽车是交通工具""]
N5["[0.9, 0.1, 0.8, ...] ← 和前两者距离远"]
N4 --> N5
第三步:相似度检索
用户问题变成向量,在数据库里找距离最近的文档块:
用户问题:"公司年假多少天?" ↓ 向量化 ↓ 检索(计算余弦相似度)找到最相似的3个块:- "入职满一年享有5天年假..."- "年假最多累计15天..."- "年假需提前3天申请..."第四步:生成回答
把问题和找到的文档一起喂给LLM:
根据以下参考信息回答问题:
参考信息:1. "入职满一年享有5天年假..."2. "年假最多累计15天..."3. "年假需提前3天申请..."
问题:公司年假多少天?
答案:根据公司规定,入职满一年享有5天年假,最多累计15天。 如需休假,请提前3天申请。三、关键组件选择
3.1 向量数据库对比
| 数据库 | 特点 | 部署方式 | 适用场景 |
|---|---|---|---|
| Pinecone | 托管、简单、扩展性好 | 云服务 | 快速上手、生产环境 |
| Chroma | 轻量、开源、易集成 | 本地/Docker | 本地开发、小型项目 |
| Milvus | 分布式、高性能 | 自部署/K8s | 大规模生产 |
| Qdrant | 高效、开源、Rust编写 | 自部署/Docker | 性能敏感场景 |
| Weaviate | 语义搜索强、GraphQL | 自部署/云 | 复杂查询场景 |
选择建议:
- 快速验证:Chroma(本地)
- 生产环境:Pinecone(托管)或 Milvus(自部署)
- 性能要求高:Qdrant
3.2 Embedding模型选择
| 模型 | 特点 | 维度 | 适用场景 |
|---|---|---|---|
| OpenAI text-embedding-3-small | 性价比高、API简单 | 1536 | 通用、快速集成 |
| OpenAI text-embedding-3-large | 效果更好 | 3072 | 追求效果 |
| BGE-M3 | 开源、中文好、多语言 | 1024 | 私有部署、中文 |
| Cohere embed-v3 | 效果好、多语言 | 1024 | 企业应用 |
| Jina Embeddings v2 | 长文本支持 | 768 | 长文档场景 |
选择建议:
- 快速上手:OpenAI text-embedding-3-small
- 中文私有部署:BGE-M3
- 追求效果:OpenAI text-embedding-3-large
四、实战代码示例
4.1 使用LangChain构建RAG
# 安装依赖# pip install langchain langchain-openai chromadb
from langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_openai import OpenAIEmbeddings, ChatOpenAIfrom langchain_community.vectorstores import Chromafrom langchain.chains import RetrievalQAfrom langchain_community.document_loaders import TextLoader
# 1. 加载文档loader = TextLoader("company_policy.txt")documents = loader.load()
# 2. 分块text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, length_function=len)chunks = text_splitter.split_documents(documents)print(f"分成 {len(chunks)} 个块")
# 3. 向量化并存入数据库embeddings = OpenAIEmbeddings(model="text-embedding-3-small")vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db")
# 4. 创建检索器retriever = vectorstore.as_retriever( search_type="similarity", search_kwargs={"k": 3} # 返回最相似的3个块)
# 5. 创建问答链llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=retriever, return_source_documents=True)
# 6. 提问question = "公司的年假政策是什么?"result = qa_chain({"query": question})
print("回答:", result["result"])print("\n来源文档:")for doc in result["source_documents"]: print(f"- {doc.metadata.get('source', 'unknown')}")4.2 使用LlamaIndex构建RAG
# 安装依赖# pip install llama-index llama-index-embeddings-openai llama-index-llms-openai
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settingsfrom llama_index.embeddings.openai import OpenAIEmbeddingfrom llama_index.llms.openai import OpenAI
# 配置Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")Settings.llm = OpenAI(model="gpt-4o-mini")
# 1. 加载文档documents = SimpleDirectoryReader("./docs").load_data()
# 2. 创建索引index = VectorStoreIndex.from_documents(documents)
# 3. 创建查询引擎query_engine = index.as_query_engine( similarity_top_k=3, response_mode="compact")
# 4. 提问response = query_engine.query("公司的报销流程是什么?")print(response)
# 5. 查看来源for node in response.source_nodes: print(f"来源: {node.node.metadata}") print(f"相关度: {node.score}")4.3 简洁版:使用ChromaDB直接实现
import chromadbfrom openai import OpenAI
# 初始化client = OpenAI()chroma_client = chromadb.Client()collection = chroma_client.create_collection("documents")
def get_embedding(text): response = client.embeddings.create( model="text-embedding-3-small", input=text ) return response.data[0].embedding
# 添加文档docs = [ "公司年假政策:入职满一年享有5天年假。", "报销流程:先提交申请,领导审批后财务打款。", "工作时间:上午9点到下午6点,午休12-1点。"]
for i, doc in enumerate(docs): collection.add( ids=[f"doc_{i}"], embeddings=[get_embedding(doc)], documents=[doc] )
# 检索query = "年假有多少天?"results = collection.query( query_embeddings=[get_embedding(query)], n_results=2)
print("相关文档:", results["documents"])
# 生成答案context = "\n".join(results["documents"][0])response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": f"根据以下信息回答问题:\n{context}"}, {"role": "user", "content": query} ])print("回答:", response.choices[0].message.content)五、六个优化技巧
5.1 技巧1:混合检索(Hybrid Search)
向量检索 + 关键词检索,取长补短:
向量检索:语义相似性好可能漏精确匹配(产品名、型号)
关键词检索(BM25):精确匹配可能漏语义相关
混合检索 = 两者结合召回率提升10-30%from langchain.retrievers import EnsembleRetrieverfrom langchain_community.retrievers import BM25Retriever
# 创建两种检索器vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})bm25_retriever = BM25Retriever.from_documents(chunks)bm25_retriever.k = 5
# 混合检索ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, vector_retriever], weights=[0.4, 0.6] # BM25占40%,向量占60%)5.2 技巧2:重排序(Rerank)
初检索后用更强的模型重新排序:
flowchart TD
N0["初检索"]
N1["Top 50 候选"]
N0 --> N1
N1["Top 50 候选"]
N2["Rerank模型"]
N1 --> N2
N2["Rerank模型"]
N3["Top 5 最终结果"]
N2 --> N3
from cohere import Client
# 使用Cohere Rerankcohere_client = Client("your-api-key")
def rerank(query, documents, top_n=5): results = cohere_client.rerank( query=query, documents=documents, model="rerank-multilingual-v2.0", top_n=top_n ) return [documents[r.index] for r in results]5.3 技巧3:Query改写
把模糊的问题变成精确的:
原问题:"这个怎么办"(太模糊)改写后:"产品X的错误代码Y如何解决"(更精确)
方法:1. 让LLM改写问题2. 提取关键词3. 补充上下文def rewrite_query(original_query, chat_history): prompt = f""" 对话历史:{chat_history} 当前问题:{original_query}
请将问题改写得更加具体和易于检索。 只输出改写后的问题,不要其他内容。 """ response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}] ) return response.choices[0].message.content5.4 技巧4:HyDE(假设文档嵌入)
用假设答案来检索:
flowchart TD
N0["用户问题"]
N1["LLM生成假设答案"]
N0 --> N1
N1["LLM生成假设答案"]
N2["用假设答案检索"]
N1 --> N2
def hyde_retrieval(query, retriever, llm): # 1. 生成假设答案 prompt = f"请为以下问题生成一个可能的答案:\n{query}" hypo_answer = llm.invoke(prompt)
# 2. 用假设答案检索 results = retriever.get_relevant_documents(hypo_answer) return results5.5 技巧5:要求引用来源
让答案可追溯:
在提示词中加入:
"请在回答中标注信息来源,格式:[来源1]如果参考信息中没有答案,请直接说'根据现有信息无法回答'"5.6 技巧6:处理”不知道”
减少幻觉:
在提示词中加入:
"如果参考信息中没有答案,请直接说'根据现有信息无法回答',不要编造。答案必须基于参考信息,不要使用你的预训练知识。"六、GraphRAG简介
6.1 什么是GraphRAG?
传统RAG处理文档是扁平的,GraphRAG引入知识图谱来表示实体之间的关系。
flowchart TD
N0["文档1"]
N1["向量1"]
N0 --> N1
N2["文档2"]
N3["向量2"]
N2 --> N3
6.2 适用场景
适合GraphRAG:- 需要理解实体关系的问题- 多跳推理("A的老板的部门是谁?")- 复杂知识结构(法律条文引用、产品依赖关系)
不需要GraphRAG:- 简单问答- 知识关系简单- 成本敏感(GraphRAG成本更高)6.3 实现框架
主流框架:- Microsoft GraphRAG:官方开源,功能完整- LlamaIndex KnowledgeGraphIndex:集成在LlamaIndex中- Neo4j + LangChain:用图数据库存储七、RAG vs 微调怎么选?
| 维度 | RAG | 微调 |
|---|---|---|
| 知识更新 | 实时更新 | 需重新训练 |
| 引用来源 | 可追溯 | 无法追溯 |
| 成本 | 低 | 高 |
| 风格定制 | 效果有限 | 效果好 |
| 部署复杂度 | 简单 | 复杂 |
| 响应延迟 | 需检索 | 直接生成 |
决策建议:
flowchart TD
N0["大多数场景"]
N1["先用RAG"]
N0 --> N1
可视化图解
7.1 RAG架构图
┌─────────────────────────────────────────────────────────────┐│ RAG完整架构 │├─────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────┐ ││ │ 离线:知识库构建 │ ││ │ │ ││ │ 原始文档 ──→ 分块 ──→ 向量化 ──→ 向量数据库 │ ││ │ │ │ │ │ ││ │ Chunker Embedding VectorDB │ ││ └─────────────────────────────────────────────────────┘ ││ ││ ┌─────────────────────────────────────────────────────┐ ││ │ 在线:问答流程 │ ││ │ │ ││ │ 用户问题 ──→ 向量化 ──→ 相似度检索 ──→ 构建Prompt │ ││ │ │ │ │ │ │ ││ │ │ Embedding VectorDB Context │ ││ │ │ │ │ ││ │ └──────────────────────────────────┘ │ ││ │ ↓ │ ││ │ LLM生成 │ ││ │ ↓ │ ││ │ 回答 │ ││ └─────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────┘常见问题 FAQ
Q1: RAG和微调有什么区别?
A:
- RAG:让模型”查阅”外部知识,知识可更新,成本低
- 微调:让模型”内化”知识,知识固定,成本高
- 关键区别:知识存储位置(外部 vs 模型内部)
Q2: 分块大小怎么选?
A:
- 小块(256-512 Token):检索精确,但上下文可能不完整
- 大块(1024-2048 Token):上下文完整,但可能引入噪音
- 推荐:512-1024 Token,配合10-20%重叠
Q3: 召回数量多少合适?
A:
- 简单问答:3-5个
- 复杂问题:5-10个
- 配合Rerank可以从更多候选(20-50个)中精选
Q4: GraphRAG什么时候用?
A:
- 需要理解实体关系(“A和B是什么关系?”)
- 多跳推理(“A的上级的部门?”)
- 一般RAG够用就不用GraphRAG(成本更高)
Q5: 如何评估RAG效果?
A:
- 准备测试问答集
- 计算准确率、召回率
- 检查答案相关性
- 检查来源正确性
小结
RAG的核心思想:给AI一本”参考书”,让它查阅后回答。
关键步骤:分块 → 向量化 → 检索 → 生成。
优化方向:混合检索、Rerank、Query改写。
RAG是解决AI知识局限最实用的方案,大多数知识类场景首选RAG。
下篇预告
RAG让AI能查知识,但AI能帮你”做事”吗?比如订票、查天气、发邮件?
参考资料
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
让AI拥有知识:RAG检索增强生成详解
https://blog.souloss.com/posts/machine-learning/llm/rag-knowledge-injection/ 部分信息可能已经过时
相关文章 智能推荐
1
RAG 检索增强生成原理
AI 深入解析 RAG 检索增强生成技术——架构原理、检索流程、Context 组装与工程实践。
2
RAG:让 AI 拥有你的知识库
AI 工程指南 RAG——让 AI 拥有你的知识库
3
AI 工程化实践
AI AI 工程化全景导览——从提示词工程到多模态系统,梳理大模型落地的核心工程能力与知识体系
4
Embedding 与向量搜索原理
AI 深入解析文本 Embedding 模型与向量检索——模型原理、索引结构、相似度计算与工程实践。
5
GraphRAG 论文解读:知识图谱增强的检索生成
AI 深度解读 Microsoft GraphRAG 论文——如何利用知识图谱改进 RAG,实现对整个文本语料库全局问题的回答。






