Skip to Content

11b - RAG 流水线详解

本文是《AI Agent 实战手册》第 11 章第 2 节。 上一节:11a-RAG概念与架构 | 下一节:11c-向量数据库对比

概述

RAG 流水线是将原始文档转化为可检索知识、并在查询时生成有据可查回答的完整数据处理链路。本节将深入拆解流水线的每个阶段——从文档摄入与解析、分块策略、嵌入模型选择、向量存储,到检索方法、检索后处理(重排序与压缩)、最终的生成与引用——每个阶段均包含工具推荐、代码示例和实现指导,帮助你构建生产级 RAG 系统。


1. 文档摄入与解析(Document Ingestion & Parsing)

文档摄入是 RAG 流水线的第一步,也是最容易被低估的环节。“垃圾进,垃圾出”——如果解析质量差,后续所有环节都会受影响。

核心挑战

挑战说明
格式多样性PDF、Word、HTML、Markdown、PPT、Excel、图片、扫描件等
结构保留表格、标题层级、列表、代码块等结构信息容易丢失
多模态内容文档中嵌入的图表、流程图、公式需要特殊处理
元数据提取作者、日期、章节标题等元数据对后续过滤检索至关重要
编码与语言多语言文档、特殊字符、编码问题

工具推荐

工具用途价格适用场景
Unstructured通用文档解析,支持 30+ 格式免费(开源);API 版 $0.01/页起企业级多格式文档处理
LlamaParseLLM 驱动的智能文档解析免费 1000 页/天;付费 $0.003/页起复杂 PDF(表格、图表、公式)
Docling(IBM)开源文档转换工具免费(MIT 开源)本地部署、隐私敏感场景
Apache TikaJava 生态文档解析免费(Apache 2.0)Java 项目集成
PyMuPDF (fitz)高性能 PDF 解析免费(AGPL)纯 PDF 处理,速度优先
MarkerPDF 转 Markdown免费(开源)PDF 转结构化 Markdown

操作步骤

步骤 1:选择解析工具

根据文档类型和需求选择合适的解析工具:

简单文本(MD/TXT/HTML)──▶ 直接读取或轻量解析 结构化 PDF ──────────────▶ PyMuPDF / Docling 复杂 PDF(表格/图表)────▶ LlamaParse / Unstructured 扫描件/图片 ─────────────▶ LlamaParse(OCR)/ Unstructured Office 文档 ─────────────▶ Unstructured / Apache Tika 代码文件 ────────────────▶ 直接读取 + 语法感知分块

步骤 2:使用 Unstructured 解析多格式文档

from unstructured.partition.auto import partition # 自动检测格式并解析 elements = partition(filename="report.pdf") # 按元素类型分类处理 for element in elements: print(f"类型: {type(element).__name__}") print(f"内容: {element.text[:100]}...") print(f"元数据: {element.metadata}") print("---")

步骤 3:使用 LlamaParse 解析复杂 PDF

