Agent 请求 142K token 进、444 出,瓶颈在缓存驻留

传统 serving 引擎的调度模型建立在三个隐性假设上:请求彼此独立、到达是开环的(来了就处理、处理完就走)、性能用请求级延迟考核。这个模型在面向 chatbot 负载设计 vLLM、SGLang 的调度器时大致成立:一次调用就是一个完整生命周期,返回即清理。Agent 工作负载把这三条假设逐条击穿。工具调用让请求不再独立:agent 调一次模型,拿到工具调用指令,等待工具返回,把结果拼回上下文,再发起下一轮。会话因此变成带 think time(思考空档)、状态回流与子 Agent 分叉的闭环工作流,可复用的 KV 状态开始主导成本。这篇专题想论证一件事:Agent 推理的核心稀缺资源,已经从单次调用的算力,转向跨轮次的 KV 状态连续性。我们从真实负载的形状说起。

一次调用变快,不等于整个任务变快:开环假设是怎么失效的

把"Agent 流量只是上下文更长"当成工作假设,是绝大多数 serving 团队踩的第一个坑。2026 年,微软 Azure 的研究者发布了 GitHub Copilot 一周生产 traces 的刻画(数据窗口为 2026 年 6 月),数据规模是 3.2M 用户、13M 会话、761M 次 LLM 调用、775M 次工具调用、95T tokens,这是目前唯一一份生产级 AI 编码 agent 的服务层负载画像。AgenticTrace

这份 traces 首先打掉的是"请求是用户敲出来的"这个直觉:87% 的 LLM 调用由 agent 自主发起。流量形状也不是一段 prompt 配一段输出,而是一个 turn 内部藏着一长串 LLM 与工具 1:1 配对的链条。更关键的是 KV 缓存的命中率。在同一个 turn 内部,命中率约为 90%,看起来一切正常;一旦跨过 turn 边界,同模型场景下掉到约 64%;如果这个 turn 换了模型,命中率只剩约 23%。AgenticTrace

反常的地方就在这里:KV 命中率并不是 workload 的稳定属性,它是被会话结构事件门控的。调度器控制不了用户什么时候结束一个 turn、什么时候把模型换掉,而这些事件直接决定上一轮留在显存里的 KV 还有没有用。命中率从 90% 掉到 23%,本质是同一个现象:前缀缓存(复用会话历史中已算过的 KV)的可用性在 turn 边界被外部事件重置。看这张图:

图注:三次调用场景下的 KV 缓存命中率横向对比。turn 内部基线约 90%,同模型跨 turn 边界降至约 64%(平均下降 26%),模型切换后仅约 23%(下降 67%)。命中率不是负载属性,而是被会话事件门控的。AgenticTrace

如果只是"KV 缓存经常失效",问题还不算严重。真正改变问题结构的是下一个事实:一个用户可见的 turn 内部不是一次请求,而是一长串自主调用。Copilot 的 traces 显示,每个 turn 中位包含约 4 次 LLM 调用;其中约 9.1% 的 turn 属于"深度循环 + 失败"类型,一次工具失败会触发 agent 的自主重试级联,单个这样的 turn 可以产生 36 次 LLM 调用。agent loop 和 request loop 是两个完全不同的对象,下面这条时间线就是一个实例:

图注:一个"深度循环 + 失败"的 turn 内部时间线:8 组 LLM 调用与工具调用配对,其中一次 run_command 失败(红色)触发下方浅红色区域标出的重试爆发,额外产生大量 LLM 调用与工具执行。图中两处省略号表示序列被截断,不代表整条时间线只有 8 组事件。AgenticTrace

把这两张图放在一起,开环假设的失效链条就完整了:传统引擎把每个请求当作独立的、开环的、一次性的流量处理,但 Agent 会话是带暂停、回流与分叉的闭环 DAG。SemiAnalysis 的 AgentX 把这种拓扑形式化为主链(main-agent chain)、子 Agent 分叉(subagent branch)与汇合点(join gate)组成的依赖图;SGLang 的 RadixAttention 则证明,理想情况下前缀复用可以是运行时自动维护的系统级机制。SGLang 理想机制存在,生产里却跨 turn 大面积失效,这个落差正是后面所有讨论的起点。

