RAG 如何落地?从原理解释到工程实现

前言

相信你看到这篇文章的时候,已经对RAG的应用有了一个初步的概念。

无非就是先接个向量库,然后把文档灌进去,再让大模型回答。

看起来似乎很简单。

但是真做起来就会发现,实现确实很简单,但难度在于回答能不能稳定、可控、便宜,还能解释。

因为 RAG 不是一个单点能力,它更像一条链路。

向量库只是里面负责检索的一环,大模型也只是负责理解、生成和决策的一环。

真正落地时,得把数据、索引、检索、重排、提示词、权限、监控这些东西一起串起来。

这篇就按工程落地的思路来讲,不搞的过于学术,重点还是说一下应用里要怎么做,哪些地方最容易踩坑。

RAG 解决了什么问题

RAG(Retrieval-Augmented Generation),本质上是在大模型生成前,先去外部知识库里找相关内容,再把这些内容连同用户问题一起喂给模型。

它主要解决三类问题:

  1. 大模型训练数据不是实时的,知识会过期

  2. 企业内部知识不在公开训练语料里,模型本身不知道

  3. 纯靠模型参数记忆,回答不可控,也没法给出处

所以,RAG 更像是给大模型外挂一个可更新、可审计、可授权的知识层。

一个最基础的链路长这样:

flowchart LR A[用户提问] --> B[问题预处理] B --> C[向量检索 / 关键词检索] C --> D[重排 Rerank] D --> E[拼接上下文 Prompt] E --> F[大模型生成] F --> G[返回答案]

整个链路还是很清晰的,但每个节点都会影响最后的效果。

向量库的定位

不能简单把向量库理解成是存文档的,它本质上是存文档的 向量表示 + 元数据 + 检索索引

简单说,大模型看文字不是直接拿字符串做相似度比较,而是先把一段文本转成 embedding,也就是一串高维向量。语义相近的文本,在向量空间里通常也更近。向量库做的事情就是:

  • 存这些 embedding

  • 支持相似度召回

  • 根据 metadata 做过滤

  • 在大规模数据下把检索速度压到可用范围

比如一条知识片段进库后,通常不只是正文,还会带这些信息:

  • chunk_id

  • document_id

  • text

  • embedding

  • title

  • source

  • department

  • language

  • created_at

  • permission_tags

工程上一般会把它理解成下面这层:

flowchart TD A[原始文档] --> B[清洗 / 切分 Chunk] B --> C[Embedding 模型向量化] C --> D[写入向量库] D --> D1[向量索引] D --> D2[文本内容] D --> D3[元数据 Metadata] D --> D4[权限标签]

所以不能把向量库当成数据库替代品。它通常是知识检索层,不是业务主存储。

RAG 最常见的落地场景

实际最容易落地的,不是那种全知全能问答的应用,而是边界清晰的知识密集型场景。

1. 企业知识库问答

比如 SOP、产品文档、制度规范、项目资料、FAQ。

这是最典型的场景,因为知识更新频繁,而且答案最好能带出处。

2. 客服和售后助手

不是让模型自由发挥,而是让它优先依据产品说明、工单知识、退换货规则来回答。

这样能减少幻觉,也方便做合规约束。

3. 内部 Copilot

比如给销售、运营、法务、HR 做文档助手。

用户不是问泛知识,而是问"我们公司的规则是什么""这个合同模板怎么写""上个版本变更了什么"。

4. 搜索增强

先做智能检索、摘要、相关推荐,再来进行回答。

5. 多文档总结和对比

比如"把这 20 份招标文件的差异总结一下"。

这类场景往往不是一次召回,而是分阶段检索、聚合、归纳。

真正落地时,系统一般长什么样

一个能上线的 RAG 系统,通常会拆成离线和在线两部分。

离线部分,负责建库

离线做的是把知识准备好,核心是数据治理。

flowchart LR A[各种数据源
PDF Word Excel Wiki DB] --> B[解析提取] B --> C[清洗标准化] C --> D[Chunk 切分] D --> E[Embedding] E --> F[向量库入库] C --> G[关键词索引] C --> H[权限和元数据写入]

这里面最容易被低估的是解析提取和切分。

很多时候出现问题,往往不是模型不行,而是原始数据质量太差,切分又很粗糙,结果召回出来的内容天然就答不准。

