系列《AI 应用生产化手册》第 11 篇(共 30 篇)|配套开源项目:github.com/ChenYingbo/...
一、先看问题:全量上线 = 全量冒险
R01 让提示词有版本了,但上线方式还是老问题:
- 全量上线 = 全量冒险------提示词改坏了,所有用户同时踩坑;回滚再快,差评已经产生了。
- "要么全上要么不上"------没有中间态,变更决策变成赌博。
- 灰度 = 玄学------"先放一部分人",但放给谁?怎么放?出问题怎么收?
解法:配置化灰度------按 user_id 确定性分桶,放量比例走配置,出问题只影响少数用户,且可随时回退。
核心认知:灰度不是"上线技巧",是"风险控制机制"------它把"一次全量的赌博"变成"分步验证的决策过程"。
二、原理
三种灰度模式
| 模式 | 做法 | 适用 |
|---|---|---|
| 影子模式(shadow) | 新版本并行跑、结果只记录不展示 | 验证"新版本会怎么回答"(不影响用户) |
| 金丝雀(canary) | 小比例真实用户先上(如 5%) | 常规变更 |
| 按用户分桶 | user_id 哈希分桶,指定比例命中新版本 | 需要"同一用户体验一致"的场景 |
本 demo 用"按用户分桶"------确定性分桶保证同一用户每次都走同一版本。
分桶:为什么用哈希不用取模
ini
错误:bucket = user_id % 100
→ 用户 ID 规律性强(连续 ID 落相邻桶),分布不均
正确:bucket = md5(user_id) % 100
→ 哈希打散,均匀落入 0-99;同一用户永远同桶(确定性)
实测验证 (scripts/rollout.py simulate):10000 用户 @10% 放量 → 命中 10.47%,桶分布每 10 段各 ~10%,完美均匀。
放量节奏与观测
| 阶段 | 放量 | 观察什么 |
|---|---|---|
| 试点 | 5-10% | 错误率、P95 延迟、评测分数 |
| 扩大 | 25-50% | 用户反馈、成本变化、质量指标 |
| 全量 | 100% | 灰度完成,回滚预案待命 |
关键:每步观察 ≥1 个完整发布周期------质量退化是概率性的,样本不够看不到。
灰度 vs A/B
| 灰度 | A/B | |
|---|---|---|
| 目的 | 降低风险(别炸) | 对比效果(谁更好) |
| 决策 | 放量还是回滚 | 保留哪个版本 |
先灰度保平安,再 A/B 找最优。
三、动手:分桶 + 放量 + 演练
bash
git clone https://github.com/ChenYingbo/ai-prod-demo.git && cd ai-prod-demo
source .venv/bin/activate
# 1. 验证分桶均匀性(10000 用户 @10%)
python scripts/rollout.py simulate --n 10000 --rollout 10
# 真实输出:canary 命中 1047 (10.47%) ✓ 分布与配置一致(均匀)
# 桶分布(0-99 每 10 段): {0:1000,1:1000,...,9:1000}
# 2. 查看放量配置
python scripts/rollout.py status
# 3. 开启灰度(10% 用户走新提示词 v1.1.0)
CANARY_ROLLOUT=10 uvicorn app.main:app --port 8000
curl -X POST http://localhost:8000/api/chat \
-H "Content-Type: application/json" -d '{"question":"什么是 RAG?","user_id":"user-001"}'
# 日志:event=canary_decision feature=chat-system user_id=user-001 bucket=xx variant=stable/canary rollout=10
灰度演练(必做)
CANARY_ROLLOUT=10启动,用user-001~user-020各发一请求- 统计日志里 canary/stable 比例(应 ≈1:9)
- 把
CANARY_ROLLOUT=0重启 → 全部 stable(灰度可关 = 回滚手段之一) - 观察:同一 user 在 10% 和 20% 放量下,命中是"单调"的(10% 时 canary 的用户,20% 时仍是 canary)
四、真实踩坑(含压测翻车现场)
- 桶不均匀 :用
user_id % 100直接取模------连续 ID 全落相邻桶;必须 md5 打散 - 灰度与缓存冲突:结果被缓存后灰度切换不生效------缓存 key 要考虑版本(本系列 C02 的 key 含 system 全文,天然隔离)
- 只看成功率不看质量:只盯错误率,质量悄悄退化------质量要靠评测样本
- 一放到底:10% 验证 5 分钟就冲 100%------样本量不够
- 放量比例写死 :改比例要发版------
CANARY_ROLLOUT走配置/env - 灰度没有回滚预案:回滚 = 放量归零(+ 降级链兜底)
- (实测)第二次压测全命中缓存 :我们做压测复现时,第二次用相同问题模板 → 全部命中第一次的缓存,QPS 从真实 1.5 飙到 415------差点把缓存性能当成模型吞吐。压测/灰度测试一定要换题或清缓存
五、小结
- 三种模式:影子 / 金丝雀 / 按用户分桶
- 哈希分桶:确定性 + 均匀性(md5 % 100,不是直接取模)
- 放量节奏:10→25→50→100,每步过观察门
- 回滚 = 放量归零------灰度可关,就是回滚手段
明天(R03):《A/B 实验》------把"哪个版本更好"从感觉变成统计结论(含真实 z 检验:85% vs 78% 居然不显著)。 收藏 + 关注,每天一篇,30 天把 AI 应用送上生产。