Continuum:面向多轮 Agent 工作负载的 KV Cache TTL 调度系统

  • 关联论文:2511.02230
  • 作者:spark
  • 更新:2026-07-05

一句话结论

Continuum 提出一种基于 KV Cache TTL(time-to-live)的多轮 Agent 请求调度策略,按 reload 成本与排队延迟选择性地把 KV cache pin 在 GPU 上,使多轮 Agent 工作流在 SWE-Bench、BFCL、OpenHand 等真实负载下的平均 Job Completion Time(JCT)提升超过 8 倍,同时改善吞吐。

它要解决的真问题

LLM 推理引擎默认用"先来先服务 + 完成即驱逐"的策略管理 KV cache——一旦请求结束,新请求到来就把它的 KV cache 踢出去腾显存。这套策略对单轮补全(chatbot / prompt-to-text)很合适,但在 Agent 工作负载下失灵:

  1. 多轮之间有工具调用间隙。 Agent 不是一次性吐出一长串 token,而是在 LLM 调用之间频繁插入工具执行(HTTP、SQL、文件 IO)。这些工具调用耗时虽然比人类回复短得多,但远长于一次 forward 的 KV 重计算成本;如果按老规矩立刻驱逐,下一轮再回来时 cache 已经被回收。
  2. 重新加载的代价很高。 工具调用一结束往往又触发新一轮 LLM 调用,cache 被驱逐后只能重算(不开启 offloading)或从 CPU 端拉回(开启 offloading),二者都会让 JCT 出现明显长尾。
  3. 工具调用时长方差巨大。 一次 SQL 查询 50 ms,一次 sandbox 执行 30 s。如果用静态阈值,会同时伤害"快工具+长 prompt"和"慢工具+短 prompt"两种场景。

传统的做法要么无条件 pin(显存吃紧)、要么无条件驱逐(长尾恶化),两种都没把"recompute / reload 成本"和"驱逐后排队成本"放在一起算。Continuum 抓住的是这两个成本动态匹配这一空白。

核心方法

1. 总体思路

对每个产生工具调用的请求,Continuum 计算一个动态 TTL 值,把它的 KV cache pin 在 GPU 上 TTL 时长;TTL 到期后允许被正常驱逐队列回收。当 deadline 内(TTL 内)对应的多轮请求回流时,可以直接复用 KV cache,绕过重算 / 重新 offload 阶段。配上 program-level FCFS(同一会话内的前后轮次保持顺序),多轮连续性即可保留。

2. TTL 决策机制

TTL 的设定需要权衡两件事:

  • Pin 代价:占用 GPU 显存,可能会阻塞新请求入场,增加排队延迟。
  • Evict 代价:若被驱逐,下轮回来要么重算(prefill 重新跑)要么 reload(offloaded cache 从 CPU 内存拉回 GPU),二者都会显著拉长该轮 LLM 调用的首 token 时间(TTFT)。

论文把这两成本显式量化成 TTL 的上界,并叠加一些工具调用时长的统计先验(p50 / p95)保证鲁棒性。直观上:

TTL = clamp(
    upper_bound_from_reload_cost,
    lower_bound_from_queueing_delay,
    tool_hist_p95,
    max_global_cap
)
  • upper_bound_from_reload_cost:保证 pin 这个 cache 的显存占用时间不超过"下次 reload 它"的开销——如果 pin 的代价已经比 reload 还要贵,那就别 pin。
  • lower_bound_from_queueing_delay:保证 pin 至少不短于系统排队延迟——否则你 pin 的瞬间就被新请求赶走,零收益。

当工具调用时长不可预测(比如一次性脚本执行)时,这套机制也能通过 TTL 上界自动退化——到期就走传统路径,不会让显存被永久卡死。

3. 与调度策略的协同

TTL 单独使用还不够。论文同时引入了 program-level FCFS:把同一 Agent 会话内的多次 forward 视为一个 program(而不是独立的"请求"),调度器保证同一 program 的轮次按顺序完成,避免一个长工具调用后轮到该会话时下 prompt context 被错误的并发请求冲掉。

