跳转至

第 17 章:部署与运维:灰度、监控与回滚

第 17 章封面

上线不是把版本推上去就算完事,而是要把不确定性关进笼子:你知道怎么灰度、怎么看盘、怎么回滚、怎么复盘。没有回滚能力的发布,本质上就是在赌博。[5][6]

AI 产品的运维比传统软件更凶险:模型微调了一下、提示词改了两个字、检索库更新了一批文档,或者工具定义换了个参数,任何一处变动都可能导致线上的行为分布发生剧烈漂移。你不能只监控错误率(Error Rate),你得监控“质量、成本、风险”这三座大山。把这些指标纳入发布门禁,你才能在持续迭代的同时,不被线上的各种“惊喜”拖垮。[6]

你将收获什么

  • 一套发布策略:从 1% 流量灰度到全量,什么时候该推进,什么时候必须暂停,什么时候立刻回滚。[5]
  • 一份运维观测口径:别光看 CPU/内存,要学会同时盯着 质量/延迟/成本/风险 四条线看。[6]
  • 一份救火 Runbook:事故发生时,脑子通常是懵的。你需要一份按表执行的操作手册,而不是现场凭直觉瞎指挥(Runbook 库详见:E-runbooks.md)。[5]

三层思考:运维就是让系统"坏"得慢一点

第 1 层:论证链条

可控发布的逻辑链条非常硬:

版本化(代码/配置/提示/模型全是版本)→ 灰度策略(先让少数人试毒)→ 观测口径(死死盯着关键指标)→ 告警处置(响了就得动)→ 回滚复盘(快速止损并转化成经验)→ 回归集更新(把坑填上,下次不准再掉进去)

如果你没有版本化,你就解释不了为什么今天变笨了;如果你没有回归集,你就防不住同一个 bug 下个月再来一次。[6]

第 3 层:落地与验收

验收这章学没学会,就看三件事: 1. 可追溯:你能明确说出这次发布到底改了哪几个提示词、换了哪个索引切片吗? 2. 可执行:你能在 10 分钟内完成回滚或降级吗? 3. 可复利:每次事故后,你能产出一个复盘文档,并把失败样本塞进自动化回归测试里吗?[6]

关键流程:可控发布闭环

不要迷信复杂的流程图,核心逻辑就这几步,缺一步就是裸奔:

1. 版本化打包:代码 + Config + Prompt + Model + Index + Tools = Version Set
2. 小流量灰度:先放 1% 或指定白名单用户(可控样本)
3. 四线观测:盯着 质量 / 延迟 / 成本 / 风险
4. 决策关口:
   - 达标 -> 推进扩量
   - 波动 -> 暂停分析
   - 越界 -> 立即回滚/降级(按表执行,别犹豫)
5. 现场取证:固定证据包(请求样本/日志/指标/版本快照)
6. 复盘闭环:结论 + 行动项 -> 只有更新了回归集,事故才算结束

建议:证据包(Evidence Pack)不要散落在聊天记录里,统一存到 reports/YYYY-MM-DD/<change-id>/。目录结构和字段模板直接抄这里:附录 D:证据包与门禁速查

版本化:把变化变成“对象”

在 AI 系统里,只管理代码版本是远远不够的。你得把下面这些也管起来: - 提示词与输出合同:Prompt 变了,输出格式(Output Contract)可能就变了。 - 检索配置与索引版本(RAG):切片大小(Chunk Size)变了,召回结果就全变了。 - 工具清单与权限边界(Agent):工具参数改了,模型可能就不会用了。 - 模型版本与后训练策略:底模微调过没?用的哪个 Checkpoint?

落地做法(0→1)

把非代码资产当成“制品”(Artifact)来管理,别让它们在文件系统里裸奔。 - 大文件:模型权重、向量索引快照,用 Git LFS 或专门的制品库(如 S3 + 版本号目录)存好。 - 数据与索引:用 DVC 或简单的 manifest 文件记录。索引不是随手跑出来的,它是一次发布的产物,必须有 index_version。 - Prompt Hub:把 prompt 集中管理,别散落在代码字符串里。日志里必须打印 prompt_version。 - Version Set:每次发布,输出一个“版本组合 ID”,包含 {code_v, prompt_v, model_v, index_v}。出了问题,先看这个组合 ID 对不对。

输出合同(Output Contract):这不仅是 JSON Schema,更是你和模型之间的契约。字段有哪些?必填项是啥?长度限制多少?允许的枚举值是什么?把它当成 API Schema 来管理,并在代码里做强校验。灰度时,这个合同就是你的法律依据。

特别提醒 RAG:向量索引必须版本化。你不能说“我更新了知识库”,你应该说“我发布了索引 v2025.12.24.01”。这样一旦线上发现检索质量雪崩,你可以像回滚代码一样,瞬间切回 v2025.12.24.00,而不是在线上且战且退,试图修复数据。[6]

