直接结论
**RAG 与微调改变的是大模型系统的不同层。**检索增强生成改变每次请求可见的证据,微调改变模型权重或适配器,长上下文则改变一次请求能直接携带多少证据。三者都不是通用赢家,也都不能保证事实正确。
应从能通过冻结评测的最小改动开始:
- 语料边界明确、仍在探索工作流时,先测 Prompt 或长上下文;
- 知识量更大、独立于模型更新、需要权限控制或引用时,再加 RAG;
- Prompt 与 Schema 优化后仍反复出现同类行为错误,再评测微调;
- 只有组合方案分别解决了可测量的故障,才同时使用 RAG 与微调。
原始 RAG 论文把参数化记忆与非参数化记忆结合,并在论文评测的知识密集型任务上取得改进,但它没有证明检索能消除幻觉。LoRA 论文展示了参数高效适配方法;其中参数量与显存结论来自特定模型和训练设置,不能直接推广到所有微调任务。
三种方案分别改变什么
长上下文改变请求
长上下文把来源材料直接放进请求,不需要检索索引,也不更新模型权重。它适合一次性分析、小而稳定的语料和早期原型,因为证据路径比较容易检查。
它的故障边界在注意力和上下文组织。材料即使装得进标称窗口,模型仍可能忽略、混淆或错误加权其中的段落。Lost in the Middle 研究在多文档问答与键值检索实验中观察到位置敏感的性能下降。这个结果应作为评测警告,而不是当前所有模型的固定阈值。
RAG 改变推理时证据
RAG 通常把离线知识路径与在线回答路径分开:
模型本身没有变化。“更新来源”也不天然等于“立即生效”:摄取、解析、索引、缓存失效与删除传播都完成后,服务版本才算新鲜。引用同样不会自动正确,系统必须保存稳定来源标识,并验证论断是否真的得到引用片段支持。
微调改变模型行为
微调用样本、偏好或奖励优化参数。监督微调可改善分类、语气、格式、术语或重复性的工具调用行为;LoRA 等参数高效方法则冻结基座模型,只训练较小的适配参数。
如果事实需要单独纠正、引用、鉴权或删除,模型权重不是合适的权威存储。微调也不能让 Schema、政策或鉴权规则变成确定性约束,这些边界仍应由可信应用代码执行。
选型不只是“知识还是行为”
“RAG 管事实,微调管行为”适合作为第一层心智模型,但不足以直接做架构决策。
| 需求 | 最先评测的基线 | 可能失败的原因 | 下一项实验 |
|---|---|---|---|
| 分析一组边界明确的文档 | 长上下文 | 位置敏感、重复 Token 成本、窗口限制 | 同模型 RAG |
| 回答持续变化的来源 | RAG | 检索遗漏、索引陈旧、无证据推断 | 混合检索、重排、拒答 |
| 稳定输出格式或标签 | Prompt + 约束输出 | 长尾错误反复出现、指令过长 | 监督微调 |
| 学习领域术语 | 示例 Prompt 或术语检索 | 对未标注表达泛化不足 | 使用留出切片微调 |
| 用专业风格回答新鲜事实 | RAG 基线 | 行为仍不稳定 | RAG + 微调 |
| 执行高影响操作 | 确定性工作流 | 模型提议不等于授权 | 策略引擎与明确审批 |
微调可以改善生成器使用 RAG 证据的方式,检索也可以为微调模型提供新鲜事实。只有当组合方案在质量、延迟、成本、运维复杂度和新增失败模式都计入后,仍优于两个简单基线,混合架构才成立。
必须拆开测量的失败模式
RAG 的失败模式
RAG 至少有四个相互独立的质量边界:
- **语料覆盖:**权威答案可能缺失、过期、重复或尚未完成索引。
- **授权候选召回:**正确证据存在,但经过租户和对象过滤后没有进入候选集。
- **排序与上下文组装:**正确片段被裁掉、截断,或被高相似度噪声包围。
- **生成与归因:**模型可能违背证据、混合不同版本,或把引用挂到不支持该论断的来源上。
先测 Recall@k 或人工判定的证据覆盖率,再测答案质量。随后补充论断支持率、引用精度、拒答、延迟和成本。一个系统可以靠拒绝几乎所有请求获得极低的已发布错误率,因此风险必须与覆盖率一起报告。
微调的失败模式
微调的风险不同:
- 训练/测试泄漏会让狭窄任务看起来已经解决;
- 低质量样本会教会模型不受支持的表达和行为;
- 模型可能在调优分布之外遗忘原有能力;
- 基座模型升级可能破坏适配器兼容性或历史评测;
- 敏感样本进入权重后很难逐条识别和删除;
- Prompt 变短不等于计入训练、托管和复核后的总成本更低。
应保留未参与开发的测试集、对抗与多语言切片、基座对照和可回滚制品,并记录基座 Checkpoint、Tokenizer、数据版本、目标函数、适配器配置、随机种子策略与 Serving Runtime。
一套可逆的决策流程
把选型设计成实验,而不是路线之争。
1. 冻结任务契约
每个测试样例至少记录输入、授权来源版本、期望证据、答案验收条件、禁止出现的论断,以及拒答是否正确。用户、文档家族或时间上存在近重复时,应按实体或时间切分,不能随机拆分后制造泄漏。
2. 建立可比较基线
至少比较:
- 基座模型 + 精简 Prompt;
- 语料可容纳时的长上下文;
- 固定检索与重排配置的 RAG;
- 不带检索的微调模型;
- 只有前四项暴露互补故障时,才加入混合方案。
在实验允许范围内固定模型快照、输出契约、超时、重试与评测器。若某个候选更换了模型,必须把它标为复合变量,不能把收益全部归因给 RAG 或微调。
3. 测量整个系统
| 层级 | 有用指标 |
|---|---|
| 检索 | 授权证据 Recall@k、MRR 或 nDCG、陈旧来源率、删除传播 |
| 回答 | 任务成功率、论断支持率、引用精度、拒答质量、高影响错误率 |
| 运维 | p50/p95/p99 延迟、超时与重试率、队列深度、索引新鲜度 |
| 经济性 | 摄取、训练、存储、推理、复核与单个合格任务成本 |
| 治理 | 租户隔离、来源许可证、保留周期、适配器血缘与回滚 |
不能拿一个系统的训练账单,与另一个系统的单次 Token 账单直接比较。应按预期流量、语料规模、更新周期和 SLO,统一折算为合格业务结果。
可运行的 Go 验收门禁
下面的标准库程序把显式产品政策应用到一组模拟实验结果。数字只是测试 Fixture,不是行业基准。候选必须同时满足质量、延迟、删除和证据约束,程序才会从合格项中选择单个合格任务成本最低的方案。
package main
import (
"fmt"
"sort"
)
type Candidate struct {
Name string
TaskSuccess float64
EvidenceRecall float64
UnsupportedClaimRate float64
P95LatencyMS int
CostPerAcceptedTask float64
DeletionTestPassed bool
}
type Policy struct {
MinTaskSuccess float64
MinEvidenceRecall float64
MaxUnsupportedClaimRate float64
MaxP95LatencyMS int
}
func passes(candidate Candidate, policy Policy) bool {
return candidate.TaskSuccess >= policy.MinTaskSuccess &&
candidate.EvidenceRecall >= policy.MinEvidenceRecall &&
candidate.UnsupportedClaimRate <= policy.MaxUnsupportedClaimRate &&
candidate.P95LatencyMS <= policy.MaxP95LatencyMS &&
candidate.DeletionTestPassed
}
func main() {
policy := Policy{
MinTaskSuccess: 0.84,
MinEvidenceRecall: 0.88,
MaxUnsupportedClaimRate: 0.05,
MaxP95LatencyMS: 1200,
}
candidates := []Candidate{
{Name: "long-context", TaskSuccess: 0.82, EvidenceRecall: 0.87, UnsupportedClaimRate: 0.04, P95LatencyMS: 1500, CostPerAcceptedTask: 0.019, DeletionTestPassed: true},
{Name: "rag", TaskSuccess: 0.86, EvidenceRecall: 0.91, UnsupportedClaimRate: 0.04, P95LatencyMS: 920, CostPerAcceptedTask: 0.012, DeletionTestPassed: true},
{Name: "fine-tuned", TaskSuccess: 0.83, EvidenceRecall: 0.56, UnsupportedClaimRate: 0.09, P95LatencyMS: 410, CostPerAcceptedTask: 0.010, DeletionTestPassed: false},
{Name: "rag-plus-fine-tuning", TaskSuccess: 0.89, EvidenceRecall: 0.93, UnsupportedClaimRate: 0.03, P95LatencyMS: 1380, CostPerAcceptedTask: 0.023, DeletionTestPassed: true},
}
eligible := make([]Candidate, 0, len(candidates))
for _, candidate := range candidates {
if passes(candidate, policy) {
eligible = append(eligible, candidate)
}
}
if len(eligible) == 0 {
fmt.Println("no candidate passed; keep the current system")
return
}
sort.Slice(eligible, func(i, j int) bool {
return eligible[i].CostPerAcceptedTask < eligible[j].CostPerAcceptedTask
})
fmt.Printf("selected=%s cost_per_accepted_task=%.3f\n",
eligible[0].Name,
eligible[0].CostPerAcceptedTask,
)
}
预期输出:
selected=rag cost_per_accepted_task=0.012
请用产品真实测量值与业务政策替换 Fixture 和阈值。不能为了让偏好的架构通过,而反向降低质量门槛。
安全与生命周期边界
RAG 必须在检索前鉴权,而不是等生成后再过滤。索引应携带租户与对象范围,缓存键也要包含授权上下文,禁止未授权候选进入 Prompt、日志或重排器。检索文本仍是不可信输入,因为文档可以携带 Prompt Injection。
微调需要数据来源、许可证与同意审查、Secret 扫描、删除策略,以及从适配器到基座 Checkpoint 和训练集版本的映射。训练任务不是合法性判断;从源库删掉一行,也不能证明其影响已经从模型制品消失。
无论使用哪种方案,模型输出都只是提议。付款、发消息、账户变更、医疗编码和法律决策仍需模型之外的权威校验,以及与影响相匹配的人工复核。
常见问题
RAG 一定比微调便宜吗?
没有通用答案。RAG 增加摄取、索引、检索、重排和输入 Token;微调增加数据整理、训练、制品管理、Serving 和回归测试。请在预期流量、语料规模、更新频率与 SLO 下比较单个合格任务成本。
RAG 能保证引用正确吗?
RAG 可以保存候选来源,但生成器可能挂错引用,或提出比证据更宽泛的论断。应评测引用精度与论断级支持关系,并把“证据不足”设计为合法结果。
团队是否应该永远先做 RAG?
不是。应先做最能代表任务的简单基线。边界明确的文档可能适合长上下文;确定性分类可能不需要检索;稳定而重复的行为也可能适合微调。只有 RAG 能解决可测量的知识问题时,才值得引入。
LoRA 能达到全量微调的效果吗?
LoRA 论文在其评测模型和任务上报告了有竞争力的结果,并在 GPT-3 实验中大幅减少可训练参数。但这不能保证任意目标模块、Rank、数据集、基座模型和部署上都等价。应与基座和可行的全量微调基线比较。
什么时候应该组合 RAG 与微调?
当 RAG 已满足证据要求,却反复违反某项行为契约,而微调能稳定修复该问题;或者微调模型满足行为要求,却需要新鲜、授权的证据时,可以组合。必须保留消融实验,避免看不出每个组件的真实贡献。