ICDCS 2026 Maestro
我讲的工作是 Maestro,它解决的是:多个 Agent 工作流共享有限 GPU 时,怎样减少长任务对短任务的阻塞,同时让多个模型和请求更有效地使用显存。
先解释一下场景。比如一个旅行规划任务,系统先分析用户需求,再调用工具查航班和酒店,最后生成完整行程。这一个用户任务会触发多次模型调用,我们把一次模型调用叫作一个 stage,也就是一个阶段。不同阶段有依赖,也可能使用不同模型。
这里的特点是,同样是调用模型,有的阶段只生成一小段工具参数,有的阶段要写很长的分析。它们看起来都是一个请求,实际运行时间和显存需求却可能差很多。当这些工作流与后台批处理一起运行时,长任务就可能挡住用户正在等待的短任务。
同时,显存里既要放模型权重,也要放每个请求的 KV Cache。KV Cache 可以理解为模型生成过程中保存的上下文计算结果,输入和输出越长,通常需要的空间就越多。如果按最大长度给每个请求预留,短请求会浪费空间;多个模型又都想留在 GPU 上,就容易互相挤占。模型换出去以后,再使用还需要重新加载。
所以我们希望提前知道:这次调用大概要生成多长、运行多久、需要多少显存,再用这些信息安排请求和模型。Maestro 分成三个部分:预测提供需求,节点运行时管理实际资源,调度器决定谁先执行、放到哪里。
第一个部分是输出长度预测。
我们发现,Agent 当前在做什么,比单看输入长度更有帮助。比如同一个旅行助手,查航班时可能只生成几十个 token 的查询参数,最后整理行程时可能输出上千个 token。这里预测的是模型生成的工具参数,不是工具返回多少内容,也不是查询工具要运行多久。
因此我们用了两阶段方法。先预测这次调用触发工具的概率,再把这个概率和其他信息一起交给长度回归器,预测会生成多少 token。前一步相当于给后一步提供一个线索:这次更像是在调用工具,还是在生成一段回答。
具体实现上,MiniLM 提取输入文本的语义,LightGBM 结合输入长度、工具数量、阶段位置和前一次调用情况做预测。工具调用分类器在不同 Agent 之间共享,长度回归器则按 Agent 区分,因为检索、分析、总结这些角色的输出规律不同。两阶段之间传递的是概率,不是先硬分成两类再分别处理。
拿到预测长度后,再换算成两项资源需求。时间上,用输入处理时间,加上预计生成 token 数乘以每个 token 的生成耗时;显存上,根据模型每个 token 的 KV 大小,估计输入加输出需要多少空间。
第二个部分是节点上的显存管理。预测告诉我们大概要用多少,节点运行时负责检查现在到底能不能放得下。
它同时管理模型权重和 KV Cache。对于模型,近期还会使用的尽量保留在 GPU;暂时不用的可以把权重移到 CPU,并保留一部分运行上下文,降低下次恢复成本;更冷的模型再退到磁盘或远端。这样,调度器看到的不只是“这个节点有没有模型”,还知道“这个模型要等多久才能用”。
对于 KV,我们利用 CUDA 虚拟内存管理,把虚拟地址和实际物理显存分开。可以先留出足够的地址范围,实际生成需要空间时,再映射物理页。这样不必一开始就把所有请求的最大空间都占满,但实际显存容量没有变大。
所以每次接收请求、增加分配时都要检查容量,预测预算外还要留余量。如果空间不够,就先考虑对其他任务影响较小的回收方式,并估计以后恢复这些状态要花多少时间,把这个代价反馈给调度器。预测低估时,也不能绕过容量检查继续分配。
第三个部分是调度。这里有两个不同的问题:一个是哪些工作流先推进,另一个是当前这次调用放在哪个节点。
先说执行顺序。我们采用预计剩余时间较短的工作流优先,也就是 SRTF 的思路。这里看的是整个工作流预计还需要多久,不只是眼前这一小步。当前阶段的时间来自长度预测,后续阶段的时间则根据工作流当前位置和相似历史执行情况估计。
举个假设例子:任务 A 当前这一步只要 1 秒,但后面预计还有 20 秒;任务 B 当前这一步要 3 秒,做完就结束。在其他优先级条件相同的情况下,我们更倾向先推进 B,让一个用户任务尽快完成。单看当前阶段最短,就会优先 A,忽略后面的依赖链。
再说放在哪个节点。我们先排除显存无法安全承载请求的节点,再给剩余节点打分。网络和显存匹配越合适,分数越高;开始执行前要等得越久、为它腾空间造成的恢复代价越大,分数越低。 最后选分数最高的节点。
这里开始前的等待,包括节点排队和模型激活。比如两台机器网络条件差不多,一台模型已经准备好,但还要排队 10 秒;另一台没有排队,模型恢复需要 3 秒。在其他条件相同的情况下,第二台更合适。如果第二台还需要回收别人的状态,造成很大的恢复代价,就要把这部分损失一起算进去。这里数字只是为了说明取舍。
交互任务和后台任务竞争时,我们还支持在阶段边界让出执行机会。也就是后台任务完成当前这次模型调用后,先不让它继续下一次,给交互任务让路。普通调度抢占不会直接打断正在进行的生成,所以遇到特别长的一次调用,交互任务仍可能等待。我们也设置了切换收益门槛、冷却时间和等待时间提升优先级,减少反复切换和后台任务长期得不到执行的问题。
把三个部分连起来看:一个请求到来,预测器先估计长度,再换算成时间和 KV 需求;节点报告模型状态、可用显存,以及腾空间的代价;调度器利用这些信息排序和选节点;执行结束后,再把真实长度和资源使用反馈回来,校准后续预测。
实验方面,论文包括物理集群实验和基于执行记录的模拟。高争用模拟中,相比 EDF,SLO 达成率提高了 23.6 个百分点,也就是更多请求能在规定时间内完成。另一个显存实验中,KV 预留的物理显存开销降低了 67.2%。这两个数字对应不同实验。
我认为这项工作的关键,是把 Agent 的行为差异变成资源调度可以使用的信息。我们先估计调用成本,再让弹性显存管理和工作流调度一起利用这些估计,减少长短任务互相阻塞,也减少多模型服务中的保守预留和反复加载。
展开:Maestro 的调度算法、评分与三个模块的关系
排序和放置使用不同的依据
工作流排序:预计剩余时间 = 当前阶段预计执行时间 + 后续工作流时间估计。后续项来自近期相似状态的执行记录中位数,状态包括角色、阶段模板、调用序号和工具意图等。工作流依赖仍需满足;预测工具参数的长度不能替代工具执行耗时。
节点放置:先检查策略和显存可行性,再计算分数,选择最高分节点:
- N 是候选节点,T 是当前模型调用。
- A 是匹配分数,考虑网络距离和显存 best-fit;不是单纯空闲显存越多越好。
- T_ready 是开始执行前的预计等待,由节点排队时间与模型激活时间组成。
- C_deg 是腾出资源所需回收方案带来的恢复代价;影响活跃执行还会计入额外惩罚。
- λ、μ 控制两类代价的权重。论文对指标做稳健归一化,时间项以毫秒报告,默认 λ = μ = 1。
论文公式 (5) 是候选节点评分,公式 (7)–(8) 是工作流剩余时间估计;两者共同服务调度,但不能合并成一个声称全局最优的 cost。Algorithm 3 遍历可行节点并选最高分,节点资源协调的 Algorithm 2 按状态优先级贪心构造回收方案。
三个模块分别交付什么
| 模块 | 输出 | 谁使用 |
|---|---|---|
| 长度与成本预测 | 输出 token 数、预计阶段耗时、KV 需求 | 排序使用时间;节点准入使用显存预算。 |
| 节点运行时 | 能否承载请求、模型激活时间、显存余量、回收恢复代价 | 调度器过滤并评分;运行时执行资源管理动作。 |
| 集群调度 | 工作流推进顺序、节点选择、阶段边界让出执行机会 | 节点执行后回传真实用量,供后续校准。 |
普通抢占与内存压力处置要分开
普通交互优先调度在两次模型调用之间切换,避免打断正在 decode 的调用;显存压力下的分级处置还可能涉及活跃 KV 换出或中止执行,这属于更高扰动的资源降级,需要计入恢复和中断代价。不能把阶段边界抢占说成任何情况下都不会中止活跃执行,也不能声称交互请求一定立即运行。
实验数字的口径
23.6 个百分点是高争用模拟中相对 EDF 的 SLO 达成率提升;67.2% 是显存实验中的 KV 预留 HBM 开销下降,不是总显存或每个请求实际 KV 数据量都下降相同比例。预测与后续服务实现的差异以两阶段预测实现章节的核对记录为准。
来源:Maestro 论文 §III-B–D、公式 (2)–(8)、Algorithm 1–3 与 §IV;预测服务细节参考此前核对的 concerto-runtime@8ee8969。
📄 论文原文:ICDCS 2026 — Maestro PDF
问题背景
传统 LLM 推理面对的是独立的一问一答请求。但现在越来越多的应用把多个 agent 组成协作工作流:比如旅行助手里有"需求分析 agent"、"机票搜索 agent"、"酒店推荐 agent"、"行程整合 agent",它们之间有 DAG 依赖,一个用户请求触发十几次甚至几十次 LLM 推理调用(称为 stage)。
这带来了三个新挑战:
- 输出长度剧烈波动:工具调用输出几十 token(JSON),用户交互输出几百上千 token。KV 缓存需求差异巨大,固定分配要么浪费要么 OOM。
- 多模型共存的内存压力:不同 agent 使用不同模型,一块 GPU 需要同时驻留多个模型权重加上所有请求的 KV 缓存。
- 混合延迟要求:交互式 stage 要低延迟,批处理 stage 要高吞吐。不区分优先级就会造成队头阻塞。
系统设计
核心思路:不把 LLM 请求当黑盒,利用 agent 上下文做前瞻性预测,指导内存管理和调度。
为什么需要预测输出长度?
Agent 工作流中的调用成本差异很大:生成工具参数通常较短,分析和总结可能较长。提前估计输出长度,可以同时为请求排序、模型驻留和 KV 预算提供依据。按需分配负责执行时的空间管理,预测则帮助系统提前安排未来需求。
两阶段流水线
消息、工具描述 → MiniLM → 384 维语义向量
├→ 全局 PCA 32 维
│ + 结构化特征
│ → 全局 LightGBM 分类器
│ → 工具调用概率
│ │
└→ Agent PCA 32 维
+ 结构化特征 + 工具调用概率
→ Agent 专属 LightGBM 回归器
→ log(1 + 输出长度)
→ expm1 → token 数
MiniLM 负责提取语义,LightGBM 负责分类和回归。分类器传递连续概率,让回归器利用工具调用的可能性,而不是先把请求硬切成两个类别。全局分类器共享跨 Agent 的行为规律,专属回归器适配各 Agent 的输出分布。
服务中的输入特征
当前服务配置使用输入 token 数、工具数量、思考模式、是否为首个阶段、阶段序号、同一 Agent 的调用次数、上一阶段输出长度,以及 PCA 语义特征。Agent 名称用于选择模型包;会话 ID 用于获取上下文。语义编码处理系统提示、对话和工具名称与描述,长文本通过重叠滑动窗口聚合。
分类器使用全局 PCA,回归器使用 Agent 专属 PCA;线上共用同一份原始语义向量,再分别投影。这让分类和回归保留各自关注的语义方向,同时减少重复编码。
训练目标与冷启动
当前服务的分类器学习工具调用标签,回归器对真实输出 token 数做 log1p 变换,采用 alpha=0.5 的分位数回归,预测中位数长度;近期样本获得更高权重。模型包保存模型、PCA 参数和特征顺序,推理按同样的顺序组装输入。
论文方法包含 isotonic 概率校准和冷启动时的共享全局模型。后续服务快照直接使用 predict_proba,缺少 Agent 模型或全局分类器时返回默认 150 token。资源安全余量由预算策略处理,不应把当前中位数回归器称为偏向高估的损失函数。
两阶段带来的价值
工具调用概率为长度回归提供显式的行为模式信号,一阶段直接回归则是自然的对照方法。论文报告输出长度 MAE 为 165.43 token、R² 为 0.7774,相比 Magnus 的 MAE 降低 19.2%。消融显示工具意图特征在非 CoT 场景更有帮助;开启 CoT 后,两种模式都可能包含推理文本,因此收益会随工作负载变化。
预测信号如何驱动下游组件
| 组件 | 消费方式 | 作用 |
|---|---|---|
| KV 预算 | 每 token KV 字节数 ×(输入长度 + 预测输出长度),再加余量 | 估计未来需求;执行时结合真实容量按需增长 |
| 阶段成本 | prefill profile + 每 token decode profile × 预测输出长度 | 估计当前阶段执行时间 |
| 工作流排序 | 当前阶段成本 + 后续工作流时间估计 | 决定任务优先级,减少依赖链上的等待 |
| 准入与放置 | 结合预测需求、模型状态和可用容量 | 选择可承载任务的节点,安排模型驻留与回收 |
论文根据近期低估误差调整资源安全余量;预测服务本身输出概率与长度,实际容量检查由资源管理层完成。节点运行结束后回传真实用量,异步整理数据和更新模型。
实现依据:concerto-runtime@8ee8969 中的 python-predictor;方法与实验结果依据:Maestro 论文 §III-B、§III-D、§IV。
模型状态管理:五种状态按层级流转——
- Running:权重在 GPU,可立即执行推理
- Sleeping:权重移到 CPU,但 GPU 上保留 CUDA Graph 和 JIT 内核缓存(约 0.5GB),重新激活省 5-8 秒
- CPU-resident:权重完全在 CPU 内存
- Disk-resident:权重在本地 NVMe
- Remote:权重在远程存储
层级化 LRU 逐级淘汰,最热的模型留 GPU,最冷的逐步退到远端。
KV 缓存管理:CUDA VMM 虚拟内存超配——40GB 物理 GPU 上分配 122GB 虚拟地址池(3 倍超配)。关键在于虚拟地址和物理页分离,按需映射物理页。三层防护:(1) 虚拟 vs 物理分离,不会同时达峰;(2) 准入控制——每个 stage 检查剩余物理页是否够;(3) 映射失败时拒绝 stage 而非崩溃。五级降级策略保证极端情况也不会 OOM。
内存核算:M_kv + M_res ≤ M_total,安全裕度 ρ ∈ [0.1, 0.3],偏向高估以避免低估导致 OOM。
基于预测的剩余执行时间排队(SRTF),优先执行快要完成的工作流。交互式和批处理 stage 分开排队。Stage 边界抢占——只在两次 LLM 调用之间切换,不打断正在解码的请求。
节点选择:适应度评分 S(N,T) = A(N,T) − λ·T_ready − μ·C_deg,综合模型就绪延迟和降级代价。
核心结果
Maestro 高频问答
低估远比高估危害大:(1) KV 缓存方面,低估导致预分配不够,解码到一半 OOM,请求被杀;高估只是暂时多占。(2) SRTF 调度方面,低估剩余时间导致长作业排到队首,造成队头阻塞;高估则长作业排后面,影响较小。所以设计安全裕度 ρ 偏向高估。
三层防护:(1) 122GB 是虚拟地址空间,物理页按需映射,不会同时分配满;(2) 每个 stage 进来先做准入控制,检查剩余物理页是否够预测需求;(3) cuMemMap 返回失败时拒绝该 stage 而非崩溃。多 agent 的 KV 使用弹性大,统计上不会同时达峰,类似内存超卖的思路。
四层存储:GPU → CPU → Disk → Remote。五种状态多出一个 Sleeping——权重在 CPU,但 GPU 上保留 CUDA Graph 和 JIT 内核缓存(约 0.5GB),重新激活省 5-8 秒。Sleeping 横跨 GPU 和 CPU 两层。设计原因:模型切换频繁时,CUDA Graph 的重建开销可观。
Token 级抢占需要和解码引擎深度集成,还要做 KV 缓存迁移,工程复杂度极高。Stage 边界抢占只需更新元数据,实测效果已足够:交互排队从 11 秒降到 2 毫秒。超长解码阶段可作为 future work。
三级回退:(1) per-role 数据不足时回退到全局模型;(2) 结构化特征(工作流位置、工具可用性)本身就有信号;(3) post-execution profiling 增量更新,几轮执行后适应新角色。
S(N,T) = A(N,T) − λ·T_ready − μ·C_deg。默认 λ = μ = 1(毫秒量级)。交互式 stage 增大网络延迟权重。用 robust min-max 归一化(5/95 分位数裁剪),防止异常值主导。验证集上选定后跨负载固定。
比如一个旅行助手 Agent:用户说"帮我规划去东京的行程",系统会依次调用:需求分析 Agent(解析意图)→ 机票搜索 Agent(调工具查航班)→ 酒店推荐 Agent(查酒店)→ 行程整合 Agent(汇总输出)。每个 Agent 背后是一次 LLM 推理调用(称为 stage),一个用户请求可能触发十几次甚至几十次 LLM 调用。这些 stage 之间有 DAG 依赖关系,不同 Agent 可能使用不同模型,输出长度差异巨大——工具调用返回几十 token 的 JSON,用户交互输出几百上千 token。
固定按最大长度预留简单但保守,实际输出较短时会浪费空间。只按需分配能减少浪费,却不能提前判断哪些请求会长时间占用资源。我用 Agent 输入与上下文预测输出长度,帮助排序、模型驻留和 KV 预算;运行时再按真实用量增长,并在空间紧张时执行准入或回收策略。
MiniLM 负责提取文本语义,LightGBM 将语义特征与输入长度、工具数量、阶段上下文结合起来做分类与回归。这样的分工适合结构化特征与文本信息混合的场景,也便于给不同 Agent 训练专属模型。工程上可以预加载树模型、复用编码结果并控制线程数量。性能评估要把文本编码与接口排队一起计入,不能只拿树模型的一次调用时间代表整个预测服务。
落到当前服务实现,结构化特征是输入 token 数、工具数量、思考模式、首阶段标记、阶段序号、同一 Agent 的调用次数和上一阶段输出长度。Agent 名称用于选择专属模型。语义特征由 MiniLM 对系统提示、对话和工具描述编码,长文本采用重叠滑动窗口,聚合为 384 维向量,再分别通过全局 PCA 和 Agent PCA 降到 32 维。分类器与回归器各用对应投影,回归器还接收工具调用概率。
预测给出初始需求,实际分配持续接受容量约束。KV 随生成按需增长,每次追加前检查空间;不足时暂停准入,或选择合适的回收与恢复策略,再将低估反馈到后续预算校准。当前回归器输出中位数,保守余量属于资源预算策略。这样预测负责改善利用率,运行时负责处理超出预算的情况。
这个问题需要分清两个层面:(1) 预留(reservation)≠ 实际使用。传统方案为每个请求预分配最大 KV 物理空间,即使实际输出很短也占着不放——这是真正的物理显存浪费。CUDA VMM 让虚拟地址远大于物理显存,但物理页按需映射,真正减少的是物理 HBM 占用;(2) 67.2% 降低的是预留的 HBM 物理显存,不是虚拟地址空间。通过输出长度预测来决定实际映射多少物理页——短请求只映射少量物理页,长请求映射更多,而不是所有人都按最坏情况分配。统计复用的前提是多 Agent 的 KV 峰值不重叠(某个 agent 在输出长文本时,其他 agent 可能正在工具调用或空闲),如果所有请求同时输出长文本确实还会 OOM,但实际 trace 中这种概率极低,且有准入控制兜底。
不只对比了 EDF。Maestro 的实验对比了 EDF、SRTF(无预测版本,纯按已执行时间排序)、FCFS(先到先服务)、Karma(LLM 推理服务最近的调度工作)。关键在于 Maestro 的核心贡献不是调度算法本身(SRTF 是经典算法),而是把预测信号引入 LLM-MAS 场景并设计了弹性显存机制来利用这些预测。实验设计的目的:EDF → 证明传统实时调度在 LLM-MAS 场景下失效(无法区分长短 stage);SRTF(无预测)→ 证明纯 SRTF 因为不知道剩余时间只能按已执行时间估计,在双峰分布下偏差大;Maestro → 证明预测信号能显著提升 SRTF 的效果。对比不是为了证明"我的排序算法更好",而是证明"预测信号有价值"。
这是不同层面的优化,两者是互补而非替代关系:vLLM/TGI 优化的是单节点内推理引擎的 batching 和 KV 管理(PagedAttention 解决内部碎片、continuous batching 提高单模型 GPU 利用率)。Maestro 解决的是多节点、多模型、多 Agent 工作流层面的调度问题——一个用户请求涉及十几个 Agent stage、用多个不同模型、分布在多块 GPU 上,需要全局视角决定"哪个 stage 放哪块 GPU、哪个模型该常驻、KV 显存怎么跨请求复用"。Maestro 可以和 vLLM 配合使用:节点内用 vLLM 做推理引擎优化(PagedAttention、continuous batching),节点间用 Maestro 做全局调度和显存管理。事实上我们的原型实现就是基于 vLLM 做的,分级权重缓存和 CUDA VMM 超配是在 vLLM 之上的额外优化。
从 Agent 请求到输出长度
Maestro 把 Agent 请求的执行成本提前转成调度信号:先预测这次调用是否涉及工具,再预测会生成多少 token,最后将长度换算成时间和显存需求。工具调用通常生成较短的结构化参数,分析与总结通常生成较长文本;同样的输入长度,执行成本可能相差很大。
python-predictor 将预测封装成 FastAPI 服务。一次请求携带会话 ID、Agent 名称、消息、可用工具和阶段上下文;响应返回工具调用概率、预测输出长度和输入 token 数。
messages + tools ──→ 输入 token 计数
│
└─────────→ MiniLM → 384 维语义向量
│
┌────────────┴────────────┐
↓ ↓
全局 PCA Agent 专属 PCA
32 维 32 维
│ │
结构化特征 ──────→ 全局 LightGBM 分类器 │
│ │
工具调用概率 ──────→ Agent 专属 LightGBM 回归器
↑
结构化特征
│
log(1 + 输出长度)
↓
expm1 → 预测 token 数
两阶段之间传递连续概率,不先用阈值把请求硬分成两类。Agent 的差异主要通过选择专属回归器和专属 PCA 来表达。
输入特征怎样获得
| 信息 | 实现字段或来源 | 提供的判断依据 |
|---|---|---|
| 输入长度 | node_input_tokens;对消息与工具应用配置的 tokenizer 和 chat template | 当前上下文规模 |
| 工具数量 | len(request.tools) | 可用工具的多少;不等于实际会调用工具 |
| 思考模式 | is_thinking_mode | 当前请求是否开启思考模式 |
| 阶段位置 | node_index_in_graph、is_first_node | 当前处于会话中的哪个阶段 |
| 同一 Agent 的调用次数 | model_index_in_agent;接口根据 Redis 中已回传的节点计数 | 首次执行还是再次调用 |
| 上一阶段输出长度 | prev_node_output_length;优先使用请求值,缺失时查 Redis | 上游刚刚产生的信息规模 |
| 输入语义 | System prompt、user/assistant 对话、工具名称和描述 | 请求到底要做什么 |
agent_name 用于查找模型包,session_id 用于关联会话上下文。当前特征选择配置纳入基础字段、阶段上下文字段、PCA 特征,并为回归器追加 classifier_probability;图的入度、出度、深度和角色 one-hot 不是这份配置中的显式输入。
代码也保留了历史均值、输出/输入比率等统计特征的生成与读取能力。最终哪些字段进入模型,由训练生成的 feature_names 决定;当前配置没有选择这些历史统计列。
文本编码与两套 PCA
语义编码使用 all-MiniLM-L6-v2,通过 ONNX Runtime 执行。默认长文本策略将内容切成最多 510 个 token 的窗口,步长为 255;窗口加上特殊 token 后按 512 长度组织,批量编码。每个窗口做带 mask 的 mean pooling 和归一化,再聚合窗口向量,得到 384 维表示。
全局分类器和各 Agent 回归器分别保存自己的 PCA 参数。两者复用同一个原始语义向量,但投影方向不同:全局 PCA 面向多种 Agent 的工具调用模式,Agent PCA 面向该角色的长度变化。降维后,语义特征与结构化特征一起输入树模型。
PCA 的运行时计算为:
低维向量 = (原始向量 - 训练均值) × 主成分矩阵的转置
这将语义模型负责的“理解输入”与树模型负责的“预测成本”分开,也便于复用编码结果。预测器内部支持传入预计算向量;普通 /predict 接口仍从文本调用编码器。
分类和回归怎样训练
全局分类器以 is_tool_call 为标签,采用 LightGBM 二分类;Agent 回归器以真实生成 token 数 usage_completion_tokens 为目标,先做 log1p 变换。全局分类器的预测概率作为额外特征注入回归器训练,与在线预测保持相同的两阶段结构。
当前服务训练配置如下:
| 项目 | 配置 |
|---|---|
| 全局分类器 | binary objective,500 棵树,学习率 0.05 |
| 每个 Agent 的回归器 | quantile objective,alpha=0.5,300 棵树,学习率 0.05 |
| 回归目标 | log1p(usage_completion_tokens) |
| 推理输出 | max(0, expm1(预测值)),转成整数 |
| 单 Agent 最小训练样本数 | 50 |
| 每次加载的数据窗口 | 最近 12,000 条记录,再按时间排序 |
| 样本权重 | 按样本新旧顺序指数衰减,近期样本权重更高 |
medium 对应中位数预测。若要针对 KV 低估采用保守预算,应在资源估计与准入策略中设置余量,而不是把这个 alpha=0.5 的回归器解释成已经偏向高估。
论文方法包含分类概率的 isotonic 校准和冷启动时的共享全局模型;当前服务直接使用分类器的 predict_proba,缺少 Agent 模型或全局分类器时返回默认 150 token。这是论文方法与后续服务版本的具体差异。
长度怎样变成调度与 KV 预算
论文将输出长度转换为模型相关的执行时间和显存估计:
阶段执行时间 ≈ 该模型的 prefill 时间(输入长度)
+ 该模型的平均每 token decode 时间 × 预测输出长度
KV 需求 ≈ 该模型每 token 的 KV 字节数 × (输入长度 + 预测输出长度)
例如,一个 32 层、8 个 KV heads、head_dim 为 128 的 BF16 模型,每 token KV 为 128 KiB。输入 2048 token、预测输出 512 token,则全模型 KV 数据量约 320 MiB;若预算另留 20% 余量,则为 384 MiB。实际分配再考虑 block 对齐、TP/PP 分布和其他显存占用。
预测为调度器提供“这次调用预计要运行多久、需要多少空间”的信号。PagedAttention 负责高效管理已经发生的 KV 分配,两者可以配合:利用预测安排任务和模型驻留,再由运行时根据真实容量决定能否继续分配。
/predict 服务本身返回长度和概率,不执行显存页映射。后续 Concerto 通过 predictor_runtime.py 接入这些字段,并用于请求级投机长度选择;这是同一预测能力在新决策层中的复用。
运行反馈与服务开销
节点完成后通过 /callback/node 回传真实 token 用量和运行信息,暂存到 Redis;工作流完成后由 Celery 汇总、补充上下文与 embedding,再持久化。后台任务根据增量计数触发训练,Agent 模型更新后通知服务重载。
这条链路实现的是“运行反馈—异步训练—模型更新”,训练不放在单次请求的同步路径中。新到达的数据可以用于后续更新,具体更新频率由阈值控制;代码快照中的两个训练触发阈值均为 1,000,000。
服务启动时预加载 Agent 模型,预测线程将树模型限制为单线程;文本编码和模型计算放到受限工作线程中,避免阻塞 FastAPI 的事件循环。耗时分别记录编码、特征准备、Redis、分类、回归,以及接口排队与处理时间,便于判断瓶颈究竟在哪里。
来源:Maestro 论文 §III-B、§III-D;concerto-runtime 提交 8ee8969 中的 moarks-20260125/python-predictor/src/,主要包括 config.py、training/predictor.py、training/trainer.py、training/bert_encoder.py、api/prediction.py、tasks.py;接入逻辑位于 tpds-p0-p1-experiments-20260724/predictor_runtime.py。代码快照日期为 2026-08-29。
设计动机与核心方法
我做的是面向 Agent 工作流的执行成本预测。一次工作流会多次调用模型,有的阶段只生成工具调用参数,有的阶段要长篇推理或汇总,单看模型大小和输入长度,很难判断这次请求到底会占用多久、需要多少显存。我利用 Agent 的输入语义和阶段上下文,先预测工具调用意图,再预测输出长度,把这个长度转成执行时间和 KV 预算,让调度器能够提前安排,而不是等请求执行起来才发现资源紧张。
因为工具调用和自由文本生成往往对应两种不同的输出模式。比如同一个研究 Agent,有时只是生成检索参数,有时要写一段分析,输出长度差别很大。我先用分类器估计工具调用概率,再把这个概率作为回归器的输入,让长度模型获得一个更明确的行为信号。
这里传递的是连续概率,不是先判成 0 或 1 再硬切换模型。一阶段回归也是合理基线;两阶段的价值在于显式利用行为模式,尤其是工具调用与文本输出长度差别较大的场景。
它们承担不同任务。MiniLM 提取输入的语义,比如当前是在搜索、分析还是总结;LightGBM 把语义向量和输入长度、工具数量、阶段位置等表格特征结合起来,预测工具调用概率与输出长度。这种组合能利用文本信息,也便于按 Agent 单独训练和更新,部署时还可以预加载模型、控制 CPU 线程和复用编码结果。
结构化特征包括输入 token 数、工具数量、是否开启思考模式、当前阶段序号、同一个 Agent 已经调用过几次,以及上一阶段输出长度。语义特征来自系统提示、对话内容和工具描述,用 MiniLM 编码后经 PCA 降到 32 维。
比如同一个 Agent 第一次拿到问题,可能需要先调用搜索工具;第二次已经拿到上游信息,就可能进入长文本分析。角色没变,但调用次数、前序输出和当前语义变了,这些信息能帮助模型区分两次调用的成本。
模型训练与工程实现
不同 Agent 的职责和输出风格相对稳定。检索 Agent、翻译 Agent、总结 Agent 的长度分布不同,使用专属回归器可以减少跨角色混杂。全局分类器共享“是否调用工具”的规律,各 Agent 回归器学习自己的长度模式,因此既有共享信息,也有角色适配。
实现上按完整 Agent 名称保存模型包,包里包含回归器、特征顺序和 PCA 参数。当前服务在 Agent 数据达到 50 条后才训练专属回归器;缺模型时先使用默认长度,待有足够数据后建立专属模型。
分类器要识别所有 Agent 中共同的工具调用模式,回归器要解释某个 Agent 内部的输出长度变化,因此两者关注的语义方向不同。全局 PCA 用全局数据训练,Agent PCA 用该 Agent 的数据训练;线上 MiniLM 只生成一份 384 维向量,再分别投影到两个 32 维空间,既共享编码成本,也保留任务差异。
默认使用重叠滑动窗口。每个窗口最多取 510 个内容 token,步长 255,加上特殊 token 后送入 ONNX 编码器批量处理。先对窗口内 token 做 mean pooling,再聚合各窗口向量。这样能覆盖长对话中的多个部分,避免只看开头或结尾;相应代价是 prompt 越长,需要处理的窗口越多,所以编码成本也要单独监控。
当前实现先对真实输出长度做 log1p 变换,再训练 LightGBM 分位数回归器,目标分位数是 0.5。log 变换压缩长短输出之间的尺度差异,中位数目标提供一个典型长度估计。线上再用 expm1 还原成 token 数。训练还对较新的样本赋予更高权重,使模型更关注近期运行模式。
预测长度和资源余量是两层决策:模型给出典型需求,调度器结合低估误差和剩余显存决定预留多少。
执行前调用预测服务,执行后通过 callback 回传真实输出长度和运行数据。节点数据先进入 Redis,工作流结束后交给 Celery 汇总入库;后台根据新增数据量决定是否训练,训练完成后让服务热重载模型。这样请求路径只负责预测,数据整理和训练异步进行,避免训练拖慢用户请求。
会,尤其是长 prompt 和突发并发,不能只看一次树模型推理。我把成本拆成请求排队、文本编码、特征准备、Redis 访问、分类和回归。实现上使用 ONNX Runtime 编码、启动预加载模型、限制原生线程数量,并把同步计算放到受限工作线程中,让 HTTP 事件循环继续处理其他请求。优化时先看哪一段占主导,再决定是复用向量、调整并发,还是减少重复数据访问。
调度价值与 KV Cache 追问
按需分配解决的是“已经需要这些 KV,怎样高效存下来”,预测解决的是“这个请求后面还会占多少资源、持续多久”。在多 Agent、多模型场景下,这个未来需求会影响请求排序、模型驻留和节点选择。例如两个请求当前都只占少量 KV,但一个很快结束,另一个会继续生成很长文本,调度策略就不应完全一样。预测提供前瞻信号,分页管理负责高效执行,两者配合才能把资源安排得更好。
把预测作为初始预算,运行时继续按真实需求增长。每次追加前检查可用容量,不够时暂停新请求准入,或者选择低代价的回收与重算策略。与此同时,把这次低估反馈给后续预算校准。这里最关键的是把“提高利用率的预测”和“约束实际分配的容量检查”配合起来,这样预测不准时仍然有明确的处理路径。
我会把输出长度预测和运行成本估计分开。输出长度主要由任务语义和生成行为决定;相同输出长度在不同缓存、batch 和资源状态下,耗时可能不同。前缀命中后,时间估计要扣除已复用部分的 prefill 成本,再结合当前排队、decode 速度,以及可能的 KV 传输或重算成本。长度预测器提供需求信号,缓存与负载观测负责把需求换算成当前系统上的成本。
现有长度模型没有把前缀命中率作为显式特征,因此这类在线状态更适合先接到成本估计层。
预测器先给当前调用的长度,再用模型的 prefill 和 decode profile 转成当前阶段时间。工作流调度还要考虑后续阶段:根据当前进度和相似历史状态估计后续成本,再与当前阶段合起来。工具调用时间也要单独处理,因为预测工具调用参数的 token 数,并不等于预测外部工具要执行多久。
我分两层看:预测层比较长度误差,并分别分析工具调用、非工具调用和思考模式;系统层看这些预测是否改善排序、显存预算和最终完成时间。论文中输出长度预测 MAE 为 165.43 token、R² 为 0.7774,相比 Magnus 的 MAE 降低 19.2%。消融比较直接回归、加入工具调用概率,以及加入语义特征后的变化。然后固定工作负载和运行资源,比较排队、SLO 达成与显存预留,建立从预测到调度收益的联系。
验证时按时间划分训练和测试,并把同一会话的数据关联起来检查,避免未来信息进入当前请求特征。平均误差之外,还要看长输出和低估尾部。
我把 Agent 工作流中的行为差异转化成了系统可用的资源信号:通过工具调用意图与输出长度的两阶段预测,提前估计阶段成本,再把这些估计接到工作流调度和多模型显存管理中。工程上补齐了预测服务、上下文获取、异步反馈和模型更新,使预测能够进入真实请求链路。这项工作的价值在于把模型行为、资源需求和调度决策连起来。
来源:Maestro 论文 §III–IV,以及 concerto-runtime@8ee8969 中 python-predictor 的训练、预测与回调实现。实现细节对应后续服务快照;实验数字取自论文。