二者合在一起的效果:

  • 多轮连续性:TTL 让 KV cache 在工具调用期间存活,FCFS 保证轮次顺序,因此同一会话内不需要重新 prefill。
  • 健壮性:TTL 到期自动驱逐,对异常长尾(工具卡死、外部依赖超时)有兜底。
  • 吞吐:占用 GPU 的 cache 到达 TTL 后自动释放,新请求的入场延迟被显式控制。

4. 伪代码

def on_tool_call_start(req):
    ttl = compute_ttl(
        reload_cost=estimate_reload_cost(req),
        queueing_delay=current_queueing_delay(req),
        tool_hist=req.tool_call_duration_history,
    )
    pin_kv_cache(req, ttl)


def on_new_request_arrive(req):
    ttl = compute_ttl(...)
    pin_kv_cache(req, ttl)
    if not fits_in_gpu():
        evict_expired_pins()         # only evict when TTL has expired
    enqueue(req)                     # program-level FCFS inside session


def on_ttl_expired(req):
    unpin_kv_cache(req)              # safe to let scheduler decide next

读者可以重点关注:compute_ttl 同时把 reload cost、queueing delay、工具时长统计三者作为输入条件,这是它和静态 cache 策略的根本差别。

关键实验与数据

论文在真实 Agent 基准上做了端到端评估:

  • 基准:SWE-Bench(编程 Agent)、BFCL(function calling)、OpenHand(多步工具调用)。
  • 模型:Llama-3.1 8B/70B、Gemma-3 12B、GLM-4.5 355B,覆盖小到大的多个量级。
  • 核心结论:相比默认"先完成即驱逐"的策略,Continuum 把多轮 Agent 工作流的平均 JCT 提升超过 8 倍,同时改善吞吐(throughput),即没有"为了尾延迟牺牲总吞吐"。
  • 鲁棒性验证:在工具调用时长方差大的场景(OpenHand 含外部 API 调用)依然有效,证实 TTL 自适应退化路径工作正常。
  • 细节数据:原文给出具体 P50/P95 数字和吞吐改善倍率(v6 版 348 KB 内含完整数字,本文作者未逐项背出数字;如需精确读数请回到 v6 PDF)。原文未明确的细粒度数字请以正文为准。

亮点与局限

亮点

  1. 贴着生产瓶颈做优化。 问题切得很具体:Agent 工具调用让 KV cache 短时间可复用。比起动辄改 attention,把现有 KV cache 的生命周期问题解决了,工程上更友好。
  2. TTL 自动退化机制。 工具时长方差大是 Agent 场景的现实,论文显式考虑了最坏情况下的退化路径,不会因为推理流量抖动就把显存锁死。
  3. 跨模型、跨基准、跨量级都验证。 Llama-3.1、Gemma-3、GLM-4.5 之间的差异巨大,能拉到 8x JCT 改善说明结论不依赖某个特定模型家族。
  4. 8x+ JCT 是工程口径指标。 不是 perplexity、不是 MMLU,是 Job Completion Time,对生产部署方最有说服力。

局限

  1. 依赖工具调用可观测性。 Continuum 需要推理引擎感知"这一请求产生了工具调用、工具调用大概会持续多久"。若推理系统上游框架(LangChain / LangGraph / 自研编排器)不暴露这些信号,Continuum 只能从更粗的粒度估计。
  2. 单实例 scope。 论文聚焦单机多卡的推理引擎调度。对于多机 prefill-decode 分离或者 PD-disaggregation 的部署,TTL 决策需要跨节点协调,原文未明确给出方案。
  3. 没有公开代码 / 模型权重之外的扩展数据。 论文提供了方法与评测,但代码与可复现脚本在原文中未明确给出链接(v6 是 2026-05 最后更新版),落地团队需要自行实现。
  4. TTL 上界对 reload cost 的估计依赖 profiling。 如果 prompt 长度极端(百万 token 级)或者 offloading 路径变化,estimate 误差会放大。

