1. RAG 是什么及适用边界
RAG(Retrieval-Augmented Generation,检索增强生成)不是单纯的“向量数据库问答”,而是一套在生成回答前检索外部资料,并要求模型基于检索证据回答的系统架构。
一个完整的 RAG 系统通常包含:
数据治理
→ 文档解析与索引
→ 查询理解
→ 检索与排序
→ 上下文构建
→ 模型生成
→ 引用验证
→ 评估与监控
RAG 可以使用不同检索器:
- BM25 或全文关键词检索;
- 向量语义检索;
- SQL 或结构化查询;
- 知识图谱查询;
- 多种检索方式组合的混合检索。
因此,RAG 不等于向量检索。向量检索只是 RAG 中可选的一条检索通道。
1.1 适合使用 RAG 的场景
- 数据规模较大,无法每次全部放入上下文;
- 数据频繁更新,不能只依赖模型训练知识;
- 用户表达与文档措辞存在明显差异;
- 回答需要引用原文并支持审计;
- 不同用户只能访问不同资料;
- 需要综合多个来源形成回答;
- 需要控制上下文 Token、延迟与成本。
1.2 不应默认使用 RAG 的场景
| 场景 | 优先方案 |
|---|---|
| 数据量很小且稳定 | 直接使用长上下文 |
| 数据高度结构化 | SQL、API 或规则查询 |
| 已知精确编号或术语 | 关键词或精确匹配 |
| 固定、确定性的业务逻辑 | 普通工作流或规则引擎 |
| 需要修改模型行为风格 | Prompt 或微调,而不是 RAG |
在决定使用 RAG 前,应先回答:
- 数据规模和更新频率是多少?
- 用户如何表达问题?
- 是否需要严格引用和权限控制?
- 长上下文、关键词检索或 SQL 是否已经足够?
- 什么指标能够证明 RAG 比基线方案更好?
2. 定义业务目标与 Eval 测试集
RAG 开发不应从选择向量数据库开始,而应从业务问题和评估标准开始。
2.1 明确系统任务
需要先定义:
- 用户是谁;
- 查询类型有哪些;
- 哪些资料是权威来源;
- 什么情况下应该拒答;
- 回答是否必须附带引用;
- 允许的延迟和单次成本;
- 错误回答会产生什么风险。
例如,法律检索系统可能要求:
输入:自然语言案情、法域、案件日期
输出:相关法条、适用版本、引用原文和教学性解释
拒答:缺少法域、日期或权威依据时停止生成确定性结论
2.2 先建立测试集
每条测试数据至少包含:
{
"query": "用户问题",
"required_source_ids": ["DOC-001"],
"acceptable_source_ids": ["DOC-002"],
"forbidden_source_ids": ["DOC-OLD-001"],
"metadata_filters": {
"jurisdiction": "CN",
"law_as_of_date": "2026-07-28"
},
"should_abstain": false
}
测试集应覆盖:
- 精确术语查询;
- 口语化查询;
- 多文档综合问题;
- 新旧版本冲突;
- 权限不足;
- 无答案或应拒答问题;
- 容易被语义相似资料干扰的问题。
2.3 建立最小基线
推荐至少建立三组基线:
基线 A:关键词/BM25 检索
基线 B:向量检索
基线 C:BM25 + 向量混合检索
没有固定测试集和基线时,无法判断增加查询改写、Reranker 或 Agent 循环后是否真的提升。
2.4 向量数据库如何选型
向量数据库选型很重要,但重要的不是记住某个产品“性能高”或“使用简单”,而是能否从业务约束和实验结果解释取舍。
选型前至少明确:
| 维度 | 需要回答的问题 |
|---|---|
| 数据规模 | 当前和一年后的 Chunk 数量、向量总量是多少? |
| 查询模式 | 纯向量、关键词、Hybrid、范围过滤分别占多少? |
| 元数据过滤 | 是否需要按租户、权限、时间、法域和版本过滤? |
| 更新模式 | 数据是批量导入、频繁更新还是大量删除? |
| 一致性 | 新增、更新和删除后,何时必须对查询可见? |
| 延迟与吞吐 | P95 延迟、QPS 和并发目标是什么? |
| 运维能力 | 团队能否维护独立集群、备份、扩缩容和监控? |
| 部署约束 | 云服务、内网私有化、单机还是跨地域? |
| 成本 | 存储、索引、计算、网络和人力成本是多少? |
| 现有技术栈 | 是否已经使用 PostgreSQL 或 Elasticsearch/OpenSearch? |
常见选择及适用起点:
| 方案 | 更适合的情况 | 需要警惕 |
|---|---|---|
| FAISS/Chroma | 本地实验、小规模原型 | 权限、持久化、分布式和运维能力有限 |
| PostgreSQL + pgvector | 已有 PostgreSQL、数据规模中等、事务与业务数据关系紧密 | 大规模混合检索和水平扩展需要额外验证 |
| OpenSearch/Elasticsearch | 已有搜索栈、BM25与向量混合检索、复杂元数据过滤 | 集群资源和运维成本较高 |
| Qdrant | 向量检索为主、过滤能力重要、希望独立向量服务 | 关键词搜索和已有数据体系的集成需评估 |
| Milvus/Weaviate等 | 大规模向量、独立检索基础设施 | 系统复杂度和团队维护成本更高 |
| 托管向量服务 | 希望快速上线且可接受数据出域和持续费用 | 厂商锁定、价格、网络和隐私约束 |
选择过程应包含相同数据和查询集上的最小实验:
固定文档与Embedding
→ 导入候选数据库
→ 测试Recall@K与过滤正确性
→ 测试P50/P95延迟和吞吐
→ 测试新增、更新、删除与重建索引
→ 估算资源和运维成本
→ 根据当前约束选择最简单的可满足方案
数据库不会直接提高语义质量。只要使用相同向量和近似搜索参数,不同数据库首先影响的是索引能力、过滤、延迟、扩展性和运维,而不是模型是否“理解”文档。
2.5 Embedding 模型如何选型
Embedding 决定查询和文档如何进入同一个语义空间,对召回质量通常比更换向量数据库更直接。
选型需要评估:
| 维度 | 说明 |
|---|---|
| 语言 | 中文、英文、多语言及混合文本表现 |
| 领域 | 通用、法律、医疗、代码等领域适配度 |
| 查询类型 | 短查询、长问题、专业术语和口语表达 |
| 最大输入长度 | 长条款或长段落是否被截断 |
| 向量维度 | 影响索引大小、内存、延迟和迁移成本 |
| 检索范式 | 是否区分 Query/Document 前缀或支持非对称检索 |
| 部署方式 | API、本地CPU、本地GPU或内网服务 |
| 成本与延迟 | 批量索引成本和在线查询延迟 |
| 数据安全 | 文档是否允许发送到第三方API |
| 版本稳定性 | 模型升级后是否能继续使用旧向量 |
不要仅根据公开榜单选Embedding。公开基准不能完全代表自己的数据、切分策略和查询分布。
推荐实验流程:
准备人工标注的Query-Document集合
→ 固定Chunk和向量数据库
→ 分别生成候选Embedding
→ 比较Recall@5、Recall@10、MRR和nDCG
→ 比较专业术语、口语查询和困难负样本
→ 记录索引体积、查询延迟和成本
→ 再决定模型
至少准备三类困难样本:
- 关键词不同但语义相同;
- 关键词高度相似但实际不相关;
- 新旧版本、否定条件或例外条款只有细微差异。
Embedding 升级不是修改一个模型名。不同模型的向量空间通常不兼容,需要:
- 记录
embedding_model和embedding_version; - 创建新索引或新向量字段;
- 重新向量化全部受影响 Chunk;
- 使用固定 Eval 对比新旧版本;
- 完成切换后再下线旧索引;
- 保留回滚方案。
面试回答可以采用:
候选模型A在公开中文检索基准上较强,但我没有直接采用。
我使用项目中的N条查询进行对比,重点测试专业术语、口语表达和错误版本干扰。
模型B的Recall@5由X提升至Y,P95延迟增加Z毫秒,向量存储增加W%,
在当前数据规模和延迟预算内收益可接受,因此选择B。
如果没有做过对比实验,应明确说这是“基于约束的初始选择”,不能将个人经验包装成已验证结论。
3. 离线知识库构建
离线阶段负责把原始资料转换成可检索、可过滤、可追溯的知识索引。
完整流程:
数据收集
→ 来源、授权与权限校验
→ 清洗、去重和格式标准化
→ 结构化解析
→ 文档分块
→ 元数据与版本管理
→ Embedding
→ 关键词索引与向量索引
→ 索引质量检查
3.1 数据来源、授权与权限
每份文档都应记录:
- 来源和原始 URL;
- 作者或发布机构;
- 获取时间;
- 使用授权;
- 文档所属租户或权限范围;
- 是否包含敏感信息;
- 是否经过人工审核。
生产系统不能先把所有资料交给模型,再通过 Prompt 要求模型“不要泄露”。权限过滤必须在检索前完成。
3.2 数据清洗和去重
随着文档规模增加,重复内容、解析错误、无效模板和冲突版本会占据检索候选位置,并增加索引及生成成本。数据清洗通常是最具性价比的优化之一。
常见清洗内容:
- HTML 导航栏、页脚和广告;
- OCR 乱码和异常字符;
- 空白页和重复页面;
- 重复标题与模板文本;
- 无效表格和图片占位符;
- 已失效或冲突版本;
- 密钥、个人信息等敏感数据;
- 文档中的提示注入或指令性文本。
建议使用内容哈希识别完全重复文档,再结合标题、来源和语义相似度识别近似重复文档。
3.3 结构化解析与分块
固定长度切分可以作为基线,但不应默认是最终方案。分块策略必须与文档结构和检索 Eval 对齐。
| 策略 | 适用场景 | 主要风险 | 建议关注指标 |
|---|---|---|---|
| 固定长度 | 格式统一的纯文本基线 | 破坏语义边界 | Recall@K、Token |
| 标题/章节分块 | 结构清晰的文档 | 块大小不均 | Recall@K、上下文长度 |
| 父子分块 | 法律、技术手册 | 索引和回溯复杂 | 召回率、引用完整度 |
| 滑动窗口 | 叙事性长文档 | 存储和内容重复 | 去重率、Token |
| 符号分块 | 代码仓库 | 跨符号依赖可能丢失 | 相关符号召回率 |
对于普通文本,可以从 300~500 Token、50~100 Token 重叠开始实验,但这不是通用最佳参数。
领域数据应优先按照天然结构切分:
法律:法律文件 → 编 → 章 → 节 → 条 → 款 → 项
代码:仓库 → 模块 → 类 → 函数
技术文档:产品 → 版本 → 章节 → 小节
父子分块的基本思路:
小块负责精确检索
→ 找到相关小块
→ 返回包含完整语义的父块给模型
3.4 元数据和版本管理
每个 Chunk 至少应保存:
{
"chunk_id": "CHUNK-001",
"document_id": "DOC-001",
"title": "文档标题",
"section_path": ["第三章", "第一节"],
"source_url": "https://example.com",
"created_at": "2026-01-01",
"effective_from": "2026-01-01",
"effective_to": null,
"status": "effective",
"tenant_id": "TENANT-001",
"allowed_roles": ["student"],
"version_hash": "...",
"embedding_version": "..."
}
元数据用于权限过滤、时间和版本过滤、增量索引、来源展示与引用回溯。
3.5 关键词索引与向量索引
关键词索引适合人名、机构名、法条编号、代码符号、产品型号、专有术语和错误信息。
向量索引适合用户使用口语化表达、查询与文档措辞不同、需要语义匹配的场景。
二者通常互补,而不是相互替代。
3.6 增量更新与删除
知识库需要处理:
- 新文档新增;
- 原文修改;
- 文档删除;
- 权限变化;
- 新旧版本并存;
- Embedding 模型升级。
可以使用内容哈希判断哪些 Chunk 需要重新解析和向量化。删除文档时必须同步删除关键词索引、向量索引和缓存,避免“幽灵文档”继续被召回。
4. 在线检索与生成
完整在线流程:
用户查询
→ 查询分类与风险检查
→ 权限、法域、时间等元数据约束
→ 判断是否需要检索
→ 查询改写或拆解
→ 多路召回
→ 融合与去重
→ 重排序
→ 上下文构建
→ 结构化生成
→ 引用验证
→ 输出或拒答
→ 保存检索轨迹
4.1 查询理解
查询理解的目标不是直接回答问题,而是生成适合检索的结构化查询。
常见操作包括意图分类、实体提取、Query Rewrite、Multi-Query、Query Decomposition 和 HyDE。
原始查询必须始终保留。查询改写可能引入模型臆测,不能无条件替代用户原意。
例如:
用户问题:偷拿手机后来又还了,还算盗窃吗?
结构化查询:
- 法域:中华人民共和国
- 案由候选:盗窃
- 争议事项:非法占有目的
- 事实变量:取走、归还、主观目的
查询改写、HyDE 和 Multi-Query 都是可选优化。只有基线测试证明存在“表达差异导致召回失败”时才应加入。
4.2 元数据与权限过滤
检索前应尽量缩小候选范围:
用户身份
→ 可访问租户和文档
→ 文档类型
→ 时间和版本
→ 业务域或法域
→ 执行检索
过滤必须发生在检索层,不能只在生成 Prompt 中声明权限。
4.3 多路召回
多路召回优先保证“不要漏掉正确资料”。典型方案:
BM25 召回 Top 20
+ 向量召回 Top 20
→ 合并候选
BM25 擅长精确术语,向量检索擅长语义表达。对于法律、代码和产品文档,混合检索通常比纯向量检索更值得优先评估。
4.4 融合与去重
不同检索器的原始分数通常不在同一尺度上,不能直接比较。
常用融合方法:
- 归一化后加权;
- Reciprocal Rank Fusion(RRF);
- 合并去重后统一交给 Reranker。
MVP 可以从 RRF 开始:
BM25 Top 20
+ Vector Top 20
→ RRF 融合
→ 去重
→ 候选 Top 20
4.5 重排序
召回阶段关注召回率,重排序阶段关注精确率。
Reranker 通常使用 Cross-Encoder 对 [查询, 文档] 对进行更细致的相关性评分,再保留 Top 3~5 个片段进入上下文。
可选方案:
- 中文或私有部署:BGE 系列 Reranker;
- 托管服务:Cohere 等 Rerank API;
- 垂直领域:先建立通用模型基线,积累标注数据后再考虑领域微调。
Reranker 会增加延迟和成本,而且无法补救召回阶段完全漏掉的文档。是否有效必须通过排序指标验证。
4.6 上下文构建
不能将检索结果简单拼接后全部发送给模型。上下文构建需要:
- 去除重复和近似重复片段;
- 保留来源、标题和定位信息;
- 补充必要的父块或相邻条款;
- 根据相关性和效力排序;
- 控制总 Token 预算;
- 将事实资料与不可信指令分离;
- 对冲突版本进行明确标记。
建议格式:
[SOURCE: DOC-001 / SECTION-03]
原文……
[SOURCE: DOC-002 / SECTION-08]
原文……
4.7 结构化生成与引用验证
Prompt 可以要求模型只根据上下文回答,但 Prompt 是软约束,不能作为可靠性边界。
建议输出结构:
{
"status": "supported",
"answer": "回答内容",
"citations": [
{
"source_id": "DOC-001",
"supporting_quote": "原文引用"
}
],
"uncertainties": []
}
程序层必须检查:
source_id是否来自本次检索结果;- 引文是否真实存在于原文;
- 文档版本是否适用于当前问题;
- 用户是否有权访问该来源;
- 引用是否能够支持对应结论。
4.8 拒答与降级
当资料不足、版本冲突或权限受限时,系统应拒绝生成确定性答案:
{
"status": "insufficient_context",
"answer": null,
"citations": [],
"missing_information": ["缺少适用日期"]
}
拒答不是失败,而是高风险 RAG 系统的重要能力。
5. RAG Eval
RAG Eval 应在基础链路跑通后立即建立,而不是等到所有优化完成后再补。
5.1 检索层评估
| 指标 | 含义 |
|---|---|
| Recall@K | 正确文档是否进入前 K 条结果 |
| Precision@K | 前 K 条结果中相关文档的比例 |
| MRR | 第一个正确结果的排名质量 |
| nDCG@K | 综合考虑不同相关等级和排名 |
| Filter Accuracy | 权限、时间和版本过滤是否正确 |
| Wrong-Version Rate | 错误版本被召回的比例 |
5.2 生成层评估
- Faithfulness:回答是否被上下文支持;
- Answer Relevance:回答是否解决用户问题;
- Citation Correctness:引用是否真实且支持结论;
- Citation Completeness:关键结论是否都有引用;
- Abstention Accuracy:无依据时是否正确拒答;
- Conflict Handling:资料冲突时是否明确呈现不确定性。
LLM-as-Judge 可以用于软质量评分,但必须抽样人工复核。引用存在性、权限、版本和结构化格式等硬约束应由程序判断。
5.3 系统层评估
- P50/P95 延迟;
- 单次查询输入和输出 Token;
- Embedding 与生成成本;
- 索引更新时间;
- 缓存命中率;
- 检索失败率;
- 超时和重试次数。
5.4 实验原则
- 固定测试集和基线;
- 每次只修改一个主要变量;
- 保存查询、过滤条件、召回结果和最终回答;
- 同时记录质量、成本和延迟;
- 不用少量人工体验代替批量评估;
- 新方案没有稳定提升时不增加生产复杂度。
6. 常见失败与排查
| 失败类型 | 典型现象 | 优先检查 |
|---|---|---|
| 数据失败 | 文档乱码、重复或版本错误 | 解析、清洗、数据源 |
| 分块失败 | 正确知识被切断 | Chunk 边界、父子块 |
| 召回失败 | 正确文档不在 Top-K | 查询、索引、Embedding |
| 排序失败 | 正确文档已召回但排名低 | 融合、Reranker |
| 过滤失败 | 召回无权限或错误版本文档 | 元数据、过滤顺序 |
| 上下文失败 | 正确资料进入 Prompt 但被忽略 | 去重、顺序、Token预算 |
| 生成失败 | 上下文正确但回答错误 | Prompt、模型、Schema |
| 引用失败 | 回答与引用不一致 | 引用绑定、原文校验 |
| 拒答失败 | 无资料时仍强行回答 | 置信度和停止条件 |
推荐排查顺序:
检查原始数据
→ 检查正确文档是否被召回
→ 检查排序位置
→ 检查进入模型的最终上下文
→ 检查模型输出
→ 检查引用验证
不要看到最终答案错误就直接修改 Prompt。很多问题实际发生在数据和检索阶段。
7. 进阶优化
只有基础检索、引用验证和 Eval 稳定后,才评估以下优化。每项优化都应对应一个已经观察到的失败类型。
7.1 Query Rewrite
适用于用户口语与文档术语不一致的情况。必须保留原查询,并评估改写是否改变原意。
7.2 Multi-Query 与 RAG-Fusion
将问题扩展成多个互补查询,分别检索后再融合结果,可以提高复杂问题的召回率。
风险包括查询偏离原问题、重复召回、成本增加和引入错误假设。
7.3 Agentic Retrieval
Agent 可以决定:
- 是否需要检索;
- 应查询哪类数据源;
- 是否拆分问题;
- 当前证据是否充分;
- 是否需要继续检索或请求人工帮助。
必须设置最大检索轮数、查询数量、Token与金额预算、查询去重和停止条件。
7.4 领域模型微调
只有积累了稳定的 Query-Document 相关性数据,并证明通用 Embedding 或 Reranker 存在系统性领域误差后,才考虑领域微调。
不应直接用业务 Chunk 盲目微调,而应准备正样本、困难负样本和独立验证集。
7.5 对话记忆
多轮问答时,应先将历史对话改写成独立查询,再进行检索。不要直接把全部聊天历史拼入检索查询或生成上下文。
8. 生产化:权限、监控、成本和更新
8.1 权限安全
- 在检索前过滤用户无权访问的文档;
- 不依赖模型自觉保密;
- 文档内容视为不可信输入;
- 对敏感操作保留审计日志;
- 多租户数据必须隔离。
8.2 可观测性
每次请求应保存:
原始查询
改写查询
元数据过滤条件
各检索通道结果与分数
融合和重排结果
最终上下文
模型输出
引用校验结果
Token、成本、延迟和错误
8.3 成本控制
- 缓存稳定查询和 Embedding;
- 使用内容哈希避免重复向量化;
- 控制召回候选和 Reranker 数量;
- 去重后再进入模型;
- 设置上下文 Token 预算;
- 简单查询优先使用关键词或结构化查询;
- 复杂查询才启用查询扩展和多步检索。
8.4 数据与索引更新
需要设计定时同步、失败重试、新旧版本切换、索引重建、Embedding版本迁移、回滚策略和删除资料后的索引清理。
9. 场景选择:长上下文、关键词、RAG 与 LLM Wiki
这些方案不是互斥关系,而是处于不同层级。
| 方案 | 主要作用 | 适合场景 | 主要风险 |
|---|---|---|---|
| 长上下文 | 直接读取完整资料 | 数据少、单文档分析 | 成本、延迟、注意力衰减 |
| 关键词检索 | 精确查找术语 | 法条、代码、产品型号 | 口语表达召回不足 |
| 向量检索 | 语义匹配 | 自然语言与文档措辞不同 | 语义相似但业务不适用 |
| Hybrid RAG | 精确与语义结合 | 专业知识问答 | 系统复杂度增加 |
| LLM Wiki | 整理知识和导航 | 中等规模团队知识 | 二次整理可能失真 |
9.1 选择顺序
数据很少且稳定 → 长上下文
查询和数据高度结构化 → SQL/API
查询包含精确术语 → 关键词/BM25
用户表达口语化 → 向量检索
精确术语和语义都重要 → Hybrid RAG
知识需要持续整理和人工维护 → LLM Wiki + 原始资料链接
任务需要主动、多步取证 → Agentic Retrieval
9.2 LLM Wiki 与 RAG 的组合
LLM Wiki 可以作为知识导航和解释层,但不能替代原始事实来源:
原始资料层:权威原文
→ Wiki层:主题摘要、目录和关系链接
→ 检索层:关键词、向量或混合检索
→ 生成层:带引用回答
高风险场景中的最终结论必须回到原始资料,不能只引用 Wiki 的二次总结。
10. 推荐实施路线
第1步:准备小规模数据和固定测试集
第2步:建立BM25基线
第3步:在固定数据库上对比Embedding模型
第4步:根据规模、过滤、延迟和运维约束选择检索后端
第5步:增加向量检索并与BM25对比
第6步:实现混合检索与去重
第7步:实现结构化生成和引用验证
第8步:建立检索、生成和系统Eval
第9步:根据失败类型增加Reranker或查询改写
第10步:确有多步检索需求时再增加Agent
学习和工程优先级:
数据质量
> Eval测试集
> 关键词和混合检索
> 引用验证
> 上下文工程
> 权限与版本管理
> Agentic Retrieval
> 更换向量数据库
RAG 的真正能力不在于接入了哪个向量数据库,而在于能否回答:
- 为什么使用这种分块策略?
- 为什么选择这个向量数据库,它解决了哪些明确约束?
- 对比过哪些Embedding,使用了什么数据集和指标?
- 正确资料是否进入 Top-K?
- 为什么选择关键词、向量或混合检索?
- Reranker 带来了多少提升和延迟?
- 旧版本和无权限文档如何过滤?
- 回答中的引用是否真实支持结论?
- 检索不足时系统能否正确拒答?
- 每次优化是否通过固定测试集得到验证?
只有这些问题可观测、可评估、可复现时,RAG 系统才从演示进入工程阶段。