RAG(Retrieval-Augmented Generation)检索增强生成是当前AIGC应用落地中最主流的架构模式。通过将外部知识库与大模型推理能力结合,RAG有效解决了模型幻觉、知识时效性和领域适配三大问题。本文以一个企业知识问答系统的搭建过程为例,完整演示从文档切分、向量化检索到Prompt工程的RAG全链路实现。
RAG系统架构与核心组件选型
RAG系统的核心流程为:用户提问 -> 文本向量化 -> 向量数据库检索 -> 检索结果注入Prompt -> 大模型生成回答。选型阶段需要确定三个关键组件:Embedding模型、向量数据库和生成模型。
Embedding模型方面,bge-large-zh-v1.5在中文语义检索任务中表现稳定,维度1024,适合大多数中文场景。对响应延迟要求极高的场景可选用bge-small-zh-v1.5(维度512),吞吐量提升约40%。向量数据库方面,Milvus适合千万级以上向量的大规模部署,Chroma适合中小规模快速验证,Qdrant在过滤检索性能上较优。
生成模型选择取决于任务复杂度。简单问答可使用蒸馏小模型,复杂推理和多轮对话场景建议使用DeepSeek-V4-Flash等具备强Agent能力的模型。
文档切分策略与元数据管理
文档切分质量直接影响检索精度。常见的切分策略有固定长度切分、按段落切分和递归切分三种。固定长度切分实现简单但容易截断语义,段落切分依赖文档结构质量,递归切分在实际工程中效果最好。
使用LangChain的RecursiveCharacterTextSplitter进行切分:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""]
)
# 加载文档并切分
with open("knowledge_base.md", "r", encoding="utf-8") as f:
text = f.read()
chunks = splitter.split_text(text)
print(f"切分结果: {len(chunks)} 个文本块")
chunk_size设置为500字符是中文文档的常用值,过大会导致检索精度下降,过小会丢失上下文。chunk_overlap设置为chunk_size的10%左右,保证相邻文本块之间的语义连续性。separators列表按优先级降序排列,递归切分会优先尝试用双换行分割,分割后仍超长则用单换行,依此类推。
每个文本块应附加元数据,包括来源文件名、页码、章节标题等。元数据在检索时可用于预过滤,提升检索精度:
from langchain.schema import Document
docs = []
for i, chunk in enumerate(chunks):
docs.append(Document(
page_content=chunk,
metadata={
"source": "knowledge_base.md",
"chunk_id": i,
"category": "技术文档"
}
))
向量化与向量数据库写入
使用HuggingFace的sentence-transformers加载Embedding模型,将文本块批量向量化后写入向量数据库:
from sentence_transformers import SentenceTransformer
import chromadb
model = SentenceTransformer("BAAI/bge-large-zh-v1.5")
client = chromadb.PersistentClient(path="./vector_db")
collection = client.get_or_create_collection(
name="knowledge_base",
metadata={"hnsw:space": "cosine"}
)
batch_size = 100
for i in range(0, len(docs), batch_size):
batch = docs[i:i+batch_size]
texts = [d.page_content for d in batch]
embeddings = model.encode(texts, normalize_embeddings=True)
collection.add(
ids=[f"chunk_{i+j}" for j in range(len(batch))],
embeddings=embeddings.tolist(),
documents=texts,
metadatas=[d.metadata for d in batch]
)
print(f"写入完成,共 {collection.count()} 条向量")
normalize_embeddings=True将向量归一化为单位长度,配合cosine距离度量效果最佳。批量写入可控制内存占用,batch_size根据机器配置调整。
检索排序与重排序优化
初始向量检索通常返回Top-K结果(K一般取10-20),但向量相似度高不等于语义相关度高。引入Cross-Encoder重排序可显著提升检索精度。Cross-Encoder将query和每个候选doc拼接后联合编码,计算更精确的相关性分数,但计算开销大于Bi-Encoder。
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("BAAI/bge-reranker-large")
def retrieve_and_rerank(query, top_k=20, final_k=5):
query_embedding = model.encode([query], normalize_embeddings=True)
results = collection.query(
query_embeddings=query_embedding.tolist(),
n_results=top_k
)
candidates = results["documents"][0]
pairs = [[query, doc] for doc in candidates]
scores = reranker.predict(pairs)
ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
return ranked[:final_k]
results = retrieve_and_rerank("如何配置Nginx反向代理")
for doc, score in results:
print(f"[{score:.4f}] {doc[:80]}...")
两阶段检索(向量检索 + 重排序)在召回率和精度之间取得平衡。向量检索负责快速召回候选集,Cross-Encoder负责精排。实测中,加入重排序后答案准确率可提升15%-25%。
Prompt工程与回答生成
检索结果注入Prompt的方式直接影响生成质量。一个结构良好的RAG Prompt应包含角色设定、检索上下文、用户问题和输出约束四部分:
def build_rag_prompt(query, context_docs):
context = "\n\n".join([f"[文档{i+1}] {doc}" for i, doc in enumerate(context_docs)])
prompt = f'''你是一个技术问答助手。根据以下检索到的文档内容回答问题。
如果文档中没有相关信息,明确说明"根据现有资料无法回答该问题"。
回答时引用具体文档编号。不要编造文档中不存在的内容。
检索上下文:
{context}
用户问题:{query}
回答:'''
return prompt
from openai import OpenAI
client = OpenAI(api_key="your-key", base_url="https://api.deepseek.com")
top_docs = [doc for doc, score in retrieve_and_rerank("如何配置Nginx反向代理")]
prompt = build_rag_prompt("如何配置Nginx反向代理", top_docs)
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=1024
)
print(response.choices[0].message.content)
temperature设为0.3降低随机性,确保回答基于检索内容而非模型自身参数知识。max_tokens设为1024控制回答长度。提示词中的”不要编造”和”引用文档编号”约束有效减少幻觉输出。
评估指标与效果优化
RAG系统上线前需建立量化评估体系。核心指标包括:检索召回率(Recall@K)、答案相关性(人工评分1-5)、事实准确率(答案中事实陈述的正确比例)。构建测试集时,准备50-100个典型问题及标准答案,计算各项指标。
常见优化方向:当召回率低时增大top_k或调整chunk_size;当精度低时引入重排序或增加元数据过滤;当事实准确率低时检查Prompt约束是否到位,或考虑降低temperature。对于多轮对话场景,可将历史对话摘要作为补充上下文注入检索流程,避免每次检索丢失对话上下文。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rag-jian-suo-zeng-qiang-sheng-cheng-xi-tong-da-jian-cong/