你是否遇到过这样的问题:ChatGPT回答关于你公司产品的问题时总是胡说八道?或者你希望AI能基于你自己的文档、知识库来回答问题,而不是依赖它训练时的通用知识?这就是RAG(检索增强生成)要解决的核心问题。
本文将带你从零开始,用Python构建一个完整的RAG应用。无需机器学习背景,跟着做就能拥有自己的智能知识库问答系统。
什么是RAG?为什么它如此重要?
RAG(Retrieval-Augmented Generation,检索增强生成)的核心思想非常直观:在让大语言模型回答问题之前,先从你的知识库中检索出最相关的文档片段,然后把这些片段作为上下文提供给模型参考。
🧠 一句话理解RAG
不开卷考试(纯LLM):学生凭记忆答题,可能记错或编造。
开卷考试(RAG):学生可以翻阅资料,根据参考资料作答,准确率大幅提升。
RAG解决了LLM的三大痛点:
- 知识过时:模型的训练数据有截止日期,RAG可以引入最新信息
- 幻觉问题:有据可查的参考文档大幅减少编造内容
- 领域知识:让通用模型变成你的专业领域专家
RAG的核心架构
一个完整的RAG系统包含两个阶段:
⚙️ 阶段一:索引构建(Indexing)— 离线处理
1. 文档加载:读取PDF、Word、网页等各类文档
2. 文本分块:将长文档切分为合适大小的片段(chunk)
3. 向量化:使用Embedding模型将文本转为向量
4. 存入向量数据库:建立高效的相似度检索索引
⚙️ 阶段二:问答生成(Retrieval + Generation)— 在线处理
1. 问题向量化:将用户问题转为向量
2. 相似度检索:在向量库中找到最相关的文档片段
3. 构建Prompt:将检索到的内容和问题组合成提示词
4. LLM生成回答:基于参考文档生成准确回答
实战:用Python构建RAG应用
下面我们用 LangChain + ChromaDB + OpenAI 构建一个可以对本地文档进行智能问答的RAG系统。
Step 1:环境准备
# 创建虚拟环境并安装依赖
python -m venv rag-env
source rag-env/bin/activate # Windows: rag-env\Scripts\activate
pip install langchain langchain-openai langchain-community
pip install chromadb unstructured python-dotenv
Step 2:加载文档并分块
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 加载指定目录下的所有文档
loader = DirectoryLoader(
"./docs",
glob="**/*.txt",
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"}
)
documents = loader.load()
print(f"加载了 {len(documents)} 个文档")
# 文本分块 - chunk_size控制每块大小,chunk_overlap控制重叠
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", ",", " "]
)
chunks = text_splitter.split_documents(documents)
print(f"分块后共 {len(chunks)} 个片段")
💡 分块技巧:chunk_size一般设为256-1024个token。太小会丢失上下文,太大会引入噪音。chunk_overlap确保重要信息不会因为分块位置不好而被截断。中文文档建议用句子边界作为分隔符。
Step 3:向量化并存入ChromaDB
import os
from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
load_dotenv() # 从 .env 文件加载 OPENAI_API_KEY
# 初始化Embedding模型
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small" # 性价比最高的嵌入模型
)
# 创建向量数据库
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db" # 持久化到本地
)
print("✅ 向量数据库构建完成!")
Step 4:构建RAG问答链
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 加载已有的向量数据库
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=OpenAIEmbeddings(model="text-embedding-3-small")
)
# 创建检索器,返回最相关的5个文档片段
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 5}
)
# 自定义Prompt模板
prompt_template = PromptTemplate(
template="""基于以下参考资料回答用户的问题。如果参考资料中没有相关信息,请诚实说明不知道。
不要编造信息。
参考资料:
{context}
用户问题:{question}
回答:""",
input_variables=["context", "question"]
)
# 构建RAG链
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": prompt_template},
return_source_documents=True
)
Step 5:交互式问答
print("🤖 RAG问答系统已启动!输入 'quit' 退出\n")
while True:
question = input("你的问题:")
if question.lower() == 'quit':
break
result = qa_chain.invoke({"query": question})
print(f"\n📝 回答:{result['result']}\n")
print("📚 参考来源:")
for i, doc in enumerate(result['source_documents'], 1):
print(f" {i}. {doc.metadata.get('source', '未知来源')}")
print(f" {doc.page_content[:100]}...")
print()
进阶优化:提升RAG效果的5个技巧
| 优化方向 |
具体方法 |
效果 |
| 混合检索 |
结合向量检索 + BM25关键词检索 |
召回率提升20-30% |
| 重排序 |
用Cross-Encoder对检索结果重排序 |
精准度显著提升 |
| 查询改写 |
用LLM将用户问题改写为更精准的检索查询 |
解决口语化查询问题 |
| 父文档检索 |
检索小块,返回其父级大块文档 |
兼顾精准度与上下文完整性 |
| 引用标注 |
让LLM标注每个论点对应的来源文档 |
可验证性,提升用户信任度 |
替代方案:不用LangChain的极简实现
如果你觉得LangChain太重,也可以用最基础的库实现一个极简RAG:
import chromadb
from openai import OpenAI
client = OpenAI()
db = chromadb.PersistentClient(path="./my_db")
collection = db.get_or_create_collection("docs")
# 添加文档(首次运行)
texts = ["文档内容1...", "文档内容2..."]
embeddings = client.embeddings.create(
input=texts, model="text-embedding-3-small"
).data
collection.add(
documents=texts,
embeddings=[e.embedding for e in embeddings],
ids=[f"doc_{i}" for i in range(len(texts))]
)
# 查询
query = "你的问题"
query_emb = client.embeddings.create(
input=query, model="text-embedding-3-small"
).data[0].embedding
results = collection.query(query_embeddings=[query_emb], n_results=3)
# 生成回答
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)
📊 性能对比:LangChain方案约150行代码,适合复杂场景;极简方案约30行代码,适合快速原型和学习理解。生产环境推荐使用LlamaIndex或Haystack等更成熟的框架。
RAG vs 微调:什么时候该用哪个?
很多人会问:为什么不用微调(Fine-tuning)让模型学习我的知识?两种方案各有适用场景:
选择指南
• 用RAG:知识频繁更新、需要引用来源、数据量大(GB-TB级别)
• 用微调:需要改变模型的风格/格式、领域术语理解、固定知识集
• 最佳实践:两者结合——先微调让模型理解领域语言,再用RAG引入实时知识
📝 总结
RAG是当前最实用的LLM应用架构之一。它不需要昂贵的模型训练,不改变原始模型参数,却能显著提升回答的准确性和时效性。从个人知识助手到企业级智能客服,RAG的应用场景几乎无处不在。希望这篇教程能帮助你迈出构建自己RAG系统的第一步!
🚀 动手试试吧!
关注 xlx.baby,获取更多 AI 实战教程和技术深度分析。
有问题?欢迎在评论区交流!
→ 访问 xlx.baby
发表回复