本文面向 Agent 领域的架构师和开发者,自顶向下地介绍推理框架的全貌。你不需要成为 CUDA 工程师,但你需要理解推理层如何影响你的每一个架构决策。
📌 本文是三篇系列的第三篇。第一篇《一句话是怎么变成 AI 回复的:LLM 的工作原理》讲模型如何处理输入并生成输出,第二篇《模型的能力是怎么来的:从预训练到 RLHF》讲模型能力的来源。本篇聚焦推理层:当模型部署为服务后,它的运行方式如何影响你的 Agent 架构决策。
目录
- 引言:为什么 Agent 开发者必须理解推理框架
- 第一章 全局视角:从训练到服务
- 第二章 推理框架的四层架构
- 第三章 主流推理框架全景
- 第四章 一个请求的完整生命旅程
- 第五章 Agent 架构师必须关注的九大主题
- 第六章 决策速查
- 结语
- 参考资料
引言:为什么 Agent 开发者必须理解推理框架
当你用 Python 写下 response = client.chat.completions.create(...) 时,背后发生了什么?
对于一个简单的聊天机器人,你可以不关心这个问题。但 Agent 不一样。Agent 是多轮、长上下文、高频调用、有分支、有并行的复杂系统。它的每一个行为(多长的上下文、多少轮调用、是否并行、输出格式要求)在推理层都有一个明确的成本和延迟代价。
不理解推理层,你就像一个不懂发动机的赛车工程师:也许能让车跑起来,但永远调不到最优。
本文将自顶向下地回答三个问题:推理框架是什么、它如何工作、以及你作为 Agent 架构师需要关注什么。如果时间有限,建议直接跳到第五章和第六章 决策速查。
下图是一个典型 Agent 系统的全栈架构。本文聚焦的是最底层:LLM 推理服务,以及它与上层 Agent 编排层之间的交互界面。编排框架(LangGraph、CrewAI 等)、工具集成(MCP、Composio 等)、知识检索(LlamaIndex、向量数据库等)的详细讨论不在本文范围内。
| |
第一章 全局视角:从训练到服务
1.1 模型训练与推理的本质区别
模型训练好比“培养一位专家”,推理则是“让这位专家上岗接诊”。训练阶段关注的是“学得好不好”,推理阶段关注的是“服务得快不快、稳不稳、省不省钱”。
训练完成后,你得到的只是一组权重文件。要让它真正服务用户,中间横亘着巨大的工程鸿沟:延迟、吞吐、显存效率、弹性与稳定性。推理框架就是为了跨越这道鸿沟而构建的一整套软件栈。
1.2 推理框架解决的四个核心问题
| 核心问题 | 说明 |
|---|---|
| 延迟(Latency) | 用户发一个请求,多快能得到回复?首 token 延迟(TTFT)和每秒生成 token 数直接决定体验。对于 Agent 场景,多轮串行调用使延迟乘数级放大 |
| 吞吐(Throughput) | 同一时刻能服务多少用户?同样的 GPU,吞吐翻倍就意味着成本减半 |
| 显存效率(Memory Efficiency) | 一张 GPU 能不能装下模型?能不能同时处理更多请求? |
| 弹性与稳定性 | 面对突发流量怎么办?某张卡挂了怎么办?Agent 是长时间运行的有状态任务,对稳定性的要求比单轮对话高得多 |
这四个问题构成了一个 “不可能三角”:延迟、吞吐、成本不可能同时最优。推理框架的本质工作就是通过精巧的系统工程,把这个帕累托前沿尽可能往外推。所谓帕累托前沿,就是“鱼和熊掌不可兼得”时,所有最优妥协方案组成的那条边界线。
1.3 推理的两个阶段:理解 Agent 成本的关键
理解推理优化的一切,都要从这个区分开始:
| 阶段 | 特征 | 瓶颈 | 用户感知指标 |
|---|---|---|---|
| Prefill(首次填充) | 接收所有输入 token,一次性并行计算,生成初始 KV Cache | 计算密集型(compute-bound),GPU 算力是瓶颈 | TTFT(首 token 延迟) |
| Decode(逐 token 生成) | 每步生成一个 token,每步都需要读取整个模型权重和 KV Cache | 访存密集型(memory-bandwidth-bound),显存带宽是瓶颈 | TPOT(单 token 生成时间) |
Agent 的上下文在每一轮调用中都在增长,prefill 阶段的代价随之加速膨胀。因此,上下文管理是 Agent 架构中需要优先考虑的设计决策。
第二章 推理框架的四层架构
一个现代推理框架可以自顶向下分为四层。理解这四层的职责和原理,就能理解你在应用层的决策如何映射到推理层的代价。
2.1 第一层:服务化与 API 层
这是你作为 Agent 开发者直接打交道的那一层。
HTTP/gRPC 服务接口:提供标准化的 API(通常兼容 OpenAI API 格式),接收请求、返回结果。无论你用 vLLM、TGI 还是 TensorRT-LLM 自建推理服务,最终暴露给你的都是一个 HTTP 端点。
流式输出(Streaming):边生成边返回 token,通过 SSE(Server-Sent Events)推送给前端。对于 Agent 场景,流式输出不仅改善用户体感延迟,还有工程上的必要性:编排层可能需要实时解析流中的工具调用指令,在检测到特定标记时中断生成、执行工具、再续回上下文。
请求队列与限流:防止突发流量压垮系统。Agent 的并行扇出模式会在某个时刻突然产生 N 倍请求,如果推理层没有良好的排队机制,所有请求的延迟都会被拖高。
多模型管理与路由:同一个服务集群可能部署多个模型,需要智能路由。Agent 架构中的“大小模型分工”策略就依赖这层能力。
监控与可观测性:延迟分布、吞吐量、GPU 利用率、显存水位、排队深度等指标的实时监控。Agent 编排层需要感知这些指标,以便做智能降级。
2.2 第二层:请求调度与批处理层
单独处理一个请求效率极低,GPU 大部分时间在等数据搬运,算力浪费严重。这一层的核心工作就是把多个请求“打包”一起算,最大化 GPU 利用率。
连续批处理(Continuous Batching)
传统做法是凑够一批再一起处理(静态批处理),问题是短请求要等长请求完成。连续批处理允许请求随到随加入、完成即离开。就像餐厅从“等人齐了才上菜”变成“来一位服务一位,吃完就走”。这是现代推理框架的标配。
PagedAttention(分页注意力)
由 vLLM 团队提出的核心创新。传统做法为每个请求预留一大块连续显存存放 KV Cache,造成巨大浪费。PagedAttention 借鉴了操作系统虚拟内存的分页思想,将 KV Cache 拆成固定大小的“页”,按需分配,不再要求连续地址空间。这使得显存利用率提升数倍,同一张 GPU 能同时服务更多请求。
为什么 Agent 开发者需要关心? KV Cache 是推理中最大的显存消耗项。以 70B 模型为例,每个 token 的 KV Cache 约占 2.5MB 显存。按此计算,一个 128K 上下文的请求仅 KV Cache 就需要约 320GB,远超单张 80GB A100 的容量。
由于 Agent 的上下文在每轮中累积增长,KV Cache 的显存压力会持续加大。PagedAttention 的显存利用效率,在很大程度上决定了同一集群能支撑的 Agent 并发数。
前缀缓存(Prefix Caching)
多个请求如果共享相同的前缀(比如 system prompt + 工具定义),它们的 KV Cache 前半段是一样的,可以缓存并复用。Agent 场景下,所有请求通常共享一段 2000-5000 token 的固定前缀,开启前缀缓存能省下大量重复计算。
推测解码(Speculative Decoding)
用一个小模型快速“猜”出若干 token,再用大模型一次性验证。猜对了就省了多轮自回归时间,猜错了只需回退。数学上可以保证输出分布与大模型完全一致。
在 Agent 场景下,多轮调用使延迟累积效应更明显,推测解码能在不损失质量的前提下将生成速度提升 2-3 倍,对缩短端到端响应时间有直接帮助。
2.3 第三层:模型执行引擎
这是最底层的计算核心,负责“模型权重 + 输入 → 输出”的实际运算。
| 技术 | 原理 | 类比 |
|---|---|---|
| 算子融合(Kernel Fusion) | 将多次 GPU 操作合并为一次,如 LayerNorm + Linear + Activation 融合成一个 CUDA kernel | 把“切菜→洗菜→下锅”改成一个连贯动作 |
| 量化推理(Quantization) | 将 FP16/FP32 权重压缩到 INT8/INT4 等低精度格式,计算量和显存占用大幅下降 | INT4 量化可让 70B 模型从 4 张 A100 降到 1-2 张,吞吐提升 2-3 倍 |
| 高效注意力(FlashAttention) | 通过分块计算(tiling)避免生成完整注意力矩阵,显存复杂度从 O(n²) 降到近似 O(n) | 近年推理性能提升中影响最大的单点技术之一 |
2.4 第四层:模型并行与分布式层
当模型太大、一张 GPU 放不下时,需要“拆”模型跨多张卡运行。
| 并行策略 | 原理 | 适用场景 |
|---|---|---|
| 张量并行(TP) | 把单个矩阵乘法切分到多张 GPU | 同一台机器内多卡(NVLink 高速通信) |
| 流水线并行(PP) | 把模型不同层放到不同 GPU,数据依次流过 | 跨机器场景 |
| 数据并行(DP) | 多个完整模型副本各自处理不同请求 | 最简单粗暴但最易扩展 |
| Expert 并行(EP) | 将不同 expert 分散到不同 GPU | 专为 MoE 架构设计 |
实际部署中往往是多种并行策略的混合,比如一台 8 卡机器内做 TP=8,多台机器间做 DP。
对于 Agent 架构师而言,第一层和第二层是你的主战场:前者决定你的系统如何与推理服务交互,后者的原理决定了你的优化策略是否有效。第三、四层了解原理即可,无需深入。
第三章 主流推理框架全景
了解了架构分层之后,来看看市面上的主要选择。
| 框架 | 特点 | 适用场景 |
|---|---|---|
| vLLM | 开源最活跃,首创 PagedAttention,易用性强,HuggingFace 生态兼容好 | 大多数团队的首选自建方案 |
| TensorRT-LLM | NVIDIA 官方出品,性能极致优化,深度绑定 NVIDIA 硬件 | 对性能有极致追求的团队 |
| SGLang | 聚焦结构化生成和编程式控制,RadixAttention 提供高效前缀缓存 | Agent 场景(大量 JSON 工具调用) |
| HuggingFace TGI | HuggingFace 官方推理服务,与 HF 模型库无缝衔接 | 快速上线 |
| DeepSpeed-FastGen | 微软出品,SplitFuse 混合调度 prefill 和 decode | 与 DeepSpeed 训练生态联动 |
| llama.cpp / GGML | 面向边缘和消费级硬件的 CPU/GPU 推理 | 端侧 Agent 或本地开发测试 |
对于使用厂商 API(OpenAI、Anthropic、Google 等)的团队,这些框架就是厂商在服务端使用的基础设施。理解它们的原理,能帮助你更好地理解 API 的延迟特征、定价逻辑和能力边界。
第四章 一个请求的完整生命旅程
把所有层次串联起来,看一个具体请求从进入到返回的完整过程。
场景假设:你的 Agent 向一个 70B 模型发送了一条包含对话历史和工具定义的请求。
| |
第五章 Agent 架构师必须关注的九大主题
数据声明:本章中的延迟、显存、成本等数值为基于公开论文、厂商定价页和社区 benchmark 的量级估算,旨在帮助架构决策时建立直觉,而非精确实测值。具体数值因硬件配置、模型实现和负载模式不同而有显著差异。有明确来源的数据已在文末参考资料中标注。
5.1 延迟构成与预算分配
Agent 的一次“行动”往往涉及多次 LLM 调用(思考 → 工具选择 → 参数生成 → 结果解析 → 再思考)。由于这些调用是串行的,总延迟等于各轮延迟之和。
如果用户能容忍 10 秒端到端延迟,一个 ReAct 风格的 Agent 可能需要 3-5 轮 LLM 调用,每轮只有 2-3 秒的预算。这直接决定了你能用多大的模型、多长的上下文。
TTFT 和 TPOT 要分别思考:
- TTFT 和输入长度强相关,Agent 场景下上下文越来越长,TTFT 会持续恶化
- TPOT 相对稳定,但输出越长、decode 总时间越长
架构建议:考虑“快慢模型分层”:用小模型做路由和简单判断,大模型只处理复杂推理。
5.2 上下文长度的真实成本
上下文不是免费的。 以 70B 模型为参考:
| 上下文长度 | TTFT | 单请求 KV Cache 显存 | 同机最大并发 | 成本(相对值) |
|---|---|---|---|---|
| 1K tokens | ~100ms | ~2.5GB | ~60+ | 1× |
| 4K tokens | ~300ms | ~10GB | ~20 | 3-4× |
| 16K tokens | ~1s | ~40GB | ~5 | 12-15× |
| 32K tokens | ~2.5s | ~80GB | ~2 | 30-40× |
| 128K tokens | ~10-15s | ~320GB | 需多机 | 150-200× |
注:上表 KV Cache 显存按无 GQA 压缩的理论上界估算。实际部署中 GQA(Grouped-Query Attention)/ MQA 等技术可将 KV Cache 降低 4-8 倍,Llama 3 70B(GQA, 8 KV heads)的实际 KV Cache 约为上表的 1/8。
上下文从 4K 到 128K,成本不是 32 倍,而是 50-100 倍,注意力的二次方项开始主导,加上并发被压缩导致 GPU 利用率下降。
实践建议
- 每增加 1K token 上下文都要问自己“这 1K 信息值不值这个代价”
- 工具定义和 few-shot examples 等固定前缀应压缩到最小并确保前缀缓存生效
- 超过 16K 的上下文要非常谨慎,考虑用 RAG 按需检索替代“把一切塞进上下文”
5.3 调用轮次的超线性增长
这是容易被忽视的成本因素。Agent 的多轮调用中,上下文在每一轮累积增长。第 N 轮的输入 = 原始上下文 + 前 N-1 轮的所有输入输出。
| 轮次 | 该轮输入长度 | 累计 token 消耗 | 累计延迟 |
|---|---|---|---|
| 第 1 轮 | 2K | 2.2K | ~6s |
| 第 3 轮 | 4.6K | 7.4K | ~20s |
| 第 5 轮 | ~12K | ~15K | ~40s |
| 第 10 轮 | ~30K | ~38K | ~100s |
| 第 20 轮 | ~70K | ~90K | ~300s |
Token 消耗是超线性增长的,不是 20 轮 = 20 倍成本,接近 n²/2 的关系。“无限制地让 Agent 自由探索”的成本会迅速爆炸。
实践建议
- 必须设置轮次上限
- 考虑每隔 N 轮对历史进行摘要重置上下文
- 工具调用的返回值应精简,只提取关键信息,不要把完整 API 响应塞回上下文
5.4 结构化输出与约束解码
Agent 的核心需求之一是让 LLM 输出结构化数据:JSON 格式的工具调用参数、枚举值的动作选择、符合 schema 的响应。
约束解码(Constrained Decoding) 在 token 采样阶段直接 mask 掉不合法的 token,100% 保证输出格式合法。这和“在 prompt 里要求输出 JSON”有本质区别:prompt 方式是“请求”,约束解码是“强制”。
主流厂商 API 已经提供了等效能力:
| 能力 | 说明 |
|---|---|
| Function Calling / Tool Use | OpenAI、Anthropic、Google、DeepSeek 等均支持。定义工具 schema,模型直接输出结构化工具调用对象,格式可靠性极高 |
| Structured Outputs | OpenAI 的 strict: True JSON schema 模式,服务端约束解码,理论上 100% 保证格式合法。Google Gemini 也有类似的 response_schema |
| Instructor 库 | 将 Pydantic 模型自动转换为 JSON Schema 注入请求,校验失败自动重试。支持多厂商 API,一套定义切换厂商只改一行代码 |
实践建议
- Agent 的工具调用应当使用约束解码或 function calling,而非依赖 prompt 引导
- 工具 schema 应尽量简单,嵌套越深、约束解码开销越大
- 约束解码的单次开销约增加 5-10%,但消除了重试和 P99 长尾,综合成本更低
5.5 并行扇出与并发控制
Agent 经常需要并行调用多个 LLM 实例,如并行搜索多个数据源、并行评估多个方案。
| 扇出因子 | 100 在线用户的瞬时并发 | 风险等级 |
|---|---|---|
| 1(串行) | 100 | 🟢 低 |
| 3(并行工具调用) | 300 | 🟡 中等,可能排队 |
| 5(并行方案评估) | 500 | 🟠 高,P99 飙升 |
| 10(大规模并行) | 1000 | 🔴 极高,限流或 OOM |
并行扇出的危险在于突发性:不是平稳增加负载,而是瞬间 N 倍。
实践建议
- 单个 Agent 任务的最大并行 LLM 调用数需要限制
- 使用信号量或令牌桶在应用层限流,不要等推理层被打爆再报错
- 评估是否真的需要并行:如果 3 个并行调用中有 2 个结果大概率被丢弃,串行加提前终止可能更经济
5.6 模型路由与成本优化
不同模型规模的成本差异可达 50-100 倍。
| 模型规模 | TTFT(4K 输入) | TPOT | 每百万 token 成本 |
|---|---|---|---|
| 7-8B INT4 | ~30ms | ~8ms | ~$0.05 |
| 70B FP16 | ~300ms | ~30ms | ~$0.50-1.00 |
| 405B+ / 旗舰闭源 | ~1-3s | ~50-80ms | ~$3-10 |
Agent 的不同子任务复杂度差异巨大:意图识别用旗舰模型是极大浪费,复杂推理用小模型又不够。
建议建立模型路由矩阵:
| 子任务 | 推荐模型 | 延迟预算 |
|---|---|---|
| 意图识别 / 路由 | 7-8B 量化模型 | < 200ms |
| 工具参数生成 | 7-8B 或 70B-INT4 | < 1s |
| 复杂推理 / 规划 | 70B FP16 或旗舰模型 | < 5s |
| 结果摘要 / 格式化 | 7-8B 量化模型 | < 500ms |
以一个电商客服 Agent 为例,合理的模型路由策略估计可节省 50-70% 的成本并显著降低延迟。
5.7 多模态推理:图像是上下文的“隐形炸弹”
前面的讨论都基于纯文本场景。但 Agent 越来越多地需要“看”:理解屏幕截图、分析文档图片、处理视频帧。
多模态推理引入了一套不同的成本模型。相比纯文本,图像 token 数量庞大且难以压缩,如果不加理解地使用,成本和延迟可能比同等文本场景高出数倍到十数倍。
多模态推理管线与纯文本的区别
纯文本 LLM 的推理管线是 文本 → Tokenizer → Embedding → Transformer → 输出。多模态模型(VLM, Vision Language Model)在前面多了一步:图像先经过 Vision Encoder(通常是 ViT)切分为 patch 并编码为“视觉 token”。这些视觉 token 通过投射层映射到与文本 token 相同的向量空间,然后与文本 token 一起送入 LLM Backbone 做注意力计算。
关键洞察:图像进入 LLM 后,本质上就变成了“一大段文本 token”,但这段 token 的数量极其庞大,且不像文本那样可以轻易摘要压缩。
一张图片到底“值”多少 token?
这是多模态推理最核心的成本问题。根据各厂商的 token 化规则:
| 厂商 | Token 化规则 | 典型图像 Token 数 |
|---|---|---|
| Claude | tokens = (width × height) / 750 | 200×200 → ~54 ・ 1000×1000 → ~1,334 ・ 1092×1092 → ~1,590 |
| GPT-4o | 低精度固定 85 tokens;高精度按 512×512 tile 切分,每 tile 170 tokens + 85 base | 低精度 → 85 ・ 高精度 1024×1024 → ~765 |
| Gemini | 原生多模态内部表示 | 每张图像 ~258 tokens(标准) |
作为对比,一段 1000 字的中文文本大约 500-700 tokens。一张普通分辨率图片的 token 开销 ≈ 2-3 页文本。
Prefill 阶段的“图像炸弹”效应
回顾 1.3 节,Prefill 阶段是计算密集型的,输入越长越慢。图像 token 会大幅膨胀输入长度:
| 场景 | 输入 Token 数 | 估计 TTFT | 相比纯文本增幅 |
|---|---|---|---|
| 纯文本对话 | ~4K | ~300ms | 基准 |
| 附带 1 张图片 | ~6K | ~500ms | +50% |
| 附带 5 张图片 | ~12K | ~900ms | +200% |
| 屏幕截图流(10 帧) | ~20K+ | ~1.5s+ | +400% |
| 视频理解(30 帧) | ~50K+ | ~5s+ | +1000% |
更棘手的是,图像 token 不像对话历史那样可以摘要,你很难“摘要”一张图片的视觉 token。这意味着多模态场景下,上下文压缩的工具箱少了一半。
KV Cache 显存压力倍增
按 2.2 节的数据(70B 模型每 token KV Cache 约 2.5MB),多模态请求的显存消耗:
- 1 张 1MP 图片 → ~1,334 tokens → ~3.3 GB KV Cache
- 5 张图片的对话 → 仅图像部分就占 ~16.7 GB KV Cache
在相同 GPU 显存下,多模态请求的最大并发量会降至纯文本的 1/3 到 1/5。
前缀缓存的部分失效
2.2 节提到的前缀缓存在多模态场景中效果打折:
- ✅ system prompt 和工具定义(文本部分)仍可缓存
- ❌ 用户上传的图片每次不同,无法命中前缀缓存
- ❌ CUA(Computer Use Agent)的屏幕截图虽然连续帧视觉上相似,但 token 级别完全不同,缓存基本失效
CUA / Browser Agent 的“截图循环”陷阱
Computer Use Agent 的工作模式是 截图 → 视觉理解 → 决策 → 执行操作 → 再截图 → ...。每一轮循环都会注入一张新的屏幕截图(约 1,500 tokens)。
叠加 5.3 节讨论的 N² 增长规律,多模态 Agent 的成本增速比纯文本 Agent 更高:
| 操作轮次 | 纯文本 Agent 累计 Token | CUA Agent 累计 Token | 差距倍数 |
|---|---|---|---|
| 第 1 轮 | ~2.2K | ~4K | 1.8× |
| 第 5 轮 | ~12K | ~25K | 2.1× |
| 第 10 轮 | ~30K | ~65K | 2.2× |
| 第 20 轮 | ~70K | ~150K+ | 2.1×+ |
视频理解
视频是多模态的极端场景:10 秒视频即使按 1 fps 采样也会产生约 15,000 tokens,30 fps 则超过 450,000 tokens,远超上下文限制。实际方案必须采用关键帧采样和帧间差分。视频推理的详细讨论超出本文范围。
多模态场景的实践建议
- 分级保留策略:当前帧保留原始图像;近期帧(2-3 轮前)降低分辨率;更早的历史帧替换为文本描述(如“上一步看到了登录页面,已输入用户名”)
- 分辨率匹配任务:OCR / 文字提取 → 高分辨率;页面布局理解 → 中分辨率;图像分类 / 粗判断 → 低分辨率(GPT-4o low detail 仅 85 tokens,是高精度的 1/9)
- 优先 OCR 替代原图:表格截图和图表等可结构化的内容,通过 OCR 转换为文本后 token 消耗降低 3-5 倍
- Vision Encoder 独立部署(自建推理):提前异步处理图像,相同图片的视觉特征跨请求缓存,两个组件独立扩缩容
5.8 当推理层出问题时:Agent 的韧性设计
前面七节讨论的都是“正常情况下如何优化”。但现实中,推理层一定会出问题:API 突然变慢、返回 429 限流、输出被截断、服务直接不可用。对于传统单轮对话,重试一次就好;但 Agent 是多轮有状态的,一次推理失败可能打断整个任务链。
Agent 开发者最常犯的错误:假设 LLM API 和调用一个普通 REST 接口一样可靠。实际上,主流 LLM API 的可用性远低于传统云服务 99.99% 的标准。根据社区观测和厂商状态页历史记录,每月数小时的不可用或降级并不罕见。
故障模式一览
Agent 开发者需要处理的推理层故障,大致分为四类:
| 类别 | 典型表现 | HTTP 状态码 | 特殊说明 |
|---|---|---|---|
| 限流 | 请求被拒绝,要求稍后重试 | 429 | Anthropic 还有 529(系统过载);OpenAI 的 429 可能是配额用尽而非临时限流 |
| 服务不可用 | 推理服务宕机或过载 | 500 / 503 | Google Gemini 的 500 有时是上下文过长导致,容易误判 |
| 超时 | 请求发出但长时间无响应 | 504 / 客户端超时 | 流式场景下,连接已建立但 token 停止推送,更难检测 |
| 输出截断 | 返回 HTTP 200,但输出不完整 | 200 ✅ | 最隐蔽的故障:stop_reason 为 max_tokens(Anthropic)或 finish_reason 为 length(OpenAI),JSON/工具调用可能被截断成非法格式 |
第四类“输出截断”值得特别警惕:它不会触发任何 HTTP 错误,看起来像一次成功的调用,但返回的内容是残缺的。如果 Agent 正在生成工具调用的 JSON 参数,截断会导致 JSON 解析失败,进而打断整个任务流。
策略一:分层重试与智能退避
不是所有错误都应该用同一种方式重试:
| 错误类型 | 重试策略 | 说明 |
|---|---|---|
| 429 限流 | 优先使用响应头中的 retry-after 值;无该头则指数退避 + 随机抖动 | Anthropic 返回 retry-after 头和剩余配额头,应优先利用 |
| 500/503 | 指数退避,sleep = min(cap, base × 2^attempt),cap 建议 30-60s | 不要立即重试,给服务端恢复时间 |
| 超时 | 先确认前一次请求是否实际完成(避免重复消耗 token),再重试 | LLM 调用非幂等,超时重试可能导致重复计费 |
| 输出截断 | 不重试,改为续写:将已生成的部分作为 assistant 消息前缀,继续生成 | Anthropic 和 OpenAI 均支持 assistant prefill 续写 |
| 401/403 | 不重试,立即告警 | 认证/权限问题重试无意义 |
关键原则:设定重试预算:重试请求量不超过总请求量的 10-20%。无限重试在推理层故障时会形成“重试风暴”,加剧过载。
策略二:模型降级链
推理层故障时,Agent 不一定要“等”,可以切换到备用模型继续工作:
实际经验:主流厂商的故障通常是模型级别而非平台级别的,某个模型出错时,同厂商的其他模型往往仍然可用。因此,同厂商内的模型降级是第一选择,跨厂商降级是最后手段(因为需要处理 prompt 格式、工具调用协议等差异)。
模型降级时注意上下文窗口差异。如果主模型的上下文已经积累到 80K tokens,降级目标的窗口只有 32K,需要先做上下文截断,否则降级调用本身会失败。
策略三:会话状态持久化与断点恢复
单轮对话失败了重来即可,但 Agent 在第 8 轮失败时,前 7 轮的工具调用结果、中间推理、用户交互都不能丢弃。
核心做法:每轮成功后持久化一个检查点(checkpoint):
恢复时从最近的检查点重新发起当前轮的 LLM 调用,跳过已执行的工具调用(注入缓存的返回值)。
这要求工具调用设计为幂等的:如果同一个工具被调用两次(因为第一次结果在推理失败时丢失),不应产生副作用。例如,用“查询或创建”替代“创建”,用唯一请求 ID 做去重。
策略四:超时预算动态分配
Agent 的多轮调用有一个总的时间预算(比如用户最多等 60 秒)。随着轮次推进,剩余预算在减少,后续每轮的超时设置应该动态调整:
| 阶段 | 剩余预算 | 策略 |
|---|---|---|
| 前期(轮次 1-3) | 充裕 | 使用主模型,正常超时(如 30s) |
| 中期(轮次 4-6) | 紧张 | 缩短单轮超时(如 15s),超时即降级到快速模型 |
| 后期(轮次 7+) | 即将耗尽 | 强制使用最快模型,预留 5s 给“优雅退出”,生成一条总结性回复告知用户当前进度 |
不要让 Agent 在最后一轮超时后“静默失败”。预留少量预算让 Agent 告诉用户“我完成了 X 和 Y,但 Z 还没做完,原因是……”,体验远好于一个空白的超时错误。
策略五:上下文溢出的主动防御
推理层的上下文窗口是硬限制,超出即报错。Agent 的上下文在每轮增长(5.3 节),如果不主动管理,总会撞到天花板。
发送前检查:在每次 LLM 调用前估算 token 数。Anthropic 提供 count_tokens() API 做精确计算;也可以用简易规则估算(中文约 1.5 token/字,英文约 1.3 token/词)。
三级压缩策略:
| 上下文占比(相对窗口上限) | 动作 |
|---|---|
| < 60% | 正常运行,无需干预 |
| 60-80% | 对 3 轮以前的历史做摘要压缩(用小模型生成摘要替换原始消息) |
| > 80% | 仅保留 system prompt + 最近 2 轮完整消息 + 历史摘要,必要时截断工具返回值 |
Anthropic 的 prompt caching 机制可以缓解限流压力:被缓存命中的 input tokens 不计入每分钟 token 限额。这意味着固定的 system prompt 和工具定义部分应尽量结构化以命中缓存,让宝贵的限额留给真正变化的对话内容。
5.9 业界实践:三大 Agent 产品的韧性设计
上面五条策略不是纸上谈兵。我们调研了三个代表性 Agent 产品:Claude Code(Anthropic 的 CLI 编码 Agent)、Gemini CLI(Google 开源的命令行 Agent)和 Manus(通用任务 Agent,已被 Meta 收购),看看它们如何在生产中解决推理层故障问题。
重试与退避:三种不同的精细度
| 维度 | Claude Code | Gemini CLI | Manus |
|---|---|---|---|
| 最大重试次数 | 2 次(SDK 默认) | 10 次 | 6 次 |
| 退避策略 | SDK 内置指数退避 | 指数退避 + ±30% 抖动,对服务端 retry-after 用 +20% 正向抖动 | 随机指数退避(1-60s) |
| 限流分类 | 不区分 | 细粒度:每日配额(不重试)vs 每分钟限流(退避重试)vs 需用户验证(暂停) | 不区分 |
| 不可重试错误 | 401/403 | 400 | TokenLimitExceeded(立即中止) |
Gemini CLI 的做法最值得借鉴:对 429 错误做细粒度分类:每日配额耗尽标记为 TerminalQuotaError(不重试,直接触发降级),每分钟限流标记为 RetryableQuotaError(退避后重试)。Manus 则对 Token 溢出绝对不重试,直接终止 agent 循环,避免在不可恢复的错误上浪费资源。
模型降级:从静默切换到健康状态机
| 维度 | Claude Code | Gemini CLI | Manus |
|---|---|---|---|
| 降级链 | Opus → Sonnet(自动) | Pro → Flash(可配置) | 无公开机制 |
| 健康追踪 | 不公开 | 状态机:健康 / terminal(永久不可用)/ sticky_retry(本轮可重试一次) | — |
| 降级模式 | 整个会话级别 | 按轮次级别,每轮重置 sticky_retry | — |
| 用户控制 | 手动 /model 切换 | 5 种选择:永久切换 / 本轮切换 / 停止 / 稍后重试 / 升级账号 | — |
Gemini CLI 实现了完整的模型健康状态机(ModelAvailabilityService):连续失败标记为 terminal(本次会话永久跳过),偶发限流标记为 sticky_retry(下一轮可重试一次,成功则恢复健康),在重试代价和可用性之间取得平衡。Claude Code 则走“大小模型分工”路线(规划用 Opus、执行用 Sonnet、探索用 Haiku),更多是按任务类型预分配而非故障降级。
上下文管理:三种截然不同的哲学
这是三者差异最大的地方:
| 产品 | 压缩触发 | 核心策略 |
|---|---|---|
| Claude Code | 95% 上下文容量 | “尽量撑,撑不住再压缩”:清除旧工具输出,摘要压缩历史对话 |
| Gemini CLI | 50% 上下文容量 | “及早压缩,保留近期”:用 LLM 生成摘要后再用另一次 LLM 调用审核摘要质量,保留最近 30% 对话 |
| Manus | 不压缩 | “不在上下文中存内容,用文件系统做外部记忆”:URL 可重新抓取、文件路径可重新读取,上下文只保留引用 |
Manus 的方案最具原创性,配套两个技巧:todo.md 注意力锚定(持续重写 todo 文件,强制关键目标出现在上下文末尾,对抗 lost-in-the-middle);KV Cache 命中率优先(prompt 前缀稳定 + append-only 构造 + 确定性序列化,缓存命中与未命中的成本差可达 10 倍)。
工具失败与循环检测
| 维度 | Claude Code | Gemini CLI | Manus |
|---|---|---|---|
| 失败处理 | 并行工具调用中单个失败不影响其他工具 | 以事件形式传播,不中断对话轮次 | 错误作为字符串返回给 LLM,agent 循环继续 |
| 错误哲学 | 报告 + 继续 | 分级传播 | “错误是数据,不是故障” |
| 循环检测 | 不公开 | 三层递进:哈希检测(≥5 次相同调用)→ 内容滑窗(≥10 个重复片段)→ LLM 双模型验证(置信度 ≥0.9) | 重复响应 ≥2 次则注入提示 |
Manus 的核心原则:“擦除失败就是擦除证据”,工具错误作为普通文本返回模型,不抛异常、不中断循环,让错误恢复成为 agent 自主行为。Gemini CLI 的三层循环检测(哈希→滑窗→LLM 双模型验证)则是防御死循环的最完整方案。
检查点与恢复
| 维度 | Claude Code | Gemini CLI | Manus |
|---|---|---|---|
| 检查点粒度 | 每次用户 prompt 创建文件快照 | 每次文件修改前在影子 Git 仓库创建快照 | 沙箱级别:睡眠/唤醒/回收/自动置换 |
| 恢复选项 | 4 种模式:恢复代码+对话 / 仅对话 / 仅代码 / 从此处摘要 | 从影子 Git 恢复文件 + 重放对话 + 重新提议工具调用 | 不可恢复时直接置换新沙箱 |
Gemini CLI 的影子 Git 仓库(~/.gemini/history/<project>/)不污染用户 Git 仓库,但保留完整版本回溯能力。Manus 更彻底,每个任务运行在独立 VM 中,不可恢复时直接置换新沙箱。
给 Agent 开发者的提炼
从三个产品的实践中可以归纳出几条共识:
- 分类处理,不要一刀切重试:区分“等一等就好”(每分钟限流)和“今天没戏了”(每日配额),对不可恢复错误果断中止
- 错误留给模型看:工具失败不要吞掉,让模型看到完整错误信息并自行调整策略
- 上下文是最贵的资源:无论用压缩(Claude Code / Gemini CLI)还是外部化(Manus),都必须有主动管理机制
- KV Cache 命中率直接影响成本和限流:prompt 前缀保持稳定,不要放动态内容(如时间戳),上下文构造保持 append-only
- 循环检测是安全网:至少做简单的重复检查,防止 agent 陷入死循环烧 token
第六章 决策速查
🔴 高代价决策(谨慎使用)
| 决策 | 推理代价 | 替代方案 |
|---|---|---|
| 上下文 > 16K tokens | TTFT > 1s,并发骤降 | RAG 按需检索,上下文压缩 |
| 单次输出 > 1000 tokens | Decode > 30s,长期霸占 GPU | 限制 max_tokens,拆分多步 |
| Agent 轮次 > 10 轮 | 累计 token ~n²/2 增长 | 中间摘要重置上下文 |
| 并行扇出 > 5 | 瞬时并发 5×,排队风险 | 应用层信号量限流 |
| 全部用旗舰大模型 | 成本 50-100× | 模型路由分工 |
| 每轮保留全部历史截图 | KV Cache 爆炸,并发降至 1/3~1/5 | 仅保留最近 2-3 帧,历史转文本描述 |
| 所有图片用高分辨率 | Prefill 延迟翻倍,token 成本 10×+ | 按任务复杂度选择分辨率档位 |
| 视频全帧送入 | 10 秒视频 ~450K tokens,超出上下文限制 | 关键帧采样 + 帧间差分 |
| 无限重试失败请求 | 重试风暴加剧过载,token 重复消耗 | 重试预算 ≤20%,指数退避 + 抖动 |
| 不检查 stop_reason 直接使用输出 | 截断的 JSON/工具调用导致解析崩溃 | 每次检查完成原因,截断时续写或重试 |
| 不区分限流类型一律重试 | 每日配额耗尽仍重试 N 次,浪费时间 | 区分 terminal(中止)vs retryable(退避) |
| 吞掉工具调用错误不返回模型 | 模型无法感知失败,重复同样的错误 | 错误作为文本返回模型,让其自行调整 |
| prompt 前缀含动态内容(如时间戳) | KV Cache 命中率归零,成本 ×10 | 动态内容放在前缀之后,保持前缀稳定 |
🟢 低代价 / 高收益决策(积极采用)
| 决策 | 收益 | 额外开销 |
|---|---|---|
| 开启前缀缓存 | 固定前缀零重复计算 | 几乎为零 |
| 使用约束解码 / Function Calling | 消除重试,P99 大幅降低 | 单次 +5-10% |
| 模型路由(大小模型分工) | 成本降 50-70% | 维护路由逻辑 |
| 精简工具返回值 | 延缓上下文 n² 增长 | 提取逻辑 |
| INT8 量化 | 吞吐 +50-100%,质量损失 <1% | 一次性量化 |
| 流式输出 + 中间状态 | 体感延迟大幅降低 | 前端解析逻辑 |
| 图片预处理(缩放至合理分辨率) | 减少 50-80% 图像 tokens | 几十毫秒 CPU 时间 |
| 截图转文本(OCR 后替代原图) | token 减少 3-5× | OCR 延迟 ~200ms |
| 低分辨率优先,按需升级 | 简单视觉任务成本降 90% | 路由判断逻辑 |
| Vision Encoder 结果缓存 | 相同图片免重复编码 | 缓存存储开销 |
| 每轮持久化检查点 | 故障后断点恢复,不丢失已完成工作 | 序列化 + 存储开销 |
| 模型降级链(同厂商优先) | 主模型故障时 Agent 继续运行 | 维护降级配置 |
| 发送前 token 计数 + 三级压缩 | 避免上下文溢出硬错误 | 计数 API 调用开销 |
| 工具错误返回模型而非抛异常 | Agent 自主恢复,错误成为学习信号 | 错误格式化逻辑 |
| 简单重复检测(循环检测) | 防止 Agent 死循环烧 token | 哈希 + 计数器 |
| 上下文 append-only 构造 | KV Cache 命中率提升,成本降 3-10× | 序列化约束 |
📐 关键公式
| |
结语
推理框架对于 Agent 架构师的意义,不在于你要去优化 CUDA kernel,而在于建立一个关键直觉:
应用层的每一个设计决策,在推理层都有一个可量化的成本和延迟映射。
- 上下文长度和调用轮次是成本的 “二次方放大器”
- 模型路由是成本的 “除法器”
- 约束解码和前缀缓存是几乎免费的 “稳定器”
- 重试策略和降级链是系统的 “安全网”
理解了这个映射关系,你就能在“用户体验、系统成本、工程复杂度”之间找到真正的最优解,而不是凭直觉猜测。
这也是为什么推理工程在大模型 Agent 时代,已经从一个后端运维话题,变成了每一位 Agent 架构师和开发者都需要掌握的核心知识。
📌 本系列的其他两篇:如果你还没读过第一篇《一句话是怎么变成 AI 回复的:LLM 的工作原理》,它会帮你建立从 Tokenization 到自回归生成的完整认知,本文中的 KV Cache、Prefill/Decode、上下文成本等概念都建立在那个基础之上。第二篇《模型的能力是怎么来的:从预训练到 RLHF》讲模型能力的来源,包括 Function Calling 是怎么训练出来的。