在线部分,负责检索和生成

sequenceDiagram participant U as 用户 participant App as 应用服务 participant VS as 向量库 participant RR as 重排模型 participant LLM as 大模型 U->>App: 提问 App->>App: Query 改写/意图识别 App->>VS: 检索 TopK 候选片段 VS-->>App: 返回候选片段 App->>RR: 重排 RR-->>App: 返回更相关片段 App->>LLM: 问题 + 检索上下文 + 指令 LLM-->>App: 生成答案 App-->>U: 答案 + 引用来源

这个链路里,向量库负责召回,大模型负责理解和生成,中间常常还要插一个 rerank 模型,不然 TopK 召回出来一堆相关但不够准的内容,最后还是会把模型带偏。

怎么把向量库和大模型结合起来

这里建议别把大模型和向量库看成两个平行组件,而是看成分工明确的两个层。

向量库负责找材料

它擅长的是:

"从大规模知识里,先捞一批大概率相关的内容出来。"

比如用户问:

新员工试用期转正流程是什么,审批节点有哪些?

向量库会去找跟"试用期""转正""审批""流程"语义接近的片段。

但它不擅长:

  • 判断哪些内容真正最关键

  • 跨多个片段做归纳

  • 识别冲突信息时做解释

  • 用自然语言组织成最终答案

大模型负责理解、归纳、生成

模型拿到检索结果后,做的是:

  • 读懂问题真实意图

  • 结合多个片段拼出完整答案

  • 识别缺失和歧义

  • 按指定格式输出

  • 给出引用和不确定性提示

所以一个靠谱的组合方式是这样:

  1. 先把检索做准

  2. 再把上下文组织好

  3. 最后约束模型只基于给定材料回答

比如这样的流程:

flowchart LR A[知识文档] --> B[Embedding 模型] B --> C[向量库] D[用户问题] --> E[Query Embedding] E --> C C --> F[召回片段] D --> G[Prompt 模板] F --> G G --> H[大模型] H --> I[答案 + 引用]

最关键的步骤:数据切分

RAG 能不能做好,主要还是看切分出来的chunk。

为什么要切分

因为大模型和 embedding 模型都不适合直接处理超长全文。

一篇几万字的文档,如果整篇进 embedding,语义会被摊薄,检索基本不准。

所以要把文档拆成适合检索的片段。

切分怎么做更靠谱

工程上常见几种方法:

1. 固定长度切分

比如每 500 字一个 chunk,重叠 100 字。

优点是实现简单,适合快速起步。

缺点也明显,容易把完整语义切断。

2. 按结构切分

按标题、章节、小节、表格、段落来切。

这个通常比固定长度更好,因为更接近人类理解结构。

3. 语义切分

根据内容语义边界切。

效果可能最好,但实现复杂,成本也高。

推荐

一般应用里比较实用的方式是:先按文档结构切,再做长度约束和少量重叠

比如:

  • FAQ:按问答对切

  • 产品说明书:按标题层级切

  • 制度文档:按条款切

  • API 文档:按接口定义切

  • 工单记录:按会话轮次或事件切

Chunk 太大和太小都不行

  • 太大,召回虽相关,但噪声多,模型不容易抓重点

  • 太小,信息不完整,模型拼不出答案

通常要在语义完整性和检索精度之间找平衡。

很多业务里,一段 chunk 控制在几百字到一千字左右,会比较常见,但没有绝对标准,得看文档类型和模型上下文长度。

只做向量检索还不够

很多人刚开始做 RAG,会直接上"用户问题 embedding -> 向量库 topK"。这能跑,但效果通常不稳定。

因为实际查询并不总是适合纯语义检索。

举个例子:

2024 版合同模板里,第 7.2 条关于违约责任怎么写的?

这里既有语义信息,也有强关键词信息,比如"2024""7.2""违约责任"。

如果只做向量检索,数字、专有名词、版本号可能抓不准。

所以更常见的落地方案是混合检索

flowchart TD A[用户问题] --> B1[向量检索] A --> B2[关键词检索 BM25] B1 --> C[候选集合合并] B2 --> C C --> D[重排模型 Rerank] D --> E[TopN 上下文] E --> F[大模型回答]

好处都不用讲,一看就明白了:

  • 向量检索补语义召回

  • 关键词检索补精确匹配

  • 重排模型把最终相关性再拉一遍

