跳到主要内容

AI 流水线中的投机执行:通过押注未来降低延迟

· 阅读需 13 分钟
Tian Pan
Software Engineer

大多数 LLM 流水线在无意中表现出了令人尴尬的顺序执行特征。一个智能体调用天气 API,等待 300 ms,调用日历 API,再等 300 ms,调用流量 API,再次等待 —— 最后才综合出一个答案。如果这三个调用是并行运行的,那 900 ms 的总延迟本可以缩减到 300 ms。没有人故意将系统设计成顺序执行;这只是在编写一个接一个的异步调用时自然而然产生的结果。

推测执行(Speculative execution)是一系列技术的统称,这些技术通过在你确定需要某些工作之前就提前执行它们,来降低感知的延迟 —— 包括运行并行的假设、预取可能的后续步骤以及同时生成多个候选输出。这些技术直接借鉴了 CPU 设计,自 20 世纪 90 年代以来,处理器就一直在推测性地执行未来的指令。应用到 AI 流水线时,这种本能 —— 押注可能的结果、取消失败者、接受偶然的浪费 —— 可以产生显著的加速。但如果你不小心选择应用时机,协调开销也可能会抵消所有的收益。

推测的三个层面

LLM 系统中的推测运行在三个不同的层面,每个层面都有不同的权衡。

Token 级推测是业界通常所说的“推测解码”(Speculative decoding)。一个轻快的小型草稿模型提前生成几个候选 Token,然后由一个大型目标模型在一次前向传播中验证所有 Token。由于目标模型可以利用其现有的并行架构同时检查多个 Token,因此有效的草稿可以廉价地被接受。如果 Google 的基准测试具有代表性 —— 在接受率高于 0.6 时,使用三个推测 Token 可获得 2–3.4 倍的加速 —— 这是推理服务中最高杠杆的延迟优化手段之一。它现在已成为 vLLM、SGLang 和 TensorRT-LLM 的生产标准。

工具调用级推测涉及预取或并行化智能体发出的外部调用。当智能体的接下来的几个工具调用彼此独立时,你可以同时发出所有调用。一个从三个数据源获取信息的搜索智能体并不需要数据源 A 的结果才能开始获取数据源 B。幼稚的实现默认是顺序执行的;正确的实现则会检测独立性并进行分发(Fan out)。

响应级推测意味着同时生成多个候选答案 —— 束搜索(Beam search)就是经典的例子。束搜索不是在每一步都贪婪地选择概率最高的 Token,而是并行维护 k 个候选序列。这种方法成本更高,但对于初始 Token 选择会显著限制后续响应质量的任务,它可以产生更高质量的输出。

这三个层面是相互关联的。一个在 Token 级别进行推测解码的生产系统,也可以在智能体级别并行化工具调用。但每个层面都有其特定的失败模式和成本特征,这就是为什么值得将它们分开考虑。

推测解码:是什么让它发挥作用(以及失效)

推测解码的核心洞察是,大语言模型的瓶颈通常是内存带宽,而非原始算力。生成一个 Token 需要从 GPU 内存中加载整个模型的权重。生成五个 Token 则需要加载一次权重并进行五次小规模的计算过程 —— 以大约相同的内存传输成本获得五倍的输出。草稿模型通过一次性提议多个 Token 来利用这一点;验证者在一次批处理中确认或拒绝它们,而无需额外的权重传输。

数学上的保证很明确:当草稿模型的提议与目标模型的预测匹配时,它们会被原样接受。当它们偏离时,你会回退到目标模型的输出。由此产生的分布在证明上与单独运行目标模型完全一致。你在不牺牲质量的前提下获得了加速。

在实践中破坏这一点的是接受率敏感性。接受率 —— 验证者接受草稿 Token 的比例 —— 是关键指标。在接受率高于 0.6 时,你会看到显著的加速。低于这个比例,草稿模型就是在浪费工作。接受率取决于草稿模型与目标模型的对齐程度、量化差距以及工作负载特征。在与目标模型不同的数据分布上训练的草稿模型接受率会很差。对目标模型进行激进的量化也会降低接受率,因为量化模型的 Logits 会偏离草稿模型训练时预测的目标。