from llama_parse import LlamaParse parser = LlamaParse( result_type="markdown", # 输出 Markdown 格式 num_workers=4, # 并行处理 language="zh", # 中文文档 ) documents = parser.load_data("complex_report.pdf") for doc in documents: print(doc.text[:500])

步骤 4:使用 Docling 本地解析(隐私优先)

from docling.document_converter import DocumentConverter converter = DocumentConverter() result = converter.convert("internal_doc.pdf") # 导出为 Markdown markdown_output = result.document.export_to_markdown() print(markdown_output[:500])

提示词模板

请分析以下文档解析结果的质量,并提出改进建议: ## 文档信息 - 文件名:[文件名] - 格式:[PDF/Word/HTML/...] - 页数:[页数] ## 解析结果样本 [粘贴解析后的前 500 字] ## 请评估 1. 文本完整性:是否有内容丢失? 2. 结构保留:标题层级、表格、列表是否正确保留? 3. 元数据质量:是否提取了有用的元数据? 4. 建议:推荐使用哪种解析工具或参数调整?

2. 分块策略(Chunking Strategies)

分块是 RAG 流水线中对检索质量影响最大的环节之一。分块策略直接决定了检索的粒度和语义完整性。

分块策略全景对比

策略原理优势劣势推荐场景
固定大小分块按固定 token/字符数切分实现简单、速度快可能截断语义快速原型、均匀文本
递归分块按分隔符层级递归切分平衡语义和大小需要调参通用推荐(生产首选)
语义分块基于嵌入相似度检测语义边界语义完整性最好计算成本高(3-5x)高质量需求、预算充足
文档结构分块按标题/章节/段落切分保留文档结构依赖文档格式规范结构化文档(技术文档、法律文件)
代码感知分块按函数/类/模块切分保留代码语义完整仅适用于代码代码库 RAG
Late Chunking先用长上下文模型嵌入整文档,再分块保留全局上下文需要长上下文嵌入模型上下文依赖强的文档
父子分块小块检索,大块返回精确检索 + 完整上下文实现复杂需要精确定位又需完整上下文

关键参数指南

┌─────────────────────────────────────────────────┐ │ 分块参数调优指南 │ ├─────────────────────────────────────────────────┤ │ │ │ 块大小(Chunk Size) │ │ ├── 256-512 tokens:高精度检索,适合 FAQ/短文档 │ │ ├── 512-1024 tokens:通用推荐,平衡精度和上下文 │ │ └── 1024-2048 tokens:长上下文需求,技术文档 │ │ │ │ 重叠(Overlap) │ │ ├── 推荐:块大小的 10-20% │ │ ├── 过小:上下文断裂 │ │ └── 过大:冗余增加、存储浪费 │ │ │ │ 经验法则 │ │ ├── 递归分块 + 400-800 tokens + 20% 重叠 │ │ │ = 大多数场景的最佳起点 │ │ └── 根据评估指标迭代调优 │ │ │ └─────────────────────────────────────────────────┘

操作步骤

步骤 1:固定大小分块(基线方案)

from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=800, chunk_overlap=160, # 20% 重叠 separators=["\n\n", "\n", "。", ".", " ", ""], length_function=len, ) chunks = splitter.split_text(document_text) print(f"生成 {len(chunks)} 个块")

步骤 2:递归分块(生产推荐)

from langchain.text_splitter import RecursiveCharacterTextSplitter # 中文文档优化的分隔符层级 splitter = RecursiveCharacterTextSplitter( chunk_size=600, chunk_overlap=120, separators=[ "\n\n\n", # 章节分隔 "\n\n", # 段落分隔 "\n", # 行分隔 "。", # 中文句号 ";", # 中文分号 ",", # 中文逗号 ".", # 英文句号 " ", # 空格 "", # 字符级(最后手段) ], ) chunks = splitter.split_documents(documents)

步骤 3:语义分块(高质量需求)

from langchain_experimental.text_splitter import SemanticChunker from langchain_openai import OpenAIEmbeddings embeddings = OpenAIEmbeddings(model="text-embedding-3-small") semantic_splitter = SemanticChunker( embeddings, breakpoint_threshold_type="percentile", breakpoint_threshold_amount=95, # 相似度低于 95 分位时切分 ) semantic_chunks = semantic_splitter.split_text(document_text)

步骤 4:代码感知分块

from langchain.text_splitter import Language, RecursiveCharacterTextSplitter # Python 代码分块 python_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.PYTHON, chunk_size=1000, chunk_overlap=100, ) # TypeScript 代码分块 ts_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.TS, chunk_size=1000, chunk_overlap=100, ) code_chunks = python_splitter.split_text(python_source_code)

步骤 5:父子分块(小块检索,大块返回)

from llama_index.core.node_parser import ( SentenceSplitter, HierarchicalNodeParser, ) from llama_index.core.retrievers import AutoMergingRetriever # 创建层级分块器:大块 2048、中块 512、小块 128 node_parser = HierarchicalNodeParser.from_defaults( chunk_sizes=[2048, 512, 128], ) nodes = node_parser.get_nodes_from_documents(documents) # 检索时用小块匹配,返回时自动合并为父块 retriever = AutoMergingRetriever( vector_retriever, storage_context, simple_ratio_thresh=0.5, # 超过 50% 子块命中则返回父块 )

提示词模板

请为以下文档推荐最佳分块策略: ## 文档特征 - 类型:[技术文档/法律合同/代码库/FAQ/论文/...] - 平均长度:[页数或字数] - 语言:[中文/英文/混合] - 结构化程度:[高(有清晰标题层级)/ 中 / 低(纯文本流)] - 查询类型:[精确事实查找 / 概念理解 / 代码搜索 / ...] ## 请推荐 1. 分块策略(固定/递归/语义/结构/代码感知) 2. 建议的块大小和重叠比例 3. 是否需要父子分块 4. 需要注意的特殊处理

3. 嵌入模型选择(Embedding Model Selection)

嵌入模型将文本转换为高维数值向量,是 RAG 检索质量的核心决定因素。选错嵌入模型,后续优化都是徒劳。

2025-2026 主流嵌入模型对比

模型提供商维度最大 Tokens价格MTEB 排名特点
text-embedding-3-largeOpenAI30728191$0.13/百万 tokens高质量通用嵌入,支持维度缩减
text-embedding-3-smallOpenAI15368191$0.02/百万 tokens中高性价比之选,适合大多数场景
embed-v4Cohere1024512免费层可用;生产版按量计费多语言支持优秀,支持搜索/分类/聚类
voyage-3-largeVoyage AI102432000$0.18/百万 tokens最高MTEB 多领域第一,代码和技术文档优化
voyage-3Voyage AI102432000$0.06/百万 tokens性价比优秀,长上下文支持
mistral-embedMistral10248192$0.10/百万 tokens最高准确率领先(77.8%),欧洲数据合规
BGE-M3BAAI(开源)10248192免费多语言、多粒度、多功能,可自托管
Jina Embeddings v3Jina AI(开源)10248192免费(自托管);API $0.02/百万 tokens任务自适应嵌入,支持 Matryoshka
GTE-Qwen2阿里(开源)102432000免费中文优化,长上下文
NV-Embed-v2NVIDIA(开源)409632768免费最高高维度高精度,需要 GPU

选择决策树

你的场景是什么? ├── 快速原型 / 预算有限 │ └── ✅ text-embedding-3-small($0.02/百万 tokens) ├── 生产级通用场景 │ ├── 英文为主 ──▶ ✅ voyage-3(性价比最优) │ ├── 中文为主 ──▶ ✅ BGE-M3 或 GTE-Qwen2(中文优化) │ └── 多语言 ──▶ ✅ Cohere embed-v4(多语言领先) ├── 最高精度需求 │ └── ✅ voyage-3-large 或 mistral-embed ├── 代码/技术文档 │ └── ✅ voyage-3-large(代码领域优化) ├── 隐私/自托管需求 │ └── ✅ BGE-M3 / Jina v3 / GTE-Qwen2(开源自托管) └── 超长文档(>8K tokens) └── ✅ voyage-3(32K)/ GTE-Qwen2(32K)/ NV-Embed-v2(32K)

操作步骤

步骤 1:使用 OpenAI 嵌入(快速上手)

from openai import OpenAI client = OpenAI() def get_embeddings(texts: list[str], model="text-embedding-3-small"): """批量获取文本嵌入向量""" response = client.embeddings.create( input=texts, model=model, ) return [item.embedding for item in response.data] # 单条嵌入 embedding = get_embeddings(["RAG 流水线是什么?"])[0] print(f"向量维度: {len(embedding)}") # 1536 # 批量嵌入(推荐,减少 API 调用) texts = ["文档片段1", "文档片段2", "文档片段3"] embeddings = get_embeddings(texts)

步骤 2:使用 OpenAI 维度缩减(降低存储成本)

# text-embedding-3 系列支持 Matryoshka 维度缩减 response = client.embeddings.create( input=["RAG 流水线详解"], model="text-embedding-3-large", dimensions=256, # 从 3072 缩减到 256,存储减少 12x ) # 精度损失约 2-5%,但存储和检索速度大幅提升

步骤 3:使用开源模型本地嵌入(隐私优先)

from sentence_transformers import SentenceTransformer # 加载 BGE-M3 多语言模型 model = SentenceTransformer("BAAI/bge-m3") texts = [ "RAG 系统的核心是检索增强生成", "向量数据库存储文档的嵌入表示", ] # 本地推理,数据不出服务器 embeddings = model.encode(texts, normalize_embeddings=True) print(f"向量维度: {embeddings.shape[1]}") # 1024

步骤 4:使用 Voyage AI 嵌入(最高精度)

import voyageai client = voyageai.Client() result = client.embed( texts=["RAG 流水线的检索阶段"], model="voyage-3-large", input_type="document", # 文档嵌入用 "document",查询用 "query" ) embedding = result.embeddings[0] print(f"向量维度: {len(embedding)}") # 1024

提示词模板

请帮我选择最适合的嵌入模型: ## 项目需求 - 文档语言:[中文/英文/多语言] - 文档类型:[技术文档/法律文件/代码/通用] - 文档规模:[千级/万级/百万级] - 查询类型:[语义搜索/精确匹配/代码搜索] - 部署方式:[云 API/自托管/混合] - 月预算:[金额] - 延迟要求:[实时 <100ms / 准实时 <500ms / 批处理] ## 请推荐 1. 首选模型及理由 2. 备选模型 3. 预估月成本 4. 需要注意的限制

4. 向量存储(Vector Storage)

向量存储是 RAG 系统的持久化层,负责高效存储和检索嵌入向量。选择合适的向量数据库直接影响检索延迟、可扩展性和运维成本。

详细的向量数据库对比见 11c-向量数据库对比

工具推荐

工具类型价格适用场景
Pinecone全托管云服务免费层可用;标准版 $70/月起零运维、快速上线
Weaviate开源 + 云服务免费(自托管);云版 $25/月起混合搜索、GraphQL API
Qdrant开源 + 云服务免费(自托管);云版 $25/月起高性能、Rust 实现
ChromaDB开源嵌入式免费本地开发、原型验证
pgvectorPostgreSQL 扩展免费(随 PostgreSQL)已有 PostgreSQL 基础设施
Milvus开源分布式免费(自托管);Zilliz Cloud 按量计费大规模(亿级向量)

操作步骤

步骤 1:使用 ChromaDB(本地开发)

import chromadb # 创建持久化客户端 client = chromadb.PersistentClient(path="./chroma_db") # 创建或获取集合 collection = client.get_or_create_collection( name="my_documents", metadata={"hnsw:space": "cosine"}, # 使用余弦相似度 ) # 添加文档 collection.add( documents=["RAG 是检索增强生成", "向量数据库存储嵌入"], metadatas=[{"source": "doc1"}, {"source": "doc2"}], ids=["id1", "id2"], ) # 查询 results = collection.query( query_texts=["什么是 RAG?"], n_results=3, ) print(results["documents"])

步骤 2:使用 Qdrant(生产部署)

from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct # 连接 Qdrant(本地或云端) client = QdrantClient(url="http://localhost:6333") # 创建集合 client.create_collection( collection_name="documents", vectors_config=VectorParams( size=1536, # 向量维度 distance=Distance.COSINE, ), ) # 插入向量 client.upsert( collection_name="documents", points=[ PointStruct( id=1, vector=embedding_vector, payload={"text": "文档内容", "source": "report.pdf", "page": 5}, ), ], ) # 搜索 results = client.search( collection_name="documents", query_vector=query_embedding, limit=5, )

步骤 3:使用 pgvector(PostgreSQL 生态)

-- 启用 pgvector 扩展 CREATE EXTENSION IF NOT EXISTS vector; -- 创建文档表 CREATE TABLE documents ( id SERIAL PRIMARY KEY, content TEXT NOT NULL, metadata JSONB, embedding vector(1536), -- 1536 维向量 created_at TIMESTAMP DEFAULT NOW() ); -- 创建 HNSW 索引(推荐,比 IVFFlat 更快) CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64); -- 相似度搜索 SELECT id, content, metadata, 1 - (embedding <=> $1::vector) AS similarity FROM documents ORDER BY embedding <=> $1::vector LIMIT 5;

5. 检索方法(Retrieval Methods)

检索是 RAG 流水线的核心环节,直接决定了 LLM 能获得什么样的上下文信息。

检索方法全景对比

方法原理优势劣势适用场景
稠密检索(Dense)基于向量余弦/点积相似度语义理解强,能匹配同义表达对精确关键词匹配弱语义搜索、概念查找
稀疏检索(Sparse/BM25)基于词频统计的关键词匹配精确匹配强,可解释性好缺乏语义理解精确术语、产品编号
混合检索(Hybrid)结合稠密和稀疏检索兼顾语义和精确匹配需要调节融合权重生产推荐(大多数场景)
多路召回(Multi-path)从多个索引/数据源并行检索覆盖面广结果融合复杂多数据源场景
知识图谱检索基于实体关系的结构化检索关系推理强构建成本高需要关系推理的场景
ColBERT延迟交互的 token 级匹配精度高、速度快索引存储大高精度检索需求

操作步骤

步骤 1:稠密检索(向量搜索)

from llama_index.core import VectorStoreIndex # 构建向量索引 index = VectorStoreIndex.from_documents(documents) # 创建检索器 retriever = index.as_retriever( similarity_top_k=10, # 返回 Top-10 结果 ) # 检索 nodes = retriever.retrieve("RAG 系统如何处理中文文档?") for node in nodes: print(f"相关度: {node.score:.4f} | {node.text[:100]}...")

步骤 2:稀疏检索(BM25)

from llama_index.retrievers.bm25 import BM25Retriever # 创建 BM25 检索器 bm25_retriever = BM25Retriever.from_defaults( nodes=all_nodes, similarity_top_k=10, ) # BM25 擅长精确关键词匹配 results = bm25_retriever.retrieve("pgvector HNSW 索引配置")

步骤 3:混合检索(生产推荐)

from llama_index.core.retrievers import QueryFusionRetriever # 融合向量检索和 BM25 检索 hybrid_retriever = QueryFusionRetriever( retrievers=[vector_retriever, bm25_retriever], similarity_top_k=10, num_queries=1, # 不做查询扩展 mode="reciprocal_rerank", # 使用 RRF 融合排序 ) results = hybrid_retriever.retrieve("如何优化 RAG 检索质量?")

步骤 4:使用 Qdrant 原生混合搜索

from qdrant_client import QdrantClient, models client = QdrantClient(url="http://localhost:6333") # Qdrant 原生支持混合搜索(稠密 + 稀疏) results = client.query_points( collection_name="documents", prefetch=[ # 稠密检索 models.Prefetch( query=dense_embedding, using="dense", limit=20, ), # 稀疏检索 models.Prefetch( query=models.SparseVector( indices=sparse_indices, values=sparse_values, ), using="sparse", limit=20, ), ], # RRF 融合 query=models.FusionQuery(fusion=models.Fusion.RRF), limit=10, )

混合检索融合算法

混合检索的关键在于如何融合不同检索器的结果。主流融合算法:

算法原理优势使用场景
RRF(Reciprocal Rank Fusion)基于排名倒数加权融合无需归一化分数,鲁棒性强推荐默认选择
线性加权score = α × dense + (1-α) × sparse简单直观,可调权重需要精细控制融合比例
分布式分数融合(DBSF)基于分数分布归一化后融合处理不同分数尺度多路召回分数差异大

RRF 公式

RRF_score(d) = Σ 1 / (k + rank_i(d)) 其中: - d = 文档 - k = 常数(通常 60) - rank_i(d) = 文档 d 在第 i 个检索器中的排名

提示词模板

请帮我设计 RAG 系统的检索策略: ## 系统信息 - 知识库规模:[文档数量和总大小] - 文档类型:[技术文档/FAQ/代码/混合] - 查询特征:[自然语言/关键词/混合] - 延迟要求:[<100ms / <500ms / <1s] ## 典型查询示例 1. [示例查询 1] 2. [示例查询 2] 3. [示例查询 3] ## 请推荐 1. 检索方法(稠密/稀疏/混合/多路) 2. 融合算法和权重建议 3. Top-K 参数建议 4. 是否需要查询转换(重写/分解/HyDE)

6. 检索后处理(Post-Retrieval Processing)

检索后处理是 Advanced RAG 的核心优化环节,通过重排序和上下文压缩显著提升最终生成质量。

6.1 重排序(Reranking)

重排序使用更精确的模型对检索结果进行二次排序,通常能将回答准确率提升 10-30%。

工具推荐

工具类型价格特点
Cohere RerankAPI 服务免费层 100 次/分钟;$1/千次查询多语言支持,即插即用
Jina Reranker v2API + 开源免费(自托管);API $0.02/千次开源可自托管
BGE-Reranker-v2开源免费中文优化,可本地部署
FlashRank开源轻量免费超轻量(<100MB),CPU 友好
LLM-as-Reranker使用 LLM 重排序按 LLM 调用计费最灵活,但成本最高

操作步骤

步骤 1:使用 Cohere Rerank

import cohere co = cohere.Client("your-api-key") # 对检索结果重排序 rerank_results = co.rerank( model="rerank-v3.5", query="RAG 系统如何处理中文文档?", documents=[node.text for node in retrieved_nodes], top_n=5, # 只保留 Top-5 ) for result in rerank_results.results: print(f"排名: {result.index} | 相关度: {result.relevance_score:.4f}")

步骤 2:使用开源 BGE-Reranker(本地部署)

from sentence_transformers import CrossEncoder # 加载交叉编码器重排序模型 reranker = CrossEncoder("BAAI/bge-reranker-v2-m3", max_length=512) # 计算查询-文档对的相关度分数 query = "RAG 系统如何处理中文文档?" pairs = [(query, node.text) for node in retrieved_nodes] scores = reranker.predict(pairs) # 按分数排序 ranked_indices = scores.argsort()[::-1] top_nodes = [retrieved_nodes[i] for i in ranked_indices[:5]]

步骤 3:在 LlamaIndex 中集成重排序

from llama_index.core.postprocessor import SentenceTransformerRerank # 创建重排序后处理器 reranker = SentenceTransformerRerank( model="BAAI/bge-reranker-v2-m3", top_n=5, ) # 在查询引擎中使用 query_engine = index.as_query_engine( similarity_top_k=20, # 先召回 20 个 node_postprocessors=[reranker], # 重排序后保留 5 个 ) response = query_engine.query("RAG 流水线有哪些阶段?")

6.2 上下文压缩(Context Compression)

上下文压缩去除检索结果中的冗余和无关内容,让 LLM 聚焦于最相关的信息。

from llama_index.core.postprocessor import LongContextReorder # 长上下文重排序:将最相关的内容放在开头和结尾 # (研究表明 LLM 对中间内容的注意力较弱——"Lost in the Middle") reorder = LongContextReorder() query_engine = index.as_query_engine( similarity_top_k=10, node_postprocessors=[reranker, reorder], # 先重排序,再重排列 )
from langchain.retrievers.document_compressors import LLMChainExtractor from langchain_openai import ChatOpenAI # 使用 LLM 提取每个文档中与查询最相关的部分 llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) compressor = LLMChainExtractor.from_llm(llm) # 压缩后的文档只包含与查询直接相关的句子 compressed_docs = compressor.compress_documents( documents=retrieved_docs, query="RAG 系统的分块策略有哪些?", )

完整检索后处理流水线

检索结果(Top-20) ┌──────────────┐ │ 去重(Dedup) │ ← 去除重复或高度相似的文档 └──────┬───────┘ ┌──────────────┐ │ 重排序(Rerank)│ ← 交叉编码器精排,保留 Top-10 └──────┬───────┘ ┌──────────────────┐ │ 上下文压缩 │ ← 提取每个文档中最相关的部分 │(Context Compress)│ └──────┬───────────┘ ┌──────────────────┐ │ 长上下文重排列 │ ← 最相关内容放开头和结尾 │(Lost-in-Middle) │ └──────┬───────────┘ 送入 LLM 生成

7. 生成与引用(Generation with Citations)

生成阶段是 RAG 流水线的最后一环,将检索到的上下文与用户查询组装成 Prompt,由 LLM 生成有据可查的回答。

工具推荐

工具用途价格适用场景
GPT-4o高质量生成,支持长上下文$2.50/$10 每百万 tokens(输入/输出)通用生产场景
GPT-4o-mini性价比生成$0.15/$0.60 每百万 tokens成本敏感场景
Claude 3.5 Sonnet长上下文生成,200K 窗口$3/$15 每百万 tokens大量上下文的 RAG
Claude 3.5 Haiku快速低成本生成$0.25/$1.25 每百万 tokens低延迟需求
Gemini 2.5 Flash超长上下文,1M tokens免费层可用;$0.15/$0.60 每百万 tokens超大上下文 RAG
Llama 3.1 70B开源大模型免费(自托管);API 按量计费自托管、隐私需求

操作步骤

步骤 1:基础 RAG Prompt 组装

def build_rag_prompt(query: str, context_nodes: list) -> str: """组装 RAG Prompt""" context_parts = [] for i, node in enumerate(context_nodes, 1): source = node.metadata.get("file_name", "未知来源") context_parts.append(f"[来源 {i}: {source}]\n{node.text}") context_str = "\n\n---\n\n".join(context_parts) return f"""你是一个基于知识库的问答助手。请严格根据以下检索到的上下文信息回答用户问题。 ## 规则 1. 只使用提供的上下文信息回答问题 2. 在回答中用 [来源 N] 标注信息出处 3. 如果上下文中没有相关信息,明确说明 4. 不要编造上下文中不存在的信息 ## 上下文信息 {context_str} ## 用户问题 {query} ## 请回答(附带来源引用):"""

步骤 2:带引用的生成

from openai import OpenAI client = OpenAI() def generate_with_citations(query: str, context_nodes: list) -> str: """生成带引用的回答""" prompt = build_rag_prompt(query, context_nodes) response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "你是一个严谨的知识库问答助手,回答时必须标注来源。"}, {"role": "user", "content": prompt}, ], temperature=0.1, # 低温度,减少幻觉 max_tokens=1024, ) return response.choices[0].message.content # 使用 answer = generate_with_citations( query="RAG 系统的分块策略有哪些?", context_nodes=reranked_nodes, ) print(answer)