对工程落地的启发

  1. 对推理引擎团队:在 KV cache manager 里把"完成即驱逐"改成"完成 + TTL",并把 TTL 与 cache 重新加载成本、当前排队延迟挂钩。可以先做 conservative TTL(如 p95 工具时长)做 A/B,再演进到论文里的 cost-based 公式。
  2. 对 Agent 框架团队:让推理引擎能识别"本轮 forward 来自哪个 program / session、它的工具调用历史是什么",把 program-level FCFS 的判断信号交给调度器。这是与论文配套的运行时不变量。
  3. 对容量规划:JCT 8x 改善意味着同样 SLA 下需要的 GPU 卡数可以显著下降;反过来同样卡数能支撑更高 QPS。论文给出的吞吐量改善数据可以作为 PoC 阶段对比基线(具体到数字级请回原 PDF v6 表格)。
  4. 对 SRE 监控:增加"P50/P95 TTL 内 cache 复用率"与"TTL 到期后被迫 reload 的比例"两个指标,长期跟踪 Agent 流量模式变化。

与同方向工作的关系

  • 传统 LLM 推理服务:vLLM、SGLang、TensorRT-LLM 等以"吞吐最大、TTFT 低"为目标,KV cache 调度主要面向单轮补全;Continuum 补的是多轮 + 工具调用细分场景。
  • 长上下文 / KV cache 压缩:StreamingLLM、Scissorhands、H₂O、KV 量化等专注"如何减少 cache",而 Continuum 关注的是"在合适时机保留 cache"这条互补路线。
  • Agent 系统综述与运行时(如多 Agent runtime、agent harness):它们解决 agent loop 编排与 harness 工程,Continuum 解决的是这一编排链路在推理引擎层最薄弱的资源调度环节,因此与 Agent runtime / harness 论文高度互补。
  • LLM 推理在线调度(如 Fluid-Guided、hindsight optimal benchmark):这些工作集中在请求调度策略本身(FCFS vs SJF vs 优先级),Continuum 把 cache 生命周期与调度耦合在一起,比单独的 request-level 调度更靠近 Agent 实际状态。

适合谁读

  • LLM 推理引擎开发者(vLLM / SGLang / TGI / 自研引擎):把 TTL 与 cost-aware 决策落到 cache manager 里。
  • Agent 平台 / Harness 团队:评估自家框架在多轮工具调用下的尾延迟。
  • AI Infra / 容量规划:把 JCT / 吞吐改善作为新一轮 GPU 采购与配额分配的依据。
  • Agent 应用开发:理解"为何我的 Agent 在工具多的时候变慢",以及为什么有些部署方要把工具调用粒度整合而不是拆碎。
  • 学术读者:OS / 系统方向,关注 multi-tenant 调度与 cache 生命周期联合优化这条线。

不确定处

  • JCT "8x"、吞吐改善的具体绝对数字(如 P50 JCT 从 X s 降到 Y s,吞吐从 A req/s 升到 B req/s)原文 v6 表格未在本文作者抓取的 abstract 中给出明细,建议落地前直接读 v6 PDF 全文(348 KB)核对。
  • 代码与可复现脚本是否公开:原文未明确(v1–v6 提交历史里也未见 GitHub 链接在 abstract 区域呈现)。
  • 多机 prefill-decode 分离 / PD disaggregation 部署下的 TTL 决策:原文未明确。
  • 与程序级 FCFS 之外的调度策略(如 priority queue、preemption)是否兼容:原文未明确讨论。

参考来源:arXiv:2511.02230 v6 abstract(2026-05-25)、paper card paper_cards/154-2511-02230.md、工作队列 queue/work-queue.md

工程落地与核查(Jay)

事实核查

