跳到主要内容

RAG 工程化

RAG,即 Retrieval-Augmented Generation,检索增强生成。它的核心思路是:回答前先从外部知识源检索相关内容,再让模型基于这些证据生成答案。

一句话概括:

RAG 工程化不是“接一个向量库”,而是把文档、索引、检索、重排、上下文、引用、权限和评估连成可靠链路。

典型链路如下:

文档采集
-> 文档解析
-> 清洗和结构化
-> chunk 切分
-> embedding
-> 建索引
-> 查询改写
-> 检索
-> rerank
-> 上下文拼接
-> 生成答案
-> 引用溯源
-> 日志和评估

RAG 适合解决什么问题

RAG 适合把外部知识接入模型,尤其是:

  • 企业内部知识库问答。
  • 产品文档问答。
  • 制度、流程、手册查询。
  • 客服知识库。
  • 技术支持。
  • 法规和合同辅助阅读。
  • 需要引用来源的回答。
  • 频繁更新、不能只依赖模型参数记忆的知识。

RAG 不适合所有问题。以下场景可能需要别的方案:

  • 需要严格事务操作:更适合工具调用或业务 API。
  • 需要复杂数值计算:更适合计算工具。
  • 需要跨大量记录聚合统计:更适合数据库查询或分析引擎。
  • 需要稳定遵循内部风格:可能需要 prompt、微调或模板。
  • 文档本身质量很差:先做知识治理比直接做 RAG 更重要。

文档解析

RAG 的上限很大程度取决于文档解析质量。模型只能使用进入索引的内容,解析阶段丢失的信息通常很难在生成阶段补回来。

常见文档来源:

  • Markdown。
  • HTML。
  • PDF。
  • Word。
  • Excel。
  • PPT。
  • Wiki。
  • 数据库记录。
  • 工单和客服对话。
  • 图片和扫描件。

解析要保留:

  • 标题层级。
  • 段落。
  • 表格。
  • 列表。
  • 代码块。
  • 图片说明。
  • 页码或章节位置。
  • 文档 ID。
  • 更新时间。
  • 权限信息。

PDF 和表格是常见难点。PDF 可能存在分栏、页眉页脚、脚注、扫描 OCR、跨页表格;表格则可能需要保留行列关系,而不是简单拼成一段文本。


清洗和结构化

进入索引前通常需要清洗。

常见处理包括:

  • 去除重复页眉页脚。
  • 去除导航栏、版权区和无关链接。
  • 统一空格和换行。
  • 保留标题层级。
  • 修正 OCR 错字。
  • 拆分超大表格。
  • 过滤低质量或过期文档。
  • 提取 metadata。

metadata 很重要,常见字段包括:

  • doc_id
  • title
  • source
  • url
  • section
  • page
  • updated_at
  • owner
  • language
  • permission
  • version

没有 metadata,就很难做过滤、引用、权限和增量更新。


Chunk 策略

chunk 是 RAG 的基本检索单元。切得太碎会丢语义,切得太大又会降低召回精度并浪费上下文。

常见切分方式:

策略特点适合场景
固定长度简单稳定文本结构弱、快速原型
按标题切分保留章节语义文档结构清晰
按段落切分易读、边界自然知识库文章
语义切分尽量保持语义完整长文、复杂说明
表格行块保留行列关系价格表、配置表、清单
Parent-child小块检索,大块回填长文档和章节型内容

常见参数:

  • chunk size。
  • overlap。
  • 最大标题深度。
  • 是否保留父标题。
  • 是否保留前后文。
  • 是否按文档类型使用不同策略。

建议每个 chunk 都带上标题路径。例如:

产品手册 > 安装部署 > GPU 要求

这样即使正文很短,检索和生成也能获得上下文语义。


Embedding 模型选择

embedding 模型负责把文本转成向量。它直接影响语义召回。

选择时要看:

  • 中文、英文或多语言能力。
  • 领域词汇覆盖。
  • 长文本编码能力。
  • 向量维度。
  • 推理速度。
  • 成本。
  • 是否支持 query / document 不同指令。
  • 是否和 rerank 模型配合良好。

embedding 模型切换后必须重建索引。不同 embedding 空间不能混用,否则向量相似度没有意义。