成熟的业务里,这套比单纯向量检索稳定很多。

Query 改写,往往比换向量库更有效

还有些线上用户的问题,经常不适合直接检索。

比如用户问:

这个流程卡住了怎么办?

这种问题太口语了,检索几乎没法直接命中。

要先做 query rewrite,把它改成更适合检索的表达。

比如系统先识别上下文,再改写为:

采购审批流程在财务复核节点卡住时的处理办法

这个动作可以让召回质量明显提升。

常见做法有三种:

1. 规则改写

适合业务固定、领域词清楚的场景。

比如把"转正""晋升""审批流"这些口语映射到系统标准词。

2. 用小模型或大模型做改写

让模型先把用户问题重写成检索友好版本。

这招对复杂问法很有用,但要注意别改偏。

3. 多路查询

同一个问题生成多个查询版本,同时去检索,再合并结果。

比如原始 query、关键词 query、扩展 query 一起用。

Prompt 怎么设计,决定大模型会不会胡说

很多团队已经把检索做的很可以了,但模型最后还是会产生臆造行为。

根本原因还是使用的 prompt 没约束住模型的行为。

一个常见的 RAG Prompt,核心要包含这几块:

  • 系统角色

  • 回答边界

  • 检索到的上下文

  • 用户问题

  • 输出格式要求

  • 找不到答案时的处理方式

举个比较简单的例子,可以抽象成这样:

text 复制代码
你是企业知识助手,请只根据提供的参考资料回答问题。
如果参考资料里没有足够信息,请明确说"现有资料不足以回答该问题"。
不要编造制度、流程、时间、责任人。

参考资料:
{{retrieved_context}}

用户问题:
{{question}}

输出要求:
1. 先直接回答
2. 再列出依据来源
3. 如果存在不确定或版本冲突,明确指出

这类 prompt 的目的就是让模型能够严格遵守我们制定的规则。

线上要稳定,还得加一层治理

真正上线后,不能只停在检索 + 生成,通常还会再做一层治理。

1. 引用校验

回答里每条关键结论最好能映射到具体 chunk。

这样用户能点回原文,系统也更容易做追责和调试。

2. 置信度判断

如果召回得分很低,或者重排后相关片段仍然松散,就别硬答。

宁可返回"没找到足够依据",也别让模型瞎补。

3. 敏感内容过滤

比如法务、医疗、金融场景,不能让模型越权输出建议。

需要在回答前后都做好检查。

4. 权限控制

这点非常重要。

不是所有用户都能看到所有知识。检索前就要带权限过滤,不能等生成后再去做裁剪。

flowchart LR A[用户请求] --> B[身份识别] B --> C[权限过滤] C --> D[检索] D --> E[重排] E --> F[生成] F --> G[安全检查] G --> H[最终答案]

更实用的技术架构

目前主流的工程方案,常见的是下面这个思路:

数据层

  • 原始文档存对象存储或业务库

  • 元数据单独存关系库或文档库

  • 向量存向量数据库

  • 关键词索引放搜索引擎

模型层

  • Embedding 模型:负责文本向量化

  • Rerank 模型:负责候选重排

  • LLM:负责回答、总结、改写

服务层

  • 文档解析服务

  • 索引构建服务

  • 在线检索服务

  • Prompt 编排服务

  • 监控与评估服务

应用层

  • Web/IM/客服工作台

  • API 接口

  • 后台管理和知识运营台

示例

当然绝对不是千篇一律,必须得根据实际的业务场景做取舍和改造。

以我之前参与过的一个企业内部知识库为例,完整的工程栈如下:

RAG 要如何评估

很多团队光跑了个demo,感觉差不多就行了,结果一上线就光速翻车。

所以demo做好之后,还得做好评估工作。

至少得拆成三层。

第一层,检索对不对

看的是:

  • 是否召回了真正相关的 chunk

  • TopK 里噪声多不多

  • 权限过滤有没有生效

  • 召回延迟能不能接受

第二层,生成对不对

看的是:

  • 是否忠于检索内容

  • 有没有幻觉

  • 是否遗漏关键信息

  • 是否有错误归纳

第三层,业务上有没有价值

看的是:

  • 是否提高了解答效率

  • 是否降低了人工转接率

  • 是否减少重复搜索

  • 用户是否愿意继续用

评估链路