所以第一章的结论是:一次调用变快,证明不了整个任务变快。单请求延迟衡量的是从 prefill 到解码的那段路,而 Agent 任务的真身是跨越几十个 turn、中间夹着工具等待的状态流。既然状态连续性成了稀缺资源,下一步就得量化:这种负载到底长什么样?

中位请求 142K 输入、444 输出:Agent 负载的真实形状

先把一个容易混淆的区别讲清楚:"上下文长"和"状态连续"不是一回事。上下文长说的是体积:一次请求带进来的 token 多。状态连续说的是时间:下一轮能不能复用上一轮留在显存里的 KV cache。前者是数据属性,后者是系统行为,两者可以完全脱节。

SemiAnalysis 的 AgentX v1.0 数据集把 Agent 负载的真实形状落到可核对的数字上。它从内部 opt-in 的 Claude Code 代理 traces 中选取 393 个会话(每个会话至少 20 个请求、至多 10 个并发子 Agent),统计结果是:单请求输入 token 的中位数 142,016,输出 token 的中位数只有 444。44% 的会话包含子 Agent,共 1,697 个 subagent groups,每个 group 的中位运行时长 2.27 分钟。AgentX

142K 进、444 出,比值超过 300:1,与 Copilot 生产 traces 里 per-call 输入输出比超过 275:1 的数字遥相呼应。聊天负载里输出 token 通常主导成本(一段 prompt 换一大段回复),Agent 负载恰好相反:成本主体在输入侧,而输入侧的大部分内容本可以被前缀缓存命中。AgentX 的语料仪表盘显示整体 cache-hit 率高达 99%,理论上限的 prefix hit rate 超过 96%。KV 复用高度可实现,前提是缓存驻留在 HBM 或可达的主机内存上。"驻留"成为决定成本的那个变量。

只看中位数还不够。AgenticBench 方法学发布了三幅 log 尺度的分位分布图,把会话行为展开成分布而不是单个数字:

图注:AgentX v1.0 的三幅对数尺度分布:输入序列长度(n=98,827)、输出序列长度(n=98,786)、跨轮次间隔延迟(n=73,383)。输入中位 142,016、输出中位 444、跨轮次间隔中位 3.84 秒且 p95 达 3.08 分钟。中位与 p95 之间的数量级差,说明只看 aggregate 或均值会掩盖真实行为。AgenticBench

三幅分布图讲了三件事。输入侧:中位 142K,p95 冲到 682,880,长尾里塞满接近百万 token 的请求,这批请求占了显存的绝大部分。输出侧:中位 444,绝大多数请求只是"嗯、然后呢"级别的短回复,它们本应是解码的最轻负载。跨轮次间隔:中位 3.84 秒,每个请求之间都夹着一个工具等待期,这是调度器要处理的 think time;而 p95 达到 3.08 分钟,说明有一部分请求的间隔长到缓存可以被安全卸载。

这三组数字里有三个口径陷阱,引用时必须盯紧:142,016 是 full variant(保留最长 1M 上下文)的中位数,256K variant 的中位输入是 88,768,两者不可混用;语料仪表盘的 99% cache-hit 是 corpus snapshot 而非 v1.0 的实测中位数;超过 96% 的 prefix hit rate 是设计目标与理论上限,不是实测值,实际能达到多少取决于 KV 池容量与淘汰策略。

会话的形状还不仅仅是"又长又不对称"。AgentX 把每个会话重建为依赖图而不是扁平文本流:

图注:AgentX 的 replay DAG 示意图:左侧是原始 trace JSON,右上是记录的时间线(main agent 与 subagent_001/002 三条泳道),右下是重建的依赖图,两条子 Agent 链从同一个主请求分叉、共享一个 join gate 后主链恢复。这是示意性合成图,不是真实会话回放。AgentX