还要注意:

  • 查询和文档是否使用相同预处理。
  • 是否需要归一化向量。
  • 是否需要给 query 加检索指令。
  • 是否需要给文档加标题、metadata 或摘要。
  • 是否需要多向量表示一篇文档。

向量库和倒排索引

RAG 检索常用两类索引:

索引适合内容
向量索引语义相似、同义表达、自然语言问题
倒排索引关键词、专有名词、编号、错误码、精确匹配

向量检索擅长“意思相近”,但不擅长精确字符串。倒排索引擅长精确匹配,但不理解语义。

工程上常见组合:

向量召回 top_k
+ 关键词召回 top_k
-> 合并去重
-> rerank
-> 选择最终上下文

选择向量库时要关注:

  • 数据规模。
  • 查询延迟。
  • metadata filter 能力。
  • 更新和删除能力。
  • 多租户隔离。
  • 备份恢复。
  • 权限过滤。
  • 混合检索支持。
  • 运维复杂度。

不要只看 demo 的相似度结果。生产系统更关心更新、过滤、延迟、权限和可恢复性。


Hybrid search 通常指语义检索和关键词检索结合。

适合场景:

  • 用户输入包含产品型号、错误码、订单号。
  • 文档里有很多专有名词。
  • 用户问题和文档措辞差异很大。
  • 既需要语义召回,又不能丢精确匹配。

常见融合方法:

  • 分别取 top_k 后合并去重。
  • 按归一化分数加权。
  • 使用 RRF。
  • 先宽召回,再 rerank。

Hybrid search 的关键不是“多加一个检索器”,而是让不同召回来源互补。需要分别观察向量召回和关键词召回贡献了哪些正确结果。


Rerank

rerank 是对召回结果重新排序。召回阶段追求“别漏”,rerank 阶段追求“把最相关的放前面”。

rerank 可以使用:

  • cross-encoder reranker。
  • LLM rerank。
  • 规则特征。
  • metadata 加权。
  • 时间衰减。
  • 权限和业务优先级。

rerank 常见收益:

  • 提升首条命中率。
  • 降低无关 chunk 进入上下文的概率。
  • 让最终 prompt 更短。
  • 提高引用准确性。

但 rerank 也会增加延迟和成本。工程上通常先召回较多候选,再 rerank 到少量片段。例如召回 50 个,重排后取 5 到 8 个进入上下文。


查询改写

用户问题不一定适合直接检索。

常见改写包括:

  • 补全省略信息。
  • 把多轮指代改成独立问题。
  • 提取关键词。
  • 生成多个检索 query。
  • 中英文术语扩展。
  • 拆分复杂问题。

示例:

用户:那它支持离线部署吗?
改写:MiniCPM-o 4.5 是否支持离线部署?

查询改写要小心引入错误前提。改写后的 query 应当保留用户原意,不能替用户补出没有依据的实体或结论。


上下文拼接

检索结果最终要拼进 prompt。拼接策略会影响答案质量。

需要考虑:

  • 放多少个 chunk。
  • 按 rerank 分数排序还是按文档顺序排序。
  • 是否合并同一文档相邻 chunk。
  • 是否保留标题路径。
  • 是否保留 metadata。
  • 是否压缩过长内容。
  • 是否区分证据和指令。
  • 多轮对话历史如何与 RAG 内容共同占用上下文。

推荐结构:

系统指令
用户问题
可用资料:
[1] 标题 / 来源 / 更新时间
内容...
[2] 标题 / 来源 / 更新时间
内容...

要求:
- 仅基于可用资料回答。
- 无资料支持时说明无法确认。
- 需要给出引用编号。

不要把检索内容混在 system prompt 里。外部文档是数据,不是更高优先级的指令。


引用和溯源

RAG 的重要价值之一是可溯源。

引用至少要能定位到:

  • 文档。
  • 章节。
  • 页码或段落。
  • chunk ID。
  • URL 或内部记录 ID。
  • 文档版本。

好的引用满足:

  • 答案中的关键结论能被引用支持。
  • 引用内容实际进入过上下文。
  • 引用粒度足够小。
  • 新旧版本不会混用。
  • 用户有权限访问引用来源。

