RAG检索增强生成架构概述与核心挑战
RAG(Retrieval-Augmented Generation)通过将外部知识库与大语言模型结合,有效缓解了LLM知识截止日期限制和幻觉问题。RAG系统的核心流程包含文档解析、文本分块、向量编码、相似度检索和上下文增强生成五个阶段,其中向量数据库选型和Chunk分割策略直接决定了检索精度和最终生成质量。
在企业级RAG部署中,检索召回率不足和上下文冗余是最常见的两类问题。召回率低导致关键信息遗漏,冗余上下文则浪费Token窗口并引入噪声。这两个问题的根源往往不在模型本身,而在于向量数据库的索引结构和分块策略的设计不当。
向量数据库选型:Milvus、Weaviate与Qdrant对比
主流开源向量数据库在索引算法、扩展性和查询延迟上各有侧重。以下从三个维度对比Milvus、Weaviate和Qdrant:
索引结构与查询性能
Milvus支持IVF_FLAT、IVF_PQ、HNSW等多种索引类型,其中HNSW在召回率和延迟上表现最优,适合百万级以上数据集。Weaviate默认使用HNSW,开箱即用但大规模数据下内存消耗较高。Qdrant同样基于HNSW,但通过Rust实现和内存优化,在单节点场景下查询延迟通常低于前两者。
水平扩展能力
Milvus采用存算分离架构,支持独立扩展Query节点和Data节点,适合千万级向量规模的多租户场景。Weaviate从1.20版本开始支持集群模式,但跨节点数据迁移仍不够成熟。Qdrant支持分片集群,但社区生态和运维工具链不如Milvus完善。
过滤查询与元数据管理
企业RAG场景中,向量检索通常需要叠加标量过滤(如按部门、日期范围筛选)。Weaviate的GraphQL过滤语法最直观,Milvus的布尔表达式过滤更灵活,Qdrant的Payload过滤在性能上占优。
Milvus HNSW索引生产部署配置
以Milvus为例,部署一个面向百万级文档的RAG检索服务:
from pymilvus import CollectionSchema, FieldSchema, DataType, Collection
doc_id = FieldSchema(name="doc_id", dtype=DataType.VARCHAR, max_length=64, is_primary=True)
embedding = FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536)
source = FieldSchema(name="source", dtype=DataType.VARCHAR, max_length=128)
created_at = FieldSchema(name="created_at", dtype=DataType.INT64)
schema = CollectionSchema(
fields=[doc_id, embedding, source, created_at],
description="RAG knowledge base"
)
collection = Collection("rag_knowledge", schema)
index_params = {
"metric_type": "COSINE",
"index_type": "HNSW",
"params": {"M": 16, "efConstruction": 256}
}
collection.create_index(field_name="embedding", index_params=index_params)
search_params = {"metric_type": "COSINE", "params": {"ef": 128}}
results = collection.search(
data=[query_vector],
anns_field="embedding",
param=search_params,
limit=10,
expr='source == "tech_docs"'
)
HNSW参数中,M值控制图的连通度(推荐16-64),efConstruction影响建图质量(生产环境建议256+),查询时ef参数越大召回率越高但延迟增加。实测在500万向量数据集上,ef=128时top-10召回率达96.3%,P99延迟12ms。
Chunk分割策略:固定窗口、语义分块与递归拆分
Chunk分割的质量直接决定检索粒度。三种主流策略各有适用场景:
固定窗口分割最简单,按字符数切分并设置重叠区间。优点是实现成本低,缺点是可能切断语义完整的段落。重叠区间通常设为chunk_size的10%-20%。
def fixed_window_split(text, chunk_size=500, overlap=100):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunks.append(text[start:end])
start += chunk_size - overlap
return chunks
语义分块利用Embedding模型计算相邻句子的相似度,在相似度骤降处切分:
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings
splitter = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=75
)
chunks = splitter.split_text(document_text)
递归字符分割是目前生产环境使用最广泛的方案,按优先级依次尝试换行符、段落、句号等自然分隔符切分:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=150,
separators=["\n\n", "\n", "\u3002", "\uff01", "\uff1f", ".", " ", ""]
)
chunks = splitter.split_text(document_text)
元数据增强检索与混合排序策略
纯向量检索在面对术语歧义和跨领域查询时容易返回语义相近但不相关的结果。两种增强方案可显著提升精度:
元数据预过滤:在向量检索前先通过标量字段缩小搜索范围。例如,用户查询”报销流程”时限定source为”财务制度”,可将噪声结果减少60%以上。
混合排序(Hybrid Search):将向量相似度与BM25关键词检索得分融合,兼顾语义理解和精确匹配:
def hybrid_search(query, vector_results, bm25_results, alpha=0.7):
vector_scores = {r["doc_id"]: r["score"] for r in vector_results}
bm25_scores = {r["doc_id"]: r["score"] for r in bm25_results}
all_ids = set(vector_scores.keys()) | set(bm25_scores.keys())
combined = []
for doc_id in all_ids:
vs = vector_scores.get(doc_id, 0)
bs = bm25_scores.get(doc_id, 0)
combined_score = alpha * vs + (1 - alpha) * bs
combined.append({"doc_id": doc_id, "score": combined_score})
return sorted(combined, key=lambda x: x["score"], reverse=True)
实测在内部知识库场景中,alpha=0.7时混合排序的MRR比纯向量检索提升18.6%,比纯BM25提升34.2%。
RAG评估指标与迭代优化方向
RAG系统的效果评估不应只看检索召回率,还需结合生成端指标。推荐以下三个核心指标:
1. Context Precision:检索返回的上下文中与问题相关内容的比例,衡量检索精准度。
2. Answer Faithfulness:生成答案与检索上下文的事实一致性,衡量幻觉率。
3. Answer Relevancy:生成答案与用户问题的相关性,衡量答非所问的比例。
Ragas框架提供了自动化评估流水线,可批量跑评测集并输出上述指标。优化方向上,当Context Precision低时应调整分块策略或增加元数据过滤;Faithfulness低时检查Chunk是否截断了关键上下文;Relevancy低时考虑在Prompt中增加问题分类指令,引导模型聚焦回答。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rag-jian-suo-zeng-qiang-sheng-cheng-xi-tong-xiang-liang-shu/