Agent AtlasAI Agent 知识库
04.02记忆与知识 · Memory & Knowledge

RAG 完整指南

从切分、Embedding、混合检索到重排与评估:一套能真正上线的检索增强生成方案。

RAG 不是「接个向量库」就完事。它是一条从文档解析到引用生成的完整管线,任何一环出问题,最终都会表现为「模型胡说」。

RAG 解决什么问题

模型不知道你的私有数据,也不知道训练截止之后的事。RAG(Retrieval-Augmented Generation)在生成前检索相关资料,放入上下文,让回答有依据。

它主要解决:

  • 知识不在模型里:内部文档、产品手册、工单记录;
  • 知识会过期:价格、政策、库存;
  • 需要引用:用户要看到来源;
  • 降低幻觉:给模型可核对的事实。

它不解决:

  • 检索不到的问题(覆盖不足);
  • 文档本身错误;
  • 需要复杂推理而非查找的问题;
  • 权限与数据隔离(要单独设计)。

完整管线

图 1 · RAG 的两个阶段
解析PDF/HTML/表格
清洗、分节
→
切分语义块 + 元数据
→
索引Embedding + 倒排
→
检索混合召回 + 过滤
→
重排精细排序 top-k
→
生成带引用回答
前三个是离线索引,后三个是在线查询。多数团队把 80% 精力花在换向量库,却忽略了切分与评估。

1. 解析:先把文档读对

PDF 是最容易出问题的格式:

  • 双栏排版被读成一行;
  • 表格变成乱序文本;
  • 页眉页脚混入正文;
  • 扫描件没有文本层;
  • 图表标题与正文分离。

处理策略:

文档类型建议
原生 PDF用布局感知解析器,保留标题层级与表格
扫描 PDFOCR,必要时人工校对关键页
HTML去导航/广告,保留正文与标题
Markdown按标题分节,保留代码块
表格单独抽取为结构化行,不要混进段落
图片图表用多模态模型生成描述,再索引

解析质量决定上限。如果一段政策被读成「退款需在 7 天内申请」和「退款需在 7 天内」,后者会直接导致错误回答。

2. 切分:语义完整优先

固定长度切分会切断论证。更稳的做法是结构优先、长度兜底:

  1. 按标题层级切(H1 → H2 → H3);
  2. 段落超过上限时再按句子切;
  3. 保留 10%–15% 重叠,避免边界丢上下文;
  4. 每块带元数据:文档 ID、标题路径、页码、版本、日期、权限标签。

经验参数:

内容chunk 大小重叠
政策/合同400–800 token10%–15%
技术文档300–600 token10%
对话/工单按轮次或问题切1 轮
表格一行或一组行表头重复

父子块(parent-child) 很实用:用小块做检索(精度高),命中后返回它所属的大块(上下文完整)。

python
def chunk_by_heading(doc, max_tokens=600, overlap=80):
    sections = split_by_headings(doc)  # 保留标题路径
    chunks = []
    for sec in sections:
        for piece in split_sentences(sec.text, max_tokens, overlap):
            chunks.append({
                "text": piece,
                "doc_id": doc.id,
                "title_path": sec.path,       # 例如:退款政策 > 时效
                "page": sec.page,
                "version": doc.version,
                "updated_at": doc.updated_at,
                "acl": doc.acl,
            })
    return chunks
不要只存正文

没有标题路径和页码,模型无法引用,用户无法核对,过滤也无法按版本/权限做。元数据不是可选项。

3. Embedding:选型与注意点

选择 Embedding 模型时看:

  • 语言:中文/多语言能力;
  • 领域:法律、医疗、代码可能需要领域模型;
  • 维度:768–3072,越高越贵;
  • 最大输入长度:要覆盖你的 chunk;
  • 是否支持指令/查询前缀:有些模型要求 query: / passage: 前缀;
  • 部署方式:API 还是本地。

实践要点:

  • 文档与查询必须用同一个模型;
  • 向量做归一化,使用余弦相似度;
  • 换模型要全量重建索引;
  • 记录模型版本,便于回溯;
  • 对专有名词、编号,向量检索不可靠,必须配合关键词检索。

4. 索引:向量库只是其中一部分

组件作用常见选择
向量索引语义召回pgvector、Qdrant、Milvus、Pinecone
倒排索引关键词/BM25Elasticsearch、OpenSearch、Postgres FTS
元数据存储过滤、权限、版本PostgreSQL
对象存储原文、附件S3 / R2

选型看四件事:过滤能力、更新成本、混合检索支持、运维复杂度。小规模(< 100 万块)用 pgvector 往往最省事;需要强过滤与大规模并发再考虑专用向量库。

5. 检索:召回阶段要「宁多勿漏」

稠密 + 稀疏

  • 稠密:语义相似,擅长「意思相近但用词不同」;
  • 稀疏(BM25):精确匹配,擅长编号、专有名词、代码;
  • 混合:两路各取 top 20–50,融合排序。

常用的融合算法是 RRF(Reciprocal Rank Fusion):

python
def rrf(rank_lists, k=60):
    scores = {}
    for ranks in rank_lists:
        for rank, doc_id in enumerate(ranks, start=1):
            scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
    return sorted(scores, key=scores.get, reverse=True)

