专栏Kimi K3 模型结构·系统11 / 11
7 min学习

Kimi K3 模型结构(10):RL 与服务,状态住在哪里

系统篇收尾。1M 上下文的 RL 和线上服务里,瓶颈从算力变成「状态放哪」:KV 块和 KDA 状态在 GPU 与 DRAM 池之间的 write-back,训练态让位到 NVMe,参考模型权重借梯度缓冲的槽位流进 GPU,沙箱的 pause / fork / snapshot,以及集群级的缓存亲和与预算准入。三篇里第三次出现双槽位模式。

目录7 节

对应论文 §5.3 "Infra for 1M Agentic RL" 和 §5.4.3 "Fleet-Level Scheduling"。前两篇的时间尺度是一步训练,这一篇拉长到 RL 的一轮迭代和线上服务的一次会话。时间一拉长,问题就变了:不再是算力够不够,而是状态放在哪里、什么时候搬。这一篇沿着状态的生命周期走一遍,§5.4.1 的前缀缓存已经在第 7 篇里讲过,这里只回指。

文本 token图像 / 视频视觉 token 与文本 token 交错后进入同一条主干残差流(prefix sum)重复 23 次3 KDA : 1 Gated MLA第 2 – 92 层每 12 层是一个 AttnRes 块AttnRes 来源(最多 9 个)每个 α 前都有这一组来源α = softmax(wₗ · RMSNorm(来源))wₗ 是每个子层各一个的可学习向量logits输出前再聚合一次全部块最终隐藏状态 + 下一个 token 的 embeddingAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和αAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和αAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和αAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和αAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和αAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和αAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和αAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和αAttention Residuals 算子:用本子层的伪 query wₗ 对来源打分,softmax 加权求和α词表 163840,隐藏维 7168Token Embedding163840 × 7168从零训练的视觉编码器,hidden 1024,12 头MoonViT-V227 层 · 0.4B · patch 14merge_type = sd2_tpool2×2 Pixel-shuffle+ 时间池化 · token ÷4PatchMergerMLPV2,GELU,RMSNormProjector4096 → 4096 → 7168词嵌入永远是一个来源= Embedding每个块的输出是块内所有子层输出之和已完成的块(每块 12 层之和)本块里已经算完的子层之和,相当于块内的普通残差流当前块 partial sumKimi Delta Attention:96 头 × 128 维,状态 128×128/头KDA第 1 层第一层不用 MoE,用一个稠密 SiTU-GLU FFNDense FFN仅第 1 层 · 中间维 33792三层 KDA,每层后接一个 Stable LatentMoEKDA×37168 → 3584 latent → 16 个 routed expert → RMSNorm → 7168Stable LatentMoE896 选 16 + 2 sharedDeepSeek 式 MLA,无位置编码,满秩 sigmoid 输出门Gated MLA×1 · NoPE同上Stable LatentMoE896 选 16 + 2 shared主干末尾额外放一层全局注意力Gated MLA第 93 层 · 收尾同上Stable LatentMoE第 93 层最终归一化RMSNorm不与 embedding 共享权重LM Head→ 163840预训练时 1 层多 token 预测;post-training 微调成投机解码的 draftMTP 层 ×1镜像主干 block · 部署时做 EAGLE-3 draft
序列 · KDA序列 · Gated MLA深度 · AttnRes宽度 · Stable LatentMoE输入 · MoonViT-V2零件
这一篇的状态主要是两种 cache:MLA 随长度增长的 KV 块,和 KDA 固定大小的递推状态。它们要一起搬家。

1M 上下文 RL 的资源约束

先看约束。K3 这么大的模型做 1M 上下文的 agentic RL,论文说资源效率是第一目标。两个前置选择继承自 K1.5 和 K2:co-located RL,训练和推理用同一批卡,让一个 1M 上下文的 RL 实验能装进几百张 GPU;partial rollout,超长轨迹分段生成,压掉尾延迟。

代价随之而来。rollout 阶段推理引擎要为长上下文长时间保留 KV cache,它和训练侧的权重、优化器状态、梯度缓冲抢的是同一批显存和 DRAM。而且 prefill 和 decode 都要高效的话,前缀管理和请求调度都得为 1M 专门设计。§5.3.1 的三个技巧对应三种状态:KV cache、训练态、参考模型权重。先看它们各自住在哪:

GPU HBM最快、最小CPU DRAM外部 KV 池住这里NVMe训练态在 rollout 期间的落脚点活跃解码的 KV 块留在 GPU闲置的可复用前缀MLA KV + KDA checkpoint被驱逐时才写回(write-back)下次复用前预取权重 + 优化器状态训练一轮结束后下到 NVMe,腾出 DRAMrollout 结束后回来参考模型权重本来放不下 GPU常驻 CPU按 chunk 流进梯度缓冲的槽位
1M 上下文 RL 里四种状态的住处。实线是往下搬,虚线是搬回来。KV 块只在被驱逐时写回 DRAM 池,KDA 的状态和对应的 MLA KV 块一起搬,生命周期对齐;训练态在 rollout 期间让位给 KV 池,下到 NVMe。

外部 KV 池:write-back,不是 write-through

1M 上下文的多步 rollout 里,一次前缀 KV miss 极贵:整个前缀要重新 prefill。三件事在放大 miss 的频率。partial rollout 让每轮迭代开头有一堆上一轮没跑完的长 prefill 请求同时到达;投机解码在固定的工具调用间隔里加快了请求周转,前缀块换手更频繁;两者叠加会触发抢占,把命中率压下去,而命中率对长上下文 RL 是关键指标。

K3 把前缀的保留和 GPU 的驻留解耦。活跃解码的块留在 GPU 的 KV cache 里;闲置但可复用的前缀,只在被 GPU 驱逐时才写回 CPU DRAM 里的外部 KV 池,下次复用之前再预取回来。KDA 的状态和它对应的 MLA KV 块一起 offload、一起预取,生命周期对齐,这和第 7 篇里「两种页共用一套分配和驱逐逻辑」是同一个设计的延续。

write-back 和 write-through 的取舍是通用的缓存问题,见推理服务的缓存与调度。放在 K3 的场景里,结论是只为离开活跃解码路径的前缀付 DRAM 空间和传输带宽,GPU 上仍然活跃的块不做冗余的 CPU 副本。

DRAM 从哪来?和训练态抢。K3 的做法是时间上错开:训练迭代结束后把权重和优化器状态下到 NVMe,把 DRAM 让给 KV 池;rollout 迭代结束后释放 KV 池,让给训练。这是存储层次图里那条到 NVMe 的箭头。

rollout 自动节流

第二个问题是并发数。多步 rollout 里上下文随轨迹推进逐渐变长,按整条轨迹的平均长度设一个固定并发,早期估不准而且过于保守,卡空着;并发设高了,后期 KV 压力上来触发抢占。两头都不对,说明这个数不该是常数。

K3 在 LLM 请求调度层加了一个自动节流:用运行时信号,活跃请求数、排队请求数、KV cache 利用率,动态控制送进推理引擎的请求数。rollout 早期上下文短,并发自然高,卡吃满;随着 KV 压力上升并发自动降下来,既不欠饱和也不过载,不需要手调。

参考模型:借梯度缓冲的槽位

第三种状态是只做前向的非策略模型,比如参考模型。RL 的 loss 要用它,但它的权重太大,常驻 GPU 放不下。

K3 把它的权重放在 CPU 内存里,需要时才物化到 GPU,而物化的位置是策略模型的 FP32 梯度缓冲。这块显存本来就在,不用额外分配也不会产生碎片;安全性在于之后真正的梯度算出来时会覆盖它,只要参考模型的前向在那之前做完就行。

具体怎么流?回到第 9 篇的 Pipeline ZeRO-2:梯度分片放 CPU 之后,每张卡在 RL 训练里只保留两个 VPP chunk 的梯度缓冲。参考模型的权重就按 chunk 流进这两个槽:一个槽做当前 chunk 的前向,另一个槽预取下一个 chunk,拷贝的时间藏在计算后面,GPU 显存一点不涨。

CPU 上的参考模型权重按 VPP chunk 分块1234GPU 上的两个槽位梯度缓冲 A当前 chunk 的前向梯度缓冲 B预取下一个 chunk 的权重用完交换角色
双槽位第二次出现:还是那两个 VPP chunk 大小的梯度缓冲,这次装的是参考模型的权重。用完之后真正的梯度会覆盖它。

沙箱:从容器到 microVM

agentic RL 的另一半状态不在模型里,在环境里。K3 用了多种沙箱运行时,容器的、GPU 的,以及一个新的基于 microVM 的运行时 AgentENV,它是这一节的主角,已经开源。三个设计目标,每个都对应一个具体的痛点。

