A/B test models in production
TL;DR · AI 摘要
Together AI平台提供端点级A/B测试方案,通过动态流量分配验证LLM模型效果,避免传统方法的基础设施耦合问题。
核心要点
- Together AI平台在端点层实现A/B测试,无需修改客户端代码
- 传统方法存在路由逻辑耦合和实验后代码残留问题
- 实验支持95%/5%到50%/50%的流量比例动态调整
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- A/B测试生产环境实现
- 传统方法
- 客户端耦合
- 流量漂移
- Together AI方案
- 端点层路由
- 动态流量分配
- 无侵入回滚
金句 / Highlights
值得收藏与分享的关键句。
传统方法需要客户端代码修改,导致基础设施耦合和实验后残留代码
Together AI平台在端点层处理路由,实验配置变更无需客户端更新
控制组流量可被重新采样分配,例如95%保持控制组,5%分配给变体
实验删除后自动回退至控制组,无需修改客户端或路由逻辑
生产环境中的A/B测试模型
摘要
A/B实验允许将某个端点的实时流量划分为固定群体,包含一个对照组和最多20个变体,每个变体分配一定比例的流量。这使你能够按选定的曝光程度,测量候选模型在真实用户中的表现。通过一次调用即可逐步提升变体比例,并使用蓝绿部署推广胜出者。删除实验后,所有流量会自动返回对照组,无需修改客户端或路由逻辑即可恢复。下文将演示如何在实时端点上运行实验:从95%/5%逐步调整到80%/20%、50%/50%,最后删除实验并检查每个阶段的实际流量分配情况。
在生产环境中为LLM实现A/B测试
迟早每个团队都会面临同样的问题:新模型相比当前模型是否真的对用户更好?这里的"更好"不是指基准测试表现,而是指用户留存率、点赞率、任务完成率等实际产品指标。
影子流量无法回答这个问题。影子测试只能验证候选模型在延迟、错误率、吞吐量等操作层面的稳定性,但其响应会被丢弃,没有任何用户会实际使用这些响应。质量评估需要真实暴露给终端用户,让部分用户使用模型B,并观察实际效果。
通常团队会通过以下方式自行实现:
- 在客户端代码中使用功能标志或用户ID哈希取模100
- 客户端在两个端点(或两个硬编码模型字符串)之间切换
- 某处的电子表格解释A组和B组的定义差异
虽然可行,但这种方式会将实验与基础设施耦合,带来后续问题:路由逻辑随应用一起发布,用户群体划分可能因客户端缓存决策而漂移,即使实验"结束"后,分支代码仍会长期残留,因为没人确定删除是否安全。
Together AI平台允许在端点层面运行A/B实验逻辑。
实现原理
A/B实验绑定到某个端点,声明包含一个对照组和一个或多个变体的成员,每个变体指向一个部署,并分配百分比权重,所有权重总和必须为100%,用于控制流量路由。
端点路由器的工作机制是:当基础流量请求到达对照组时,实验会重新采样并重新分配流量,例如95%保留在对照组,5%分配给变体。
具体机制说明:实验会细分对照组的基础流量份额。路由首先通过权重分配处理请求;当A/B实验的胜出者是对照组时,请求会根据实验变体的百分比重新采样。由于对照组是唯一入口,拆分成员的百分比即代表绝对流量份额。值得注意的是,当对照组的拆分权重为0时,实验将没有可细分的流量,导致整个实验无法获得任何流量。
重要的是,变体部署不得包含在端点的流量分割中,平台要求变体的权重必须为零;只有控制组存在于基准分割中。实验将完全拥有分配给变体的流量,其百分比即为其流量份额。如果变体还能从分割中获取容量加权流量,您的测量结果将被悄然误导。可以这样理解:应将变体设置为影子部署:创建后状态为READY,权重为零,然后由实验的百分比设置引导流量至该变体。
另一个重要点是,A/B百分比是真实的固定流量份额,总和为100%,且与副本数量无关。我们特意将其与流量分割权重(按就绪副本计算并遵循容量规则)区分开来。实验是一种测量工具;在测量过程中,您希望分割保持恒定,而不是随自动扩展漂移。
创建95/5实验:
tg beta endpoints ab my-org/candidate-model --control $CONTROL_DEPLOYMENT --percent 5您的客户端不会注意到这个实验,因为从表面上看,端点名称、API和密钥保持不变。在后端,5%的请求现在将由变体候选模型处理。
内部机制:扩容、测量、结束
扩容是重新发送成员列表
没有单独的“扩容”API,更新将替换完整的成员列表,这保持了思维模型的简洁(实验始终完全由其成员定义),并使每次扩容都成为可审查的明确变更:
# 第二周:候选模型在5%表现良好 —> 提升至20%
client.beta.endpoints.ab_experiments.update(
id=experiment_id,
endpoint_id=endpoint_id,
update_mask="members",
etag=experiment.etag, # 同事的并发扩容将被拒绝,而非覆盖
members=[
{"deployment_id": control_dep, "percent": 80, "role": "AB_EXPERIMENT_MEMBER_ROLE_CONTROL"},
{"deployment_id": variant_dep, "percent": 20, "role": "AB_EXPERIMENT_MEMBER_ROLE_VARIANT"},
],
)更新通过etag进行保护,因为如果同事在您编写更新时进行了扩容,您的更新将被拒绝,而非静默覆盖他们的设置。
通过这种API设计,您仍需做出常见暴露选择:分配多少流量到B组:
| 分割比例 | 信号速度 | 风险 | 使用场景 | |---------|---------|-----|---------| | 95/5 | 慢(需要体积/时间) | 最小 | 新模型首次真实暴露 | | 80/20 | 中等 | 可控 | 候选模型通过5%测试,需要更显著的读数 | | 50/50 | 最快 | 半数用户 | 两个已知良好选项的后期确认 |
最多可包含20个变体成员,您还可以运行多向测试。例如,您可能想尝试一个全精度端点,同时与三个其他量化变体(V1、V2、V3)进行对比。只要百分比总和仍为100%,并且恰好有一个控制组,这将按预期工作。
测量:将平台指标与产品指标结合
每个请求都由特定的部署处理,每个平台指标都可以按部署进行查看 —— 因此比较的基础设施层面(延迟、错误率、每组吞吐量)是一个过滤条件,而不是一个独立项目。产品层面的分析由您掌控:将每个响应对应的部署信息(包含在响应元数据中)与您的质量指标(评分、重试次数、任务完成数)一同记录,而连接键仅仅是部署 ID。平台不会擅自猜测您的质量指标,而是让归因变得简单直观,使您的分析系统能够做出判断。
结束阶段:先推广,后删除
假设实验显示变体版本胜出。结束实验需要分两步操作:
- 通过灰度发布进行推广。从控制组部署向变体部署执行蓝绿发布。健康检查门、传播等待时间和回滚保护机制均适用。
- 删除实验。所有实验路由配置将被清除,100%流量将遵循端点的基础流量分配规则(发布后该规则指向胜出版本)。
如果变体版本失败,您只需删除实验,流量将完全回归控制组。在后端,变体部署会自动缩容至零或被删除。
tg beta endpoints rm abx_abc123 # 删除实验;100%流量回归基础分配
tg beta endpoints rm dep_variant123 # 或删除变体部署 —— 自动解除实验 + 分配规则特殊情况
实验进行中变体版本性能下降。影响范围有多大?
仅限其所属组。部署是独立监控和独立自动扩展的,这意味着表现不佳的变体不会拖累控制组。要解决这个问题,您可以重新发送排除故障变体的成员组,用户将在一定传播时间后重新回到控制组。这也是为什么建议从5%起步的原因。
观察到的流量分配比例是否与配置的百分比一致?
在有意义的流量规模下,答案是肯定的!下面的实验展示了这一现象。在小窗口期内可能会出现抽样噪声:1000个请求的5%份额是一个小样本。如果观察到的流量比例偏离配置值且持续偏离,请检查上方的设置规则。
A/B实验和灰度发布能否在同一个端点同时运行?
可以,且组合顺序定义为:路由机制首先解析基础分配,然后执行A/B实验(对控制组流量进行细分),最后进行灰度发布(在源部署和目标部署之间重新抽样)。平台仍然强制要求每个端点只能有一个活跃的灰度发布,但如果阶段设置存在重叠,系统会设计为能够兼容组合。
用户群体是否对每个用户保持粘性?
分配机制使用请求的抽样键,例如请求体中的顶层 prompt_cache_key 或用户字段,因此携带相同键的请求会保持路由一致性,用户可以在会话期间持续保留在同一个测试组中。没有键的请求会按请求随机分配(下方的实验测量使用了无键流量,因此观察到的流量比例与配置百分比高度吻合)。如果您的研究需要用户级别的一致性(尤其是多轮质量对比场景),请发送一个稳定的用户字段。
从头到尾展示一次 A/B 实验
我们对一个实时端点执行了完整的实验生命周期,包括从 95/5 创建、逐步调整到 80/20、再逐步调整到 50/50,最后删除实验。在整个过程中我们保持每秒 3 个请求的稳定流量,每个请求都会被标记为为其提供服务的部署版本。下图展示了变体端点接收到的流量,绿色圆点表示变体流量占比:
以下是每个阶段配置与实际观察到的流量占比:
配置(控制组 / 变体组) | 实际观察值 | 请求数 ---|---|--- 95 / 5 | 95.3 / 4.7 | 1,330 80 / 20 | 79.2 / 20.8 | 1,348 50 / 50 | 50.2 / 49.8 | 删除后 | 100.0 / 0 | 360
本次实验中有三个关键细节需要特别说明:
- 每次渐进调整仅需一次调用,你可以通过当前 etag 重新发送完整的成员集。在两次渐进调整中,etag 会依次递增为 1 → 2 → 3;如果使用了过期的 etag,系统会拒绝该请求,而不是静默覆盖同事的修改。
- 流量分发是快速但非即时的。我们在每次更新后等待约 75 秒再进行测量;路由层获取实验配置变更的延迟与流量分割变更的延迟相当,都在 30-60 秒范围内。
- 删除操作:在删除实验后,我们发送了 360 个连续请求,所有请求都落在控制组。除了删除操作本身,系统不会保留任何剩余的用户群体逻辑。
以下是控制台在端点「流量测试」标签页中显示的实验界面(A/B 测试和影子测试共享此页面):
立即尝试!
你需要满足以下条件才能进行实验:
- 一个正在处理流量的控制组部署
- 一个已创建且状态为 READY 的候选部署(未参与流量分割)
操作步骤如下:
- 以 95/5 的比例创建实验
- 等待直到收集到足够的数据量(5% 的设置就是为了在数据收集期间保持低暴露量)
- 当数据就绪时通过单次更新进行渐进调整,当结论明确时通过发布操作完成切换,实验结束后执行删除操作(无需额外清理)
📚 文档:专用模型推理 → A/B 测试 https://docs.example.com/ab-testing