步骤 3:结构化引用输出

from pydantic import BaseModel class Citation(BaseModel): source: str page: int | None = None quote: str class RAGResponse(BaseModel): answer: str citations: list[Citation] confidence: float # 0-1 # 使用 OpenAI 结构化输出 response = client.beta.chat.completions.parse( model="gpt-4o-mini", messages=[ {"role": "system", "content": "基于上下文回答问题,返回结构化引用。"}, {"role": "user", "content": prompt}, ], response_format=RAGResponse, ) rag_response = response.choices[0].message.parsed print(f"回答: {rag_response.answer}") print(f"置信度: {rag_response.confidence}") for cite in rag_response.citations: print(f" 来源: {cite.source} | 引用: {cite.quote}")

步骤 4:流式生成(低延迟体验)

def stream_rag_response(query: str, context_nodes: list): """流式生成 RAG 回答""" prompt = build_rag_prompt(query, context_nodes) stream = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "你是知识库问答助手。"}, {"role": "user", "content": prompt}, ], temperature=0.1, stream=True, ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) stream_rag_response("RAG 流水线有哪些阶段?", reranked_nodes)

提示词模板

RAG 系统 Prompt(带引用和置信度)

你是一个基于知识库的专业问答助手。 ## 核心规则 1. **严格基于上下文**:只使用提供的上下文信息回答,不要使用你的训练知识 2. **标注来源**:每个关键信息点用 [来源 N] 标注出处 3. **诚实不确定性**:如果上下文信息不足以完整回答,明确说明哪些部分无法确认 4. **不编造**:绝对不要编造上下文中不存在的信息 ## 上下文信息 [检索到的文档片段,每个片段标注来源编号] ## 用户问题 [用户查询] ## 回答格式 1. 直接回答问题 2. 关键信息标注 [来源 N] 3. 如有不确定之处,在末尾说明 4. 最后给出置信度评估(高/中/低)