所以评估链路最好拆开:

flowchart LR A[用户问题集] --> B[检索评估] A --> C[生成评估] A --> D[业务指标评估] B --> B1[Recall K] B --> B2[MRR / nDCG] C --> C1[Faithfulness] C --> C2[Answer Correctness] D --> D1[解决率] D --> D2[人工接管率]
Recall@K

表示在前 K 个检索结果中,找回了多少相关结果。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> R e c a l l @ K = 前K个结果中命中的相关项数量 全部相关项数量 \mathrm{Recall@K}=\frac{\text{前K个结果中命中的相关项数量}}{\text{全部相关项数量}} </math>Recall@K=全部相关项数量前K个结果中命中的相关项数量

MRR(Mean Reciprocal Rank)

表示第一个正确结果排名倒数的平均值。

单个样本:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> R R = 1 第一个相关结果的排名 \mathrm{RR}=\frac{1}{\text{第一个相关结果的排名}} </math>RR=第一个相关结果的排名1

整体平均:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> M R R = 1 样本总数 ∑ i = 1 N 1 第i个样本中第一个相关结果的排名 \mathrm{MRR}=\frac{1}{\text{样本总数}}\sum_{i=1}^{N}\frac{1}{\text{第i个样本中第一个相关结果的排名}} </math>MRR=样本总数1i=1∑N第i个样本中第一个相关结果的排名1

nDCG(Normalized Discounted Cumulative Gain)

用于衡量排序结果质量,考虑相关性分级和排序位置折损。

DCG@K

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> D C G @ K = ∑ i = 1 K 2 第i个结果的相关性分数 − 1 log ⁡ 2 ( i + 1 ) \mathrm{DCG@K}=\sum_{i=1}^{K}\frac{2^{\text{第i个结果的相关性分数}}-1}{\log_2(i+1)} </math>DCG@K=i=1∑Klog2(i+1)2第i个结果的相关性分数−1

IDCG@K

表示理想排序下的 DCG:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> I D C G @ K = ∑ i = 1 K 2 第i个理想结果的相关性分数 − 1 log ⁡ 2 ( i + 1 ) \mathrm{IDCG@K}=\sum_{i=1}^{K}\frac{2^{\text{第i个理想结果的相关性分数}}-1}{\log_2(i+1)} </math>IDCG@K=i=1∑Klog2(i+1)2第i个理想结果的相关性分数−1

nDCG@K

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> n D C G @ K = 实际排序的DCG@K 理想排序的IDCG@K \mathrm{nDCG@K}=\frac{\text{实际排序的DCG@K}}{\text{理想排序的IDCG@K}} </math>nDCG@K=理想排序的IDCG@K实际排序的DCG@K

Faithfulness

Faithfulness 常见做法是看回答中的陈述有多少能被证据支持。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> F a i t h f u l n e s s = 被证据支持的陈述数量 回答中的总陈述数量 \mathrm{Faithfulness}=\frac{\text{被证据支持的陈述数量}}{\text{回答中的总陈述数量}} </math>Faithfulness=回答中的总陈述数量被证据支持的陈述数量

Answer Correctness

Answer Correctness 通常没有唯一固定公式,常写成:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> A n s w e r C o r r e c t n e s s = f ( 模型回答 , 标准答案 ) \mathrm{Answer\ Correctness}=f(\text{模型回答},\text{标准答案}) </math>Answer Correctness=f(模型回答,标准答案)

如果采用简单准确率,可以写成:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> A c c u r a c y = 回答正确的样本数量 总样本数量 \mathrm{Accuracy}=\frac{\text{回答正确的样本数量}}{\text{总样本数量}} </math>Accuracy=总样本数量回答正确的样本数量

解决率

表示问题最终被成功解决的比例。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> R e s o l u t i o n R a t e = 已解决的问题数量 问题总数量 \mathrm{Resolution\ Rate}=\frac{\text{已解决的问题数量}}{\text{问题总数量}} </math>Resolution Rate=问题总数量已解决的问题数量

人工接管率

表示需要转人工处理的比例。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> H a n d o f f R a t e = 转人工处理的问题数量 问题总数量 \mathrm{Handoff\ Rate}=\frac{\text{转人工处理的问题数量}}{\text{问题总数量}} </math>Handoff Rate=问题总数量转人工处理的问题数量

