RAG系统架构概述
检索增强生成(RAG)通过将外部知识库与大语言模型结合,缓解模型幻觉问题,提升回答的事实准确性。其核心流程分为三个阶段:文档预处理与向量化、语义检索、生成回答。企业部署RAG系统时,性能瓶颈集中在检索质量和推理延迟两个维度。
文档分块策略对检索质量的影响
分块(Chunking)是RAG系统的第一步,直接决定检索召回率。固定长度分块实现简单,但容易截断语义完整的段落。基于句号或段落标记的分块能保留语义完整性,适用于结构化文档。
对于技术文档,推荐使用重叠分块(Overlapping Chunks),相邻块之间保留10%-20%的重叠内容,避免关键信息被切断。以下是一个基于LangChain的分块实现:
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=64,
separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""],
length_function=len
)
chunks = text_splitter.split_text(document_text)
print(f"分块数量: {len(chunks)}")
chunk_size设为512 tokens是经验值,在大多数embedding模型上表现稳定。中文文档需要特别注意分隔符设置,中文标点符号应纳入separators列表。
Embedding模型选型与向量化
向量化阶段选择Embedding模型,直接影响语义检索的精度。开源模型中,BGE-M3和m3e-base在中文场景表现优于多数商用API。BGE-M3支持稠密检索、稀疏检索和多向量检索三种模式,覆盖多语言场景。
向量化时需注意维度对齐。以BGE-M3为例,输出维度为1024,存入向量数据库时需确保collection的维度配置一致:
from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
embeddings = model.encode(
chunks,
batch_size=12,
max_length=8192,
return_dense=True,
return_sparse=False,
return_colbert_vecs=False
)
dense_embeddings = embeddings['dense_vecs'] # shape: (n, 1024)
向量数据库选型与索引优化
Milvus和Qdrant是目前主流的开源向量数据库。Milvus 2.4+版本支持GPU加速的IVF_FLAT和HNSW索引。HNSW(分层可导航小世界图)在召回率和查询速度之间取得了较好的平衡,适合大多数RAG场景。
HNSW索引的关键参数:
# Milvus HNSW索引参数
index_params = {
"index_type": "HNSW",
"metric_type": "COSINE",
"params": {
"M": 16, # 每层最大连接数,影响内存占用和召回率
"efConstruction": 200 # 建图时的搜索宽度,影响索引质量
}
}
M值设为16在百万级向量量级下内存可控,efConstruction=200能保证建图质量。查询时通过ef参数控制搜索宽度,ef=64在多数场景下召回率可达95%以上。
混合检索提升召回率
纯向量检索存在语义漂移问题——语义相近但事实不符的文档也会被检索到。引入BM25关键词检索与向量检索融合,可显著提升召回精度。这套方案称为混合检索(Hybrid Search)。
实现思路是同时执行BM25和向量检索,再通过倒数排名融合(RRF)合并结果:
import numpy as np
fromRank_bm25 import BM25Okapi
def hybrid_search(query, vector_db, bm25_corpus, top_k=10, alpha=0.5):
# 向量检索
vec_results = vector_db.search(query_embedding, top_k=top_k * 2)
# BM25检索
tokenized_query = query.split()
bm25 = BM25Okapi(bm25_corpus)
bm25_scores = bm25.get_scores(tokenized_query)
bm25_top = np.argsort(bm25_scores)[-top_k * 2:][::-1]
# RRF融合
rrf_scores = {}
for rank, doc_id in enumerate(vec_results):
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + 1 / (50 + rank)
for rank, doc_id in enumerate(bm25_top):
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + alpha / (50 + rank)
return sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)[:top_k]
重排序模型进一步过滤噪声
检索阶段返回的候选集存在一定比例的噪声文档。重排序模型(Reranker)对query-doc对进行精细化的相关性打分,能将Top-k的准确率提升10%-20%。
BGE-Reranker-v2-m3是目前中文场景表现突出的开源重排序模型,推理速度相对可控:
from FlagEmbedding import FlagReranker
reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)
pairs = [[query, doc] for doc in candidate_docs]
scores = reranker.compute_score(pairs, normalize=True)
# 按分数排序,取Top-5
ranked = sorted(zip(candidate_docs, scores), key=lambda x: x[1], reverse=True)[:5]
上下文窗口管理与Prompt构建
检索到相关文档后,需要构建Prompt送入LLM生成回答。上下文窗口管理的关键在于Token预算分配。假设LLM支持8K tokens,推理回答预留1500 tokens,系统提示和对话历史预留1000 tokens,则检索文档可用预算约5500 tokens。
def build_prompt(query, retrieved_docs, max_context_tokens=5500):
context = ""
current_tokens = 0
for i, doc in enumerate(retrieved_docs):
doc_tokens = count_tokens(doc) # 使用tokenizer精确计算
if current_tokens + doc_tokens > max_context_tokens:
break
context += f"\n[文档{i+1}]\n{doc}\n"
current_tokens += doc_tokens
prompt = f"""你是一个技术问答助手。根据以下检索到的文档回答问题。如果文档中没有相关信息,请明确说明无法回答。
参考文档:
{context}
问题:{query}
回答:"""
return prompt
推理延迟优化方案
RAG系统的端到端延迟由检索延迟和推理延迟两部分组成。检索阶段在HNSW索引下,百万级向量查询时间可控制在50ms以内。瓶颈在于LLM推理。vLLM和TensorRT-LLM是两种主流的推理加速方案。
vLLM通过PagedAttention机制优化KV Cache管理,吞吐量相比HuggingFace Transformers提升10-20倍。部署时推荐使用AWQ或GPTQ量化模型,在精度损失可控的前提下将显存占用降低至原来的1/4:
# vLLM部署量化模型
from vllm import LLM, SamplingParams
llm = LLM(
model="Qwen/Qwen2.5-14B-Instruct-AWQ",
quantization="awq",
tensor_parallel_size=2, # 2卡并行
gpu_memory_utilization=0.9,
max_model_len=8192
)
sampling_params = SamplingParams(temperature=0.7, max_tokens=1500)
outputs = llm.generate(prompts, sampling_params)
评估指标与持续优化
RAG系统上线后需要建立量化评估体系。核心指标包括检索阶段的Recall@k(召回率)和MRR(平均倒数排名),生成阶段的答案准确率和忠实度。RAGAS框架提供了自动化评估方案:
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision
result = evaluate(
dataset={
"question": questions,
"answer": answers,
"contexts": retrieved_contexts,
"ground_truth": ground_truths
},
metrics=[faithfulness, answer_relevancy, context_precision]
)
print(result)
faithfulness衡量回答是否忠实于检索文档,answer_relevancy衡量回答与问题的相关性,context_precision衡量检索文档的精确率。三项指标均高于0.8时,系统达到可用标准。定期用bad case驱动分块策略和检索参数调整,能实现持续优化。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rag-jian-suo-zeng-qiang-sheng-cheng-xi-tong-bu-shu-shi-jian/