跳到正文
Together AI Blog·· 2026-08-17精选AI 评分63

Together AI 推出端点级 LLM A/B 实验功能

A/B test models in production

AI 导读

Together AI 在平台上推出端点级 A/B 实验功能,实验挂在端点上,声明一个 control 和一个或多个 variant,各自指向一个部署并设置百分比,总和须为 100%。

推荐理由

原文给出端点级 A/B 实验的完整机制与实测流量数据,可据此判断生产环境模型灰度对比的落地方式。

正文 · AI 翻译

在生产环境中为 LLM 实施 A/B 测试

迟早每个团队都会想回答同一个问题:与当前模型相比,新模型对我们的用户真的更好吗?不是基准测试上的更好,而是在留存率、点赞率、任务完成率,或者你的产品实际衡量的任何指标上的更好。

影子流量无法回答这个问题。影子测试只能告诉你候选模型在运维层面是可靠的——延迟、错误、吞吐量都没问题,但它的响应会被丢弃;没有任何用户会基于这些响应采取行动。质量问题需要让最终用户真正接触到,让一部分用户使用模型 B,然后比较发生了什么。

通常团队会在应用层自己构建这套机制,使用以下一些组合:

  • 在客户端代码中使用功能开关或对用户 ID 进行 hash-mod-100。
  • 客户端在两个端点(或两个硬编码的模型字符串)之间切换。
  • 某个地方放着一张电子表格,解释 A 组与 B 组的含义。

这能行,但它以伤害后续工作的方式将实验与基础设施纠缠在一起:路由逻辑随应用一起发布,随着客户端缓存决策,队列划分可能会漂移,而且即使在实验“结束”之后,分支代码仍会长期存在,因为没人确定删除它是否安全。

Together AI 平台允许你在端点级别运行 A/B 实验逻辑。

工作原理

一个 A/B 实验附加到一个端点上,并声明成员,其中恰好有一个对照组和一个或多个变体,每个都指向一个部署,每个都有一个 percent 设置,这些设置之和必须为 100,控制流量路由。

端点路由器的工作方式是,每当基础流量向对照组发送请求时,实验会在各分支之间重新采样并重新分配,使得 95% 留在对照组,5% 流向变体。

准确说明其机制:实验会细分对照组在基础流量拆分中所占的份额。路由首先通过权重拆分解析请求;当胜出者是一个 A/B 实验的对照组时,请求会按实验各分支的百分比在它们之间重新采样。由于对照组是拆分成员百分比中的唯一入口,因此这些百分比就是绝对流量份额。同样值得注意的是,拆分权重为零的对照组不会给实验留下任何可细分的内容,因此整个实验将不会收到任何流量。

重要的是,变体部署不得出现在端点的流量拆分中,平台要求变体权重为零;只有对照组存在于基础拆分中。实验将完全拥有路由到变体的流量;其百分比就是其流量份额。如果变体也能从拆分中获取按容量加权的流量,你的测量结果就会在不知不觉中出错。一种理解方式是,你应该像设置影子部署那样设置变体:创建、READY、权重为零,然后让实验百分比设置将流量路由到它。

这里的另一个重要点是,A/B 百分比是真正固定的流量份额,总和为 100%,并且与副本数量无关。我们特意让它与流量拆分权重(按就绪副本计算并随容量变化)不同。实验是一种测量工具;你希望在你测量时拆分保持恒定,而不是随自动扩缩容而漂移。

创建一个 95/5 实验:

    
      tg beta endpoints ab my-org/candidate-model --control $CONTROL_DEPLOYMENT --percent 5
          

你的客户不会注意到这个实验,因为从表面上看,相同的端点名称、API 和密钥都保持不变。在后端,现在将有 5% 的请求由变体候选者来应答。

底层机制:逐步放量、度量、结束

逐步放量就是重新发送成员集合

没有单独的“放量”API,一次更新会替换整个成员列表,这保持了心智模型的简单(实验始终恰好等于其成员所声明的内容),并使每次放量都成为一次显式、可审查的变更:

    
      # Week 2: candidate looks good at 5% —> go to 20%
