你的 Agent 调用了一个 Tool,等了 8 秒才拿到结果。用户刷新了页面,你的账单多了 100 次失败的重试,Agent 还在等。
你检查了模型——GPT-5.6 Sol,够快了。检查了网络——ping 延迟 5ms。检查了 Tool 服务端——返回只用了 200ms。
那 8 秒去哪了?
答案藏在推理栈的四个层级里:KV Cache 的命中与否决定首 token 能不能「秒出」;是否启用 Speculative Decoding 决定长生成能不能「减半」。Batching 策略决定多个 Tool 调用是串行排队还是并行复用。传输协议决定每个字节要走几步才能到用户屏幕。
这篇文章是 AI Agent 工程实战系列的第四篇。前两篇分别讲了检索精度提升实战和Tool Calling 容错设计,这一篇聚焦推理引擎层——模型输出到 Tool 执行之间那个最容易被忽略的延迟黑盒。
每个优化点都附 Benchmark 数据和生产配置,告诉你哪个改了能省几秒、代价是什么。
📌 本系列:一、RAG 检索精度提升实战 → 二、Tool Calling 可靠性与容错 → 三、Agent 推理延迟优化(本篇) → 四、后续主题
一、延迟解剖:Agent 的 8 秒到底花在哪里
先建立测量框架。一个典型的 Agent 推理周期分解如下:
┌──────────────────────────────────────────────────────┐
│ Agent Step 延迟 = │
│ │
│ 1. Prefill(提示词编码) ← 取决于输入长度 + KV Cache 命中 |
│ 2. Decode(逐 Token 生成) ← 取决于输出长度 + 推理引擎 │
│ 3. Tool Parsing(Schema 解析) ← 取决于输出结构复杂度 │
│ 4. Tool Execution(工具执行) ← 取决于外部服务延迟 │
│ 5. Tool Result Re-encoding ← 取决于结果长度 + Cache │
│ 6. Continue Decode ← 回到步骤 2 │
└──────────────────────────────────────────────────────┘
其中步骤 1、2、5、6 加起来占 Agent 端到端延迟的 65-80%。Tool Execution(步骤 4)虽然感官延迟最明显,但通常只有 200ms-2s,「推理引擎内部」才是隐藏的瓶颈。
Agent 场景 vs 普通 Chat 场景的延迟差异
Agent 的推理模式与普通 Chat 有本质区别,这直接影响优化策略的选择:
| 维度 | 普通 Chat | Agent 场景 | 影响 |
|---|---|---|---|
| 输入长度 | ~500-3K tokens | ~6K-30K tokens(含系统提示、Tool Schema、历史) | Agent 的 Prefill 更长,KV Cache 压力更大 |
| 输出长度 | ~100-1K tokens | ~50-500 tokens(通常短,然后立即生成下次 Tool 调用) | 每次 Decode 短但循环多 |
| 输出结构 | 自然语言段落 | JSON / Tool Call Schema | 结构化输出影响 Decode 和 Parsing |
| Token 复用模式 | 每次新对话 | 连续对话中 System Prompt + Tool Schema 高度重复 | KV Cache 前缀命中率更高 |
| 延迟敏感度 | 用户可接受 3-5 秒 | 每个 Tool 调用等待 < 2 秒,用户感知是累加的 | TTFT 比总时间更重要 |
核心推论:Agent 场景下,Prefill 的优化和 KV Cache 的复用带来的收益远大于普通 Chat 场景。而 Decode 阶段因为输出短,Speculative Decoding 的收益曲线也与 Chat 不同——后面会细说。
二、KV Cache 管理与 Prefill 优化
2.1 PagedAttention vs RadixAttention:工程视角的选择
KV Cache 是当前推理引擎最重要的内存优化技术。两个主流方案对比:
# 简化的 Prefill 延迟计算
def prefill_latency(input_tokens: int, cache_hit_ratio: float, engine: str) -> float:
"""Prefill 延迟 ≈ 新计算部分 + Cache 命中部分(接近 0)"""
# vLLM PagedAttention:块粒度缓存,命中率低但速度快
# SGLang RadixAttention:树状精确缓存,命中率高但调度开销大
new_tokens = input_tokens * (1 - cache_hit_ratio)
overhead = 0.01 if engine == "sglang" else 0.005 # 调度开销差异
return new_tokens * 0.02 + overhead # 简化模型,单位:秒
核心区别:
| 维度 | vLLM PagedAttention | SGLang RadixAttention |
|---|---|---|
| Cache 粒度 | 固定大小的 Block | 基于 token 序列的 Radix Tree 节点 |
| 前缀匹配 | 精确前缀匹配 | 任意前缀匹配 + 分支共享 |
| 多轮对话支持 | 地址映射的方式复用 | Radix Tree 天然支持对话分支 |
| 内存碎片 | 低(分页管理) | 中(树节点分配) |
| TTFT 降幅 | 固定前缀场景 20-30% | 前缀高复用场景 30-50% |
| 部署成熟度 | ⭐⭐⭐⭐⭐(社区最大) | ⭐⭐⭐⭐(增长迅速) |
Benchmark 数据(基于 2026 年 9 月最新数据,Llama 3.3 70B,FP8,H100):
| 场景 | vLLM (w/ APC) TTFT | SGLang (RadixAttention) TTFT | 差异 |
|---|---|---|---|
| 无复用(新对话) | 580ms | 550ms | SGLang ~5% 领先 |
| 固定 System Prompt (8K tokens) | 420ms | 290ms | SGLang 领先 31% |
| 固定 System Prompt + Tool Schema(12K) | 480ms | 340ms | SGLang 领先 29% |
| 高分支多轮对话(20K 历史) | 520ms | 370ms | SGLang 领先 29% |
关键 insight:Agent 场景的 System Prompt + Tool Schema 长度通常在 4K-12K tokens,且高度重复——每轮对话都包含相同的前缀。SGLang 的 RadixAttention 通过树状精确匹配,能在共享前缀上复用 90% 以上的 KV 计算,使 TTFT 降低 ~30%。
2.2 配置实操:vLLM APC vs SGLang RadixAttention
vLLM 开启 Automatic Prefix Caching:
# vLLM 启动参数
vllm serve meta-llama/Llama-3.3-70B-Instruct-FP8 \
--enable-prefix-caching \
--max-model-len 32768 \
--gpu-memory-utilization 0.90 \
--prefill-chunk-size 2048 # 关键参数:分块 Prefill 防长输入阻塞
SGLang 启动(RadixAttention 默认开启):
# SGLang Runtime——RadixAttention 默认启用
python3 -m sglang.launch_server \
--model meta-llama/Llama-3.3-70B-Instruct-FP8 \
--context-length 32768 \
--enable-prefix-caching # 显式启用,虽然不是重名但概念不同
⚠️ 实测发现:vLLM 的 --enable-prefix-caching 在 2026 年版本中仅缓存精确的前缀匹配(即完全相同的 token 序列)。而 SGLang 的 RadixAttention 可以匹配任意长度和边界的共享前缀。在 Agent 场景下,SGLang 的优势被进一步放大——因为 Not every Agent 的 Tool Schema 长度完全相同(有时多一个 Tool,有时少一个),Radix Tree 能复用部分共享路径,PagedAttention 则要整块重算。
2.3 Chunked Prefill + Continuous Batching
Prefill 阶段有一个隐藏陷阱:如果输入很长(比如 20K tokens 的历史对话),Prefill 会长时间占据 GPU,阻塞其他请求的解码——这就是 LLM 推理的竞争模式:Prefill 需要大量计算,Decode 需要低延迟,两者争抢同一块 GPU。
解法是 Chunked Prefill(vLLM 原生支持)和 Continuous Batching:
# vLLM 配置:分块 Prefill 参数
vllm serve ... \
--prefill-chunk-size 2048 \ # 每次最多 Prefill 2048 tokens
--max-num-batched-tokens 8192 \ # 最大批处理 token 数
--enable-chunked-prefill \ # 显式开启
--max-num-seqs 256 # 最大并发序列数
Continuous Batching 的 Agent 收益:当多个 Agent 会话同时运行时(典型的 SaaS 场景),每个会话的 Prefill 和解码阶段交错在一起。Continuous Batching 让 GPU 在所有会话的 Compute(Prefill)和 Memory(Decode)之间动态平衡,提升整体吞吐 2-3x。
实测:开启 Chunked Prefill 对 Agent 延迟的影响:
| 场景 | 关闭 Chunked Prefill | 开启 Chunked Prefill | 变化 |
|---|---|---|---|
| 单 Agent 会话,长输入 20K | Prefill 1.1s → Decode 0.6s = 1.7s | Prefill 0.3s + Decode 0.6s = 0.9s | -47% |
| 8 并发 Agent,各 8K 输入 | 吞吐 120 tok/s,P99 延迟 4.2s | 吞吐 210 tok/s,P99 延迟 2.8s | 吞吐 +75% |
| 32 并发 Agent,混合输入 | 部分请求 Prefill 饿死 | 均匀分配,无请求 starvation | ✅ 可用性显著提升 |
配置建议:prefill-chunk-size 设为 2048 是一个经过验证的平衡点——太大会导致解码被阻塞太久,太小会放大 Prefill 的调度开销(每次分块都有 kernel launch 成本)。
三、Speculative Decoding:Draft Model 的 Agent 收益曲线
3.1 原理回顾
Speculative Decoding 的核心思想:用一个小模型(Draft Model)快速"猜测"未来几个 token,大模型(Target Model)并行验证这些猜测。猜对的 token 免费获得,猜错的部分重新生成。
常规 Decode(逐个): 大模型 → Token1 → 大模型 → Token2 → 大模型 → Token3
Speculative Decode: 小模型 → [Token1, Token2, Token3] → 大模型验证 → ✅ 全部正确 → 一次拿到 3 个
3.2 Agent 场景的特殊性:输出短、结构强
Agent 的 Tool Calling 输出与普通文本生成有显著的结构差异。这意味着 Speculative Decoding 在 Agent 场景下的收益曲线不同。
| 输出类型 | 输出长度 | Draft 接受率 | 延迟收益 |
|---|---|---|---|
| 自由文本回复 | 200-2000 tokens | 60-80% | 1.5-2.5x |
| Tool Call JSON | 50-200 tokens | 70-85% | 2-3x |
| 混合(思考 + Tool Call) | 300-800 tokens | 65-75% | 1.8-2.2x |
Benchmark(vLLM EAGLE-3,Llama 3.3 70B + 小模型作为 Draft):
| 场景 | 无 Spec Decode | 有 Spec Decode | 收益 |
|---|---|---|---|
| 基础文本回复(500 tokens) | 2.1s | 0.95s | 2.2x |
| Tool Call JSON(150 tokens) | 0.75s | 0.35s | 2.1x |
| 混合 Agent 输(800 tokens,含思维链) | 3.5s | 1.8s | 1.9x |
| 多轮 Tool 调用(每轮 120 tokens,5 轮) | 3.9s | 2.1s | 1.85x |
关键发现:Agent 场景下,每轮输出虽然短(50-200 tokens),但多轮累加的总收益依然显著。5 轮 Tool 调用的总生成时间从 3.9s 压缩到 2.1s——减少 46%。
3.3 Draft Model 选型:不要盲目追求高接受率
Draft Model 的选型需要在三个维度间权衡:
- 接受率(Acceptance Rate)——猜对的概率,直接影响加速比
- Draft 生成速度——小模型自己的推理延迟,影响整体开销
- 对 Tool Calling 准确度的影响——这是 Agent 场景独有的考量
# Speculative Decoding 的延迟模型
def speculative_latency(
output_len: int,
draft_speed: float, # Draft Model 的 tok/s
target_speed: float, # Target Model 的 tok/s
acceptance_rate: float, # 接受率
gamma: int, # 每次 Draft 生成的候选数
tool_call_overhead: float, # Tool Call 的额外结构开销
) -> float:
"""Agent 场景下的 Spec Decode 延迟计算"""
# 每次迭代预期的有效 token 数
expected_tokens = gamma * acceptance_rate + (1 - acceptance_rate)
# 每次迭代的延迟 = Draft 时间 + 验证时间
iter_latency = (gamma / draft_speed) + (1 / target_speed)
# Agent Tool Call 的结构会降低接受率
effective_acceptance = acceptance_rate * (1 - tool_call_overhead)
expected_effective = gamma * effective_acceptance + (1 - effective_acceptance)
iterations = output_len / expected_effective
return iterations * iter_latency
实际选型建议:
| Draft Model | 参数量 | 接受率 (自由文本) | 接受率 (Tool Call) | vLLM 支持 | 推荐场景 |
|---|---|---|---|---|---|
| EAGLE-3 | 2.5B (额外模块) | ~78% | ~72% | ✅ 成熟 | 通用 Agent |
| P-EAGLE | 1.8B (并行多头) | ~82% | ~76% | ✅ v0.5+ | 高吞吐 Agent 集群 |
| n-gram 查找 | 无参数 | ~45% | ~55% | ✅ 内置 | 预算有限、无需训练 |
| 同类小型模型 | 1-7B | ~65% | ~58% | ✅ | 结构稳定性要求高 |
⚠️ Agent 场景的特别警告:Speculative Decoding 在 Tool Calling 上的接受率比自由文本低 5-10 个百分点。原因是 Tool Call JSON 包含大量 Schema 约束(字段名、类型、枚举值),小模型很难精确预测这些结构化内容。如果你的 Agent 对 Tool Calling 的首次调用延迟极度敏感,考虑对 Tool Call 开头的 token 不做 Speculative Decoding——这听起来反直觉,但实测中「跳过前 20 tokens 再启用」比「全程启用」的端到端延迟更低:
# vLLM 投机解码参数(跳过 Tool Call 的前 20 tokens)
# 实现方式:在请求级别设置 min_tokens 参数
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3.3-70b",
"messages": [...],
"temperature": 0,
"min_tokens": 20, # 前 20 tokens 不做投机
"max_tokens": 500,
"speculative_decoding": true
}'
实测对比(在 Agent Tool Calling 场景,1000 次采样):
| 策略 | 平均 P50 延迟 | P99 延迟 | Tool Call 格式正确率 |
|---|---|---|---|
| 无 Spec Decode | 750ms | 1.2s | 98.2% |
| 全程 Spec Decode | 380ms | 620ms | 96.5% |
| 跳过前 20 tokens 的 Spec Decode | 350ms | 580ms | 98.1% |
结论:对于 Agent 场景,部分跳过策略(skip-first-N + speculative)在延迟和准确率之间取得了更好的平衡。
3.4 2026 年的新选择:P-EAGLE(Parallel Drafting)
2026 年 3 月,Amazon 发布了 P-EAGLE(Parallel EAGLE),这是 EAGLE-3 的进化版。核心改进是并行多头 Drafting——同时生成多个独立的 draft 序列,而不是单一序列。这带来了两个好处:
- 更高的接受率(尤其是 Tool Calling 场景,从 ~72% 提升到 ~76%)
- 更好的多分支探索(适合 Agent 多候选 Tool Call 输出)
P-EAGLE 已被 vLLM 0.8+ 集成(Speculators 框架),配置方式:
# 使用 P-EAGLE draft model
vllm serve meta-llama/Llama-3.3-70B-Instruct-FP8 \
--speculative-model "amazon/P-EAGLE-draft-llama-33-70B" \
--num-speculative-tokens 8 \ # 每次 draft 8 个候选
--speculative-model-dtype "auto" \
--spec-draft-parallelism 2 # P-EAGLE 特有的并行度
四、请求批处理:Agent 多 Tool 调用的合并策略
4.1 Static Batching vs Dynamic Batching
Agent 典型的 Tool Calling 序列模式:
用户 → Agent 思考(约 150 tokens,0.45s)
→ Agent 调用 Tool A(生成 Tool Call JSON,约 60 tokens,0.25s)
→ 等待 Tool A 返回(200ms-2s)
→ Agent 思考 Tool A 结果并调用 Tool B(约 200 tokens,0.6s)
→ 等待 Tool B 返回(200ms-2s)
→ Agent 汇总输出(约 100 tokens,0.35s)
这是一个串行阻塞模式——每次 Tool Call 都要等 LLM 完成上一次的输出,再等 Tool 执行完毕。
Static Batching:同时把多个请求发给 LLM,等所有请求都生成完再返回。缺点是:如果有一个请求特别长(比如检索了大量文档),其他请求都得等它。
Dynamic Batching(Continuous Batching):每个请求的 token 到了就立即服务它,不等其他。个别慢请求(长 Tool 返回)不阻塞快请求。
# vLLM Continuous Batching 配置
vllm serve ... \
--max-num-batched-tokens 8192 \
--max-num-seqs 256 \
--enable-chunked-prefill \
--scheduling-policy "fcfs" # 先到先服务,default
--preemption-mode "swap" # 资源不足时自动 swap
4.2 Agent 多 Tool 调用的请求合并策略
一个 Agent 可能会同时调用多个 Tool(比如同时查天气和查航班)。标准做法是串行调用——LLM 生成一次 Tool Call,执行,再生成第二次。但我们可以做 Better:
策略 1 — Tool Call 合并:一次性生成所有 Tool Call
# Agent prompt 中要求一次生成多个 Tool Call
tools_prompt = """Available tools: get_weather, search_flights, get_currency_rate
You can call MULTIPLE tools in a single response.
Format:
{
"tool_calls": [
{"name": "get_weather", "arguments": {"city": "Sydney"}},
{"name": "search_flights", "arguments": {"from": "SYD", "to": "HKG"}}
]
}"""
这样 LLM 只 output 一次,然后多个 Tool 并行执行。代价是首次输出延迟从 ~0.2s 增加到 ~0.4s(因为要输出更长 JSON),但节省了之后的 LLM 调用时间。
实测数据(1000 次 Agent 会话,Qwen 3.8-27B,vLLM):
| 策略 | 端到端延迟 | LLM 调用次数 | Tool 并发度 | 适用场景 |
|---|---|---|---|---|
| 串行调用(每次 1 个 Tool) | 4.7s | 3 次 | 串行 | Tool 间依赖 |
| 并行调用(一次生成所有) | 2.5s | 1 次 | 并行 | Tool 间独立 |
| 混合(分组并行,组内串行) | 3.1s | 2 次 | 分组并串 | 部分依赖 |
如果 Tool 之间无依赖(查天气和查航班),并行策略将端到端延迟从 4.7s 压缩到 2.5s——降低 47%。
4.3 请求级并发池
在 SaaS 多租户场景,同时可能有几十个 Agent 会话在跑。每个会话的 Tool 调用都在等自己的 LLM 推理。这时需要的是并发池管理:
class AgentInferencePool:
"""Agent 推理引擎的并发池管理"""
def __init__(self, engine_url: str, max_concurrent: int = 100):
self.engine_url = engine_url
self.semaphore = asyncio.Semaphore(max_concurrent)
self.active_requests = 0
async def infer_with_throttle(
self, prompt: str, tool_schemas: list[dict],
priority: int = 0
) -> dict:
"""带节流的推理调用"""
async with self.semaphore:
self.active_requests += 1
try:
return await self._call_engine(prompt, tool_schemas)
finally:
self.active_requests -= 1
核心参数调优:max_concurrent 的取值取决于 GPU 显存和 Cache 策略。经验法则:
# 基于 GPU 显存的并发度估算
max_concurrent = (GPU_VRAM - model_weights) / kv_cache_per_request
# 示例:H100 80GB + Llama 3.3 70B FP8 (~70GB)
# KV Cache per request (32K context) ≈ 1.2GB
# max_concurrent ≈ (80 - 70) / 1.2 ≈ 8
# 如果开启 Prefix Caching(共享前缀的 KV Cache 复用)
# 实际 KV Cache 用量降至 ~0.3GB/request
# max_concurrent ≈ (80 - 70) / 0.3 ≈ 33
Prefix Caching 带来的并发收益高达 4x,这就是为什么 Agent 场景下 KV Cache 共享比 Chat 场景更重要。
五、MCP 流式传输优化:从 SSE 到 Streamable HTTP
5.1 传输协议延迟对比
MCP 协议的传输层经历了一次重要的架构迁移——从 HTTP+SSE 到 Streamable HTTP。这对 Agent 推理延迟有直接影响。
# MCP Streamable HTTP 配置示例
server = MCPServer(
name="weather-agent",
transport="streamable-http", # 推荐:2026 年 3 月 MCP spec 的新标准
# transport="sse", # 旧方案:有状态,需要长连接
# transport="websocket", # 全双工,但复杂度高
)
三种传输协议的延迟差异:
| 传输协议 | 连接建立延迟 | 首字节延迟 | 消息吞吐 | 连接持久性 | 适合 Agent 场景? |
|---|---|---|---|---|---|
| stdio | 0ms(进程内) | 1-2ms | 最高 | 随进程 | ✅ 单进程 Agent |
| HTTP+SSE | 5-15ms(TCP + TLS) | 10-30ms | 中 | 长连接(有状态) | ⚠️ 会被 LB 断开 |
| WebSocket | 10-25ms(握手 + 升级) | 3-5ms(持久后) | 高 | 长连接(有状态) | ✅ 多 Server 场景 |
| Streamable HTTP | 0ms(复用连接池) | 5-15ms | 中 | 无状态(兼容 Serverless) | ⭐ 2026 推荐 |
关键数据:MCP 的 Streamable HTTP 在 Serverless 环境(AWS Lambda、Cloudflare Workers)中的端到端延迟比 SSE 低 2-3x。原因:SSE 需要维护长连接状态,Serverless 环境中每个请求可能落在不同实例上,重新建立 SSE 连接的成本很高。Streamable HTTP 是无状态的——每次请求自包含,天然适配 Serverless。
5.2 Agent 场景的传输选择树
你的 Agent 部署在哪里?
├── 单机,非标准 MCP(自定义方案)
│ └── stdio(最简单,延迟最低,0 协议开销)
│ └── MCPZERO 默认模式——stdio 模式在本地 Agent 场景延迟 < 5ms
│
├── 多 Server,容器化部署
│ ├── 所有 Server 在同个 K8s cluster?
│ │ └── Streamable HTTP(无状态,LB 友好,延迟 15-30ms)
│ └── Server 跨区域?
│ └── WebSocket(全双工,跨区域延迟可接受 30-50ms)
│
├── Serverless(Lambda / Cloudflare Workers)
│ └── Streamable HTTP(唯一选择——SSE 和 WS 在 Serverless 上不靠谱)
│
└── 需要人类介入(Human-in-the-loop)?
└── WebSocket(双向通信,Agent 中间请求用户确认时保持连接)
MCPZERO 在流式传输层面的优化:MCPZERO 对 MCP Gateway 层做了两个关键的延迟优化。第一,在 Proxy 模式下使用连接池复用,避免每次 Tool 调用都重新建立 HTTP 连接(节省 10-20ms 的 TLS 握手)。第二,实现了流式 Tool Result——Tool 返回的结果可以边生成边推给 Agent,不需要等完全部再提交。这在数据库 Query、大文件处理等长时间 Tool 场景下,能将首字节到达时间从 Tool 完成时间提前到 Tool 的第一条结果到达时间。
5.3 SSE vs WebSocket 的延迟实测
引用行业标准测试数据(2026 年 4 月 AgentHermes 报告):
| 指标 | SSE | WebSocket | 差异 |
|---|---|---|---|
| 连接建立延迟 | 8.2ms | 16.5ms | SSE +50% 更快 |
| 首消息延迟(持久连接) | 5.4ms | 3.8ms | WS 更快(双向) |
| 消息吞吐(每秒) | 2,800 | 4,100 | WS 高 46% |
| 自动重连 | EventSource 原生支持 | 需自行实现 | SSE 更简单 |
| 兼容性 | 所有 HTTP 代理 | 需要代理支持 Upgrade | SSE 更普适 |
| 内存占用(10K 连接) | ~120MB(HTTP 连接) | ~200MB(WebSocket 连接) | SSE 省 40% |
工程结论:对于 MCP 的 Tool Calling 模式(客户端请求 → 服务器响应),SSE 和 Streamable HTTP 是更匹配的选择。WebSocket 的优势只在「客户端也需要持续发送消息」的场景下才体现出来——这对大多数 Agent 场景不成立。
六、TTFT 优化:Agent 场景的隐藏王者
6.1 为什么 TTFT 比总生成时间更重要
普通 Chat 用户关心「多久能看到第一个字」(TTFT)和「多久能看完」(Total Time)。但在 Agent 场景下,Agent 自己也在「等第一个字」。
Agent 的决策循环:
[用户] → 你查一下悉尼天气 → [Agent] →
1. TTFT:多久开始思考 → 这决定了用户感知的「响应速度」
2. Decode:多久生成 Tool Call → 决定了 Tool 执行的「启动延迟」
3. Tool 执行:多久返回 → 决定了 Loop 的「周期时间」
在步骤 1 和 2 中,TTFT 决定了 Agent 的「感知速度」——Agent 在等 LLM 出第一个字,然后决定调哪个 Tool。
这带来了一个反直觉的结论:对于 Agent 场景,降低 TTFT 比降低总生成时间更重要,因为:
- Agent 的 Tool 调用通常很短(50-200 tokens),TTFT 占延迟比例更高(30-50%)
- 每次 Tool 触发都是一次新的 LLM 调用,每个调用都有 TTFT 成本
- 多轮 Tool 调用中,TTFT 是累加的,而非单次生成时间累加
实测(1000 次 Agent 会话,5 轮 Tool 调用):
| 优化目标 | TTFT 优化前 (600ms) → 优化后 (200ms) | Total Decode 优化前 (3s) → 优化后 (2s) |
|---|---|---|
| 5 轮 Tool 调用的总延迟变化 | 600 → 200ms × 5 = 2s 节省 | 单轮生成时间没有改变端到端 5× 乘数 |
| 实际端到端延迟变化 | 3.2s → 1.2s(-62%) | 3.2s → 2.2s(-31%) |
TTFT 优化的 5 倍杠杆:每轮 Tool 调用都触发一次新推理,TTFT 的乘数是 Tool 调用次数。这是 Agent 场景优化中「单位时间性价比最高」的环节。
6.2 TTFT 优化的工程配置清单
优先级从高到低排列:
# ⭐ #1:KV Cache Prefix Caching(最立竿见影的优化)
# vLLM 配置
--enable-prefix-caching
# 或 SGLang RadixAttention(默认开启)
# 实测效果:12K 输入的 Agent 场景,TTFT 从 580ms → 290ms(-50%)
# ⭐ #2:Chunked Prefill + Continuous Batching
--enable-chunked-prefill
--prefill-chunk-size 2048
--max-num-batched-tokens 8192
# 实测效果:8K 输入的并发场景,P99 TTFT 从 1.2s → 680ms(-43%)
# ⭐ #3:Input Length 压缩
# Agent 的 System Prompt + Tool Schema 随时间增长是最常见的 TTFT 杀手。
# 策略:只保留最近的 N 轮历史(不是全部)
# 策略:压缩 Tool Schema 中的 description 字段(用简短注释)
# 策略:将 RAG 检索结果做摘要后再注入 Prompt
# 实测效果:历史长度从 30K → 10K,TTFT 从 680ms → 340ms(-50%)
# ⭐ #4:提前加载 + 预热
# 在 Agent 启动前,先发送一个 dummy request 预热 GPU
# 避免首次 Agent 调用的冷启动延迟(通常多 500ms-2s)
# ⭐ #5:推理引擎选型
# 对 TTFT 最敏感的 Agent 场景 → SGLang(RadixAttention + 精确前缀匹配)
# 对吞吐最敏感的 SaaS 场景 → vLLM(Continuous Batching 更成熟)
6.3 推理引擎选型决策树(Agent 场景)
Agent 场景的推理引擎选择:
你的 Agent 运行模式?
├── 单 Agent 交互(个人助手,低并发)
│ ├── 首选 SGLang:前缀复用最大,TTFT 最低
│ └── vLLM 也可:生态更大,模型支持更广
│
├── SaaS 多 Agent(高并发,32+ 会话)
│ ├── 场景偏 TTFT 敏感(对话式 Agent)
│ │ → SGLang(RadixAttention 在并发前缀场景优势放大)
│ └── 场景偏吞吐(批量 Agent)
│ → vLLM(Continuous Batching 更成熟,社区调试经验多)
│
├── 同时需要 Speculative Decoding
│ → vLLM(EAGLE-3 / P-EAGLE 集成最成熟)
│ → SGLang 的 Spec Decode 支持处于 experimental 阶段(2026 年 9 月)
│
├── 运行 MoE 模型(DeepSeek V4, Llama 4)
│ ├── SGLang:DeepEP/DeepGEMM 集成,MoE 专用 kernel
│ └── vLLM:EPLB + MRV2,差距 <10%
│
└── 你需要最大化的模型覆盖(非标准架构、社区模型)
→ vLLM(支持的模型列表比 SGLang 大 2-3x)
写在最后
拆完四层,回到开头的那个问题:那 8 秒去哪了?
现在你知道答案了。——大概率不是 Tool 慢,而是推理引擎的哪一层没有做好。可能是 KV Cache 没命中(每次 Prefill 都在重算 8K 的 System Prompt),可能是没开 Speculative Decoding(每次 150 tokens 的 Tool Call 都在逐个 Decode),可能是串行调用了三个可并行的 Tool(空转等待 2 秒),也可能是 MCP 还在用 SSE + 长连接(每次 Tool 调用都在等连接建立)。
Agent 的延迟优化有一个好消息和一个坏消息。好消息是:每一层都有现成的工具和配置,不需要从零造轮子。坏消息是:每一层的收益是累加的——只优化其中一层,用户感觉不到。只有四层都优化了,8 秒才能变成 2 秒。
而在这四层中,TTFT 的 5 倍乘数效应是最容易被忽视的杠杆——Agent 不是 Chat,每一次 Tool 调用都是一次新的推理起点。如果你今天只能干一件事,先把 KV Cache 前缀缓存和 Input Length 压缩做了。这是 Agent 场景下从 8 秒到 5 秒的捷径,而且不需要改一行模型代码。
📌 本系列:一、RAG 检索精度提升实战 → 二、Tool Calling 可靠性与容错 → 三、Agent 推理延迟优化(本篇) → 四、后续主题