Appearance
第22章 从零构建一个生产级 RAG 系统
"The best RAG is the one that ships and keeps shipping." — 所有 SOTA 论文都不如一个能稳定迭代的 pipeline
本章要点
- 生产 RAG 的参考架构:三条链路 + 四大服务——不绕不弯的落地蓝图
- 四阶段交付路线:MVP(2 周)→ 离线稳定(1 个月)→ 反馈闭环(2 个月)→ 持续优化——每阶段有明确 done 标准
- 关键代码骨架:ingest pipeline、检索链路、生成链路、评估 pipeline 四个核心 loop
- 从 0 → DAU 10 万 的演进:架构不变、只在每个组件上逐步加能力
- 团队配置:2-3 人起步够用、> 15 人时走专业化分工
22.1 参考架构
前 21 章的机制汇总到一个系统里、长这样:
四大服务群:
- Gateway:鉴权 / 限流 / user_context
- Online 检索链路:Query Understanding → Retrieve → Rerank → Pack → Generate → Cite
- Offline 索引链路:Parse → Chunk → Embed → Index → Publish
- Feedback:Logger → Evaluator → Memory
前 21 章每一章对应这架构里的某一段——这一章是给架构填具体选型和代码骨架。
22.2 关键组件选型(2026 年推荐)
MVP 阶段一套"不出错"的默认选型:
| 组件 | 推荐 | 备注 |
|---|---|---|
| Web 框架 | FastAPI / axum | Python / Rust 二选一 |
| Embedding | bge-m3(自托管)/ text-embedding-3-small(API) | 中文选前 |
| 向量库 | Qdrant(中小)/ pgvector(复用 DB) | 第 11 章 |
| BM25 | Qdrant 内置 / Elasticsearch | 选简单 |
| Rerank | bge-reranker-v2-m3 | 第 14 章 |
| LLM | Claude Sonnet 4.6 / GPT-5 / DeepSeek V3 | 按质量成本选 |
| Query rewrite | Claude Haiku 4.5 | 小模型够用 |
| 评估 | ragas + 自建 gold set | 第 20 章 |
| 可观测性 | OpenTelemetry + Grafana | 标准栈 |
| 缓存 | Redis | query / embedding / rerank 三层 |
| 消息队列 | Kafka / Redpanda | 离线 pipeline |
| 调度 | Prefect / Dagster | 离线 pipeline |
这套组合 MVP 跑得起来、扩展性也够。大规模时只需要把某些组件升级(如 Qdrant → Milvus、Redis → Dragonfly)。
22.3 四阶段交付路线
阶段 1:MVP(2 周)
目标:能跑、能用、有 demo。
交付物:
- 离线索引:一次性脚本、从 S3 读 100 份样本文档 → chunk → embed → 入 Qdrant
- 在线检索:FastAPI 接口、hybrid 检索 top-10 → 喂 Sonnet → 返答案
- 无 rerank、无 Contextual Chunk、无 rewrite
- 简易前端(Streamlit / 一个 React 页面)
Done 标准:内部 5-10 人连续使用一周、采集 50+ badcase。
代码骨架:
python
# ingest_mvp.py
async def ingest_one(doc_path):
doc = await parse(doc_path) # unstructured.io
chunks = chunk(doc, size=400, overlap=80)
for c in chunks:
c.vec = await embed(c.text)
await qdrant.upsert(chunks)
# retrieve_mvp.py
async def rag(query, user):
q_vec = await embed(query)
dense_hits = await qdrant.search(q_vec, filter=user_filter(user), limit=10)
bm25_hits = await es.search(query, filter=user_filter(user), limit=10)
merged = rrf([dense_hits, bm25_hits], k=60, top_k=5)
prompt = pack(query, merged)
answer = await llm.chat(prompt)
return {"answer": answer, "citations": [c.id for c in merged]}不到 100 行核心代码。够跑 MVP。
阶段 2:离线稳定化(1 个月)
目标:知识库能稳定每日更新、能回滚、能扩展。
交付物:
- 离线 pipeline 用 Prefect / Dagster 编排,分段可重入(第 8 章 §8.2)
- 版本化索引、原子切换、48 小时回滚窗口(第 4 章 §4.2)
- 增量更新基于 content_hash(第 8 章 §8.2 方案 C)
- 基础监控(解析成功率、索引规模、QPS、延迟)
- 软删除 + 事件驱动(第 8 章 §8.2 避免"deleted=true 幽灵"事故)
Done 标准:连续 4 周 周度更新零事故、回滚脚本演练过一次。
阶段 3:反馈闭环和评估(2 个月)
目标:可观测、可定位、可迭代。
交付物:
- OpenTelemetry trace(第 3 章 §3.9)
- gold set 500 条、每日自动回归
- ragas 评估接入、faithfulness + answer_relevance 每日看板
- 用户 feedback 收集(thumbs up/down + 自由文本)
- 四层评估指标(第 20 章)完整看板 + 告警
- 初步 hybrid + rerank(bge-reranker-v2-m3)上线
Done 标准:每周能给出"本周 badcase 主要是什么类型、占比多少"的归因报告。
阶段 4:持续优化(永久)
目标:按归因报告迭代、质量和成本持续改善。
可能的优化路径:
- Query Rewrite 和 HyDE(第 15 章)
- Contextual Chunk(第 6 章 §6.6)
- Rerank 微调(第 14 章 §14.5)
- Prompt cache 和模型分层(第 21 章)
- Agent Memory(第 18 章)
- GraphRAG(第 19 章、如果有全局 / 多跳需求)
每次优化走归因 → 假设 → A/B → 决策 → 部署五步。数据驱动、不靠感觉。
22.4 关键代码骨架:四个 Loop
生产 RAG 的核心是四个 loop,每个 loop 独立可部署、独立演化。
Loop 1:Ingest Pipeline
python
# ingest.py (简化)
@flow(name="rag-ingest")
async def ingest_flow(doc_uris: list[str]):
for uri in doc_uris:
try:
# 1. 解析
doc = await parse_task(uri) # cached by content_hash
# 2. 分块
chunks = chunk_task(doc, strategy="structured", fallback="fixed")
# 3. Embedding (content_hash 复用 cache)
for c in chunks:
c.vec = await embed_task(c.text_with_context)
# 4. Upsert + 清理旧 chunk
await upsert_chunks(doc.id, chunks)
await cleanup_orphan_chunks(doc.id, current_chunk_ids=[c.id for c in chunks])
# 5. 发 event
await event_bus.publish("doc_indexed", {"doc_id": doc.id, "chunk_count": len(chunks)})
except Exception as e:
await dlq.put(uri, e)关键点:每一步都可缓存、失败入 DLQ、不阻塞其他文档。
Loop 2:Online Retrieval
python
# retrieve.py
async def retrieve(query: str, user_ctx: UserContext) -> list[Chunk]:
# 1. Query understanding(可能并行)
rewritten = await rewrite_task(query, user_ctx) # 含 disambiguation + HyDE
# 2. 并行召回
dense_task = dense_retrieve(rewritten, user_ctx, top_k=50)
bm25_task = bm25_retrieve(rewritten, user_ctx, top_k=50)
dense_hits, bm25_hits = await asyncio.gather(dense_task, bm25_task)
# 3. RRF 融合
fused = rrf([dense_hits, bm25_hits], k=60, top_k=30)
# 4. Rerank
top_k = await rerank(query, fused, top_k=5)
# 5. cache (query_hash, user_hash) -> top_k 5 分钟
await cache.set(cache_key(query, user_ctx), top_k, ttl=300)
return top_k关键点:并行召回(asyncio.gather)、统一 user_ctx filter 下推到向量库和 BM25。
Loop 3:Generation + Citation
python
# generate.py
async def generate(query: str, chunks: list[Chunk], user_ctx: UserContext) -> Response:
# 1. Context pack(U 形排列 + 结构化)
packed = pack_context(chunks, strategy="u_shape")
# 2. Prompt 构造(cacheable prefix + dynamic suffix)
prompt = build_prompt(
system=SYSTEM_PROMPT, # cacheable
few_shot=FEWSHOT, # cacheable
context=packed, # dynamic
query=query, # dynamic
)
# 3. LLM 调用(流式)
stream = llm.stream(prompt, cache_control="ephemeral")
# 4. citation 验证(后处理)
answer, citations = await parse_answer_and_citations(stream)
grounding = await verify_grounding(answer, chunks, citations)
# 5. 组装 response
return Response(
answer=answer,
citations=citations,
confidence=compute_confidence(grounding, chunks),
trace_id=current_trace_id(),
)关键点:prompt 结构化(可 cache + 引用锚点)、grounding 后处理验证。
Loop 4:Evaluation
python
# eval.py
@schedule(cron="0 3 * * *") # 每天凌晨 3 点
async def daily_regression():
results = []
for qa in load_gold_set():
actual = await rag_full_pipeline(qa.query, qa.user_ctx)
metrics = await ragas_eval(
query=qa.query,
gold_chunk_ids=qa.gold_chunks,
actual_chunk_ids=[c.id for c in actual.retrieved],
actual_answer=actual.answer,
context=actual.packed_context,
)
results.append(metrics)
# 上报指标
emit_metrics(aggregate(results))
# 告警
if mean(results, "faithfulness") < 0.85:
alert("Faithfulness 跌破 0.85")关键点:每日跑、多指标聚合、低于阈值告警。
22.5 从 0 → DAU 10 万 的演进
架构不变、每个组件逐步升级:
DAU < 1000 (种子期)
- pgvector(省)+ 单机 FastAPI
- 无 rerank、简单 hybrid
- 成本 < $500/月
DAU 1000 - 10000
- Qdrant 单机、加 bge-reranker
- Redis 三层缓存
- 离线 pipeline 用 Prefect
- 成本 $2000-5000/月
DAU 10000 - 100000
- Qdrant 3 节点集群 或 Milvus
- 微调 rerank
- Prompt cache 上线
- 离线 pipeline 多 worker
- 成本 $15000-50000/月
DAU > 100000
- Milvus 分片集群 或 Qdrant + 分片
- 多区域部署
- 专用 Embedding 和 Rerank 服务集群
- Agent Memory(如果场景需要)
- GraphRAG(如果有全局/多跳需求)
- 成本 $100000+/月
每阶段不是推倒重来、是 在同一架构上加组件、升级配置。这是参考架构的价值——演进不需要重写核心逻辑。
22.6 团队配置演化
- 1-2 人(MVP):全栈工程师 + 产品
- 3-5 人(稳定期):+ 数据工程师(专门维护离线 pipeline)、+ ML 工程师(微调 rerank)
- 6-10 人(成熟期):+ 可观测性工程师、+ 安全 / 合规工程师
- > 15 人:按链路分组(online / offline / eval)、专业化
团队小时不要乱分工——全栈搞定一切推进快。人多后按链路拆组、每组有明确 owner。
22.7 第一天的 checklist
从零启动一个生产 RAG 项目、第一周该做的:
- [ ] 明确业务目标(答对率、延迟、成本预算各自的硬约束)
- [ ] 锁定知识库范围(什么文档、多大规模、更新频率)
- [ ] 选栈(按第 22.2 节的推荐选)
- [ ] 搭 MVP pipeline(100 份文档、一条 end-to-end)
- [ ] 构造 50 条 gold set(真实用户问过的或人工编的)
- [ ] 写评估脚本(ragas 最小集)
- [ ] 画监控看板(最少 QPS、延迟、成本、faithfulness)
- [ ] 写事故响应 runbook(第 4 章 §4.8 类型)
前 8 项做完、你有一个能迭代的基础。少任何一项、都会成为后续的瓶颈。
22.8 常见失败模式和解药
生产 RAG 项目常见的失败模式:
失败 1:追求 SOTA 忽略稳定
项目组花 2 个月实现 GraphRAG / Agent Memory,产品还没上线。解药:先出 MVP 收真实反馈,高级技术按需引入。
失败 2:没有 gold set 盲优化
改了 prompt、改了 chunk、改了 embedding、都"感觉更好"——但没回归验证。解药:gold set 优先级 = P0,任何改动先过回归。
失败 3:忽视离线链路
团队所有精力在优化在线链路、离线 pipeline 用脚本凑合——3 个月后知识库更新一次出 bug。解药:阶段 2 的工程投入不能省。
失败 4:过早优化成本
MVP 阶段就纠结"这行代码会不会多花 $0.001"。质量都没到、省钱白搭。解药:先质量再成本——先证明系统有用、再降费用。
失败 5:不用真实数据
Demo 用 few-shot 精选例子跑、上线用真实用户 query 就崩。解药:MVP 开始就用真实数据(脱敏后)评估。
失败 6:没有回滚机制
索引 / 模型 / prompt 改完没办法回滚——出事故只能硬扛。解答:每项改动都有明确的回滚路径,部署前演练一次。
22.9 性能回归的常见触发器
生产 RAG 上线半年到一年、性能回归几乎必然发生。提前知道触发器、减少事故:
- 知识库自然漂移:新文档质量下降、或旧文档失去时效相关性。召回命中率慢慢降、用户抱怨变多
- 用户查询模式变化:产品推新功能后、用户问法从"怎么用 A"转"A vs B 的区别"——GraphRAG 能力不够会暴露
- 第三方服务降级:Embedding API 升级导致向量空间略变、老索引和新 query 不兼容。Anthropic / OpenAI 偶有静默升级
- 依赖库升级:LangChain / LlamaIndex 主版本升级经常改 API;某个依赖 bump 可能改变 chunking 行为
- 部署环境变化:从 A100 迁 H100、CPU 容器换架构——推理数值精度略有差异、边缘 case 表现变化
- 数据源污染:爬虫引入一批错误文档(网页改版、PDF 解析器 bug)、悄悄进索引后污染检索
预防靠持续的自动化评估(第 20 章)+ 关键依赖锁版本 + 变更审批流程。没有这三道防线、早晚中招。
22.10 生产 RAG 的组织学
技术之外、组织形态影响 RAG 项目能否持续。
产品 vs 工程的张力
RAG 有两条价值路径:
- 产品路径:做用户体验、算对的答案、降低挫败感
- 工程路径:压延迟、降成本、提吞吐
两条路经常冲突——为了 3 个点的答对率加 500ms 延迟值不值?没有产品经理 + 工程师共识、项目很容易卡在决策上。
成熟团队的做法:把三元约束(质量/延迟/成本)量化成指标、给每个指标定硬门槛。越过门槛的改动自动合入、跨门槛的停下来讨论。这比"感觉能不能上"高效。
反馈机制的组织成本
第 20 章讨论的三层评估(离线回归 / 在线采样 / 人工抽样)都需要持续人力投入:
- 人工抽样每周 4-8 小时
- Gold set 季度刷新 1-2 天
- Badcase 归因 review 每月一次 4 小时
这些工作没人干、评估就流于形式、数据质量会退化。团队里必须有明确的 owner 负责评估 pipeline、不能靠"大家有空看看"。
可持续的工作节奏
RAG 是马拉松、不是冲刺。推荐节奏:
- 周一 status review(看数据、定本周优先级)
- 周三 deep work 日(不开会、只做开发)
- 周五 deploy + 复盘
这套节奏让质量改善是连续的、而非大起大落。
22.11 和现有业务系统的集成
生产 RAG 很少是孤立服务——需要和现有业务栈集成。集成点和对应的设计决策:
集成点 1:鉴权系统
RAG 不自己做登录——继承业务的鉴权:
- SSO / OAuth:企业客户统一身份(Okta、Azure AD、飞书 / 钉钉)
- JWT 传递:业务 gateway 签 JWT、RAG 服务验签读 user_context
- 多租户边界:从 JWT 读 tenant_id、下推到向量库 filter
不要造轮子、复用现有鉴权——减 50% 实现成本、合规审计也直接继承。
集成点 2:客服 / 工单系统
客服场景 RAG 的典型集成:
- 客户发消息 → 客服系统路由 → RAG 答初稿 → 客服人工 review → 发出
- 命中率高的问题 → 自动闭环、不用人工
- 命中率低的问题 → 升级人工客服 + trace 用于改进
集成靠 webhook 或 event bus——RAG 订阅"新消息"、发"候选答案"。
集成点 3:CMS / 知识库源
文档更新要驱动 RAG 索引更新:
- CMS webhook:Confluence / Notion / SharePoint 改动 → 推 event 到 RAG 队列
- 文件系统监听:S3 bucket 事件、OSS 对象存储通知
- 定期全扫:兜底防止 webhook 丢
集成点 4:产品 UI
RAG 的响应 UI 不是单独页面——通常嵌入产品某处:
- 客服聊天窗
- 帮助中心搜索框
- 产品内侧边栏 AI 助手
- IDE 插件(代码 RAG)
UI 集成要关注:流式渲染 / 引用高亮跳转 / 反馈按钮 / 对话历史持久化。这些属于前端工程、但 API 设计要支持。
集成点 5:可观测性栈
复用企业已有监控:
- 延迟 / 错误率进 Grafana + Prometheus
- 业务埋点进分析平台(Amplitude / Mixpanel / GrowingIO)
- 错误进 Sentry
- Trace 进 Jaeger / Tempo
不要为 RAG 单独搭监控栈——维护不起。
22.12 长期维护的心智模型
生产 RAG 上线后的工作不是"完成了"——是"开始了"。三个心智模型:
- 它是一个生命体:文档更新、用户行为变化、LLM 升级——系统要跟着变
- 它是一个团队项目:一个人搞不定长期运维——必须有团队 + 明确 onboarding 文档 + 交接流程
- 它是一个产品:不只是技术栈、还要看用户价值——功能要迭代、场景要扩展
长期健康的 RAG 有如下特征:
- 每周有明确的迭代目标(新 chunking 策略 / 新 rerank 模型 / 新 domain 接入)
- 每月有一次完整的归因 review
- 每季度有一次架构健康检查(延迟漂移、成本漂移、质量漂移)
- 每半年评估一次栈的新组件(新 Embedding 模型、新向量库版本、新 rerank 方案)
22.13 从这本书出发
这本书讲完了 RAG 的主要工程机制——但没有一本书能讲完所有细节。真正的生产经验来自你在自己项目里的:
- 每一次 badcase 的归因
- 每一次架构选型的权衡
- 每一次线上事故的复盘
- 每一次和用户对话里发现的盲点
这本书是起点不是终点。推荐把本书当作参考手册——遇到问题时翻相关章节、结合自己的上下文做决定。
22.14 合规、审计与灾难恢复
RAG 系统处理的是企业最敏感的两类数据——内部知识和用户对话。这两样的合规和容灾在 POC 阶段可忽略、生产上线时是硬要求。这一节把前面章节散落的合规话题(第 7 章权限、第 18 章 memory 隐私、第 8 章增量)整合成上线前的综合视角。
合规框架的四个维度
- 数据分级:public / internal / confidential / restricted 四级、chunk 级别打标、权限下推到向量库 filter(第 7 章)
- 审计日志:谁在什么时间检索了什么 chunk、最终 LLM 看到了什么 context、生成了什么答案
- 用户权利:GDPR / PIPL 要求可删除、可导出、可审计——memory 和对话历史是重灾区
- 行业专项:医疗 HIPAA、金融等保、欧盟 AI Act 等各有细则
生产上线前这四项都要有明确的实现负责人、不能全部"以后再说"。
审计日志的最小字段集
无论什么合规框架、审计日志至少要能回答 "谁在什么时候问了什么、看到了什么、答了什么":
| 字段 | 为什么必要 |
|---|---|
trace_id | 串联同一请求的 retrieval / generation |
user_id + tenant_id | 定位"谁"、多租户边界 |
query + timestamp | 原始问题 + 时间 |
retrieved_chunk_ids + source_docs | LLM 看到了哪些内部资料 |
prompt_hash + model + prompt_cache_hit | 生成阶段的决策证据 |
answer + citations | 系统给出的最终答案 |
user_feedback | 用户事后反馈(如有) |
日志进对象存储(S3 / OSS)+ 索引进 OpenSearch / ClickHouse——按需查询。保留周期按合规要求:GDPR 6-12 月、金融常 5-7 年、医疗 HIPAA 6 年起。
日志本身也含敏感信息——日志的访问也要审计,这不是套娃、是合规底线。
用户权利的工程落地
GDPR / PIPL 赋予用户三项关键权利、对应三个工程能力:
删除权(right to be forgotten):用户请求 → 清除所有相关数据
- 包括 memory facts、对话历史、日志里的 user content、可能的 embedding index(需要点对点删除)
- 典型 SLA:72 小时内完成
- 工程实现:给每个用户一份 data map,删除时按 map 一次性清扫
导出权(data portability):用户获取自己的所有数据副本
- 包括历史对话、memory、profile
- 通常 JSON 或 CSV 格式、打包下载链接
- SLA:30 天内
访问审计权:用户能查看"谁在什么时候访问过我的数据"
- 对企业客户尤其重要("给我看谁用 AI 助理查过我的合同")
- 需要预留访问日志的用户可见视图
这三项如果事后补、会发现数据散落在向量库、PG、Redis、S3、日志系统各处——初始架构设计时就要统一 user_id 路径,事后集成难度 10 倍。
灾难恢复:RPO 与 RTO 设计
灾难恢复计划要明确两个指标:
- RPO(Recovery Point Objective):事故后能容忍丢失多少数据。典型 RAG:15 分钟到 1 小时
- RTO(Recovery Time Objective):事故后多久能恢复服务。典型 RAG:30 分钟到 4 小时
达成这些目标靠三层备份:
| 层级 | 内容 | 恢复速度 | 成本 |
|---|---|---|---|
| L1 元数据 | Postgres(chunk metadata / user profile) | 分钟级 WAL 流复制 | 低 |
| L2 向量索引 | Qdrant / Milvus snapshot | 小时级(重建快) | 中 |
| L3 原始文档 | S3 跨区复制 | 分钟级 | 低 |
L2 是重点——向量索引重建要几小时。策略是保留最近 3 个 snapshot(日级),事故时拉最近一份恢复。业务数据用 L1+L3 兜底、L2 丢了大不了重建。
灾难演练不能省
备份有但没演练过 = 相当于没备份。典型事故模式:备份 6 个月里一直跑、但从没验证过能恢复——真到需要时发现 snapshot 格式不兼容、恢复脚本半年没更新已失效。
生产 RAG 的演练节奏:
- 月度:单组件恢复(向量库从 snapshot 恢复、PG 从 WAL 回滚)
- 季度:完整链路恢复(从全站宕机到恢复服务)
- 年度:多区域故障切换(primary region 整挂时切 secondary)
演练结果写进 runbook——下次事故时按 runbook 操作,而不是现场推理。
数据驻留与跨区域
欧盟客户的数据不能出欧盟、国内部分客户的数据不能出国——数据驻留要求在架构早期就决定:
- 单区域部署 + 多租户共享存储:简单、但不支持全球客户
- 多区域隔离部署:每个区域独立全栈(向量库、Redis、PG)、用户数据 sticky 到所属区域
- 混合模式:元数据跨区、敏感数据本区——工程复杂
多区域的难点不是部署、是数据一致性 + 版本同步:知识库更新时所有区域索引要一致。主从复制 + 事件总线是主流方案。
合规的反模式
生产 RAG 里常见的合规反模式:
- "先上线再补合规":初期不打 audit log、不做权限分级——等合规审查才补、代价是重写半个系统
- 权限只在 UI 层:后端 API 不做 user_ctx filter 下推、聪明用户绕过 UI 拿到别人数据
- 日志里留明文 PII:身份证、银行卡直接写 log——合规审计第一条就挂
- 备份从不演练:以为 backup 脚本跑了就稳——真出事时发现不能恢复
生产上线前做一次合规 checklist 审查(ISO 27001 或公司内部 checklist),比上线后 fire-fighting 省 10 倍代价。
22.15 事故响应 playbook:RAG 的 on-call 手册
§22.8 列了失败模式、§22.9 讲了性能回归——都是事前防御。这一节补上事中响应:线上出事了怎么办。RAG 的事故特性和传统 Web 服务不同——一个 RAG 事故可能是检索错、生成错、或两者组合、用户感知是"答案不对"而非"服务 500"。没有专门的 playbook、on-call 同学只能现场摸索、MTTR 拉长到小时级。
事故的典型分类
按严重度的响应时限
| 级别 | 响应时限 | 处置团队 | 通知范围 |
|---|---|---|---|
| P0 | 15 分钟 | 核心团队 + 安全 + 法务 | CTO + 合规 + 相关业务 |
| P1 | 30 分钟 | on-call + 团队 lead | 团队全员 + 业务接入方 |
| P2 | 2 小时 | on-call | 团队内部 |
| P3 | 24 小时 | 指派专人 | 定期 review |
P0 事故不等"定位清楚再动手"——先立即止损(关 feature flag、切 fallback、封用户等)、事后再慢慢查。
响应三阶段
检测:监控指标 + 告警 + 用户反馈三源头。关键是不要漏报——降级 + 告警宁可假阳多、别漏真阴。
定位:按 RAG 链路从尾到头查:
- 用户看到的答案 → 查 trace
- Trace 里的 prompt 和 context → 看检索结果合理吗
- 检索结果 → 看 query 是否合理
- query → 看请求参数
多数事故卡在前两步就能定位。第 3、4 步属于深度事故。
修复:分两档——
- 紧急缓解:关 feature、切 fallback、回滚版本——分钟级
- 根因修复:找到 bug、补代码、加测试——几天级
两档并行、缓解优先。
Playbook 模板
每类常见事故都应该有 playbook。例子 "LLM API 高错误率":
markdown
# Playbook: LLM API 错误率 > 5%
## 症状
- generation_error_rate > 5% 持续 2 分钟
- 用户报告"答不出来"
## 立即动作(5 分钟内)
1. 确认告警真实性:看 Grafana 的 error 分布
2. 切到 fallback 模型(Sonnet → Haiku):`config.llm.primary = "haiku"`
3. 通知业务接入方"可能质量降级"
## 深度定位(30 分钟内)
1. 查 error 类型:429 / 500 / timeout
2. 查 Anthropic status page
3. 查是否刚上线了新 prompt 或模型参数
4. 查是否被上游流量打崩(QPS 突增)
## 可能根因
- Anthropic 端故障 → 等恢复 + fallback
- rate limit 触发 → 联系 sales 扩额度 + 限流
- 新 prompt 导致返回异常 → 回滚 prompt
- 上游流量异常 → 排查上游
## 回滚步骤
- Prompt 回滚:`git revert <commit>` + deploy
- 模型回滚:config 切回
- 完全失败:启动 static fallback
## 复盘要求
- 24 小时内产出 timeline
- 1 周内补修复 + test每个 P1+ 事故都应该有对应 playbook——没有就写、事故结束就维护。Playbook 库是团队运维能力的沉淀。
常见定位套路
- recall 降、用户投诉答不准:查 embedding 服务版本、向量库索引版本、最近是否重建
- 延迟涨、但错误率正常:查下游服务(向量库、rerank)的 P99、多半某个服务变慢了
- 成本暴涨但 QPS 稳定:查 token / request 的分布、通常是 prompt 变长或 context top-k 调大
- 某个租户频繁报错:查该租户的索引 size、metadata、是否数据异常
- 某类 query 答错:query classifier 可能误路由、检查路由分布
这些套路不是万能的、但 80% 的事故能用它们快速定位。
事后复盘(postmortem)的要求
每个 P0/P1 事故都要写复盘文档:
- Timeline:从事故发生到完全恢复的分钟级时间线
- 根因:不是"XXX 坏了"、是 "为什么 XXX 坏了" 五问到底
- 影响:多少用户、多少请求、多少金额、合规影响
- 做对的:这次响应里哪些机制帮了忙(告警准、playbook 有效等)
- 做错的:哪些机制没起作用、或决策慢了
- Action items:分 P0 立即修 / P1 一月内 / P2 季度内
复盘不对人——对系统和流程。目标是"下次更快更准"、不是追责。追责文化会让事故被隐藏、损害长期运维。
On-call 值班的组织
- 轮值:团队人均每 4-6 周值一次周、防止疲劳
- 交接:周交接会过一遍上周事件、pending 修复项、playbook 更新
- 培训:新 on-call 先跟班 2 周、熟悉所有主要 playbook
- 权限:on-call 有"宣告降级"的权限、不用等 lead 批准
on-call 的核心诉求是"授权和 playbook"——没有这两样、on-call 同学被困在"不敢动" + "不知道动什么"的死循环。
事故驱动的系统演化
成熟 RAG 团队的演化 60% 由事故驱动:
- 事故 → 复盘 → Action item → 代码/流程改进
- 新 playbook → 下次事故 MTTR 压缩
- Action item 积累 → 季度架构演进
没有事故就"平稳"了——往往是没发现问题而已。成熟团队每季度至少 2-3 个 P1/P2 事故是健康信号——说明告警灵敏、用户反馈通畅、团队在学。
22.16 自建 vs 商用 vs 开源:组件选择的决策框架
§22.2 给了具体组件选型推荐——但每个组件都有自建 / 商用托管 / 开源自托管 三条路。选错一个方向、后期切换代价巨大。这节给出跨组件通用的决策框架——不只是"选哪个具体产品"、而是"选哪种部署方式"。
三条路的本质区别
每条路的深层特征:
- 自建:代码 100% 自控、工程成本极高。除非是核心差异化能力、否则不建议
- 商用托管:上手快、运维零——但 TCO 可能远超自托管、且被厂商锁定
- 开源自托管:需要 DevOps 能力、但长期最经济 + 可迁移
按组件的推荐默认选择
| 组件 | 默认推荐 | 原因 |
|---|---|---|
| LLM | 商用 API | 模型更新快、自托管 GPU 贵 |
| Embedding | 开源自托管(小规模用商用) | 可批量、成本可控 |
| 向量库 | 开源自托管(MVP 可用托管) | 数据所有权敏感 |
| Rerank | 开源自托管 | 模型小、推理便宜 |
| BM25 | 开源(Elasticsearch / Tantivy) | 工程成熟、无需商用 |
| Memory | 商用托管(Zep / Letta) | 生态年轻、自建代价高 |
| Observability | 开源栈(Prometheus + Grafana) | 和现有运维一致 |
| Evaluation | 开源(ragas / trulens) | 框架为主、不值得商用 |
这只是默认起点——按下面决策表微调。
决策表:三个问题
问 3 个问题就能大致定方向:
这个组件是不是你的核心差异化?
- 是 → 可能要自建 / 定制
- 否 → 直接用现成(商用或开源)
规模大到自托管有 TCO 优势吗?
- < 小规模 → 商用(运维成本不划算)
- 中大规模 → 开源自托管
- 超大规模 → 可能要自建专用组件
数据 / 合规约束有多强?
- 强(金融 / 医疗 / 政务) → 自托管优先
- 中等 → 看区域 / 厂商承诺
- 弱(纯内部工具) → 商用省事
三个问题拍板基本不会错。
详细 TCO 对比
以 向量库 为例、算一年 TCO(100 万 chunk × 1024 dim 规模):
商用托管(Pinecone / Qdrant Cloud):
- 服务费:$300-600/月
- 运维成本:近 0(每月 0.05 FTE ≈ $500)
- 一年 ~$10K
开源自托管(Qdrant / Milvus):
- 硬件:$200/月(1 台 32GB RAM 云主机)
- 运维:0.2 FTE × 每年 $200K = $40K
- 一年 ~$42K
商用看起来便宜? 但只在小规模——10 倍规模时:
| 规模 | 商用 | 自托管 |
|---|---|---|
| 100 万 chunk | $10K | $42K |
| 1000 万 chunk | $100K | $60K |
| 1 亿 chunk | $500K+ | $200K |
大规模自托管更省钱——因为商用按量几乎线性、自托管的运维 FTE 基本固定。拐点约在千万级。
厂商锁定的真实成本
商用路线的隐藏成本:锁定。
典型锁定形式:
- API 兼容性:换厂商 SDK 要重写接入代码、2-4 周
- 数据格式:Pinecone 的向量导出到 Milvus 需要格式转换
- 功能特性:某厂商独有的 filter / reranking 一旦用了、换走要找替代
- 定价:厂商涨价时你只能接受(或承担迁移成本)
锁定成本不是 0——在做商用选型时要有退出计划:
- 数据能导出吗?(能 import 其他厂商)
- API 有标准协议吗?(如 OpenAI-compatible API)
- 定价历史稳定吗?(看 2-3 年历史、有无频繁涨价)
看起来悲观、但 3-5 年 RAG 项目周期内、厂商可能收购、停服、涨价——每一样都可能发生。
什么时候应该自建
自建是最激进选项——一般不推荐、但某些场景值得:
- 极度规模化:运营全球性 RAG、单位成本的 5% 优化都值
- 独有算法:用了某篇前沿论文的方法、开源实现不存在
- 合规硬要求:某些国家 / 行业不允许任何外部组件
- 核心 IP:这个组件的技术本身是公司价值
大多数 RAG 项目没有这些条件——自建只是徒增复杂度。
混合策略:分层选型
最成熟的 RAG 项目常是 混合:
- LLM:商用 API(Claude + GPT 双厂商、互相备份)
- Embedding:开源自托管(bge-m3)
- 向量库:开源自托管(Qdrant 集群)
- Rerank:开源自托管(bge-reranker)
- 其他:按需商用 / 开源
混合策略的好处:
- 成本可控(大头组件自托管)
- 核心能力不锁死(LLM 双厂商)
- 工程复杂度 balanced
迁移的真实难度
不同组件迁移难度天差地别:
| 组件 | 迁移难度 | 典型工期 |
|---|---|---|
| LLM(Claude ↔ GPT) | 易(prompt 改、API 改) | 1-2 周 |
| Embedding 模型 | 中(需要全量重 embed、§9.13) | 2-4 周 |
| 向量库 | 中(数据导出 + schema 映射) | 4-8 周 |
| Rerank | 易(模型替换) | 1 周 |
| BM25 引擎 | 中(Elasticsearch → Tantivy 等) | 3-6 周 |
| Memory 系统 | 难(状态迁移) | 8-12 周 |
规划选型时考虑迁移难度权重——难迁移的组件选型要更保守。
更新这些决策的频率
技术格局每年变化——决策不是一劳永逸:
- LLM 市场:每季度 review(新模型 / 新定价)
- Embedding:每半年 review(SOTA 更新)
- 向量库:每年 review(新版本特性)
- 其他:每年一次
不 review 就是"被过时的决策绑架"——三年前选的组件现在可能有更好的替代。
选型的组织纪律
这些决策不能一个人拍板——典型流程:
- 小决策(单个组件):Tech Lead + 团队讨论
- 中决策(组件换厂商):Eng Manager + 架构师 review
- 大决策(整体技术栈方向):CTO + 工程 VP + 业务代表
有明确决策文档(ADR、Architecture Decision Record)——记录为什么选、考虑过什么替代、什么时候 review。没文档的决策 6 个月后没人记得为什么——再改又从头来。
22.17 RAG 项目的 KPI 设计:北极星 + 平衡指标
前 16 节讲了架构、交付、运营——但团队怎么知道项目做得好不好?技术指标很多(recall、P99、faithfulness)——哪个才是"真北"?很多 RAG 团队跟不清自己的 KPI——一会儿追 recall、一会儿追延迟、一会儿追成本——没有统一方向。这节给 RAG 项目的 KPI 设计一套体系。
为什么 RAG 项目常没有北极星
没有统一北极星的后果:团队 7 个人、4 个方向、每个都觉得"我的方向最对"——效率低、决策慢。
三层 KPI 结构
三层的关系:工程指标改善 → 产品指标改善 → 业务指标改善。任一链条断了、说明定义有问题。
北极星指标的选择
北极星是业务层唯一最重要的指标——代表项目成败。RAG 项目的典型候选:
- 客服 RAG:人工转接率(越低越好)、AHT(平均处理时间)、客户满意度
- 内部知识问答:员工月均使用次数、替代人工咨询的比例
- 研发 Copilot:代码采纳率、开发提速百分比
- 销售 assistant:辅助成交的 deal 数、销售生产力提升
共同特点:直接和业务价值挂钩、不是工程指标的代理。
平衡指标:防止走偏
单一北极星有副作用——容易被"gaming"。平衡指标防止这个:
| 北极星 | 风险 | 平衡指标 |
|---|---|---|
| 采纳率 | 系统拍用户马屁、满意度虚高 | faithfulness(答案必须基于证据) |
| 使用次数 | 刷次数、答案质量不重要 | 用户满意度 |
| 转接率 | 系统乱答、用户不转接但不满 | 后续 NPS / 复购 |
| 代码采纳 | 采纳了但 bug 多 | PR merge 率 / 后续 revert |
北极星 + 至少 2 个平衡指标 = 完整 KPI 体系。单看北极星会被游戏。
按角色的 KPI
不同角色看不同指标是正常的——但都要能链回北极星:
- 工程师:recall / P99 / cost(工程层)
- ML 工程师:faithfulness / NDCG(工程到产品)
- 产品:CTR / 满意度 / 追问率(产品层)
- 业务:采纳数 / 节省时间(业务层 = 北极星)
- CEO / 管理层:ARR / 毛利 / 留存
团队每周 review 时、每个角色看自己的层、但能说清自己指标和北极星的关系。没法说清的指标——可能不值得关注。
KPI 的演化
RAG 项目不同阶段、KPI 重心不同:
阶段 1 MVP(1-2 月):
- 北极星:能跑通 demo、内部能用
- 工程:基础可用性(上线时间、基础 recall)
阶段 2 稳定化(3-6 月):
- 北极星:内部 DAU 增长、使用频次
- 工程:P99 延迟、错误率
阶段 3 商业化(6-12 月):
- 北极星:付费用户数、ARR
- 工程:成本 / 用户、单位经济学
阶段 4 规模化(12+ 月):
- 北极星:LTV / CAC、毛利
- 工程:多租户、全球化、合规
KPI 跟着阶段换——不是一套终身用。
OKR 的具体写法
季度 OKR(Objectives + Key Results)示例:
text
Objective: Q2 把企业内部 RAG 采纳率从 30% 提到 50%
Key Results:
1. 答案满意度(thumbs up rate)从 70% 提到 82%
2. P99 延迟从 2.5s 降到 1.8s
3. 覆盖知识库从 80% 部门扩展到 95%
不是 Key Results:
× 升级到 rerank v2(这是 action、不是 result)
× recall 提 3 点(这是工程指标、不直接对应 objective)OKR 要求 Key Results 是可测量结果——不是"做了什么"、是"达到了什么"。
KPI 的反模式
- 指标越多越好:20 个 KPI、团队不知道优先级、全都做不好
- 北极星选工程指标:recall 是工程指标、不是北极星——业务不关心
- 不和 compensation 挂钩:KPI 停在口头、没约束力
- 不更新:半年前定的 KPI、项目已经换方向、指标还没变
- 只看均值:均值漂亮但 P99 崩、长尾用户流失
数据驱动的组织文化
好的 KPI 只是起点——需要组织文化配合:
- 每周 review、面对数据不回避
- 错了就承认、不粉饰
- 数据指向什么、就做什么——不是先做再找数据支持
这种文化不是技术问题、是领导力问题——但没有它、最好的 KPI 设计也落不了地。
从评估到 KPI 的桥接
§20.18 讨论了评估到 action 的闭环——KPI 是这个闭环的上层目标:
text
业务 KPI(如采纳率)
↑
产品指标(如满意度)
↑
工程指标(如 faithfulness)
↑
评估 gold set + badcase
↑
Action items每层都要能链到上一层——否则就是"工程自 high"。
不要忘了成本维度
很多 KPI 讨论只谈"好"——但好的代价同样关键。KPI 里必须有:
- 单位成本(cost per useful answer)
- 团队效率(features per quarter)
- 系统复杂度(components / tech debt)
光看"好"不看"代价"的 KPI 会导致过度工程——做了很多技术先进的东西、但性价比低。
最终:KPI 是沟通工具
KPI 不只是度量——是跨角色沟通工具。定好 KPI 后:
- 工程师知道"我改这个能提升业务"
- 产品知道"工程做了什么、对业务多少帮助"
- 业务知道"系统进步到哪、下一步在哪"
没有 KPI、团队的每次讨论都要从头对齐——效率 10 倍降。这是成熟团队的分水岭之一。
22.18 新人 onboarding 到 RAG 项目
RAG 项目的复杂度让新人 onboarding成为挑战——不是单纯学 API、是理解一套复杂系统。做得好的团队、新人 1-2 周上手;做得差的、新人 2-3 月还没进入状态、质量差 10-20×。这节给出 RAG 项目 onboarding 的结构化方法——让团队成员快速贡献、知识得以传承。
Onboarding 的挑战
不做好 onboarding——新人靠自己摸索几个月、团队效率低。
三阶段 onboarding
Week 1:基础和环境
- 读本书 ch1-4 了解 RAG 全貌
- 了解业务(产品经理 1 小时 intro)
- 搭好本地环境(跑通 demo)
- 看系统架构图 / 组件清单
- 读关键代码的骨架(不是所有、核心 loop)
Week 2:深入细节
- 按工作方向深入(后端 / ML / 前端各读对应章节)
- 看 recent incidents 的 postmortem
- 跟班 on-call 观察
- 做第一个小 task(修一个 known bug)
Week 3-4:产出
- 独立做一个功能
- 参加 code review 和 design review
- 开始加入 rotation
- 有 mentor 指导
每个阶段有明确 deliverable——不是"自学完事"。
必读资料清单
给新人的材料 package:
- 本书:按角色重点章节
- 内部架构文档:这个 RAG 项目的具体架构
- 关键 postmortems:过去 1 年的事故复盘(学团队的教训)
- Runbook:on-call 手册
- API spec:外部 / 内部 API 文档
- 关键论文:Contextual Retrieval、Lost in the Middle 等
按清单读——不是"有空再看"。
必会工具清单
新人要熟悉的工具:
- 代码环境:IDE、git、CI 流程
- 数据工具:SQL、Python / pandas
- 监控工具:Grafana、Jaeger(看 trace)
- 云工具:K8s、AWS / GCP CLI
- AI 工具:LLM playground、Gradio 等
不熟的工具——先学这些、再做 RAG 的任务。
导师制(Mentor)
每个新人配 mentor:
- 入职第一月每日 15 分钟 check-in
- 第二月每周 1 小时 sync
- 第三月独立后仍 available for questions
Mentor 不是只答问题——传文化和"为什么":
- 为什么选这个向量库
- 为什么 chunk size 是 400
- 过去踩过什么坑
这些 why 只能口传——不是文档能完全覆盖的。
第一周的具体任务
推荐给新人的任务类型:
- 修一个小 bug:能接触真实代码、小风险
- 改一个小 UI:快速有成就感
- 加一个简单指标:了解监控
- 写一个单元测试:了解 CI
别让新人第一周做:
- 改核心 prompt(风险高、需要背景)
- 升级依赖(可能引入 regression)
- 重构(没背景会搞砸)
循序渐进——从小到大。
知识传承的文档
关键文档每季度 review 更新:
- 架构决策记录(ADR):每个重要决策的背景和 trade-off
- Postmortem 库:按时间线和类型索引
- On-call runbook:事故响应手册
- Glossary:术语表(避免新人不懂内部黑话)
- FAQ:常问问题和标准答案
文档不更新 = 新人被误导——定期 review 是纪律。
知识的口传阶段
有些东西永远无法写进文档:
- 为什么 Claude 比 GPT 在我们场景好——靠实验感觉
- 哪类 query 容易答错——靠 oncall 经验
- 哪个团队好合作、哪个难——靠人际历史
这些靠日常对话传承——mentor / code review / 闲聊——文化的一部分。
新人的安全网
避免新人"一个错误毁掉生产":
- 沙箱环境:新人先在 staging 试、不直连 prod
- Code review:所有改动必过 review
- 权限限制:新人 3 月内没有 prod 写权限
- Rollback readiness:改动能快速回滚
这些限制保护新人也保护系统——不是不信任。
Onboarding 的反馈机制
新人入职 1 月、3 月、6 月做 retro:
- 哪些文档有用
- 哪些缺失
- 哪些过时
- 哪些工具慢
基于反馈更新 onboarding 材料——onboarding 本身是持续迭代的产品。
新人的贡献里程碑
典型节奏:
- Week 2:第一个 PR(小改动)
- Month 1:独立完成小 feature
- Month 3:负责一个组件(和 mentor 搭档)
- Month 6:能 own 一块区域、帮新新人 onboard
- Year 1:熟练、可以 mentor 新人
超出这个节奏说明有问题——要么 onboarding 太慢、要么 setting up 有问题。
Onboarding 的投入和 ROI
新人 onboarding 成本:
- Mentor 时间:每周 3-5 小时 × 3 月 = 40-60 人时
- 文档维护:每季度 1-2 人天
- 新人产出损失:第 1 月接近 0、第 2-3 月 50%、第 4 月 80%
ROI:
- 好 onboarding:3 月后新人 100% 产出
- 差 onboarding:6-12 月才 100%、中间可能走人
每个 onboarding 成功 = 一个长期贡献者——长期 ROI 极高。
团队扩张时的 onboarding 挑战
团队从 5 人到 20 人——onboarding 的 bottleneck:
- Mentor 不够(老人都当 mentor、没时间做项目)
- 文档跟不上(系统变化快、文档滞后)
- 文化稀释(老人的 "常识" 新人不知道)
应对:
- 指定 onboarding owner:专人负责材料和流程
- 把 mentor 时间作为正式工作:不是"义务"
- 分层 onboarding:新人 → old 新人 → 老人、传承链
Onboarding 的文化信号
一个团队的 onboarding 质量是文化信号:
- Onboarding 好 = 团队重视人才、愿意投资
- Onboarding 差 = "你自己搞定"、人才流失
招聘时候选人会问"onboarding 怎么样"——回答能看出团队成熟度。
反模式
- "新人自学":只给仓库地址、爱看不看
- Mentor 是负担:没认可、老人不愿做
- 文档没 owner:缺失和过时无人修
- No feedback:不问新人反馈、不改进
- 权限太宽:新人有 prod 权限、一不小心毁坏
Onboarding 和知识传承是长期工程
Onboarding 不是 "来一个人做一次"——是组织的持续工程:
- 人员流动必然发生
- 系统持续变化
- 文档要跟
- 文化要传
这项投资看不到短期 impact、长期(2-5 年)决定团队能否持续壮大。把 onboarding 做好的 RAG 团队、比没做的竞争力高两个级别。
22.19 全书回顾
22 章的核心骨架:
六个部分串起一个完整知识体系。每个部分的核心 take-away 在各章的"本章小结"里——本书的末尾不再复述。
22.20 跨书关联和延伸阅读
本书和系列其他书的呼应:
- 《LangGraph 设计与实现》:RAG 作为 Agent 的一个 node、和其他工具节点编排
- 《Tokio 源码深度解析》:异步 runtime 底层——RAG 的在线链路大量依赖 async
- 《Hyper 与 Tower:工业级 HTTP 栈》:RAG 的 API gateway 和 HTTP 中间件模式
- 《Serde 元编程》:trace / schema / 数据契约的序列化基础
- 《vLLM 推理内核深度解析》:LLM serving 侧优化——降 RAG 推理成本
- 《MCP 协议》:Agent 如何标准化地访问 RAG 作为 tool / resource
- 《Axum 设计与实现》:如果用 Rust 写 RAG 服务、axum 是主流选择
推荐的外部资源:
- 论文:BEIR、MTEB、ColBERT v2、Contextual Retrieval、Lost in the Middle、HyDE、MemGPT、Microsoft GraphRAG
- 框架:LangChain、LlamaIndex、Haystack、ragas、trulens
- 博客:Anthropic Research、LangChain blog、LlamaIndex blog、Qdrant blog、Cohere blog
22.21 结语
RAG 从 2023 年的新概念到 2026 年已经是 AI 应用的标准基础设施。它将继续演化——随 LLM 能力、Embedding 质量、向量库性能、Agent 架构的变化而调整。但核心原则会稳定:
- 知识带边界进入索引、按业务语义检索、按证据生成、按引用验证
这四句话是本书 22 章的压缩版。记住它们、其他的细节按需查阅。
最后给一个建议:把 RAG 系统当作一个活的项目维护——不是一次性交付。它的质量取决于你每周每月对它的关注。越持续投入、越能长期获得回报。
祝你的 RAG 项目上线顺利。Happy retrieving。