不可靠引用包括:

  • 只在末尾列几个相关文档。
  • 引用标题但正文不支持结论。
  • 引用真实文档,却回答了文档里没有的内容。
  • 使用过期文档回答当前制度。

生产系统可以保存答案句子和证据 chunk 的映射,便于人工质检和问题回放。

相关阅读:


权限过滤

企业 RAG 必须处理权限。

权限过滤不能只在答案阶段做。正确方式是在检索前或检索时就应用权限约束。

需要确认:

  • 当前用户能访问哪些文档。
  • 文档权限是否随组织架构变化更新。
  • chunk 是否继承文档权限。
  • 搜索结果标题是否也可能泄密。
  • 引用链接是否需要二次鉴权。
  • 日志中是否会记录敏感内容。

危险做法:

先检索所有文档,再让模型不要回答无权限内容。

这会把无权限内容暴露给模型和日志,属于系统设计问题。

更好的做法:

用户身份
-> 权限过滤条件
-> 只检索可访问文档
-> 生成答案
-> 返回可访问引用

增量更新

知识库会持续变化。RAG 系统需要处理新增、修改、删除和权限变化。

增量更新要记录:

  • 文档 ID。
  • 文档版本。
  • 更新时间。
  • 内容 hash。
  • 解析状态。
  • chunk 版本。
  • embedding 版本。
  • 索引状态。

常见策略:

  • 新文档:解析、切块、向量化、入库。
  • 修改文档:删除旧 chunk 或标记失效,再写入新 chunk。
  • 删除文档:删除索引和缓存。
  • 权限变化:更新 metadata filter 相关字段。
  • embedding 模型升级:全量重建索引。

要避免新旧 chunk 同时存在。否则模型可能把过期制度和新制度混在一起回答。


RAG 评估

RAG 评估要拆开看检索和生成。

检索侧指标:

  • Recall@k。
  • MRR。
  • nDCG。
  • 首条命中率。
  • 正确文档覆盖率。
  • 权限过滤正确率。
  • 召回延迟。

生成侧指标:

  • 答案正确性。
  • 忠实性。
  • 引用准确率。
  • 拒答正确率。
  • 完整性。
  • 格式符合率。
  • 用户满意度。

评估样例应覆盖:

  • 有明确答案的问题。
  • 无答案的问题。
  • 多文档综合问题。
  • 需要精确编号的问题。
  • 过期文档和新文档冲突问题。
  • 用户无权限问题。
  • 多轮指代问题。
  • 表格和 PDF 问题。

只评估最终答案不够。要保存:

query
改写后的 query
召回结果
rerank 结果
进入 prompt 的上下文
最终答案
引用
人工标注

这样才能定位是解析错、没召回、排错序、拼接错,还是模型生成错。


常见幻觉和召回问题

问题常见原因
答案编造没检索到证据,prompt 又要求必须回答
引用不支持结论模型生成时没有严格绑定证据
正确文档没召回chunk 切分差、embedding 不匹配、关键词缺失
召回很多无关内容query 太泛、top_k 太大、缺少 rerank
旧制度和新制度混用增量更新和版本管理不完整
用户看到无权限内容检索前没有权限过滤
表格问答错误表格结构被解析成普通文本
多轮追问答错查询改写没有补全指代
答案太长或跑题上下文噪音太多,prompt 约束不足

工程检查清单

  • 文档解析保留标题、表格、页码和来源。
  • chunk 策略按文档类型区分。
  • 每个 chunk 带 metadata 和权限信息。
  • embedding 模型和索引版本绑定。
  • 向量检索和关键词检索按场景组合。
  • rerank 输入和输出可观测。
  • 上下文拼接保留引用编号。
  • 外部文档不作为系统指令。
  • 无证据时允许拒答。
  • 引用能定位到具体来源。
  • 权限过滤在检索前或检索时完成。
  • 增量更新能处理删除和权限变化。
  • 评估同时覆盖检索和生成。
  • 日志能回放完整 RAG 链路。

RAG 的工程难点不在于“把资料放进 prompt”,而在于让每一次回答都能追溯到正确、最新、可访问的证据。