灰度发布:别拿全量用户做实验

灰度的本质很简单:在真实环境验证假设,但把爆炸半径控制在你赔得起的范围内。[5]

可控发布流程示意图

灰度策略卡:按表操作,禁止临场发挥

维度 说明
灰度对象 内部员工 / 白名单用户 / 1% 随机租户
观察窗口 至少覆盖一个完整的使用周期(如 24 小时)
通过门槛 质量不降、延迟达标、成本可控、风险为零
暂停条件 任一守门指标出现“看不懂的波动”
回滚条件 守门指标越过红线(Error Rate, Latency, Cost)

门槛怎么定?

别拍脑袋定个绝对值。用基线(Baseline)波动范围。 - 基线:取上一个稳定版本过去 7 天的 P50/P95 数据。 - 红线:绝对不能触碰的底线(如:注入攻击成功、敏感数据泄露)。

监控线 指标 通过门槛 (Example) 暂停阈值 (Example) 回滚阈值 (Example)
质量 采纳率、任务成功率 ≥ 基线 < 基线 - 3% < 基线 - 5% 或 出现严重 Bad Case
延迟 P95、超时率 ≤ 基线 + 10% > 基线 + 15% > 基线 + 25% 或 超时率翻倍
成本 单次 Token 消耗 ≤ 基线 + 10% > 基线 + 15% > 基线 + 25% 或 预算耗尽速率异常
风险 注入/越权/泄露 0 事件 命中红队样本 任一 越权/泄露/注入 成功即回滚

灰度推进手册:推进、暂停、回滚

灰度最考验人心态的时候,就是数据“似是而非”的时候。比如延迟涨了一点点,但用户没投诉。这时候千万别开会讨论“这算不算问题”,直接按表执行:信号不确定就暂停,指标越界就回滚,只有证据确凿才能推进。[5][6]

决策表(打印出来贴墙上):

信号状态 你的动作 必须留存的证据 资产回写
绿灯:指标稳定,业务指标向好 推进 (扩量/延长观察) 当前版本号;关键指标截图;Trace 对比 新增正向样本:把这次改进带来的收益固化成测试用例
黄灯:指标波动,但在历史范围内 暂停 (保持流量,查原因) 分桶对比报告(新旧版本/不同租户);失败日志 补充分布样本:把导致波动的特定输入/场景加到回归集
红灯:错误率/延迟/成本 越界 回滚 (切回上一版本) 触发回滚的告警图;最小复现请求 (Curl) 事故复发样本:把这个坑写进阻断级测试,严防死守
黑灯:安全事故 (注入/泄露) 止损 (强回滚 + 关入口) 审计日志;攻击链路;影响面清单 红线样本:攻击样本入库,以后这就是发布红线

监控口径:四线同看

AI 产品上线后,如果你只看 HTTP 200/500,那你离死不远了。必须建立四维监控视图:[6]

  1. 质量线:用户到底爽不爽?(点赞/点踩、采纳率、任务完成度)。
  2. 延迟线:这玩意儿慢不慢?(P95、首字延迟 TTFT、总生成耗时)。
  3. 成本线:这玩意儿贵不贵?(Token 消耗速率、GPU 占用、API 账单预估)。
  4. 风险线:这玩意儿安不安全?(越权调用、敏感词触发、注入尝试)。

风险治理:把 AI 特有风险做成门禁

风险控制不是靠人盯着看,而是要自动化阻断。

风险控制卡

风险类型 监控指标 动作
注入突破 注入攻击成功率 发现 1 例即回滚
越权调用 未授权的 Tool Call 立即阻断 + 封禁用户 + 报警
数据泄露 PII 匹配命中率 超过基线即回滚 + 审计
幻觉 无引用断言比例 暂停灰度,补测数据
过度拒答 拒答率飙升 调整安全策略,别把正常用户杀误伤了

观测性:把“看见”变成默认能力

不要等出了事再去加日志。最低要求:打通 Logs (日志)Metrics (指标)Traces (链路) 三件套。并且,必须有一个唯一的 ID 把它们串起来。[61]

观测性三件套架构图

最小可观测字段合同

请把下面这些字段刻在你的代码里,缺一不可:[61]

  • trace_id: 全链路唯一标识,没有它你根本查不清问题。
  • version_set: {model: "gpt-4", prompt: "v1.2", index: "20251224"},出了事得知道是哪个版本惹的祸。[6]
  • tokens_in / tokens_out: 算钱用的,也是算延迟瓶颈用的。[6]
  • tool_calls: 调了啥工具?耗时多少?成功没?
  • latency_ms: 别只记总耗时,要拆分:排队、检索、首字、生成。