✅ 已被原文支持: - KV Cache TTL 机制核心描述:Abstract 明确"In this paper, we present Continuum, a serving system to optimize job completion time for multi-turn agent workloads by introducing time-to-live mechanism for KV cache retention",与正文解读一致。 - 评估基准:SWE-Bench / BFCL / OpenHand,Abstract 确认。 - 评估模型:Llama-3.1 8B/70B / Gemma-3 12B / GLM-4.5 355B,Abstract 列出。 - JCT 8× 改善:Abstract 原话 "improves the average job completion times by over 8x while improving throughput",verbatim 一致。 - program-level FCFS 机制:正文解读描述与 Abstract "program-level first-come-first-serve" 一致。 - v6 版本:arXiv submission history 显示 v6 为最新(2026-05-25),确认是 2026 年中更新的最新工作。

⚠️ 存疑 / 待核实: - "一次 SQL 查询 50 ms,一次 sandbox 执行 30 s":正文解读中的这两个数字为 illustrative examples,Abstract 未给出具体量级;原文可能是作者基于经验数据的典型场景举例,而非实测统计值。引用时建议标注"(典型值示例,非原文实测统计)"。 - JCT 8× 改善的具体场景:Abstract 未说明"8×"是跨所有基准的平均值还是某个基准的峰值数字;v6 PDF 正文(348 KB)可能有更细粒度的 breakdown。 - TTL 具体参数范围:原文未给出 max_global_capupper_bound_from_reload_cost 的量级,工程落地需要自己 profiling。 - Continuum 代码未发布:截至 v6(2026-05-25),arXiv 页面和摘要区均未见 GitHub 链接;落地团队需自行实现。

可读性精修

  1. "先完成即驱逐"的 KV cache 默认策略描述:vLLM 的默认驱逐策略其实比"先完成即驱逐"更复杂(是 LRU + 显存压力共同决定)。原文解读的描述是"完成即驱逐"的简化版,适合教学但不够精确。引用时可在该句后加"(简化描述,实际 vLLM 等引擎还有显存压力阈值等辅助条件)"以避免误导。
  2. TTL clamp 公式的可读性:正文给出的伪代码化 TTL 公式是高度抽象的工程直觉描述,不是论文原文的数学形式。应在该段加一个说明:"上述 clamp 公式是解读作者基于方法论描述的工程化重构,不代表论文原文的精确数学定义"。
  3. "8× JCT 改善 vs 同时改善吞吐":这两个 claim 在 Abstract 中同时出现,但 JCT(Job Completion Time)改善和 throughput 改善在系统层面存在潜在的 trade-off(改善 JCT 通常意味着更激进地 pin cache,可能降低吞吐)。原文同时声称两者均改善,说明 TTL 机制设计没有引入这个 trade-off——这个反直觉结论应在正文中点明,而不是作为两个独立 claim 平行列出。

工程落地:实际系统怎么用,坑在哪

1. Continuum 的核心工程前提:工具调用可观测性

Continuum 的 TTL 决策依赖三个输入信号:estimate_reload_costcurrent_queueing_delaytool_call_duration_history。这三个信号在生产系统中的可获得性差异巨大:

tool_call_duration_history:最易获得,前提是 Agent 框架记录每次工具调用的起始时间戳。大部分生产系统(LangChain v0.3+ / LangGraph / 自研)都有这个埋点。风险:如果工具调用是通过外部 service(HTTP call)执行的,结束时间可能需要 webhook 或 polling 才能可靠获取。

current_queueing_delay:中等难度。需要推理引擎暴露"当前队列深度"和"当前请求的预估等待时间"。vLLM / SGLang 目前不直接暴露 queueing_delay 这个指标,需要:

# 工程化获取 queueing_delay
current_queue = vllm_engine.get_queue_state()
estimated_wait = sum(req.remaining_prefill_time for req in current_queue)

remaining_prefill_time 本身也是估算,不是精确值。

estimate_reload_cost:最难。需要知道如果 KV cache 被 evict,下一轮 reload 需要多少时间。这取决于: - 是否开启 offloading(开启时 reload ≈ CPU→GPU 带宽,关掉时 reload = 完整 prefill 重算) - prompt 长度(影响重算 FLOPs) - 当前 GPU 利用率(影响 reload 被排到多后面)

工程上最保守的做法:estimate_reload_cost = 完整 prefill 时间(prompt_length → first_token)。这个估计不依赖 offloading 策略,是任何系统都能跑的下界。

