Agent灰度别随机分流,用一致性哈希给用户分桶
灰度放量这事我以前用 random() 分流,吃过一个特别隐蔽的亏:同一个用户连着问两句,第一句被分到新版 prompt,第二句又随机回了旧版,回答风格前后不一致,用户当场就懵了——"你刚才不这么说话啊"。后来换成按用户 ID 做一致性哈希分桶,同一个人永远落在同一桶,问题没了。这篇就讲清楚为啥要分桶、怎么分。
随机分流的两个坑
-
同一用户体验不连续。 上面那个例子,多轮对话里跳来跳去,体验割裂。
-
没法稳定观测。 想看某个用户在新版下的完整表现,结果他一半请求在新版一半在旧版,数据根本没法归因。
灰度的核心诉求是"同一个用户稳定地待在同一组",随机做不到,得用哈希。
一致性分桶怎么写
把用户 ID 哈希到 0-99 一百个桶里,再按放量比例划一条线,线左边走新版,右边走旧版。放量从 5% 加到 20%,只是把线往右挪,已经在新版的用户不会被踢回旧版——这就是"一致性"。
import hashlib
def bucket_of(user_id, salt="agent_v18"):
# salt 跟着实验走,换实验就换 salt,避免和上个实验的分桶相关
h = hashlib.md5(f"{salt}:{user_id}".encode()).hexdigest()
return int(h, 16) % 100 # 稳定落到 0-99
def pick_version(user_id, rollout_pct):
return "new" if bucket_of(user_id) < rollout_pct else "old"
# 放量 5%:bucket 0-4 的用户进新版
ver = pick_version(user_id, rollout_pct=5)
prompt = NEW_PROMPT if ver == "new" else OLD_PROMPT
几个我补上的关键点
-
salt 一定要跟实验绑定。 不加 salt,所有实验都用同一套哈希,那永远是"运气好"的那批人在所有新功能里当小白鼠,样本有偏。每个实验换个 salt,分桶就独立了。
-
放量只增不减时才一致。 5%→20% 安全(新版用户不回退)。但要是你 20%→10% 缩回去,被踢出的那 10% 又变回旧版,体验也是断的——回退尽量直接砍到 0(全量回滚)而不是缩比例。
-
灰度版本号埋进日志。 每条请求记下走的是 new 还是 old,不然事后想对比两版效果,连哪条是哪版都分不清。我吃过这亏,跑了三天灰度发现没记版本号,数据废了。
怎么判断该不该全量
我盯三个指标分桶对比:转人工率、平均轮次、用户重复追问率。新版这三个都不比旧版差,且样本够(每桶几千条以上),才往上放量。光看"感觉新版回答更好"不算数,得有数。
说点不好的
一致性哈希分桶不是银弹。它假设"用户 ID 稳定可得",但有些场景用户是匿名的,只有个会话 ID,会话一断分桶就变了——这种只能退而求其次按会话分桶,接受"同一人不同会话可能不同组"。还有,分桶只解决"分得稳",分完两版到底哪个好,还得靠你前面埋的指标和耐心,急不得。
模型推理走的讯飞星辰 MaaS 现成 API,灰度时新旧两版 prompt 可以挂同一个模型只换提示词,也可以顺手对比不同模型,切换不用动部署,分桶逻辑在我网关层就搞定了。
你们灰度是随机分还是按用户哈希分?放量节奏怎么定的?评论区聊聊。
更多推荐


所有评论(0)