这张图回应了第一章遗留的问题:闭环结构不是抽象概念,44% 的会话真的会分叉出子 Agent,主链暂停、分支并行、join 汇合。正是这种结构决定了第二章的结论:Agent 负载的计算主体不是"生成新 token",而是维护一个长寿命、会暂停又会回流的 KV 工作集。那么问题来了:这个工作集的缓存,是留、是卸、还是搬?

缓存留、卸、还是搬:一个决策如何绑死路由、调度与 P/D 配比

"KV 工作集"这个词不是比喻。把操作系统的工作集模型搬过来看:进程的页面工作集若驻留在物理内存里,缺页率就低;一旦被换出,下一次访问就要从磁盘搬回来,代价高一个数量级。Agent 的 KV 工作集同理:驻留在 HBM 里,下一轮 turn 直接命中;被卸到 DRAM 或 SSD,下一轮就要搬运或重算 prefill。驻留与否决定下一轮是重算还是搬运,所以"这轮结束之后 KV 往哪放"是一个系统级决策,它同时约束四个子系统。

KV 池化:让缓存成为独立的一层资源

第一,把 KV 变成可调度的池子。 MemServe 把这个问题抽象成 MemPool 三层 API:memory 负责 KV block 的分配与释放,index 用 radix tree 维护 token 到缓存块的映射,transfer 负责把缓存搬到指定位置,分别回答"放哪、在哪、怎么搬"。MemServe

Mooncake 是同一思路的工业版:Kimi 把 GPU 集群重组为 prefill 池、decode 池和由闲置 CPU/DRAM/SSD/RDMA 构成的分离式 KVCache 池,由中央调度器 Conductor 统筹。它的四步工作流是:优先复用远端内存里的前缀缓存,增量 prefill,经 RDMA 流式传输 KV 到 decode 节点,再由连续批处理解码。这套架构让 Kimi 在真实负载且满足 SLO 的前提下多处理了 75% 的请求。Mooncake

先看 MemServe 的架构,它给出"KV 是一等调度资源"的心智模型:

图注:MemServe 三组件架构:顶部 Global Scheduler,中部三类推理实例(prefill-only、decode-only、PD-colocated),底部横跨全部实例的 Elastic Memory Pool(HBM + DRAM 两层)。缓存不再依附于某个引擎,而是独立的一层资源。MemServe

缓存如何一步步长进解耦流程,MemServe 的 PD-Caching 五步设计画得很直白:

图注:PD-Caching 设计流程:左侧 prefill-only 实例与右侧 decode-only 实例之间,标号 1 到 5 的箭头展示 A-KV(活跃 KV)与 H-KV(历史 KV)如何在 prefill 结束后沉淀进 radix-tree 支撑的 MemPool index,再在后续请求中跨实例复用,也就是"留、卸、搬"三种动作的具体路线。MemServe

Mooncake 的架构图展示了工业部署形态:

图注:Mooncake 架构总览:prefill 池、decoding 池与 KVCache 池(CPU/DRAM/SSD/RDMA)三类资源池分离,中央 Conductor 负责全局调度与跨池 KV 传输。这是架构图,不是具体部署实例图。Mooncake

P/D 解耦:隔离干扰,但引入 KV 传输

第二,prefill 和 decode 要不要分池(P/D 解耦)。 "留还是卸"的答案是"搬"的时候,就出现第二个决策。DistServe 给出形式化语言:goodput,也就是在 TTFT(首个输出 token 之前的等待时间)与 TPOT(首个 token 之后每个输出 token 的平均间隔)双 SLO 达标的前提下,每张 GPU 能承载的最大请求率。它指出 co-location(prefill 和 decode 挤在同一批 GPU 上)有三个结构性缺陷:干扰、调度失效、并行度耦合。实测中,13B 模型在 colocation 下每 GPU 的 goodput 只有约 1.6 req/s,解耦后提升到 3.3 req/s。DistServe