容易踩的坑

坑 1:文档一股脑灌进去

没做清洗、去重、版本管理,最后库里一堆旧文档、重复文档、废弃版本。

检索命中后,模型很容易回答出过期内容。

所以版本控制一定要有,至少要能识别:

  • 最新版

  • 已废弃

  • 草稿

  • 正式生效

坑 2:只看召回,不看上下文拼装

检索出 10 个 chunk,不代表全都该塞给模型。

上下文太长,噪声太多,模型一样会答歪。

通常要做:

  • 去重

  • 合并相邻 chunk

  • 按来源分组

  • 控制 token 长度

  • 保留最关键引用

坑 3:对所有问题都走同一条链路

不是所有问题都该进 RAG。

有些是闲聊,有些是计算,有些是事务操作,有些是数据库查询。

更好的做法是先做好意图识别。

flowchart TD A[用户问题] --> B{意图识别} B -->|知识问答| C[RAG] B -->|闲聊| D[直接 LLM] B -->|查数据库| E[结构化查询] B -->|执行任务| F[Agent / Workflow]

坑 4:过度相信更强大的模型

如果召回错了、chunk 错了、版本错了,模型再强也只是更流畅地说错话。

RAG 的下限很多时候由检索决定,上限才由模型决定。

坑 5:忽略成本

线上最容易炸的是 token 成本和检索延迟。

成本主要来自三块:

  • embedding 建库成本

  • 向量检索和 rerank 成本

  • LLM 上下文和生成成本

所以常见优化手段有:

  • 离线预处理,减少在线计算

  • 控制 chunk 数量和长度

  • 小模型做 query rewrite / 意图识别

  • 高成本模型只处理复杂问题

  • 热问题做缓存

上线路径

如果是从 0 到 1 做,不建议一开始就搞特别复杂。

一般可以按这个顺序来。

第一步,先做一个最小闭环

  • 选一个垂直场景

  • 选一批高质量文档

  • 做基础切分和建库

  • 用向量检索 + 大模型回答

  • 答案带引用

先把链路跑通,别一上来追求全公司通用。

第二步,补混合检索和重排

当你发现能输出回答,但是不稳定时,优先补这两块。

这通常比盲目换大模型更有价值。

第三步,补评估和监控

至少要知道:

  • 哪些问题没召回到

  • 哪些答案有幻觉

  • 哪些知识源最常出错

  • 哪些 query 最影响体验

第四步,补权限、版本、运营后台

一旦从 demo 走向生产,这些东西都是硬要求,没啥好说的。

一套更接近生产环境的闭环

最后可以把整个 RAG 系统理解成这样一个闭环:

flowchart LR A[知识采集] --> B[清洗切分] B --> C[Embedding / 索引构建] C --> D[向量库 / 搜索引擎] D --> E[在线检索] E --> F[重排与上下文构建] F --> G[大模型生成] G --> H[答案反馈与日志] H --> I[评估与优化] I --> B I --> E I --> F

最重要的是,这是一项持续优化的工程。

线上问答日志、未命中问题、低分反馈,都会反过来推动 chunk 策略、query 改写、知识运营和 prompt 调整。

最后

万字长文。

不想被 AI 淘汰,就要学会利用 AI 。

与君共勉。

相关推荐
AI营销快线1 小时前
AI营销获客难?原圈科技深度解析SaaS系统增长之道
大数据·人工智能
南滑散修2 小时前
机器学习(四):混合高斯模型GMM
人工智能·机器学习
卷心菜投手ovo2 小时前
一个页面支持自定义字段,后端该怎么设计数据库?
后端
隔壁家滴怪蜀黍2 小时前
AgentScope MsgHub 多智能体通信机制详解
后端
孟陬2 小时前
国外技术周刊 #3:“最差程序员”带动高效团队、不写代码的创业导师如何毁掉创新…
前端·后端·设计模式
柯儿的天空2 小时前
Mem0深度解析:给你的ai agent加上长期记忆,让ai从“健忘“到“过目不忘“
人工智能·gpt·自然语言处理·ai作画·aigc·ai编程·agi
FluxMelodySun2 小时前
机器学习(二十五) 降维:主成分分析(PCA)及特征值分解
人工智能·算法·机器学习
Cosolar2 小时前
Transformer训练与生成背后的数学基础
人工智能·后端·开源