另一个关键的失效模式是批大小(Batch size)。推测解码是一种受内存限制的优化 —— 它在 GPU 未充分利用时很有帮助。在高批大小(通常为 32+)下,GPU 已经处于计算受限状态。添加草稿模型的前向传播会变成纯粹的开销。vLLM 文档明确警告说,“推测解码在现实系统中表现出脆弱且高度波动的性能”。正确的方法是在低并发时启用它,并在请求率超过曲线拐点时自动禁用它。

并行工具调用:更容易实现的胜利

对于大多数应用开发者来说,推测性解码(speculative decoding)发生在推理服务基础设施内部,并不是你可以直接控制的。并行工具调用(Parallel tool calling)则是更具即时操作性的技术。

考虑一个需要天气数据、日历空闲情况和交通状况来回答调度问题的智能体。顺序执行:300 ms + 300 ms + 300 ms = 900 ms。并行执行:max(300 ms, 300 ms, 300 ms) = 300 ms。逻辑很简单。实现障碍通常是认知上的——开发者默认采用顺序执行,因为这在代码中读起来更自然。

LLMCompiler 框架(ICML 2024)通过将智能体规划视为编译过程,将这一过程正式化。规划器(planner)发出工具调用的依赖图;执行器(executor)同时调度所有没有前置依赖的调用,然后在调度下一层之前汇聚它们的结果。这实现了规划阶段(需要发生什么)与执行阶段(以什么顺序发生)的分离,这正是正确的抽象方式。

关键的前提条件是独立性。只有当工具调用不共享可变状态,且彼此都不需要对方的输出时,它们才是可并行的。这听起来显而易见,但在实践中很容易被忽略:如果工具 A 的结果改变了你要传递给工具 B 的参数,你就不能并行化它们。依赖图必须准确。对独立性的错误假设会导致错误的行为,而不仅仅是白费功夫。

尾部延迟(tail latency)问题也值得一提:并行执行意味着你需要等待最慢的那个调用。如果三个调用分别耗时 200 ms、300 ms 和 800 ms,并行延迟就是 800 ms——这甚至比按序排列良好的顺序执行的延迟中位数还要差。竞速模式(Racing patterns)在这里会有所帮助:发起多个等效的调用,获取第一个结果,取消其余调用。当你拥有多个返回等效结果的提供商时,这种方法效果很好。

预取下一个工具调用

预取(Pre-fetching)将并行调用的理念延伸到了智能体推理步骤中。在智能体处理当前工具调用的结果时,你是否可以在智能体明确决定调用下一个工具之前,就开始执行它?

PASTE 框架(模式感知的推测性工具执行,Pattern-Aware Speculative Tool Execution)通过学习智能体工具日志中的常见序列来实现这一点。如果调用天气 API 的智能体有 80% 的概率接下来会调用日历 API,那么你可以在看到天气调用完成时立即推测性地启动日历调用。如果智能体确认了这一预测,结果已经在传输中了。如果智能体选择了不同的方向,你就取消这个推测性调用。

这比并行调用更复杂,因为它需要一个针对智能体行为的预测模型,而不仅仅是静态依赖分析。PASTE 中的有界浪费模型(bounded waste model)可以防止失控的推测:如果接受率低于阈值,推测深度会自动降低。这种自我调节机制非常重要,因为只有当你的预测足够准确时,预取才是有利可图的。一个在每一步都预取错误工具调用的糟糕预测模型,会增加 API 成本而不会降低延迟。

当协调开销抵消收益时

推测执行的失败模式与它的优势同样重要。推测执行通常有三种方式会让情况变得更糟:

伪装成并行任务的顺序任务。 并行工具调用示例中的加速假设所有调用都是独立的。需要循序渐进推理的任务——每一步都依赖于前一步——无法进行有意义的并行化。将并行性强加于固有的顺序推理会破坏正确性。在协调不当的多智能体系统中,误差放大效应可能非常显著:关于多智能体失败模式的研究发现,与正确排序的执行相比,无结构的并行网络会将误差放大 17 倍。

