核心是同一用户固定分组、结果可复现、配置热更新;因rand.intn和time.now().unixnano()导致分流不可重现,无法区分模型变化与分流故障。

Go 里做 AI 模型的 A/B 测试,核心不是“调用哪个模型”,而是“同一用户永远进同一组、结果可复现、配置能热更”——否则你根本分不清是模型变了,还是分流逻辑崩了。
为什么不能用 rand.Intn 或 time.Now().UnixNano() 做分流
常见错误是写个 if rand.Intn(100) 来切 10% 流量。问题在于:
-
rand.Intn每次运行种子不同,重启服务后用户分组全乱; -
time.Now().UnixNano()更糟——毫秒级变化导致同一请求在不同 handler 中哈希结果不一致; - AI 模型实验常需回溯某用户历史行为,若分组不可复现,所有归因分析都失效。
真正可用的分流必须满足:输入相同 userID(或 requestID),输出永远相同整数。Go 1.21+ 推荐直接用 hash/maphash,它比 murmur3 更轻、更安全、无第三方依赖。
用 hash/maphash 实现稳定分桶的实操要点
别照抄文档直接 new 一个 maphash.Hash 就 write ——容易踩三个坑:
- 必须全局复用同一个 seed:
var globalSeed = maphash.MakeSeed(),每次调用h.SetSeed(globalSeed),而不是每次请求都maphash.MakeSeed(); -
userID是int64?别用strconv.Itoa转字符串再哈希——负数、前导零会导致字节序列不一致;改用binary.PutVarint(h, &id)或unsafe.Slice(unsafe.Pointer(&id), 8); - 哈希输入里不要拼实验名(比如
"recommender_v3:" + userID)——后续扩组或改比例时,哈希值全变,用户重分桶;实验名应作为路由参数独立传入,不参与哈希计算。
示例代码片段:
func getBucket(userID int64, totalBuckets uint64) uint64 {
h := maphash.New()
h.SetSeed(globalSeed)
binary.PutVarint(h, &userID)
return h.Sum64() % totalBuckets
}
按权重分配(如 30%/70%)别用 if-else,用前缀和匹配
如果模型 A 占 30%、模型 B 占 70%,直接写 if hash%100 看似简单,但有隐患:
- 当权重不是整百数(比如 17%/83%),
%100引入额外偏差; - 后续加 C 组(17%/83%/0% → 17%/50%/33%),所有条件分支都要重写;
-
hash.Sum64()是 uint64,取模运算在非 2 的幂时分布不均,尤其当哈希本身不够理想时。
正确做法是算总权重,再用前缀和匹配:
weights := []int{30, 70}
total := 100
h := hash.Sum64() % uint64(total)
acc := 0
for i, w := range weights {
acc += w
if int(h)
<p>这个逻辑兼容任意整数权重,且无精度损失,加组/调权都不动核心逻辑。</p>
<h3>配置必须支持热加载,key 名要防非法字符</h3>
<p>AI 模型实验上线后,经常要动态调流量比例、关某组、切新模型版本。硬编码配置或拼接 Redis key 都会出事:</p>
-
fmt.Sprintf("ab_config:%s", expName)—— 若expName是"rec-v3/beta",Redis key 含斜杠,查不到还难 debug; - 没版本号字段的 JSON 配置,旧代码读到新增字段会静默忽略,导致权重错配却无报错;
- 配置没加 TTL 或监听机制,改完配置要等 5 分钟才生效,线上问题响应滞后。
建议 key 命名用 "ab_cfg_" + strings.Map(func(r rune) rune { if unicode.IsLetter(r) || unicode.IsDigit(r) { return r } return -1 }, expName),配置结构体带 version int 字段,用 fsnotify 或 Redis pub/sub 触发热更新。
最易被忽略的一点:AI 模型 AB 测试中,**分流逻辑和模型推理必须解耦**。分流只决定“用哪个模型”,不决定“怎么调用模型”。一旦把模型加载、超参、fallback 逻辑混进分流函数里,出问题时你分不清是哈希错了,还是模型 infer 失败了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











