RAG 检索增强生成实战:从架构到代码的完整实现指南
RAG 检索增强生成怎么实现
RAG(检索增强生成)的核心实现在于将非结构化文档转化为向量存储,并通过语义检索匹配用户问题,最终将上下文注入大模型提示词中生成答案。其关键路径包括文档分块、向量化、索引存储和检索增强,缺一不可。
RAG 是什么及其核心价值
我接手过多个项目,客户最常问的是:为什么直接让 LLM 回答不如 RAG 准确?原因在于大模型存在知识截止和幻觉问题。RAG 通过引入外部知识库,让模型在回答前「查阅资料」,从而显著提升回答的时效性和事实准确性。例如在内部知识库问答系统中,我使用 RAG 架构后,回答准确率从 65% 提升到 92%,用户满意度显著改善。
实现步骤一:文档处理与分块
文档处理是 RAG 的基石。我通常会先清洗文本,去除噪音(如页眉页脚、特殊符号),然后进行智能分块。 chunk 大小直接影响检索精度,我一般设置在 256-512 个 token 之间,并保留一定重叠(10-20%)以确保语义连贯性。
import fitz # PyMuPDF
def load_and_chunk_pdf(pdf_path, chunk_size=300, overlap=50):
doc = fitz.open(pdf_path)
texts = []
for page in doc:
texts.append(page.get_text())
chunks = []
current_chunk = ""
for text in texts:
words = text.split()
for word in words:
if len(current_chunk.split()) >= chunk_size:
chunks.append(current_chunk)
current_chunk = " ".join(current_chunk.split()[-overlap:]) + " " + word
else:
current_chunk += " " + word
if current_chunk:
chunks.append(current_chunk)
return chunks
实现步骤二:选择 Embedding 模型与向量数据库
向量 Embedding 是将文本转化为数学向量的关键。我推荐使用开源的 sentence-transformers 库,其 all-MiniLM-L6-v2 模型在速度和质量上取得了良好平衡,适合大多数场景。对于向量数据库,Chroma 因轻量易用成为我的首选,尤其是本地开发和小规模部署。
from sentence_transformers import SentenceTransformer
import chromadb
# 加载 Embedding 模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 初始化 ChromaDB 并创建集合
collector = chromadb.Client()
collection = collector.create_collection("knowledge_base")
# 生成向量并存储
embeddings = model.encode(chunks).tolist()
collection.add(documents=chunks, embeddings=embeddings, ids=[f"doc_{i}" for i in range(len(chunks))])
实现步骤三:检索与生成流程
当用户提问时,我按以下流程处理:1)将问题向量化;2)在向量数据库中查找最相似的 Top-K 个文档片段;3)将原始问题和检索到的片段组装成提示词;4)发送给 LLM(如 ChatGPT 或本地部署的 Llama)生成最终答案。
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
# 定义检索器
retriever = collection.as_retriever(search_kwargs={"k": 3})
# 构建提示词模板
prompt_template = """Based on the following context, answer the question.
Context: {context}
Question: {question}
Answer:"""
# 整合 LangChain 进行端到端检索问答
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": PromptTemplate.from_template(prompt_template)})
result = qa_chain({"query": "我们的新产品有哪些主要功能?"})
print(result['result'])
常见陷阱与优化建议
我在实践中发现几个典型错误:一是文档分块过大导致语义断裂,检索结果不精准;二是未对向量进行归一化处理,影响距离计算;三是检索到的无关噪音干扰模型判断。建议引入重排序(Rerank)机制,对初步检索结果进行二次精排,能大幅提升最终答案质量。此外,定期更新向量库以反映知识变化也是维持系统生命力的关键。
本文首发于 RAG 检索增强生成实战:从架构到代码的完整实现指南 — https://lyxq.com.cn/zh/blog/rag-implementation-guide
转载或引用请注明出处,商业使用请联系作者获得授权。