RAG 的“效果好看”很容易,但一旦进入真实产品环境,工程问题会迅速出现:知识库更新后答非所问、同一问题检索结果漂移、引用无法解释来源。本文从“版本与可追溯”角度,给出一套能长期运维的 RAG 工程框架。

1. RAG 的核心风险:知识库不是静态的
RAG 常见故障并不是模型不会答,而是:
- 文档更新后检索到的片段与预期不一致
- 引用拼接策略导致 citation 不可解释
- 线上请求量上来后检索延迟飙升
因此,工程上要把“知识库状态”当作一等公民:
- 知识库版本(KB Version)
- 索引版本(Index Version)
- 检索策略版本(Retrieval Policy Version)
2. 工程管线建议:显式化每一步的输入输出
graph TD
Q[User query] --> V[Policy: select KB versions]
V --> R[Retrieve: chunks + citations]
R --> C[Cache: retrieval results]
C --> P[Prompt builder with citations]
P --> G[LLM generation]
G --> A[Answer validation: cite check]
A --> O[Observability: store retrieval trace]
2.1 KB / Index / Policy 三元组
请求每次都要带上这三元组,保证“可复现”:
- 用哪个 KB 版本
- 用哪个索引
- 检索策略是否改变(top_k、filters、reranker)
2.2 检索缓存(Retrieval Cache)
对相同 query(或相近语义)的检索结果做缓存,减少重复成本。 注意缓存粒度要与版本绑定,否则会出现“缓存污染”。
3. 引用可追溯:让“为什么这么答”可回放
建议每条回答都记录一条 retrieval trace(检索轨迹):
| Trace 字段 | 含义 |
|---|---|
| kb_version | 知识库版本 |
| index_version | 索引版本 |
| retrieval_policy | 检索策略版本 |
| retrieved_chunks | 命中的 chunk 列表 |
| citations | citation 映射到 chunk 的关系 |
当用户反馈“引用不对”,你可以: 1) 用相同 kb/index/policy 复现检索 2) 对照 prompt builder 的拼接方式 3) 再决定是修索引还是修引用策略
3.1 Trace 的结构化示例(字段可落日志)
下面给一个“概念 trace”(你可以直接用来设计日志 schema):
graph LR
Q[query] --> P[policy]
P --> KB[KB version]
KB --> I[Index version]
I --> C[retrieved chunks]
C --> A[citations mapping]
A --> T[trace stored in observability]
{
"query": "如何做RAG版本管理?",
"kb_version": "kb_2025_12_01",
"index_version": "index_2025_12_10",
"retrieval_policy": "top_k=8,rerank=true",
"retrieved_chunks": ["chunk_1024", "chunk_2033"],
"citations": [{ "chunk_id": "chunk_1024", "span": [0, 24] }],
"trace_id": "trace_9f3a2b"
}
4. Prompt builder 的工程化建议
不要把引用当作“装饰”。建议:
- 强制回答中使用引用占位符(例如
[c1]) - 在验证阶段检查:引用占位符数量与命中 chunk 数量是否匹配
下面是一个可执行的“校验思路”(伪代码):
function validate_citations(answer_text, citations):
required_tags = extract_citation_tags(answer_text)
if required_tags is empty:
return fail("no citations")
if required_tags not subset of citations:
return fail("citation mismatch")
return pass()
5. 你可以直接落地的最小版本(MVP)
如果你想先做能上线的 MVP,可以按下面步骤:
- 固化 KB 版本与索引版本的生成机制(每次发布必有版本号)
- 在日志里保存 retrieval trace(哪怕先简单保存 chunk id)
- 做一个轻量的 citation mismatch 告警(而不是等用户投诉)
- 最后再做缓存与 reranker 的灰度

6. 最后:版本管理是一种“产品承诺”
当你写下:
- “我们引用的来源是可回放的”
- “我们能复现当时的检索结果”
用户的信任感就建立起来了。RAG 的价值不仅在于“答得快”,更在于“答得可解释、可追溯”。
向量库选型(Milvus、pgvector、Qdrant 等)只影响工程实现,不改变这条主线:版本要可发布、索引要可重建、轨迹要可回放。 把三元组写进日志与发布流程,比多换一个 reranker 更能救命。