RRF 不需要两路分数同尺度,简单稳定。

过滤与多样性

  • 按租户、权限、版本、日期过滤(在检索前做,避免越权);
  • 用 MMR 或相似度阈值去重,避免返回 5 段几乎一样的内容;
  • 多文档问题时,保证每个来源至少有一条。

top-k 怎么定

召回阶段取 20–50 条,重排后取 4–8 条进入上下文。太少容易漏,太多会稀释注意力并增加成本。

6. 重排:把「相关」变成「最相关」

向量相似度是粗排。Cross-encoder 或 LLM 重排能看到查询与文档的交互,精度更高。

text
召回 50 条 → 重排模型打分 → 取 top 6 → 组装上下文

代价是延迟。常见折中:

  • 只对 top 20 重排;
  • 用小型重排模型;
  • 对简单查询跳过重排;
  • 缓存相同查询的结果。

7. 组装上下文:顺序、引用、预算

  • 按相关性排序,但把最关键的来源放在开头或结尾(中间容易被忽略);
  • 每段加编号与来源:[1] 退款政策 v3 · p.2;
  • 去重、截断,控制在预算内;
  • 明确告诉模型:「只根据以下资料回答;资料不足时说不知道」;
  • 要求每个事实性陈述标注引用编号。
text
[1] 来源:退款政策 v3 · 第 2 页 · 2026-08-01
退款申请需在签收后 7 天内提交,商品需保持完好。

[2] 来源:FAQ · 退款时效
审核通过后 1–3 个工作日原路退回。

8. 生成:允许说不知道

RAG 的提示词要明确:

text
只使用提供的资料回答。
每个事实性陈述后标注来源编号,如 [1]。
如果资料不足以回答,输出「根据现有资料无法确定」,并说明缺什么。
不要使用资料之外的知识补充细节。

同时做输出校验:

  • 引用编号是否存在于本次上下文;
  • 引用的段落是否真的支持该结论(可用小模型校验);
  • 是否出现资料中没有的数字/日期。

评估:分开看检索与生成

层级指标说明
检索Recall@k正确文档是否在 top-k 中
检索MRR / nDCG正确文档排得有多靠前
检索覆盖率测试问题是否都有对应文档
生成Faithfulness回答是否忠于资料
生成Answer Relevance是否回答了问题
生成Citation Accuracy引用是否支持结论
端到端通过率人工或 LLM 评审
运维P95 延迟、单次成本检索 + 重排 + 生成

一个实用做法:先构造 50 条「问题 + 正确文档 ID + 标准答案」,分别测检索召回和生成质量。如果召回率只有 60%,再怎么调提示词也没用。

失败模式对照

现象根因修复
答「资料中没有」但明明有召回失败 / 过滤过严检查 Recall@k,放宽过滤,加关键词检索
引用了不支持的段落重排差 / 提示不严加强重排,要求逐句引用,做引用校验
答案混入旧版本版本过滤缺失元数据加版本,检索时只取有效版本
多文档问题只答一半top-k 太少 / 去重过度增加多样性,按来源配额
表格数字错解析错乱表格结构化抽取,单独索引
延迟高重排 + 大模型 + 多轮缓存、缩小 top-k、模型路由
越权检索过滤在生成后检索前按 ACL 过滤

成本与延迟预算

一个可参考的预算:

text
查询改写        100–300 ms
向量检索        20–100 ms
关键词检索      10–50 ms
重排(20 条)   100–400 ms
生成(流式)    首 token 0.5–2 s
--------------------------------
总计            1–3 s(不含生成完)

如果要求 1 秒内,考虑:缓存、跳过重排、小模型、预检索。

一个最小技术栈

text
解析:unstructured / pymupdf4llm / Docling
切分:自定义(按标题 + 句子)
Embedding:多语言模型(按评测选)
索引:PostgreSQL + pgvector + 全文索引
重排:bge-reranker / Cohere Rerank / LLM
生成:通用模型 + 引用提示
评估:Ragas / 自建脚本 + 人工抽检

不要一上来就上复杂架构。先用 100 份文档 + 50 条问题跑通,再根据失败案例加组件。

检查清单

  • 解析保留标题、页码、表格结构
  • 切分语义完整,带元数据与重叠
  • 文档与查询使用同一 Embedding 模型
  • 混合检索(向量 + 关键词)
  • 检索前做权限/版本/租户过滤
  • 有重排,top-k 合理
  • 上下文带编号与来源
  • 提示要求引用、允许说不知道
  • 分别评估检索与生成
  • 有延迟与成本预算

小结

  • RAG 是完整管线,解析与切分决定上限。
  • 召回宁多勿漏,重排负责精度,组装负责可读与可引用。
  • 检索和生成要分开评估,先看 Recall@k。
  • 权限过滤必须在检索前。
  • 先用小规模跑通,再按失败案例加复杂度。

下一篇处理更难的查询:一次检索不够时,Agent 如何自己决定检索什么。

读完了?进度会保存在本地浏览器。
内容会持续修订。发现错误或想补充,欢迎反馈。 遇到问题?查排查手册 →