RAG检索增强生成架构搭建:向量数据库选型与Prompt工程实践

RAG(检索增强生成)通过将外部知识库接入大语言模型,有效缓解模型幻觉和知识时效性不足的问题。生产环境中搭建一套完整的RAG系统,涉及向量数据库选型、文档分块、Embedding模型部署、检索召回优化及Prompt模板设计等多个环节。本文以实际部署流程为主线,逐步拆解各组件配置要点。

RAG系统架构设计与核心组件选型

典型RAG系统的数据流分为离线索引和在线检索两个阶段。离线阶段完成文档加载、分块、向量化及入库;在线阶段接收用户查询,经过向量化后在向量库中检索Top-K相关片段,拼接到Prompt中提交给LLM生成回答。

向量数据库选型需要考量召回精度、写入吞吐、运维成本三个维度。Milvus适合大规模生产场景,支持IVF、HNSW等多种索引类型;Qdrant在中等数据量下性能优异,Rust实现保证了低延迟;Weaviate内置模块化向量化能力,对新手友好。以下是基于Milvus的Docker部署配置:

# docker-compose.yml
services:
  milvus-standalone:
    image: milvusdb/milvus:v2.4.0
    command: ["milvus", "run", "standalone"]
    environment:
      ETCD_ENDPOINTS: etcd:2379
      MINIO_ADDRESS: minio:9000
    ports:
      - "19530:19530"
    depends_on:
      - etcd
      - minio
  etcd:
    image: quay.io/coreos/etcd:v3.5.5
    environment:
      ETCD_AUTO_COMPACTION_MODE: revision
      ETCD_AUTO_COMPACTION_RETENTION: "1000"
    command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379
  minio:
    image: minio/minio:RELEASE.2023-03-24T21-41-23Z
    command: minio server /minio_data

文档分块策略对检索质量的影响

分块是RAG系统中影响召回率的关键环节。固定长度分块(如每500字一段)实现简单但容易截断语义完整性;按段落和标题分块能保留文档结构信息;重叠分块在相邻块间保留一定重叠区域,减少边界信息丢失。

实际项目中推荐使用递归字符文本分割器,结合自定义分隔符层级逐级拆分:

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=64,
    separators=["\n## ", "\n### ", "\n\n", "\n", "。", ",", " "]
)
chunks = splitter.split_text(document_text)

chunk_overlap设为chunk_size的10%到15%之间,能在边界信息保留和存储冗余之间取得平衡。对于技术文档类内容,按Markdown标题层级分割的效果显著优于纯长度分割,因为同一个小节内的内容通常语义聚焦。

Embedding模型本地部署与批量向量化

中文场景下bge-large-zh-v1.5是效果较好的开源Embedding模型。通过SentenceTransformer库加载,批量编码文档片段:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('BAAI/bge-large-zh-v1.5')

def embed_texts(texts, batch_size=32):
    embeddings = model.encode(
        texts,
        batch_size=batch_size,
        normalize_embeddings=True,
        show_progress_bar=True
    )
    return embeddings

vectors = embed_texts(chunks)
for i, vec in enumerate(vectors):
    collection.insert({
        "id": i,
        "vector": vec.tolist(),
        "text": chunks[i],
        "source": "doc_001"
    })

normalize_embeddings=True对后续余弦相似度检索至关重要,归一化后点积等价于余弦相似度,计算更快。批量编码时batch_size控制在32到64之间,GPU显存不足时适当调小。

向量检索召回率优化与混合检索

纯向量检索在某些场景下召回率不足,尤其是查询中包含精确关键词时。混合检索(Hybrid Search)结合BM25关键词检索和向量语义检索,能显著提升召回效果。Milvus 2.4版本支持稀疏向量字段,可在同一Collection中同时存储稠密和稀疏向量:

# 混合检索参数配置
search_params = {
    "data": [query_dense_vector],
    "anns_field": "dense_vector",
    "param": {"metric_type": "COSINE", "params": {"nprobe": 16}},
    "limit": 20
}
results = collection.search(**search_params)

# 使用重排序模型精排Top-20
from FlagEmbedding import FlagReranker
reranker = FlagReranker('BAAI/bge-reranker-v2-m3')
candidates = [r.entity.get('text') for r in results[0]]
scores = reranker.compute_score([[query, doc] for doc in candidates])
top5 = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)[:5]

nprobe参数控制搜索的簇数量,增大可提升召回率但增加查询延迟。生产环境中nprobe取值16到32是常见配置。重排序模型将召回的Top-20精排为Top-5,对最终回答质量的提升非常明显,代价是增加约50ms的推理延迟。

Prompt工程模板设计与防幻觉策略

检索到的上下文如何拼接到Prompt中,直接影响LLM输出的准确性。以下是一个经过生产验证的Prompt模板:

prompt_template = """你是一个技术问答助手。请严格根据以下检索到的上下文回答问题。

上下文信息:
{context}

问题:{question}

要求:
1. 答案必须基于上下文内容,不得编造信息
2. 如果上下文中没有相关信息,直接回答"根据现有资料无法回答该问题"
3. 引用上下文时标注来源文档编号
4. 回答保持简洁,避免无关扩展
"""

第二条约束是防幻觉的关键——明确指示模型在信息不足时拒绝回答,而非凭预训练知识编造。将context按相关度从高到低排列,LLM对前置内容的关注度更高。Token预算控制在4096以内,超出时优先截断相关度低的片段。temperature参数设为0.1到0.3之间,降低输出随机性。

多轮对话场景中,将历史检索结果缓存到会话上下文,避免重复检索带来的延迟。对于答案中需要引用原文的场景,在分块时记录每个chunk在原文中的位置偏移,生成回答时可回溯原始段落。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rag-jian-suo-zeng-qiang-sheng-cheng-jia-gou-da-jian-xiang/

(0)
小编小编
上一篇 15小时前
下一篇 15小时前

相关推荐