Thinking Machines 的Tinker改变了人们做后训练的方式。你在自己的笔记本电脑上用纯 Python 编写训练循环,Tinker 在其 GPU 上运行前向和反向传播。我们非常喜欢它,与我们合作的许多团队也是如此。
然而,随着这些团队扩大规模,他们不断提出同样两个问题:
- 一旦你运行几十个实验,按 token 计费就会变得昂贵。
- 你只能使用提供商决定支持的模型和功能。
我们认为一个好的 API 值得不止一种实现。今天我们发布Spindle,一个开源、兼容 Tinker 的训练服务器,运行在你的 Modal 工作区中。因为服务器归你所有,你可以自定义其中的任何内容,并且你支付的是 GPU 时间而不是 token。我们的默认配置经过调优,开箱即可最大化 GPU 利用率,我们计划持续发布优化,进一步推高性价比。
部署服务器只需一条命令:
然后,针对该服务器运行你的训练脚本。
你的团队应该在什么时候使用 Spindle?
Modal 现在覆盖了整个后训练技术栈,从按需集群到为你运行训练循环的库。你该从哪里开始?
如果你不想编写训练循环,可以使用我们的训练库,Modal Dojo,我们昨天发布了它。只需提供一个模型和一个数据集,Dojo 会处理其余的一切。如果你想掌控直到集群的所有细节,Modal 提供原始原语:按需多节点集群以及用于 RL 推演的沙箱。
Spindle 介于两者之间。你用自己定义的损失、奖励和环境编写训练循环,Spindle 在其下处理分布式训练。它支持全参数训练和多 LoRA,覆盖广泛的开放模型。对于运行大量实验的团队,Spindle 支持数百个并发训练运行,性能相近,且与闭源 API 相比成本显著降低。
底层实现
Spindle 兼容 Tinker 的 API 由一个分布式控制平面支撑,该控制平面处理请求和会话管理,以及基于Modal Functions构建的容器自动扩缩容。
Spindle 的后端由一组解耦的训练引擎和推理池组成。请注意,它们不必运行在相同的加速器或精度上!我们写过在这些情况下如何保持 RL 稳定,并利用固有的成本节省和更高的 GPU 可用性。
我们的整个系统构建在 Modal 原语之上:Modal Clusters使大模型的传入请求能够被调度到动态可用的 RDMA 连接节点上,而无需预留集群,Modal Volumes提供快速、持久的检查点保存,以及Modal Servers提供快速推理层。在构建多 LoRA 训练的过程中,我们还向上游贡献了多项改进到Miles和SGLang,我们将继续投资于开源生态。
设计一个高性能的多租户训练系统涉及克服各种挑战,我们必须仔细思考。这带来了许多令人兴奋的工作,如今支撑着 Spindle 的核心组件,包括多客户端批调度、异步检查点保存、增量权重同步,以及面向多 LoRA 和 KV 感知请求路由的自定义推理路由器。你可以阅读更多关于我们设计的内容这里。
Spindle 实战
在过去几个月里,我们运行了大量 RL 工作负载,涵盖数学推理、智能体编程和搜索智能体。以下是几个例子。
后训练一个多轮搜索智能体
我们复现了Training Search Agents with GRPO(作者 Jasper Lu),该工作训练gpt-oss-20b在一个 SEC 文件语料库上执行多轮智能体搜索,使用关键词匹配、grep 和读取工具。任务是整理一组能回答查询的文档。
我们拉取了他的开源代码,只改动了BASE_URL和API_KEY以指向我们的 Spindle 部署,然后运行uv run。就这样。
对比两个 Spindle 种子、我们在 Tinker 上对同一代码的重新运行,以及作者公布的训练结果,我们看到 Spindle 在所有更新中与两者的奖励差距都在 0.01 以内。
然后我们在相同的 32 条留出查询上对第 16、20 和 24 次更新的检查点进行了评分,观察到类似的一致性:
扩展 Spindle 以适配你的工作负载
如果你通过闭源 API 进行训练,就只能受限于提供商决定支持的模型和功能。当你需要它们没有的东西时,除了等待或寻找变通方案,你几乎无能为力。
由于 Spindle 是开源的,并且运行在你的 Modal 工作区中,你可以按需进行修改,而我们的配置层让这件事变得极其简单。例如,当我们想在长程软件任务上训练 Qwen3.5-9B 时,当时我们只有一个默认的 16k 上下文配置。通过两处简单的修改,我们在配置中将其提升到了 256k:
这次运行在可抢占 GPU 上训练了约 3 天,每当机器被回收时都会自动从检查点恢复。它还通过 multi-LoRA 让 8 个客户端共享同一批 GPU,这意味着你可以用一次的成本和时间完成 8 次长上下文消融实验。更多内容见下文。
复合成本节省
智能体强化学习的大部分时间都花在生成 rollout 上,这既需要较长的解码时间,也需要执行延迟。当一个客户端在等待漫长的多轮 rollout 时,它的训练器 GPU 大多处于空闲状态。根据工作负载的不同,我们观察到,服务于单个客户端的训练器(即使运行异步 RL)只有不到一半的时间处于忙碌状态。
在完全异步 RL 中,应对训练器利用率不足的标准做法是扩展推理算力,并允许略微 off-policy 的 rollout 始终为训练器提供数据。Multi-LoRA 与这种方法完美契合:在固定的训练器规模下,你可以为单次运行扩展推理,也可以在固定的推理预算下增加运行数量,或者两者兼做!
Spindle 的 multi-LoRA 后端通过让多个客户端共享同一个训练器,把训练器的空闲时间利用起来。当一个客户端在等待 rollout 时,训练器会运行另一个客户端的训练步骤。当多个客户端共享同一批 GPU 时,每次运行的成本只是专用算力下的一小部分。
为了量化这一点,考虑同一个简单的 GRPO 任务(在 DAPO-math 上运行 27B 模型),由 1 到 8 个客户端共享同一批训练器和推理 GPU(训练用 4×H200,推理用 12×B200)。
观察训练器的利用率,我们就能看出成本收益来自哪里:
在只有一个客户端时,训练器会有很长的空闲期。当四个客户端交错执行各自的训练步骤时,训练器的忙碌率保持在约 88%。增加客户端会降低每次运行的成本,代价是每次运行完成得稍慢一些。注意这里有一个上限:超过四个客户端后,我们观察到训练器出现抖动,这会显著降低每个客户端的吞吐量。
同样的效应也适用于智能体工作负载。以下是我们于 Spindle 上运行的一些工作负载,从单轮数学到长时程智能体,每一项都与在 Tinker 上运行相同脚本的结果进行了对比:
| 工作负载 | 配置 | 客户端数 | 每 100 万输出 token 成本 | 相对 Tinker 的降价幅度 |
|---|---|---|---|---|
| DAPO-math,单轮(Qwen3.8-27B) | Tinker | 1 | $9.77 | — |
| Spindle:4×H200 训练,12×B200 推理 | 2 | $3.57 | −63% | |
| Spindle:8×H200 训练,8×H200 推理 | 8 | $1.99 | −80% | |
| 搜索智能体,最多 40 轮(gpt-oss-20b) | Tinker | 1 | $5.06 | — |
| Spindle:8×H200 训练,16×H200 推理 | 4 | $4.06 | −20% | |
| Spindle:8×H200 训练,16×H200 推理 | 8 | $3.80 | −25% | |
| SWE-Gym,256K 上下文(Qwen3.8-27B) | Tinker | — | $10.79(估算) | — |
| Spindle:8×B200 训练,8×B200 推理 | 4 | $6.17 | −43% | |
| Spindle:8×B200 训练,12×B200 推理 | 8 | $5.82 | −46% |
为 GPU 付费而不是为 token 付费,意味着你从 GPU 上获得的每一点额外吞吐量都会降低你的每 token 价格。这些实验带来的更广泛启示是:如果你让 GPU 保持忙碌,例如同时运行多个实验,Spindle 的性价比会远高于闭源 API。
立即试用
要开始使用,请按照安装说明在你的 Modal 工作区中部署 Spindle。
Spindle 是开源的,我们希望与使用它的人一起构建它。如果你遇到 bug,或者需要我们尚未支持的模型或功能,请提交 issue。对于新的模型配置或现有配置的性能调优,你可以直接提交 PR。若要向上游贡献更大的改动,请来我们的 Slack与我们聊聊,以便我们讨论你的想法!
我们仍处于 beta 阶段,正在打磨性能优化、改进自动扩缩容、新功能和训练配方等等。敬请期待,祝你 Spindling 愉快!🍽️