高保真的隔离。 agent 越强、任务越难,探索就越激进,甚至会试图 reward hacking。早期用传统容器沙箱时,作者观察到过 agent 的意外操作直接造成内核 panic 和死锁。但反过来又不能限制探索,复杂任务需要接近真实的环境,agent 应该可以随意挂盘、跑容器、甚至开虚拟机。AgentENV 用 Firecracker 跑隔离的 microVM,隔离度和保真度都是容器给不了的。

灵活的生命周期。 底层支持增量 checkpoint 和恢复,只保存上次 checkpoint 之后被写脏的内存页,checkpoint 和 resume 的延迟分别低到 133 ms 和 49 ms。在此之上有三个高层操作。Pause / Resume:暂停的沙箱不占内存和 CPU,而 agent 等待模型推理结果的时间可以占沙箱生命周期的 98%,所以等待时直接暂停。Fork:从原沙箱的精确状态复制出一个新沙箱,原来的继续跑,用于无副作用的判分。Snapshot:定期快照,用于错误恢复。

高效率、高密度。 数万个沙箱、每个带一套不同的镜像,要在几秒内起来。镜像格式用 OverlayBD,配自研的 ublk 驱动、存储层共享和 P2P 传输,大规模下做到亚秒级启动;写时复制的内存和 page cache 优化让真实负载下的内存超售比达到 6.5 倍。整个 K3 的训练和评测一共创建了 51,219,741 个沙箱、1,505,678 个镜像。

集群级调度:预算与亲和

最后把视角从一个服务实例拉到整个集群池。§5.4.3 说,到了这一层挑战从「每个请求的效率」变成「可预测性」:一次前缀缓存 miss 比 hit 贵几个数量级,一阵 1M 请求的突发可以把短请求饿死。两条策略对应这两个问题,它们背后的一般原理(缓存亲和、一致性哈希、按类别做预算)在推理服务的缓存与调度里,这里只写 K3 的数字和取舍。

正常主集群故障请求带会话 id 和长度按类别扣预算短请求 / 长上下文各一份一致性哈希会话 → 主集群 + 备集群主集群持有这个会话的前缀缓存备集群没有缓存,故障时重 prefill命中前缀只 prefill 增量(约 4K)重 prefill整个前缀(约 400K)
两条集群级策略串在一条路径上。预算准入让长上下文的突发最多吃掉自己那份容量;缓存亲和让会话跟着前缀缓存走,备集群由一致性哈希均匀分散,单个集群故障时的重 prefill 摊到整个集群池上。

缓存亲和调度。 1M 上下文下一个典型的编码请求带着 400K 的前缀,但只需要 prefill 4K 的增量。命中时只算 4K,miss 时算 400K,两个数量级。所以请求跟着缓存走,路由到持有这个会话前缀缓存的集群。K3 用一致性哈希给每个会话钉两个集群:主集群服务流量,备集群在主集群故障时接管并重新 prefill,备份均匀散在整个集群池上,单个集群故障的重 prefill 摊给很多集群。常态下缓存局部性保住,故障影响有界。

这是「双槽位」第三次出现:主和备,一个在用,一个待命。前两次的槽在一张卡上,这次的槽是两个集群。

预算准入。 K3 的生产流量里短请求不到 2K,长请求到 1M,单个请求的代价跨了三个数量级,按「平均请求」做的容量规划在这种方差下失效,典型故障是一阵长上下文请求吃满算力、后到的短请求首 token 延迟一起恶化。K3 给不同的请求类别分配各自的资源预算,长上下文的突发最多消耗自己那一份,不会拖垮其他类别的 SLO。

三篇系统篇的收尾

回头看这三篇各自的一张图:第 8 篇的时间线,标的是谁在等谁;第 9 篇的账本,标的是每一行住在哪、搬走付什么;这一篇的存储层次,标的是状态什么时候往下搬、什么时候搬回来。三张图说的是同一件事的三个时间尺度:一次前向、一步训练、一轮迭代或一次会话。

再回头看第 7 篇的结论,「系统在为结构的选择付账」。这三篇把账单展开了:MoonEP 为 896 个 expert 的路由付了 E/RE/R 个冗余槽位;显存账本为 2.8T 参数付了 FP8、重算、offload 和两个 chunk 的 double buffer;KV 池为 KDA 加 MLA 的混合缓存付了一套对齐的生命周期。付账的方式反复是同一个:找到一段空闲,把代价藏进去;找到两个槽位,让它们轮转。

到这里 Kimi K3 的连载全部结束。本连载没有覆盖的部分:数据与预训练配方(§3)、post-training 的 SFT、RL 算法、多教师蒸馏(§4)、评测(§6)。