差距来自结构而非调参,看这张 teaser 图:

图注:上方面板是 P90 TTFT 随请求率的变化:现有系统在约 1.6 req/s 后陡升,而只跑 prefill 的参考 GPU 平台能撑到高得多的请求率;下方面板是对应的 P90 TPOT(每个输出 token 的延迟)。垂直虚线标出 90% SLO 达成阈值。两条单阶段曲线的天花板远高于合体曲线,这就是解耦释放的空间。DistServe

注意解耦的代价:KV 要在池间传输。这也解释了为什么 MemPool 的 transfer API 和 Mooncake 的 RDMA 传输是必需品,而不是优化项。

路由:sticky 的负载均衡不是负载均衡

第三,请求该路由到哪个实例。 KV 状态的位置一旦成为稀缺资源,这就是个真问题。Zhou 在 2026-05 的 position paper 里把 vLLM 的 5 种路由策略(round-robin、random、power-of-two-choices、consistent hashing、cache-aware)全部定性为通用启发式,并指出 LLM serving 路由有三个经典负载均衡没有的特性:sticky assignment(一旦路由就不能轻易迁移,因为要搬整个 KV cache)、KV 随解码单调增长、解码长度在路由时未知。ServingOpt

更微妙的是 DP 多 worker 内嵌 EP 时的同步屏障:每个 worker 先本地做 attention,再做 all-to-all 同步,attention 耗时正比于该 worker 的 KV 长度,于是最快 worker 必须空等最慢 worker:

图注:DP 下内嵌 EP 的 barrier 同步示意:4 个 worker 各自持有 KV cache,其中一个 worker 的 KV 长条显著更长,attention 计算更慢,其余 worker 只能在同步屏障处空等。空闲由最慢者决定,这解释了对 KV 长度做精确均衡的必要性,而非经典轮询所能解决。ServingOpt

统筹:workflow-aware runtime

第四,谁来把这些决策捏在一起。 2026-09 的 AgentServ 把前三类优化封装成联合约束优化:Predictor 预测每个部署在每块设备上的运行时间、峰值内存与加载时间,Constructor 枚举并合并不同工作流间的生命周期动作,Scheduler 联合决策复制体、放置与资源顺序,最终在异构 GPU 池上把 makespan 缩短 36.8%。AgentServ

缓存放哪这一个决策,横贯了 KV 池化、P/D 配比、路由亲和与调度公平四个子系统。任何破坏状态局部性的局部提速,都可能被重算、传输或排队抵消。这条判断有没有机制层面的验证?有,用 vLLM 自己的实验来看。

把长 prefill 切成 512 token 一段,为什么吞吐反升 93%

vLLM 团队与 Inferact 在 2026-09-08 的博客里,用 DeepSeek V4 Pro 在 B300 上跑了 AgentX 同一套 harness,给出一个机制级别的答案。他们先把 agentic serving 的问题拆成三个平面:控制平面管会话路由、缓存亲和与 P/D 配比,执行平面管并行度与 kernel,数据平面维护混合 KV manager、KV connector 与分布式共享 KV 缓存池。这一章只进控制平面,聚焦 prefill 队列的调度;执行平面与数据平面只在牵涉到时顺带点名。vLLMxAgentX

问题出在控制平面最不起眼的一环:prefill 队列。一个长度 142K 的长 prefill 一旦进入 batch,就会在接下来的很多步里独占整个 token budget,同 rank 上排队的短 turn(通常只有几百到几千新增 token)只能干等。这就是 head-of-line blocking,队首阻塞。它还不止影响长请求自己:长 prefill 占着预算时,整批的 decode 都在陪跑。

vLLM 的解法是 chunked prefill(把长 prefill 切成小段):--long-prefill-token-threshold=512,每步最多允许 512 token 的 prefill 进入 batch。效果是短 turn 每一步都能插队、尽早开始解码;代价则明明白白写在同一段话里:长请求自己的 TTFT 上升。vLLMxAgentX