实战案例:构建完整的生产级 RAG 流水线

场景

为一家技术公司构建内部知识库问答系统,支持对技术文档、API 文档和代码仓库的智能检索与问答。

完整实现

""" 生产级 RAG 流水线完整示例 技术栈:LlamaIndex + Qdrant + OpenAI + Cohere Rerank """ from llama_index.core import ( VectorStoreIndex, SimpleDirectoryReader, Settings, StorageContext, ) from llama_index.core.node_parser import SentenceSplitter from llama_index.vector_stores.qdrant import QdrantVectorStore from llama_index.embeddings.openai import OpenAIEmbedding from llama_index.llms.openai import OpenAI from llama_index.postprocessor.cohere_rerank import CohereRerank from qdrant_client import QdrantClient # ============================================ # 阶段 1:配置全局设置 # ============================================ Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0.1) Settings.embed_model = OpenAIEmbedding( model="text-embedding-3-small", dimensions=512, # 维度缩减,节省存储 ) # ============================================ # 阶段 2:文档摄入与解析 # ============================================ documents = SimpleDirectoryReader( input_dir="./knowledge_base", recursive=True, required_exts=[".md", ".txt", ".pdf", ".py", ".ts"], filename_as_id=True, ).load_data() print(f"加载了 {len(documents)} 个文档") # ============================================ # 阶段 3:分块 # ============================================ splitter = SentenceSplitter( chunk_size=512, chunk_overlap=100, paragraph_separator="\n\n", ) # ============================================ # 阶段 4:向量存储(Qdrant) # ============================================ qdrant_client = QdrantClient(url="http://localhost:6333") vector_store = QdrantVectorStore( client=qdrant_client, collection_name="tech_knowledge_base", ) storage_context = StorageContext.from_defaults(vector_store=vector_store) # ============================================ # 阶段 5:构建索引(嵌入 + 存储) # ============================================ index = VectorStoreIndex.from_documents( documents, storage_context=storage_context, transformations=[splitter], show_progress=True, ) # ============================================ # 阶段 6:配置检索 + 重排序 + 生成 # ============================================ cohere_rerank = CohereRerank( model="rerank-v3.5", top_n=5, ) query_engine = index.as_query_engine( similarity_top_k=20, # 先召回 20 个 node_postprocessors=[cohere_rerank], # 重排序后保留 5 个 response_mode="tree_summarize", # 树状摘要模式 ) # ============================================ # 阶段 7:查询 # ============================================ response = query_engine.query( "我们的 API 认证机制是如何实现的?请详细说明。" ) print("回答:", response.response) print("\n来源:") for node in response.source_nodes: print(f" - {node.metadata.get('file_name', '未知')} " f"(相关度: {node.score:.4f})")