2. TTL 决策的实现路径(从简到难)

路径 1:保守固定 TTL(最快落地,1–2 天)

def compute_ttl_conservative(req):
    # 以工具时长 p95 为 TTL 上界,不做 cost estimation
    tool_p95 = get_historical_p95(req.session_id)  # 来自 metrics
    return min(tool_p95, 300)  # 最多 pin 5 分钟,防止显存永久卡死

适用场景:刚开始做 A/B test,不需要改动调度器核心。 效果预估:比 baseline(完成即 evict)好,比完整 Continuum 差。

路径 2:自适应 TTL(中期目标,1–2 周)

def compute_ttl_adaptive(req):
    reload_cost = estimate_prefill_time(req.prompt_length)
    queue_delay = current_queue_delay()
    tool_p95 = get_historical_p95(req.session_id)

    ttl_lower = queue_delay * 1.2   # 至少覆盖排队延迟
    ttl_upper = reload_cost          # pin 代价不超过 reload 代价
    return clamp(tool_p95, ttl_lower, ttl_upper, max_cap=600)

适用场景:推理引擎已暴露 queue metrics,团队有 profiling 数据。

路径 3:完整 Continuum(完整版,1–2 个月) 在路径 2 基础上,增加: - Program-level FCFS 调度支持(识别同一 session 的多轮请求) - KV cache 引用计数(同一 cache 被多个 session 引用时的 pin 计数) - Cross-request prefix sharing(当多个请求共享系统 prompt 时,cache 可共享 pin)

3. vLLM/SGLang 集成:哪里要改

Continuum 的 TTL 机制与现有 vLLM / SGLang 的 KV cache 管理器直接冲突,集成时需要改核心逻辑:

vLLM 集成点vllm/cache_block_manager.py_should_swap_out() 方法。当前实现是"请求完成后立即 evict"。改动方向:在 AllocationStatus 里加入 TTL 字段,_should_swap_out() 变成:

def _should_swap_out(self, block):
    if block.is_pinned():
        return False  # pinned block 不 evict
    if block.ttl_expired(now):
        return True   # TTL 过期,正常 evict
    return self._legacy_eviction_criteria()  # fallback 到原有 LRU 逻辑

⚠️ 关键风险:vLLM 的 cache_block_manager 不感知"session / program"概念,需要在上游(Agent 框架层)维护 session→cache_block_ids 的映射,并在每个 session 的首轮 forward 时把这个映射传给 vLLM engine。这是集成最复杂的部分。

SGLang 集成点sglang/srt/kv_cache_manager.py。SGLang 的 RadixAttention 天然支持 prefix sharing 和 cache reuse,TTL 机制可以复用现有的 cache_pool.pin() 接口,改动比 vLLM 略小。

4. Program-level FCFS 的工程实现

Program-level FCFS 不是简单的"同 session 请求按顺序处理"——它要求调度器:

  1. 识别 program 边界:把来自同一 session_id 的多个 LLM forward 调用归为一个 program。
  2. 优先调度:当多个 program 的请求同时 ready 时,优先调度那些 KV cache 还存活(未被 evict)的 program。
  3. 避免队头阻塞:一个长 prompt 的请求不应该 BLOCK 同一 program 内的短 prompt 请求。

工程实现:

def schedule_request(req):
    program = session_to_program[req.session_id]

    # 优先调度有存活 cache 的 program
    if program.has_live_cache():
        return dispatch_to_gpu(req, reuse_cache=True)

    # 否则按 FCFS
    return enqueue_program_fcfs(req)

⚠️ 坑:如果同一 program 内的两次 LLM 调用之间插入了非 LLM 的计算(如 Python 代码执行),program-level FCFS 无法感知这个"硬屏障"——此时即使 KV cache 还活着,program 逻辑上也不能复用(因为中间状态变了)。解法:在 program 层面加显式的"checkpoint + restore"语义,让调度器知道何时可以复用、何时必须重新计算。

5. 多机 PD-disaggregation 场景:Continuum 的盲区