看这张会话视图的机制图:

图注:同一 rank 上 6 条会话时间线的双面板对照(示意,非按比例):左面板不设上限,一条长 prefill 连续多步独占 batch budget,短缓存的追加轮次排队干等,图中标注这些会话完成 6 轮;右面板设 512 token 上限,长 prefill 被切成小段,短轮次每一步都能插入并与解码交错,完成 15 轮。画面标注可复述,但不作量化对比来源。vLLMxAgentX

这里要澄清评价口径:收益用 TPGS 衡量,TPGS 是每秒每 GPU 处理的总 token 数,把输入、输出与缓存命中的 token 全部计入。+93% 是 sweep 区间内的峰值(up to),不是平均;~2.3× 的 P90 交互速度(每用户每秒收到 token 数的 P90 分位)同理。更重要的是,收益的来源是重排共享 batch budget,让短 turn 每步都能加入,而不是某个 kernel 突然变快。这正是第二章那个 142K/444 比值在调度层面的必然结果:计算主体是维护 KV 状态,那么把短 turn 的"状态续写"尽早调度,比一口气算完长 prefill 更划算。

第二层调度问题出在并行组上。MoE 的 all-to-all 通信逼着数据并行组内各 rank 步调一致(lockstep)前进,任何一个 rank 做 prefill 都会拖慢整组,而 prefill 的到达步调又参差不齐。--prefill-schedule-interval=4 把 prefill 工作拉齐到公共节拍上,只允许每第 N 步接纳 prefill,其余步纯 decode:

图注:DEP8 组(8 个 rank)的 prefill 调度节拍对照(示意,非按比例):左面板 prefill 分散到达、反复 stall 同步组,出现 6 个被拉长的步;右面板开启 interval=4 后 prefill 汇聚到节拍步上,被拉长的步降到 3 个,中间都是纯 decode 步。vLLMxAgentX

模拟器侧的对照指向同一结论:HeteroPanacea 对 8 个模型系统扫描后发现,P/D 解耦的收益从 I/O 比约 10 才开始跨过平价线,prefill-heavy 的 agentic 工作负载正落在收益区间内。HeteroPanacea 而 vLLM 也已经通过 MooncakeStoreConnector 把分布式 KV 池接到数据平面,说明这一层的工程形态已经收敛:池化是标配,调度精细化是下一步。

第三章说的"局部提速会被重排与排队抵消",在这里得到机制验证;而"长请求 TTFT 上升"这个客观代价,正好引出最后一个问题:拿什么衡量 Agent serving 才不算自欺欺人。

拿什么衡量 Agent serving:吞吐之外还要报告什么

如果调度器开始为了让短 turn 插队而牺牲长请求的 TTFT,那么"峰值 token 吞吐"这个坐标就彻底失效了。AgenticBench 方法学把 agentic 负载与固定序列负载的差异拆成六个维度:请求形状、到达模式、前缀复用、工具时间、并行模型与缓存寿命,每个维度都对应 serving 栈上的一个旋钮。然后它给出替代坐标,要求八项指标同时报告:

指标回答的问题
每芯片输出吞吐单位算力产出多少新 token
P90 交互速度客户端感知的响应速度
TTFT / ITL(token 间延迟)首 token 等待与之后每个 token 的节奏
输入吞吐前缀命中的流量规模
缓存来源分布KV 来自 HBM、主机内存还是重算
队列与在途深度排队压力与并发水位
每百万 token 成本经济性

并按九条对比规则执行,其中最重要的是"在相同的 P90 交互速度或延迟目标下比较"。AgenticBench

这份规范背后的判断,AgentX 方法学原文写得比谁都直白:a single latency statistic does not describe the full run,一个延迟数字描述不了整个过程。AgentX 为什么?因为闭环负载下,吞吐相近的两个系统行为可以完全不同:一个稳定命中缓存,一个正在反复驱逐重算。均值和小数点掩盖的,正是 KV 状态的存活情况。