案例分析

  • 维度缩减:使用 dimensions=512 将 text-embedding-3-small 的 1536 维缩减到 512 维,存储减少 3x,检索速度提升,精度损失约 2-3%
  • 过召回 + 重排序:先用向量搜索召回 20 个候选(高召回率),再用 Cohere Rerank 精排到 5 个(高精度),这是生产系统的标准模式
  • 树状摘要:使用 tree_summarize 模式处理多个上下文片段,比简单拼接更能生成连贯的长回答
  • 元数据追踪:通过 filename_as_id=True 保留文件来源信息,支持回答溯源

避坑指南

❌ 常见错误

  1. 跳过文档解析质量检查,直接进入分块

    • 问题:PDF 解析后表格变成乱码、标题层级丢失、图片中的文字未提取,导致后续检索到的内容不可用
    • 正确做法:在流水线中加入解析质量检查步骤——抽样检查解析结果,验证表格、标题、列表等结构是否正确保留。对复杂 PDF 使用 LlamaParse 等 LLM 驱动的解析工具
  2. 使用同一个嵌入模型处理查询和文档,但不区分 input_type

    • 问题:部分嵌入模型(如 Voyage AI、Cohere)对文档嵌入和查询嵌入使用不同的编码策略,混用会降低检索质量
    • 正确做法:文档嵌入时使用 input_type="document",查询嵌入时使用 input_type="query"。始终查阅嵌入模型的官方文档确认是否需要区分
  3. 只用向量检索,忽略混合搜索

    • 问题:用户查询包含专有名词、错误代码、产品编号等精确信息时,纯向量搜索可能找不到精确匹配
    • 正确做法:生产系统默认使用混合检索(向量 + BM25),通过 RRF 融合排序。对于技术文档和代码库场景,混合搜索通常比纯向量搜索提升 15-25% 的检索准确率
  4. 重排序模型的 max_length 设置过小

    • 问题:交叉编码器重排序模型有最大输入长度限制(通常 512 tokens),超长文档块会被截断,导致重排序不准确
    • 正确做法:确保分块大小与重排序模型的 max_length 匹配。如果块较大,考虑使用支持更长输入的重排序模型,或在重排序前先做上下文压缩
  5. 生成阶段温度设置过高

    • 问题:RAG 系统的目标是基于事实生成回答,高温度(>0.5)会增加创造性但也增加幻觉风险
    • 正确做法:RAG 生成阶段使用低温度(0-0.2),确保回答紧贴检索到的上下文。只有在需要创意性总结时才适当提高温度
  6. 没有对流水线各阶段进行独立评估

    • 问题:RAG 系统回答质量差时,无法定位是检索问题还是生成问题,盲目调参浪费时间
    • 正确做法:分别评估检索质量(context precision、context recall)和生成质量(faithfulness、answer relevance),针对性优化瓶颈环节。详见 11f-RAG评估与优化