论文聚焦单实例调度,对 prefill-decode 分离架构未给出方案。生产中 PD 分离部署时:

Prefill Pool ──[KV transfer]──► Decode Pool
     (高算力 GPU)              (高带宽 GPU, 大 batch)

Continuum 的 TTL 机制在 PD 分离架构下有一个关键问题:KV cache 的 TTL 到期时,它可能同时存在于 Prefill 实例和 Decode 实例中。需要:

  1. 跨实例 TTL 协调:TTL 到期信号需要同时发给 Prefill 和 Decode 实例,避免"Prefill 已 evict,Decode 还在 pin"的跨实例不一致。
  2. KV 传输与 TTL 的耦合:如果 TTL 临近到期,新请求到达时应该触发 KV 传输(而不是等下一轮 prefill),因为 TTL 到期后 KV 会消失。
  3. Disaggregation 引入的额外延迟:KV 传输本身(跨 GPU 或跨机)有带宽开销,TTL 的决策公式中应加入 KV 传输时间(kv_transfer_time),而不仅仅是 reload cost。

6. 生产监控指标:最少可行集合

落地 Continuum 后,必须新增以下 metrics(现有 vLLM / SGLang dashboard 通常不自带):

# KV Cache 复用率
cache_reuse_rate = (cache_hits / total_multi_turn_requests)
cache_hit_by_ttl = (cache_hits_due_to_ttl / total_multi_turn_requests)

# TTL 效率
avg_ttl_set = mean(ttl_values_set_per_request)
ttl_expiry_rate = (expired_ttls / total_pinned_requests)
evict_before_reuse = (evicted_while_session_alive / total_evictions)

# 系统影响
jct_p50 / jct_p95 / jct_p99 = percentiles of job_completion_time
throughput = total_requests_completed / time_window
gpu_memory_utilization = (pinned_cache_size / total_gpu_memory)

推荐 alert 阈值(初始值,需要按实际流量调): - cache_reuse_rate < 0.3:说明 TTL 设置过短,大量 cache 在 session 存活期内被 evict - ttl_expiry_rate > 0.8:说明 TTL 上界设置过于宽松,cache 被 pin 但很少被复用(等于资源浪费) - jct_p99 相对于 baseline 恶化 > 20%:TTL 机制引入了新的长尾风险

7. 最常见的 Continuum 落地失败模式

  1. TTL = 无限(永久 pin):没有设置 max_global_cap,当 agent 遇到 bug 卡死时 KV cache 永不释放,显存被耗尽,所有新请求 queuing。解法:强制 max_global_cap(建议 300–600 秒)+ 当 GPU memory utilization > 90% 时触发强制 evict。
  2. Program-level FCFS 实现遗漏:只加了 TTL 但没改调度器,同一 session 的第二次请求仍然被 FCFS 调度,KV cache 虽然 pin 住了但被后面的长请求 block。解法:调度器必须感知 program 边界,优先调度有 live cache 的 session。
  3. 工具调用时长低估:实际生产中,HTTP 调用有 retry、polling 等待等,实际耗时可能是"首次返回时间"的 2–5 倍。TTL 只参考 p95 可能不够。解法:TTL 上界应该用"p99 + 2× retry_cost"来计算。
  4. 多租户场景下的 pin 公平性:一个 session pin 了大量 KV cache(如长文档多轮对话),影响其他 tenant 的请求入队。解法:在 TTL 计算中加入"显存公平性权重",当 pinned_memory_fraction > 0.5 时对新 pin 请求做降权。

核查结论

Continuum 的核心 claim(JCT 8× 改善、TTL + program-level FCFS 机制)在 arXiv v6 Abstract 中有直接 verbatim 支持,可信度高。主要工程落地障碍是:① 代码未发布需自行实现,② 需要工具调用可观测性基础设施(部分生产系统缺失),③ PD-disaggregation 场景未覆盖(大规模部署的硬墙),④ 生产监控指标需从零建立。JCT 8× 数字是摘要级 claim,建议引用前先回 v6 PDF 核对具体基准 breakdown,以避免以偏概全。