client.beta.endpoints.ab_experiments.update(
    id=experiment_id,
    endpoint_id=endpoint_id,
    update_mask="members",
    etag=experiment.etag,   # a teammate's concurrent ramp gets rejected, not overwritten
    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。平台刻意不去猜测你的质量指标;它让归因变得轻而易举,这样你的分析系统就能做出评判。

结束:提升,然后删除

假设实验显示变体胜出。结束它分为两步:

  1. 通过 rollout 提升。从对照组部署到变体部署运行一次蓝绿 rollout。健康门禁、传播等待和回滚安全措施都适用。
  2. 删除实验。所有实验路由都会消失,100% 的流量随后遵循端点的基础流量拆分,而在 rollout 之后,该拆分指向你的胜出者。

而如果变体落败,你只需删除实验,流量就会完全回到对照组。在后端,变体部署会缩容到零或被删除。

    
tg beta endpoints rm abx_abc123        # delete the experiment; 100% returns to the base split
tg beta endpoints rm dep_variant123    # or delete the variant — auto-unwinds experiment + split
    

边缘情况

变体在实验中途性能下降。影响范围有多大?

仅限其队列。部署是独立监控和独立自动扩缩的,这意味着一个表现不佳的变体不会拖垮对照组。要解决这个问题,你可以重新发送不包含该故障变体的成员集合,其用户将在一定的传播时间后回到对照组。这也是从 5% 开始是个好主意的原因。

观察到的份额真的与配置的百分比相符吗?

在有意义的流量规模下,是的!要了解实际运行的例子,请查看下面的实验。在较小的窗口内,你可以预期会有采样噪声:1,000 个请求中的 5% 份额是一个小样本。如果你观察到的份额偏差很大并且持续偏差,请检查上面的设置规则。

A/B 实验和 rollout 可以在同一个端点上运行吗?

是的,组合顺序定义为:路由首先解析基础拆分,然后是 A/B 实验(细分对照组的份额),接着是 rollout(在源部署与其 rollout 目标之间重新采样)。平台仍然对每个端点强制实施一个活动 rollout,但这些阶段被设计为在重叠时能够组合。

队列是按用户固定的吗?

分配使用请求的采样键,例如请求体中的顶层 prompt_cache_key 或 user 字段,因此携带相同键的请求会一致地路由,给定用户可以在整个会话中保持在一个测试分支中。没有键的请求实际上按请求随机分配(我们下面的实验测量使用了无键流量,这就是为什么观察到的份额与百分比如此接近)。如果按用户一致性对你的研究很重要,特别是如果你有多轮质量比较,请发送一个稳定的 user 字段。

拆分下的自动扩缩容会发生什么?

每个成员部署根据自己的策略进行扩缩容,按其自身的流量份额确定规模。一个 5% 的变体,边界为 1-2,一个 95% 的对照组,边界为 2-8,是完全正常的形态。你应该独立观察每个队列的副本数,这就是我们在下图中捕获的内容。

展示一个 A/B 实验,从开始到结束

我们在一个实时端点上运行了完整的生命周期,我们以 95/5 创建,逐步调整到 80/20,再调整到 50/50,然后删除。我们这样做时保持稳定的 3 RPS 流,每个请求都归属于服务它的部署。下图显示了变体上看到的流量,绿色点捕获了变体流量份额:

以下是每个阶段配置的与观察到的流量份额:

配置(对照组 / 变体) 观察到的 请求数
95 / 5 95.3 / 4.7 1,330
80 / 20 79.2 / 20.8 1,348
50 / 50 50.2 / 49.8 1,348
已删除 100.0 / 0 360

运行中值得指出的三个细节:

  • 每次调整实际上只是一次调用,你可以用当前的 etag 重新发送完整的成员集。etag 在两次调整中推进 1 → 2 → 3;过期的 etag 会被拒绝,而不是静默覆盖队友的更改。
  • 传播很快但不是即时的。我们在每次更新后等待约 75 秒再进行测量;路由层以与流量拆分更改相同的 30–60 秒时间尺度获取实验更改。
  • 删除:移除实验后,我们发送了 360 个连续请求,它们全部落在对照组上。除了删除留下的需要展开的内容外,没有遗留的队列逻辑。

以下是控制台在端点的流量测试选项卡上显示的实验(A/B 测试和影子测试共享该页面):

自己试试!

你需要一个带有正在服务流量的对照组部署的端点,加上一个候选部署(已创建,READY,不在流量拆分中)。然后:

  1. 以 95/5 创建实验。
  2. 让它运行直到你有足够的量——5% 的全部意义在于在你收集数据时保持低曝光。
  3. 当数据表明时,用一次更新进行调整。当结果决定时,用 rollout 进行提升。完成后删除——没有其他需要清理的。

📚 文档:专用模型推理 → A/B 测试

来源:Together AI Blog · together.ai