✅ 最佳实践

  1. 流水线各阶段独立可测试——每个阶段(解析、分块、嵌入、检索、重排序、生成)都应该可以独立运行和评估
  2. 从简单开始,逐步优化——先用递归分块 + 向量检索 + 直接生成跑通,再逐步加入混合搜索、重排序、上下文压缩
  3. 元数据是免费的性能提升——为每个文档块添加丰富的元数据(来源、日期、章节、作者),支持过滤检索
  4. 批量嵌入而非逐条调用——嵌入 API 支持批量输入,批量处理可减少 API 调用次数和延迟
  5. 定期重建索引——知识库更新后及时重建索引,避免检索到过时信息

相关资源与延伸阅读

  1. LlamaIndex 官方文档 — Ingestion Pipeline :LlamaIndex 数据摄入管线的完整文档,包含分块、嵌入、存储的详细配置
  2. LangChain — Text Splitters 文档 :LangChain 所有分块策略的详细说明和代码示例
  3. Firecrawl — Best Chunking Strategies for RAG in 2025 :2025 年分块策略的全面对比和基准测试
  4. Pinecone — Choosing an Embedding Model :嵌入模型选择的实用指南,包含 MTEB 基准分析
  5. Qdrant — Hybrid Search with Reranking Tutorial :Qdrant 混合搜索和 ColBERT 重排序的实战教程
  6. Cohere — Rerank API 文档 :Cohere 重排序 API 的官方文档和最佳实践
  7. Agenta — The Ultimate Guide to RAG Chunking Strategies :分块策略的深度指南,包含性能对比数据
  8. Production-Ready RAG Systems: End to End Guide :生产级 RAG 系统的端到端构建指南

参考来源


📖 返回 总览与导航 | 上一节:11a-RAG概念与架构 | 下一节:11c-向量数据库对比

Last updated on