RAG检索增强生成系统搭建:从文档切分到Prompt工程全流程实战

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/

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

相关推荐