Thinking Machines 的 Tinker 改变了人们进行后训练的方式。你在自己的笔记本电脑上用纯 Python 编写训练循环,Tinker 则在其 GPU 上运行前向和反向传播。我们非常喜欢它,与我们合作的许多团队也是如此。
然而,随着这些团队扩大规模,他们不断提出同样的两个问题:
- 当你运行数十个实验时,按 token 计费会变得昂贵。
- 你只能使用提供商决定支持的模型和功能。
我们认为一个好的 API 值得不止一种实现。今天我们发布 Spindle,这是一个开源、兼容 Tinker 的训练服务器,可在你的 Modal 工作区中运行。由于服务器归你所有,你可以自定义其中的任何内容,并且你支付的是 GPU 时长费用,而不是 token 费用。我们的默认配置经过调优,开箱即可最大化 GPU 利用率,我们计划持续发布优化,进一步提升性价比。
只需一条命令即可部署服务器:
spindle deploy 然后,针对该服务器运行你的训练脚本。
你的团队应在何时使用 Spindle?
Modal 现在覆盖了整个后训练技术栈,从按需集群到为你运行训练循环的库。你该从哪里开始?
如果你不想编写训练循环,可以使用我们的训练库, Modal Dojo这是我们昨天发布的。只需提供一个模型和一个数据集,Dojo 会处理其余的一切。如果你想掌控从集群往下的所有环节,Modal 提供原始基础组件: 按需多节点集群 以及 用于强化学习 rollout 的沙箱.
Spindle 介于两者之间。你用自己定义的损失函数、奖励和环境编写训练循环,Spindle 负责其下的分布式训练。它支持全参数训练,并可在多种开源模型上进行多 LoRA 训练。对于运行大量实验的团队,Spindle 支持数百个并发训练任务,性能相近,且与闭源 API 相比成本显著降低。
在底层
Spindle 的 Tinker 兼容 API 由分布式控制平面支撑,该控制平面负责请求与会话管理,以及基于以下组件构建的容器自动扩缩容: Modal Functions.
Spindle 的后端包含一组解耦的训练引擎和推理池。请注意,它们不必运行在相同的加速器或精度上!我们已 撰文介绍 了在这些情况下如何保持强化学习稳定,并利用固有的成本节省和更高的 GPU 可用性。
我们的整个系统构建在 Modal 原语之上: Modal Clusters 使大模型的传入请求能够被调度到动态可用的 RDMA 连接节点上,而无需预留集群, Modal Volumes 提供快速、持久的检查点保存,以及 Modal Servers 提供快速的推理层。在构建多 LoRA 训练的过程中,我们还向 Miles 和 SGLang上游贡献了多项改进,并将继续投入开源生态。
设计一个高性能的多租户训练系统需要克服各种挑战,我们必须仔细思考这些问题。这带来了许多令人兴奋的工作成果,如今它们支撑着 Spindle 的核心组件,包括多客户端批调度、异步检查点保存、增量权重同步,以及面向多 LoRA 和 KV 感知请求路由的自定义推理路由器。你可以在此处阅读更多关于我们设计的 内容.
Spindle 实际运行
Over the past couple months, we’ve run an expansive set of RL workloads across math reasoning, agentic coding, and search agents. Here are a few examples.
Post-training a multi-turn search agent
We reproduced Training Search Agents with GRPO by Jasper Lu, which trains gpt-oss-20b to perform multi-turn agentic search over a corpus of SEC filings with keyword-matching, grep, and read tools. The task is to curate a set of documents that answer a query.
We pulled his open-source code, changed only the BASE_URL and API_KEY to point at our Spindle deployment, and hit uv run. That was it.
Comparing two Spindle seeds, our own rerun of the same code on Tinker, and the training results that the author published, we see that Spindle tracks both to within 0.01 reward across all updates.
We then scored checkpoints at updates 16, 20, and 24 on the same 32 held-out queries and observed similar parity:
Extending Spindle to fit your workload
If you’re training through a closed API, you’re limited to whatever models and features the provider decides to support. When you need something they don’t have, there isn’t much you can do besides wait or find a workaround.
Since Spindle is open-source and runs in your Modal workspace, you can make changes as needed, and our config layer makes this incredibly simple. For example, when we wanted to train Qwen3.5-9B on long-horizon software tasks, we only had a default 16k-context config at the time. With two simple changes, we bumped it to 256k in the config:
class Config(Qwen35_9B_16k):
...
max_context_length = "256k" # was "16k"
context_parallel_size = 4 # was 1
This run trained for about 3 days on preemptible GPUs, resuming from checkpoints automatically whenever a machine was reclaimed. It also had 8 clients sharing the same GPUs through multi-LoRA, which means you can do 8 long-context ablations for the cost and time of one. More on that next.
Compounding cost savings
Agentic RL spends most of its time generating rollouts, requiring both long decode times and execution latency. While a client waits on long multi-turn rollouts, its trainer GPUs mostly sit idle. Depending on the workload, we’ve observed that a trainer serving a single client (even running async RL) is only busy for less than half the time.
In fully async RL, the standard approach to trainer underutilization is to scale out inference compute and allow slightly off-policy rollouts to always feed the trainer. Multi-LoRA fits perfectly with this approach: for a fixed trainer size, you can scale out inference for a single run, scale up the number of runs on a fixed inference budget, or do both!
Spindle’s multi-LoRA backend puts trainer idle time to work by letting many clients share the same trainer. While one client waits on rollouts, the trainer runs another client’s training step. When several clients share the same GPUs, each run costs a fraction of what it would on dedicated compute.
To quantify this, consider the same simple GRPO job (a 27B model on DAPO-math) with one to eight clients sharing the same trainer and inference GPUs (4×H200s for training, 12×B200s for inference).
Looking at the trainer’s utilization, we can see where the cost benefits come from:
With a single client, the trainer has long idle periods. With four clients interleaving their training steps, it stays ~88% busy. Adding clients lowers the cost of each run, at the expense of each run finishing a bit more slowly. Note that there is a limit: past four clients here, we observe thrashing of the trainer which significantly degrades per-client throughput.
The same effect scales to agentic workloads. Here are some workloads we ran on Spindle, from single-turn math to long-horizon agents, each compared with running the same script on Tinker:
| Workload | Setup | Clients | $/1M output tokens | Price reduction vs. Tinker |
|---|---|---|---|---|
| DAPO-math, single-turn (Qwen3.8-27B) | Tinker | 1 | $9.77 | — |
| Spindle: 4×H200 train, 12×B200 inference | 2 | $3.57 | −63% | |
| Spindle: 8×H200 train, 8×H200 inference | 8 | $1.99 | −80% | |
| Search agent, up to 40 turns (gpt-oss-20b) | Tinker | 1 | $5.06 | — |
| Spindle: 8×H200 train, 16×H200 inference | 4 | $4.06 | −20% | |
| Spindle: 8×H200 train, 16×H200 inference | 8 | $3.80 | −25% | |
| SWE-Gym, 256K context (Qwen3.8-27B) | Tinker | — | $10.79 (est.) | — |
| Spindle: 8×B200 train, 8×B200 inference | 4 | $6.17 | −43% | |
| Spindle: 8×B200 train, 12×B200 inference | 8 | $5.82 | −46% |
Paying for the GPUs instead of tokens means every bit of extra throughput you get out of them lowers your price per token. The broader takeaway from these experiments is that if you keep your GPUs busy, for example by running several experiments at once, Spindle offers much better price-performance than closed-source APIs.
Try it now
To get started, follow the setup instructions to deploy Spindle in your Modal workspace.
Spindle is open source, and we want to build it with the people using it. If you hit a bug or need a model or feature we don’t support yet, open an issue. You can open a PR directly for new model configs or performance tuning on existing ones. To upstream anything bigger, come chat with us in our Slack so we can discuss your ideas!
We’re still in beta, and we’re cooking up performance optimizations, improved autoscaling, new features and training recipes, and much more. Stay tuned and happy Spindling! 🍽️
]