示例:一个能跑的最小观测闭环 (Python)

别整那些虚的,先搞定最基本的 trace_id 透传和指标暴露。我们复用 docs/examples/inference/budgeted_gateway.py (假设已存在,或参考下文逻辑)。

怎么验证你的观测闭环是通的?

  1. 启动网关 (Mock 模式): bash python3 docs/examples/inference/budgeted_gateway.py --provider mock --port 8787 & PID=$! # 等待服务启动 sleep 2

  2. 发请求带上 Trace ID: ```bash # 构造 payload python3 -c "import json; print(json.dumps({'user_id': 'u1', 'prompt': '测试回滚', 'budget_ms': 1000}, ensure_ascii=False))" > /tmp/payload.json

    发送请求,强制带上 x-trace-id

    curl -sS -X POST http://127.0.0.1:8787/chat \ -H 'Content-Type: application/json' \ -H 'x-trace-id: canary-test-001' \ --data-binary @/tmp/payload.json > /tmp/response.json

    echo "Response:" cat /tmp/response.json ```

  3. 检查指标: bash curl -sS http://127.0.0.1:8787/metrics | grep "gateway_requests" 预期输出:你应该能看到 gateway_requests_totalgateway_requests_latency_seconds 有变化。

  4. 清理: bash kill $PID rm /tmp/payload.json /tmp/response.json

这个实验告诉你:Trace ID 是你唯一的救命稻草。无论是日志、指标还是报错,只要带上这个 ID,你就能还原现场。[61]

推理服务:从能跑到能运维

服务选型不是看谁跑分高,是看谁能运维。

  • KServe: 适合已经是 K8s 重度用户的团队,标准化程度高,运维界面统一。[63]
  • TGI (Text Generation Inference): Hugging Face 出品,适合想快速把模型跑起来且性能不错的团队,支持流式、支持量化,开箱即用。[49]
  • vLLM: 吞吐量大杀器,如果你自己搞推理服务,大概率会用到它作为后端。

选型灵魂拷问: 1. 能 10 分钟回滚吗? 如果回滚需要重新打镜像、推仓库、拉起容器,那就不合格。最好是切配置就能回滚。 2. 能限流吗? 别指望自动扩缩容救你,那是滞后的。必须有配额(Quota)和限流(Rate Limiting)。 3. 多租户隔离了吗? 别把 A 公司的 Prompt 泄露给 B 公司。

告警与处置:先保命,再治病

告警响了,优先级如下: 1. 降级:关掉费钱的工具,切断复杂的逻辑,只留核心对话。 2. 限流:把超额的用户踢掉,保住大盘。 3. 回滚:切回上一个版本。 4. 复盘:等火灭了,再慢慢查原因。

模板:事故 Runbook (10 分钟止损版)

把这个表格填好,打印出来,贴在屏幕旁边。

阶段 时间限制 动作指令 产出物
感知 T+3min 确认影响谁?挂了多少?核心功能能不能用? 影响范围通告
止损 T+7min 能降级先降级,不能降级立刻限流,搞不定就回滚。别查 Bug! 止损操作记录
取证 T+10min 截取告警图,保存失败的 Trace ID,导出当前配置快照。 证据包 (Evidence Pack)
定位 T+N 拿到证据包,在预发环境复现。 最小复现 Payload
根治 T+24h 修复 Bug,把 Case 加到回归集,写复盘文档。 回归集更新 + 复盘报告

常见陷阱

  1. “幽灵发布”:出了事问改了啥,回答“好像改了点 Prompt”。如果你不能精确指向一个 Version Set,你就是在裸奔。[6]
  2. “无代表性灰度”:灰度只放了内部员工,结果上线被真实用户的长文本把 GPU 显存撑爆了。灰度样本必须包含攻击样本、长文本样本和高成本样本。[6]
  3. “回滚死循环”:想回滚,发现数据库结构变了,或者索引格式变了,回不去了。永远保留上一个版本的兼容性,或者保留旧版本的全套环境。[5][6]

交付物清单与验收标准

  • [ ] 灰度策略卡:明确写着通过和回滚的数字门槛。[5]
  • [ ] 四线监控面板:Grafana 上必须有 质量/延迟/成本/风险 四个板块。[6]
  • [ ] Runbook:必须有一份 10 分钟止损手册。[5]
  • [ ] 可观测性证明:任意给一个 Trace ID,能查到它的输入、输出、耗时拆解和版本号。[61]
  • [ ] 推理服务方案:明确是用 KServe、TGI 还是托管 API,并有配套的回退路径。[49][63]

下一章

运维保的是底线,评测保的是上限。下一章我们将深入构建一套无情的评测机器:离线评测、在线监控、红队演练与持续回归。去这里:18-evaluation.md

参考

详见本书统一参考文献列表:references.md