小张是某公司的 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):能查阅资料,答案更准确
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)
把文本变成向量,让语义相似的文本距离更近:
第三步:相似度检索
用户问题变成向量,在数据库里找距离最近的文档块:
用户问题:"公司年假多少天?" ↓ 向量化 ↓ 检索(计算余弦相似度)找到最相似的 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)
初检索后用更强的模型重新排序:
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(假设文档嵌入)
用假设答案来检索:
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 引入知识图谱来表示实体之间的关系。
6.2 适用场景
适合 GraphRAG:- 需要理解实体关系的问题- 多跳推理("A 的老板的部门是谁?")- 复杂知识结构(法律条文引用、产品依赖关系)
不需要 GraphRAG:- 简单问答- 知识关系简单- 成本敏感(GraphRAG 成本更高)6.3 实现框架
主流框架:- Microsoft GraphRAG:官方开源,功能完整- LlamaIndex KnowledgeGraphIndex:集成在 LlamaIndex 中- Neo4j + LangChain:用图数据库存储七、RAG vs 微调怎么选?
| 维度 | RAG | 微调 |
|---|---|---|
| 知识更新 | 实时更新 | 需重新训练 |
| 引用来源 | 可追溯 | 无法追溯 |
| 成本 | 低 | 高 |
| 风格定制 | 效果有限 | 效果好 |
| 部署复杂度 | 简单 | 复杂 |
| 响应延迟 | 需检索 | 直接生成 |
决策建议:
可视化图解
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 能帮你”做事”吗?比如订票、查天气、发邮件?
参考资料
- LangChain RAG Tutorial - LangChain RAG Tutorial 官方文档
- LlamaIndex Documentation - LlamaIndex Documentation 官方文档
- Microsoft GraphRAG - Microsoft GraphRAG GitHub 仓库
- Chroma Documentation - Chroma Documentation 官方文档
- Rerank Guide - Rerank Guide PDF 文档
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






