A/B 实验(Experiments)
A/B 实验让”红色按钮还是蓝色按钮转化更高”这类问题用数据回答:在 Console 定义实验与变体权重, SDK 侧用一致性 hash 把用户稳定分流到变体,上报曝光(Exposure)与转化(Conversion), Argus 实时聚合各变体的去重转化率并给出统计显著性——运行中默认以序贯 always-valid 检验为主结论, 从机制上防止”边跑边看、看到 p 小于 0.05 就停”的偷看(peeking)谬误。
实验没有独立后端服务:定义与分流在 config 服务(与 Feature Flag 共享分桶引擎)、结果统计在
observability-core、采集在 ingestion(POST /v1/experiment)。能力地图见
docs/07-design/service-experiment.md;
统计口径真相源是
docs/02-product/03-baseline-metrics-dictionary.md Part B。
核心概念
实验、变体与状态
- 实验(Experiment):一个
key(如btn_color)+ 若干变体(Variant)。 - 变体权重(weight):非负整数,按权重比例分配流量(如 control=50 / treatment=50); 权重 0 = 不参与分配。
- 状态机:
draft(草稿)→running(运行中)→stopped(已停止),可从 stopped 重置回 draft。只有running状态参与分流——停止实验后所有用户自动回落对照 / 默认体验。
一致性 hash 分流
分桶是确定性纯函数(services/config/internal/assign/assign.go):
bucket = FNV-1a_32( experiment_key + ":" + user_id ) mod 总权重
按变体 position 升序累加权重区间,bucket 落入哪个区间即命中该变体- 稳定命中:同一
(user_id, experiment_key)任何时间、任何端算出同一变体——用户不会 “这次红下次蓝”。 - 实验间独立:
experiment_key拌入 hash 输入,同一用户在不同实验中的分组互不相关, 避免”所有实验都砸中同一批小白鼠”。 - 安全降级:实验非 running、无变体、正权重和为 0、或
user_id为空时不分配 (调用方按对照 / 默认兜底),绝不随机命中。
曝光与转化
- 曝光(exposure):用户被实际分到并”见到”某变体。分析的分母是去重曝光用户数
unique_exposed。 - 转化(conversion):用户达成目标(可带
metric名与数值value)。分子是 去重转化用户数unique_converted;转化率 =unique_converted / unique_exposed, 按用户去重——同一用户转化 3 次只算 1。 - 只有产生过曝光的用户才计入实验;iOS / Android SDK 的转化上报会归因到本会话已曝光的变体, 未曝光先转化的调用会被丢弃。“转化时间必须晚于首次曝光”的服务端强校验为规划中。
显著性:序贯为主、固定样本揭盲为辅
结果面板对每个非对照变体给出两族判定(对照 baseline 默认取 key 排序的首个变体):
| 判定 | 是什么 | 什么时候可信 |
|---|---|---|
| 序贯 always-valid(mSPRT) | always_valid_p_value / sequential_significant | 运行中随时看都有效——任意时刻查看都控制假阳性率,是运行中实验的主结论 |
| 固定样本 z 检验 | 两比例 z 检验(合并标准误)+ 多变体 Holm-Bonferroni 校正后的 p_value_adjusted / significant_95 | 只在达到预定样本量后单次揭盲才有效;运行中反复读它并据此早停会把假阳性率从 5% 抬到 20% 以上 |
小样本保护:期望成功 / 失败数不足(np < 5 经验法则)时标记 low_sample,此时正态近似不可靠,
不要判赢家——“不显著”也不等于”无差异”。
防偷看(peeking):不要用固定样本 p 值做”看起来快显著了,再等两天”式的决策——那正是 假阳性膨胀的来源。运行中一律读序贯判定;固定样本判定留到达到计划样本量后一次性揭盲。
接入
平台矩阵
| 平台 | 取变体 | 自动曝光 | 手动曝光 / 转化上报 | 快速开始 |
|---|---|---|---|---|
| iOS | Argus.getVariant(key)(需 enableAutoExperiments + userId) | ✅(getVariant 即曝光,会话内去重) | trackExposure / trackConversion | /quickstart/ios/ |
| Android | Argus.getVariant(key)(同上) | ✅ | trackExposure / trackConversion | /quickstart/android/ |
| Web | Argus.config.getVariant(key, def)(需 remoteConfig: true) | —(规划中) | —(规划中,可直调 REST) | /quickstart/web/ |
| Flutter | Argus.configGetVariant(key, def)(同上) | — | — | /quickstart/flutter/ |
| React Native | Argus.config.getVariant(key, def)(同上) | — | — | /quickstart/react-native/ |
分流要求事件带 user_id(init 传 userId 或调用 identify),否则不参与实验。
最小接入片段
iOS
var config = ArgusConfig(/* dsn ... */)
config.enableAutoExperiments = true // init 后台拉取实验分配;需设置 userId
// 取变体并自动上报曝光(会话内去重);nil = 未分配,按对照兜底
if Argus.getVariant("btn_color") == "treatment" {
showRedButton()
}
// 用户达成目标时上报转化(归因到本会话已曝光的变体)
Argus.trackConversion("btn_color", metric: "purchase", value: 29.9)
// 也可完全手动:自有分配逻辑时只做上报
Argus.trackExposure("btn_color", variant: "treatment")Console 使用
进入 Console 左侧导航 Experiments 页(先在左上角选择项目):
- 新建实验:填实验 key(如
btn_color)与描述;key 重复返回 409。 - 管理变体:展开实验,添加变体(key 如
control/treatment、非负整数权重、排序); 启动前至少添加 2 个变体;权重 0 的变体不参与分配。 - 状态操作:草稿 → 启动(进入 running,开始分流)→ 停止(全部回落对照)→ 可重置草稿后调整再启动。
- 结果面板(展开实验可见,近 14 天,可刷新):每个变体一行——
- 曝光用户(及曝光次数)、转化用户(及转化次数)、转化率(按用户去重)、转化值;
- 提升 / 显著性:相对对照的 lift 百分比 + 判定徽章——
✓ 显著·序贯(可安全下结论)、运行中未显著、⚠ 样本不足(勿判赢家);固定样本 p 值放在悬浮提示(tooltip)中并附 偷看警示。
样本量 / MDE(最小可检测效应)设计器、SRM(样本比例失衡)自动校验、护栏指标自动止损、 贝叶斯统计为规划中能力;当前请在实验设计时自行预估样本量,并对流量分配异常保持警惕。
SDK API 速查
| 能力 | iOS | Android | Web / RN | Flutter |
|---|---|---|---|---|
| 取变体(自动曝光) | Argus.getVariant(key) | Argus.getVariant(key) | — | — |
| 取变体(仅取值) | Argus.configGetVariant(key, def) | Argus.configGetVariant(key, def) | Argus.config.getVariant(key, def) | Argus.configGetVariant(key, def) |
| 上报曝光 | Argus.trackExposure(key, variant:) | Argus.trackExposure(key, variant) | — | — |
| 上报转化 | Argus.trackConversion(key, metric:value:) | Argus.trackConversion(key, metric, value) | — | — |
MCP 工具
接入 Argus MCP Server 后,AI 能按正确统计口径解读实验
(源码:experiment_list.ts /
experiment_results.ts):
| 工具 | 用途 | 关键参数 |
|---|---|---|
experiment.list | 列出项目的实验:key、状态(draft / running / stopped)、描述、变体 key | project_id |
experiment.results | 各变体曝光 / 转化 / 去重转化率 + 双轨显著性(固定样本 z 检验与序贯 mSPRT),变体按转化率降序 | project_id、experiment_key、days(1–90,默认 14)、baseline(指定对照,缺省取 key 排序首个变体) |
工具描述内置了防 peeking 指引:对运行中实验,AI 会以 sequential_significant 为结论依据,
不会用固定样本 p 值诱导”再观察几天”。
配额与计费
- 1 条曝光或转化事件 = 1 个计费事件(event),计入项目事件量配额。
- 详见 套餐、配额与计费。
FAQ
为什么运行中的实验一直显示”运行中未显著”?
序贯检验为了保证”随时看都有效”,判定天然比固定样本保守——这是特性不是缺陷。样本继续积累后
若真有差异会转为 ✓ 显著·序贯;也可等达到计划样本量后读固定样本判定(单次揭盲)。
同一个用户会不会一会儿 A 组一会儿 B 组?
不会。分桶是 hash(experiment_key + user_id) 的确定性函数,跨端、跨时间稳定。但前提是
user_id 稳定——匿名用户(无 user_id)不参与分配。
对照组是哪个变体?
结果统计默认取变体 key 排序的首个为 baseline(建议把对照命名为 control,天然排前);
MCP experiment.results 可用 baseline 参数显式指定。
改变体权重会打乱已有分组吗? 权重决定 hash 桶区间的切分,调整权重会导致部分用户的命中区间移动。建议实验运行中不要调权重; 要扩量请在设计时就用目标比例,或停止实验后另起新实验 key。
两个变体的曝光量差得离谱(如 9000 vs 11000)怎么办? 这大概率是分流 / 采集 / 上报 bug(SRM,样本比例失衡),此时整个实验数据不可信,不要下结论。 自动 SRM 校验为规划中,当前请人工核对各端曝光上报是否对称。