RAG(检索增强生成)通过将外部知识库检索与大语言模型生成能力结合,有效缓解大模型幻觉问题并扩展知识边界。在企业级AI工具链落地过程中,向量数据库选型、Embedding模型部署、检索策略调优三个环节直接决定RAG系统的回答质量与响应延迟。本文围绕这三个核心环节,给出可落地的配置方案与代码示例。
RAG架构核心组件与数据流转
RAG系统的数据流分为离线索引和在线检索两个阶段。离线阶段将文档分块后通过Embedding模型转为向量,写入向量数据库建立索引;在线阶段将用户查询同样向量化,在向量数据库中检索Top-K相似文档,拼接为上下文后送入大模型生成回答。
# RAG核心流程伪代码
from langchain.schema import Document
from langchain.vectorstores import Milvus
from langchain.embeddings import HuggingFaceEmbeddings
# 离线索引:文档分块 + 向量化 + 入库
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-large-zh-v1.5",
model_kwargs={"device": "cuda"},
encode_kwargs={"normalize_embeddings": True}
)
chunks = text_splitter.split_documents(raw_docs)
vector_db = Milvus.from_documents(
chunks,
embeddings,
connection_args={"host": "127.0.0.1", "port": "19530"},
collection_name="knowledge_base"
)
# 在线检索:查询向量化 + 相似检索 + 上下文拼接
query = "Kubernetes Pod调度策略有哪些?"
results = vector_db.similarity_search(query, k=5)
context = "\n\n".join([doc.page_content for doc in results])
prompt = f"基于以下参考资料回答问题:\n{context}\n\n问题:{query}"
response = llm.generate(prompt)
向量数据库选型对比:Milvus vs Qdrant vs Weaviate
向量数据库是RAG架构的存储底座,选型需要综合考量数据规模、查询延迟、部署成本和生态兼容性。Milvus适合亿级向量的大规模场景,支持分布式架构和多种索引类型(IVF_FLAT、IVF_PQ、HNSW);Qdrant在中小规模场景下部署轻量,Rust实现带来更低延迟;Weaviate内置模块化向量化能力,对GraphQL查询支持较好。
# Milvus 2.4 HNSW索引配置示例
from pymilvus import CollectionSchema, FieldSchema, DataType, Collection, connections
connections.connect(host="127.0.0.1", port="19530")
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1024),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=65535),
FieldSchema(name="source", dtype=DataType.VARCHAR, max_length=256)
]
schema = CollectionSchema(fields, description="RAG knowledge base")
collection = Collection("rag_kb", schema)
# HNSW索引参数:M=16, efConstruction=256
index_params = {
"index_type": "HNSW",
"metric_type": "IP",
"params": {"M": 16, "efConstruction": 256}
}
collection.create_index("embedding", index_params)
# 检索参数:ef=64 控制召回率与延迟
collection.load()
search_params = {"params": {"ef": 64}}
results = collection.search(
data=[query_vector],
anns_field="embedding",
param=search_params,
limit=10,
output_fields=["text", "source"]
)
HNSW索引在Milvus中的M参数控制图的连接度,值越大召回率越高但内存消耗也更大。efConstruction影响构建质量,ef影响查询精度。对于10万级文档的知识库,M=16、efConstruction=256、ef=64是较为均衡的配置,单次检索延迟在50ms以内。
Embedding模型部署:bge-large-zh与文本分块策略
Embedding模型的选择直接影响检索准确率。中文场景下BAAI/bge-large-zh-v1.5在MTEB榜单表现优异,输出1024维向量,支持归一化内积相似度计算。部署方式可以选择HuggingFace本地加载或使用Text Embeddings Inference(TEI)服务化部署。
# TEI服务化部署bge-large-zh
# Docker启动
# docker run -d --gpus all -p 8080:80 \
# -v /data/models:/data \
# ghcr.io/huggingface/text-embeddings-inference:1.5 \
# --model-id BAAI/bge-large-zh-v1.5 --port 8080
import requests
def get_embeddings(texts):
response = requests.post(
"http://127.0.0.1:8080/embed",
json={"inputs": texts},
timeout=30
)
return response.json()
# 批量向量化:建议batch_size=32
batch_size = 32
all_embeddings = []
for i in range(0, len(chunks), batch_size):
batch = [c.page_content for c in chunks[i:i+batch_size]]
emb = get_embeddings(batch)
all_embeddings.extend(emb)
文本分块策略同样关键。固定长度分块容易截断语义,推荐使用RecursiveCharacterTextSplitter,按段落-句子-字符层级递归切分。chunk_size建议500-1000字符,overlap设为chunk_size的10%-20%,保留上下文连贯性。
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=150,
separators=["\n\n\n", "\n\n", "\n", "。", "!", "?", ";", ",", " ", ""],
length_function=len
)
chunks = splitter.split_documents(raw_docs)
检索策略优化:混合检索与重排序
纯向量检索在处理精确术语匹配时存在短板。混合检索结合BM25稀疏检索与向量稠密检索,通过RRF(Reciprocal Rank Fusion)算法融合排序结果,能显著提升召回质量。
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 10
vector_retriever = vector_db.as_retriever(search_kwargs={"k": 10})
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.3, 0.7] # 向量检索权重更高
)
# Cross-Encoder重排序:对召回结果二次精排
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("BAAI/bge-reranker-large", max_length=512)
candidates = ensemble_retriever.get_relevant_documents(query)
pairs = [[query, doc.page_content] for doc in candidates]
scores = reranker.predict(pairs)
ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
top_docs = [doc for doc, _ in ranked[:5]]
Cross-Encoder重排序在召回阶段后增加一次精确排序,bge-reranker-large模型对query-document相关性判断的准确率显著优于单纯向量相似度。实际测试中,加入重排序后RAG系统回答准确率可提升15%-25%,代价是增加约200ms的推理延迟。
RAG系统效果评估与持续优化
RAG系统的效果评估应从检索质量和生成质量两个维度衡量。RAGAS框架提供了faithfulness(忠实度)、answer_relevancy(回答相关性)、context_precision(上下文精确率)、context_recall(上下文召回率)四项核心指标。定期用测试集评估这些指标,针对性调整chunk大小、检索Top-K、重排序模型等参数。
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from datasets import Dataset
eval_data = Dataset.from_dict({
"question": test_questions,
"answer": generated_answers,
"contexts": retrieved_contexts,
"ground_truth": ground_truths
})
result = evaluate(
eval_data,
metrics=[faithfulness, answer_relevancy, context_precision, context_recall]
)
print(result)
# {'faithfulness': 0.87, 'answer_relevancy': 0.92,
# 'context_precision': 0.85, 'context_recall': 0.79}
context_recall偏低通常意味着检索阶段遗漏了关键文档,可通过增大Top-K或优化分块策略改善;faithfulness偏低则说明大模型生成时引入了上下文之外的信息,可通过调整prompt约束或降低temperature参数控制。整个RAG系统优化是一个迭代过程,建议建立评估流水线持续监控各项指标变化。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rag-jian-suo-zeng-qiang-sheng-cheng-jia-gou-shi-zhan-xiang/