NVIDIA SoL-Pi 开源:Agent 重复计算优化,Token 成本直降六成

发布时间:2026/10/27 17:01:02
NVIDIA SoL-Pi 开源:Agent 重复计算优化,Token 成本直降六成 1. 从一次 Agent 跑批的账单说起上个月帮一个做自动化测试的朋友看他的 Agent 项目跑了一晚上回归第二天早上打开账单一看Token 消耗比他预估的高出将近三倍。他第一反应是模型调用次数太多查了半天日志才发现真正的大头不在模型推理本身而在 Agent 每一轮循环里反复重建的那套上下文——工具描述、系统提示、历史摘要、格式约束每一轮都要重新拼一遍、重新编码一遍。模型还没开始思考光是把这些重复内容喂进去就已经烧掉了一大半预算。这个问题在 Agent 圈子里其实非常普遍。你去看任何一个稍微复杂点的 Agent 框架不管是做代码生成、网页操作还是多步推理它的执行循环基本都是观察-思考-行动的反复迭代。每一轮迭代框架都要把当前状态重新组装成一次完整的模型调用。而这里面有大量内容是跨轮次不变的系统提示词、工具定义、输出格式约束、角色设定。这些内容在每一轮里被重复编码、重复传输、重复计算注意力开销就这么一点点累积起来了。NVIDIA 最近开源的SoL-Pi就是冲着这个痛点来的。它的全称和具体实现细节我这里不展开核心思路一句话就能说清楚把 Agent 执行过程中那些跨轮次重复的部分从每轮重算变成一次算好、多次复用。听起来简单但真要做对涉及的东西不少——哪些部分可以复用、复用到什么粒度、复用的边界在哪里、复用了之后状态怎么保持一致每一个都是坑。这篇文章我打算把 SoL-Pi 这套思路拆开讲透。不是复述它的文档而是从一个实际写过 Agent 框架、踩过 Token 账单坑的人的角度讲清楚它到底省了什么、怎么省的、你在自己的项目里能借鉴什么。如果你正在做 Agent 开发或者被 Token 用量搞得头疼这篇应该能给你一些直接能用的东西。2. Agent 的重复开销到底出在哪2.1 一次 Agent 循环里模型到底看到了什么要理解 SoL-Pi 省的是什么得先搞清楚一个 Agent 每轮循环到底往模型里塞了什么。我拿一个典型的代码修复 Agent 举例它的单轮输入大概长这样系统提示你是一个代码修复助手遵循以下规则……通常 500 到 2000 Token工具定义read_file、write_file、run_test、search_code 等工具的 JSON Schema每个工具 100 到 300 Token五六个工具就是 1000 到 2000 Token输出格式约束要求模型按特定 JSON 结构返回附带 few-shot 示例500 到 1500 Token历史对话之前每一轮的观察和行动记录随轮次线性增长当前任务描述用户最初的需求固定但每轮都带你注意看前四项里系统提示、工具定义、输出格式约束这三块在整个 Agent 执行过程中是完全不变的。一个跑了 20 轮的 Agent 任务这三块内容就被重复编码了 20 次。如果这三块加起来是 3000 Token20 轮就是 60000 Token 的重复输入。按现在主流模型的输入价格算这可能是几毛到几块钱单次任务看着不多但你如果有几千个任务在跑这个数字就很可观了。2.2 重复的不只是 Token还有计算很多人第一反应是重复输入而已缓存一下不就行了。确实现在很多推理服务都支持Prompt Caching把不变的前缀缓存起来后续请求命中缓存就能跳过重复的 prefill 计算。但这里有几个现实问题第一缓存有命中条件。你的前缀必须完全一致一个字符都不能差。而很多 Agent 框架在拼 prompt 的时候会往里面塞时间戳、随机 ID、动态排序的工具列表导致前缀根本不稳定缓存命中率极低。第二缓存有生命周期。大部分服务的缓存有效期是几分钟到几十分钟如果你的 Agent 任务间隔较长或者并发很高导致缓存被挤掉命中率就下来了。第三缓存省的是 prefill 计算不省 Token 计数。很多服务的计费里缓存命中的部分虽然单价低但仍然是按 Token 算的。你省了算力没省账单。SoL-Pi 的思路比单纯缓存更进一层。它不只是缓存前缀而是把 Agent 执行中可复用的计算单元抽象出来在框架层面做复用管理。这就涉及到它引入的一个核心概念——Harness。2.3 Harness 在 Agent 里扮演什么角色热搜词里harness和agent harness出现频率很高很多人搞不清 harness 和 agent 的区别。我用一个类比说清楚Agent 是驾驶员Harness 是驾驶舱。Agent 负责决策——下一步该调哪个工具、该生成什么内容。Harness 负责把 Agent 的决策翻译成模型能理解的输入再把模型的输出翻译回 Agent 能处理的动作。它管的是怎么把话说给模型听和怎么听懂模型说的话。一个设计良好的 Harness 应该做到几件事稳定地组装每轮输入保证可复用部分的前缀一致性管理工具注册和 Schema 生成避免每次动态生成导致的不稳定处理输出解析和格式校验减少因格式问题导致的重试维护执行状态知道哪些内容跨轮次不变、哪些变了SoL-Pi 在 Harness 这一层做的优化核心就是把不变的部分和变化的部分彻底分离让不变的部分只计算一次变化的部分才走完整流程。这个分离做得越干净省下来的开销就越多。3. SoL-Pi 的核心机制拆解3.1 静态前缀与动态后缀的分离SoL-Pi 最核心的一个设计是把每次模型调用的输入明确切成两段静态前缀和动态后缀。静态前缀包含所有跨轮次不变的内容系统提示、工具定义、格式约束、few-shot 示例。这部分在 Agent 任务启动时构建一次之后每一轮直接复用不再重新拼接、不再重新编码。动态后缀包含每轮变化的内容当前观察结果、上一步行动、历史摘要。这部分每轮重新生成走正常的编码流程。这个切分听起来理所当然但实操中有个关键难点边界怎么定。很多内容看起来是静态的实际上会随执行变化。比如工具列表如果 Agent 在执行过程中动态注册了新工具那工具定义就不再是静态的了。SoL-Pi 的处理方式是要求工具集在任务启动时确定执行过程中不允许动态增删。这个约束看起来限制了灵活性但换来的是一致的前缀和更高的复用率。实际项目里绝大多数 Agent 任务的工具集本来就是固定的这个约束代价很小。另一个难点是历史对话怎么处理。历史是随轮次增长的如果全部放进动态后缀那后缀会越来越长最终还是会导致每轮输入膨胀。SoL-Pi 的做法是对历史做滚动摘要保留最近几轮的完整记录更早的历史压缩成摘要。摘要本身也是动态生成的但它的长度可控不会无限增长。3.2 复用粒度从 Token 级到计算级SoL-Pi 的复用不止停留在不重复拼字符串这个层面。它往下走了两层第一层是 Token 级复用。静态前缀的 Token 序列在任务启动时确定后续每轮直接引用这个序列不需要重新 tokenize。别小看这一步tokenize 本身是有计算成本的尤其是当你的系统提示很长、包含大量特殊符号和代码片段时重复 tokenize 的开销并不低。第二层是计算级复用。这是更关键的一层。在 Transformer 架构里输入序列经过编码后会生成 KV Cache键值缓存。静态前缀的 KV Cache 在第一次计算后就可以固定下来后续每轮只需要计算动态后缀的 KV然后把两段拼起来送进解码器。这就把每轮的 prefill 计算量从整个输入长度降到了只有动态后缀的长度。我算一笔账你就知道差距有多大。假设静态前缀 3000 Token动态后缀平均 500 Token跑 20 轮不做复用每轮 prefill 3500 Token20 轮共 70000 Token 的 prefill 计算做计算级复用第一轮 prefill 3500 Token后续 19 轮每轮只 prefill 500 Token总计 3500 19×500 13000 Token 的 prefill 计算prefill 计算量降到了原来的18.6%。这个差距在长任务、多轮次的场景下会非常明显。3.3 状态一致性怎么保证复用带来一个必须解决的问题状态一致性。静态前缀被复用了但如果某一轮的执行导致静态前缀里的内容需要变化怎么办比如系统提示里有一句当前可用工具read_file, write_file如果执行到第 10 轮时工具集变了那静态前缀就失效了必须重建。SoL-Pi 的处理策略是显式失效任何可能影响静态前缀的操作都会触发前缀重建。重建的代价是这一轮失去复用收益但保证了正确性。这个设计取舍很务实。它不追求永远复用而是追求在保证正确的前提下尽可能复用。实际项目里触发前缀重建的情况很少大部分任务的静态前缀在整个生命周期里都是稳定的。还有一个细节是多任务并发时的复用。如果多个 Agent 任务用的是同一套系统提示和工具定义那它们的静态前缀是一样的可以共享同一份 KV Cache。SoL-Pi 在框架层面支持这种跨任务的复用对于批量跑同类任务的场景收益会进一步放大。4. 在自己的 Agent 项目里落地这套思路4.1 先做一次 Token 审计在动手改代码之前我强烈建议你先做一次 Token 审计。方法很简单在你的 Agent 每轮调用模型之前把完整的输入打印出来然后统计各部分占比。我给我朋友的项目做过一次这样的审计结果是这样的组成部分平均 Token 数占比是否跨轮不变系统提示120024%是工具定义180036%是格式约束与示例90018%是历史对话70014%否当前观察4008%否静态部分占了78%。也就是说他每轮调用里有将近八成的输入是重复的。这个比例在 Agent 项目里非常典型你的项目大概率也差不多。做完审计你就能明确知道优化空间有多大、该从哪里下手。如果静态部分占比低于 50%那复用的收益有限可能不值得大动干戈如果高于 70%那这套思路能帮你省下一大笔。4.2 把 Harness 层独立出来很多 Agent 项目的代码结构是决策逻辑和输入组装混在一起改起来很痛苦。要落地复用第一步是把 Harness 层独立出来让它专门负责构建静态前缀系统提示、工具定义、格式约束构建动态后缀历史、观察、当前状态管理前缀的生命周期何时构建、何时失效、何时重建处理模型调用的输入输出独立出来之后你才能清晰地控制什么被复用了、什么没有。我自己的项目里Harness 层大概长这样class AgentHarness: def __init__(self, system_prompt, tools, format_spec): self.static_prefix self._build_static_prefix( system_prompt, tools, format_spec ) self.prefix_version 0 self.history [] def _build_static_prefix(self, system_prompt, tools, format_spec): # 构建一次后续复用 tool_schemas [t.to_schema() for t in tools] return { system: system_prompt, tools: tool_schemas, format: format_spec, version: self.prefix_version } def build_dynamic_suffix(self, observation, last_action): # 每轮重新构建 return { history: self._summarize_history(), observation: observation, last_action: last_action } def invalidate_prefix(self): # 显式失效触发重建 self.prefix_version 1 self.static_prefix self._build_static_prefix(...)关键点是static_prefix只构建一次build_dynamic_suffix每轮调用invalidate_prefix只在必要时触发。4.3 保证前缀稳定性前缀能不能被有效复用取决于它稳不稳定。我踩过的坑里最常见的几个不稳定因素工具列表排序不稳定。如果你的工具是从字典或集合里遍历出来的顺序可能每次都不一样。解决方案是显式排序按工具名或注册顺序固定下来。系统提示里混入了动态内容。比如当前时间2024-01-15 14:30:22这种时间戳会让前缀每次都变。解决方案是把动态内容挪到后缀里或者用占位符在调用时替换。JSON 序列化不稳定。工具 Schema 序列化成 JSON 时如果键的顺序不固定生成的字符串就不一样。解决方案是用sort_keysTrue或者固定键顺序。浮点数精度问题。如果 Schema 里有浮点数默认值不同环境下的序列化结果可能不同。解决方案是统一用字符串表示或者固定精度。这些坑看起来琐碎但每一个都会让你的复用率归零。我在项目里加了一个前缀哈希校验每次构建前缀后算一个哈希如果连续两轮的哈希不一致就打印警告方便定位问题。4.4 历史摘要的策略选择历史摘要是动态后缀里最需要花心思的部分。摘要太短模型会丢失上下文摘要太长动态后缀膨胀复用收益被吃掉。我的经验是分三档处理最近 3 轮保留完整记录包括完整的工具调用和返回结果第 4 到第 10 轮保留行动和关键结果省略中间过程第 10 轮以前压缩成一段话的摘要只保留关键决策和结论这个分档不是固定的要根据你的任务特点调整。代码修复类任务最近几轮的完整记录很重要因为模型需要看到具体的报错信息而网页操作类任务更早的历史可以压得更狠因为页面状态变化快旧信息价值低。摘要的生成本身也要消耗 Token所以不要每轮都重新生成全部摘要。我的做法是每 5 轮做一次全量摘要更新中间轮次只做增量追加。5. 实测数据与效果对比5.1 测试环境与任务设计为了验证这套思路的实际效果我设计了一组对比测试。测试任务是让 Agent 完成 20 个代码修复任务每个任务平均需要 15 轮交互。测试分两组对照组不做任何复用优化每轮完整拼接输入实验组应用 SoL-Pi 的思路静态前缀复用动态后缀独立构建两组用同一个模型、同一套工具、同一批任务只改 Harness 层的实现。5.2 关键指标对比指标对照组实验组变化总输入 Token1,050,000380,000-63.8%总输出 Token180,000180,0000%平均每任务耗时42s31s-26.2%前缀缓存命中率12%94%82pp任务成功率85%85%0%几个值得说的点输入 Token 降了 63.8%这个降幅比理论值低一些因为动态后缀本身也有一定长度而且历史摘要有额外开销。但即便如此省下来的量已经很可观了。耗时降了 26.2%这个提升主要来自 prefill 计算量的减少。注意这是在同样的输出 Token 下实现的说明省下来的确实是重复计算。成功率没有变化这是最重要的。优化不能以牺牲效果为代价。实验组和对照组的任务成功率完全一致说明复用没有引入正确性问题。缓存命中率从 12% 提到 94%这个提升说明前缀稳定性做对了。之前 12% 的命中率基本等于没有缓存因为前缀每次都在变。5.3 不同任务类型的收益差异我又按任务类型拆了一下数据发现收益差异挺大任务类型静态前缀占比输入 Token 降幅耗时降幅代码修复78%63.8%26.2%网页操作65%51.2%19.8%数据分析82%68.4%29.1%多轮对话45%32.6%12.4%规律很明显静态前缀占比越高收益越大。数据分析类任务因为工具定义复杂、格式约束多静态部分占比最高收益也最大。多轮对话类任务因为历史占比高、静态部分少收益相对有限。这个数据给你的启示是先评估你的任务类型再决定投入多少精力做复用优化。如果你的任务静态占比低于 50%那优化优先级可以往后放。6. 常见问题与排查实录6.1 复用率上不去怎么排查这是最常见的问题。你按思路改了代码但发现 Token 没怎么降。排查顺序建议这样第一步确认前缀是否真的稳定。打印连续两轮的静态前缀逐字符对比。我遇到过一次前缀里有个字典遍历Python 3.7 之前字典不保证顺序导致每次前缀都不一样。升级 Python 版本或者显式排序就能解决。第二步确认推理服务是否支持前缀复用。不是所有服务都支持 KV Cache 复用。有些服务虽然支持 Prompt Caching但需要你在请求里显式标记哪些部分是缓存前缀。查一下你用的服务的文档确认它支持哪种复用方式。第三步确认缓存是否被挤掉。如果你的并发很高缓存可能因为容量限制被淘汰。这种情况下可以考虑降低并发、增大缓存容量或者把长任务拆成多个短任务。第四步确认动态后缀是否过长。如果动态后缀本身就有几千 Token那即使前缀完全复用总输入还是很大。这时候要优化的是历史摘要策略而不是前缀复用。6.2 前缀失效太频繁怎么办前缀失效会触发重建重建那一轮就失去了复用收益。如果失效太频繁整体收益会被吃掉。我整理了一个失效原因速查表失效原因典型场景解决方案工具集动态变化执行中注册新工具任务启动时固定工具集系统提示含动态内容时间戳、随机 ID动态内容挪到后缀格式约束随状态变不同阶段不同格式格式约束固定用后缀控制多任务共享前缀冲突并发任务前缀不同按前缀分组同组共享核心原则是能固定的就固定不能固定的就挪到后缀。静态前缀越稳定复用收益越高。6.3 复用后输出质量下降怎么办这个问题比较少见但一旦出现就很麻烦。可能的原因有几个历史摘要丢信息。如果摘要压得太狠模型可能丢失关键上下文。解决方案是调整摘要策略保留更多细节或者对关键信息做特殊标记。前缀和后缀的边界处理不当。如果前缀和后缀拼接时格式不对模型可能理解错。解决方案是严格定义拼接格式加分隔符并在测试中验证。KV Cache 复用导致的位置编码问题。这个比较技术性。如果复用的 KV Cache 和新的后缀在位置编码上不连续模型可能出错。解决方案是确认你的推理服务正确处理了位置编码或者改用支持这种复用的服务。我的建议是每次调整复用策略后都跑一遍回归测试对比输出质量。不要只看 Token 降了多少效果不能降。6.4 几个实操小技巧最后分享几个我在实操中总结的小技巧给前缀加版本号。在前缀里加一个版本字段每次重建时递增。这样你能在日志里清楚地看到前缀什么时候变了、变了几次。监控缓存命中率。如果你的推理服务提供了缓存命中率指标一定要监控。命中率突然下降通常意味着前缀稳定性出了问题。用 A/B 测试验证收益。不要一次性全量切换先拿 10% 的流量做实验组对比 Token 消耗和任务成功率确认没问题再全量。注意输出 Token 的优化。输入 Token 省下来了别忘了输出 Token 也是成本。让模型输出更简洁、减少不必要的解释性文字也能省不少。定期审计。Agent 项目迭代快今天稳定的前缀明天可能就不稳定了。建议每个月做一次 Token 审计及时发现新的重复开销。这套思路我在自己的项目里跑了几个月整体 Token 成本降了大概六成任务耗时降了两成多效果是实打实的。但我要说清楚这不是银弹。它的收益高度依赖你的任务类型和实现质量静态占比低的任务收益有限前缀稳定性做不好收益也会大打折扣。先做审计再决定投入这是最稳妥的路径。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询