失败推测的成本。 你运行的每一个推测分支都会产生实际成本。Token 级的推测会生成草稿 Token,如果接受失败,这些 Token 将被丢弃。预取的工具调用会返回结果,如果智能体采取了不同的路径,这些结果将被忽略。在分支因子为 2、深度为 4 的基于树的推测解码设置中,会生成一个包含 15 个候选 Token 的树;如果只有一条包含 4 个 Token 的路径被接受,那么就有 11 个 Token 被浪费了。在大规模应用中,除非仔细限制,否则这种浪费可能会超过节省的成本。

速率限制耗尽。 并行工具调用和预取会同时成倍增加你的 API 请求速率。一个以每个 300 ms 速度进行 4 次顺序调用的智能体,每秒发出大约 3–4 个请求。并行运行这些调用意味着同时发出 4 个请求。在此基础上再加上预取的推测调用,可能会让你迅速触及速率限制(rate limit),从而产生一个新的瓶颈,抵消掉延迟方面的节省。

决策框架

并非所有的延迟都值得通过推测来优化。这里有一个实用的启发式方法:

当你控制或能影响推理基础设施、并发量低、输出较长(100+ Token)且你使用的模型有良好的草稿模型支持时,采用 Token 级推测(推测性解码)。在 Batch size 超过 32 或请求速率导致推理框架显示吞吐量下降时,自动禁用它。

当你能够画出工具调用的依赖图,且某些调用没有前置依赖时,采用 并行工具调用。这几乎总是值得做的——认知开销低,在任何异步框架中实现都很直接,且收益与独立调用的数量成正比。

只有当你拥有历史数据来训练预测模型、能够监测接受率,并且在预测效果不佳时有自动回退到非推测执行的机制时,才采用 预取。这是一种高投入的优化,在特定场景下(如工具序列可预测的高流量智能体)会有显著收益。

当输出质量比成本更重要,而不是以延迟为首要考量时,采用 响应级并行(束搜索、多候选生成)。束搜索(Beam search)会使计算成本随束宽(beam width)线性增加。质量瓶颈通常在成本节省之前就已经出现了。

协调成本

这一类技术中的每一种都增加了顺序执行代码所没有的协调复杂性。你需要处理失败分支的取消。你需要在不丢失上下文的情况下传播来自并行调用的错误。你需要确保并行变异(mutations)不会产生竞态条件。随着流量模式的变化,你还需要监控采纳率并调整预测参数。

那些从预测执行中获得实际收益的团队,通常是那些首先投入了可观测性建设的团队。他们可以衡量开启预测执行前后的实际采纳率、实际浪费率以及实际尾部延迟分布。如果没有这些仪表化手段,预测执行只是一个披着性能外衣的额外开销。

CPU 的类比在这里也很有启发性:CPU 中的预测执行也引入了新的故障模式(如 Spectre 和 Meltdown)。性能提升是实实在在的,但安全影响也同样真实存在。AI 流水线预测也有其对应的版本:一个不仅进行读取、还预测性地执行写入或变异(mutations)——而不仅仅是读取——的预取系统,可能会产生本应被丢弃的预测分支所带来的真实副作用。工具调用预取对于幂等的读取操作通常是安全的,但对于任何改变状态的操作都是危险的。

未来趋势

趋势正朝着透明化处理预测执行的服务基础设施发展,这样应用开发者就无需操心。vLLM 的预测解码(speculative decoding)、LLMCompiler 的并行调度以及 PASTE 风格的模式感知调度,都在朝着根据工作负载特征自动做出正确选择的方向迈进。

不会自动化的是应用层关于哪些工具调用实际上是相互独立的决策。这需要理解你的业务领域,并正确构建依赖图。这部分仍然需要工程判断力,也是大多数团队能获得最大剩余收益的地方。

最简单的第一步:查看你当前 Agent 的工具调用,在纸上画出依赖图,并统计有多少个调用没有前置节点。这个数字告诉了你理论上的并行预算。如果这个数字大于 1,那么你可能正在错失降低延迟的机会。

References:Let's stay in touch and Follow me for more thoughts and updates