这是《LLM 应用开发 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 一个生产级 RAG 系统不只是“向量库 + 模型”,还包括数据接入、解析、索引、检索、生成、引用、评测、更新、权限、缓存和观测。本章把这些模块串成完整系统。
13.1 总体架构
离线管道
Source -> Parser -> Chunker -> Embedding -> Vector / Search Index
在线链路
User Query
-> Auth / Rate Limit
-> Query Understanding
-> Hybrid Retrieval
-> Filter / Dedupe
-> Rerank
-> Context Builder
-> LLM
-> Guardrail / Citation Check
-> Response
反馈闭环
Logs -> Bad Case -> Dataset -> Eval -> New Version
13.2 数据接入
数据源分类:
| 类型 | 示例 | 同步方式 |
|---|---|---|
| 文件系统 | PDF、Word、Markdown | 定时扫描或事件 |
| Confluence / Wiki | 团队文档 | Webhook / API |
| 数据库 | 商品、订单、工单 | CDC 或定时快照 |
| 工单系统 | 历史案例 | API 增量拉取 |
| 网页 | 官方文档 | 受控爬取 |
接入要求:
- 记录源 ID 和版本;
- 支持删除和权限同步;
- 处理重试和死信;
- 任务可幂等;
- 状态可观测;
- 源数据可重建索引。
13.3 在线请求流程
async def answer(question: str, user: User):
query = await rewrite_query(question, user.session_id)
candidates = await retrieve(query, user)
ranked = await rerank(query, candidates)
context = build_context(ranked, max_tokens=4000)
prompt = render_prompt(
question=question,
context=context,
citations=[c.chunk_id for c in ranked],
)
answer = await call_llm(prompt)
verify_citations(answer, context)
return answer
每一步都要有独立超时、日志和降级策略。
13.4 上下文构造
推荐格式:
以下是参考资料。资料可能不完整,不要使用外部知识补充。
[1] 来源:员工手册 v3 / 考勤 / 远程办公
更新时间:2026-01-10
内容:
远程办公需提前一天在 OA 系统申请。
[2] ...
用户问题:...
回答要求:
1. 优先使用编号小的相关资料;
2. 引用来源编号;
3. 资料冲突时说明差异;
4. 信息不足时明确说明。
构造规则:
- 控制 token 预算;
- 相邻块可合并;
- 表格保留结构;
- 无关候选丢弃;
- 不把权限字段暴露给模型;
- 引用 ID 与 chunk 映射保存在服务端。
13.5 引用与可解释性
答案示例:
{
"answer": "远程办公需要提前一天在 OA 系统申请。",
"citations": [
{"chunk_id": "manual-v3#128", "quote": "提前一天在 OA 系统申请"}
],
"confidence": "medium"
}
服务端校验:
- 引用 chunk 是否存在;
- 是否属于本次检索结果;
- quote 是否真实出现;
- 用户是否有权限查看;
- 文档是否仍然有效;
- 答案与引用是否冲突。
13.6 权限模型
User -> tenant_id -> allowed_doc_types -> roles -> doc ACL
原则:
- 检索前确定身份;
- 查询携带租户和角色;
- 引擎执行硬过滤;
- 输出引用再次检查;
- 缓存按权限隔离;
- 权限变更及时生效;
- 管理员调试日志也脱敏。
不能把所有文档都嵌入上下文后让模型“自己判断谁能看”。
13.7 数据新鲜度
| 场景 | 策略 |
|---|---|
| 政策文档 | 生效时间、失效时间 |
| 产品价格 | 查询数据库,不做静态 RAG |
| 库存 | API 实时查询 |
| 官方文档 | 定时同步和版本比较 |
| 工单案例 | 增量同步 |
索引要有更新时间,答案可展示资料时间。对强实时数据,RAG 只负责解释规则,数值由服务端查询。
13.8 缓存设计
缓存层级:
| 层 | 键 | 内容 |
|---|---|---|
| 语义缓存 | 规范化问题 + 权限 +索引版本 | 最终答案 |
| 检索缓存 | 查询 + 过滤 + 索引版本 | chunk ID |
| Embedding 缓存 | 文本 + 模型 | 向量 |
语义缓存要谨慎:
- “我的订单”不同用户不能共享;
- 时间敏感问题不宜缓存;
- 相似不等于等价;
- 命中后仍要执行权限检查;
- 记录命中率与错误率。
13.9 评测体系
| 层级 | 指标 |
|---|---|
| 解析 | 结构准确率、OCR 错误率 |
| 切块 | 边界错误、上下文充分性 |
| 召回 | Recall@K、MRR |
| 重排 | NDCG、Precision@N |
| 生成 | Faithfulness、Answer Relevance |
| 引用 | Citation Accuracy |
| 工程 | 延迟、成本、错误率 |
| 业务 | 解决率、人工转接率、用户反馈 |
线上采样:
自动评分:规则 + 模型评估
人工评审:按风险分层抽样
用户反馈:赞 / 踩 / 修正
13.10 部署与回滚
版本组合:
rag-release-v12
parser_version = pdf-v4
chunker_version = heading-v6
embedding_model = emb-v2
index_version = idx-20260825
rerank_model = rerank-v1
prompt_version = answer-v8
llm_model = chat-v5
灰度:
- 按用户比例;
- 按租户;
- 按问题类型;
- 对比新旧指标;
- 保存 trace 可回放;
- 出现质量回退立即切回。
13.11 常见故障
| 现象 | 层 | 处理 |
|---|---|---|
| 检索不到 | 解析/切块/索引 | 查 chunk 和向量 |
| 检索到但答错 | rerank/prompt | 查排序和上下文 |
| 答案无引用 | Prompt/模型 | 强制 schema |
| 引用错误 | 映射校验 | 服务端验证 |
| 新旧政策同时出现 | 数据治理 | 生效时间过滤 |
| 跨租户泄露 | 权限 | 硬过滤和测试 |
| 成本高 | 上下文 | token 预算和缓存 |
| 延迟高 | 检索/rerank | 并行、缩减候选 |
本章小结
RAG 工程化由离线索引管道、在线问答链路和反馈闭环组成。核心设计点包括数据版本、权限硬过滤、混合召回、重排、上下文预算、引用校验、缓存、评测和灰度回滚。把每一层的状态记录清楚,问题才能被定位和持续优化。
思考题
- RAG 离线管道和在线链路分别关注什么?
- 为什么引用必须由服务端校验?
- 哪些实时数据不应该进入静态索引?
- 语义缓存有哪些风险?
- 如何设计一次 RAG 版本回滚?