"在同一 P90 下比较"不是口号,它有直接可见的曲线。AgentX 发布的 Pareto 前沿图把延迟与成本摊成一条单调曲线:

图注:MiniMax M3 428B 在 B200 单 SKU(vLLM)上的延迟成本前沿:横轴 P90 交互速度、纵轴每芯片 token 吞吐,7 个点对应 7 档 client 并发数,曲线单调下降。高并发抬升吞吐,同时压低每个客户端的交互速度,比较系统时必须固定同一 P90 锚点。图中坐标仅作方法学示例,不援引为权威结论,也不据此推断其他 GPU SKU。AgentX

这条曲线的形状本身就是论证:在 Agent 负载下不存在"又高吞吐又低延迟"的自由位置,只有一串权衡点。谁的调度策略能把整条曲线往右上推,谁才算做了有效优化。

评价单位也得换。会话不是一个扁平的 prompt 列表,而是带 spawn/join 边的依赖图,所以完成单位是"会话"而不是"请求":

图注:AgentX 把会话表示为依赖图的又一视角:主 agent 轮次、并行子 agent、join 汇合点与无 join 的辅助调用各有独立节点类型,回放时延迟被记录为边标签。会话是图,不是请求队列。AgenticBench

再看第二章那三幅 log 分位分布:aggregate 中位把 p95 的约 68 万 token 请求和 p95 的 3 分钟跨轮次等待全部抹平了,而这恰是队列深度与缓存来源指标要暴露的东西。这背后还有更广的证据:Copilot 的生产 traces 同样拒绝单延迟数字,不同 archetype 的 token 用量横跨 50 倍;第三章那条路由问题,也只有在新指标下才看得见。AgenticTraceServingOpt

回到开头那句话:Agent 推理的核心稀缺资源,已经从单次调用的算力,变成跨轮次的 KV 状态连续性。这决定了衡量坐标必须跟着换。峰值 token 吞吐衡量不了 Agent serving,会话完成时间、p90 交互性、TTFT、缓存来源与队列深度才是该上报的东西;评价系统,就去看它在固定交互 SLO 下的延迟成本前沿。下次看到一张只报吞吐的 Agent serving 对比图,先问它三件事:P90 交互速度是多少,缓存命中来自哪里,队列有多深。答不上来的 benchmark,不配用来调你的架构。

参考文献

本专题引用的核心资料如下,建议深入学习时阅读原文:

  1. AgentX Methodology and Datasets(SemiAnalysis InferenceX) — https://inferencex.semianalysis.com/agentx
  2. Agentic Coding in the Wild: Characterizing GitHub Copilot Traces at Production Scale — arXiv:2608.00101
  3. vLLM x AgentX: Optimizing for Real-World Agentic Serving(vLLM Team + Inferact) — https://vllm.ai/blog/2026-09-08-vllm-agentx
  4. Latency-Aware Orchestration for Multi-Agent LLM Workflows on Heterogeneous GPUs — arXiv:2609.03335
  5. MemServe: Context Caching for Disaggregated LLM Serving with Elastic Memory Pool — arXiv:2406.17565
  6. Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving — arXiv:2407.00079
  7. SGLang: Efficient Execution of Structured Language Model Programs — arXiv:2312.07104
  8. Agentic Benchmark for LLM Inference: Metrics and Methodology(SemiAnalysis InferenceX) — https://inferencex.semianalysis.com/blog/agentic-benchmark-agent-benchmark-guide
  9. DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving — arXiv:2401.09670
  10. When Does Disaggregation Pay? Simulating Prefill–Decode–Attention–FFN Specialization for Agentic LLM Inference — arXiv:2608.03741
  11. Position: LLM Serving Needs Mathematical Optimization and Algorithmic Foundations, Not Just Heuristics — arXiv:2605.01280
Agent 请求 142K token 进、444 出,瓶颈在缓存驻留