LLM 智能体成本优化
一套用于削减 LLM 智能体成本的四层方法论。适用于任何会进行多轮对话、加载工具 schema、处理用户上传文件的、会调用工具的智能体。
核心公式
智能体成本 = 轮次数 × 每轮 Token 数 × Token 单价
这三个维度都可以优化。按影响力从大到小依次攻克:
| 层级 | 要做什么 | 典型节省幅度 |
|---|---|---|
| 工具加载 | 只加载需要的工具(裁剪 schema) | 节省 30%-50% 的 token 成本 |
| 轮次压缩 | 每一轮做更多事(批处理、合并、跳过) | 节省 50%-80% 的轮次成本 |
| 缓存稳定性 | 不要打破前缀(正确处理确认/不确定的情况) | 每次缓存重建成本 $0.3-0.5 |
| 附件复用 | 只解析一次,复用缓存路径 | 节省 2-3 轮 |
第一层:按领域感知加载工具
问题
你的智能体连接了多个工具服务器,暴露出成百上千个工具。每次请求都会把全部工具 schema 加载进提示词里——哪怕用户问的只是一个简单任务,每一轮也要背上 3 万到 6 万 token 的"固定成本"。
解决方案
1. 建立一张"领域 → 工具"映射表。 扫描你的技能/任务文档,确定每种任务类型实际需要哪些工具。生成一份静态映射表:
{
"domainA": {
"servers": ["server1", "server2"],
"tools": ["tool_x", "tool_y"],
"disallowed": ["tool_z"]
},
"domainB": { ... }
}
2. 加一个轻量级分类器。 在主智能体启动之前,用一个便宜、快速的模型把每条用户消息分类到某个领域/任务类型。这是一个路由决策,不该由主智能体来做。
3. 按领域过滤工具。 只连接分类后的领域实际需要的服务器,把没用到的工具 schema 从模型的上下文里彻底移除。
核心洞察
> 智能体拥有的工具越多,每一轮的"固定成本"就越高。根据任务类型动态裁剪工具集,是最直接的 token 优化手段。
第二层:轮次压缩
问题
大多数 LLM 智能体每一轮只做一件事:
- 分块读取一个大文件(10 轮以上)
- 一次只转换一个值(N 个值就要 N 轮)
- 每个阶段都单独设一个检查点/确认环节
- 把主智能体本来就能内联处理的任务,硬是甩给一个子智能体去做
每一轮都要背上完整的对话历史——轮次数是成本里最大的乘数因子。
三种压缩策略
A. 工具层面:批处理 + 拆分文件
- 对重复性操作(日期、ID、数值)接受数组参数——一次调用处理 N 项,而不是 N 次调用
-
按类型/渠道拆分大文件的输出,让智能体只读取需要的部分,而不是一整块
-
把预处理逻辑(日期转换、ID 生成、数据校验)放进工具本身——不要让 LLM 去做这些事
B. 流程层面:跳过不必要的步骤
- 简单场景:在主智能体里内联处理,而不是派发给子智能体(省下子智能体重建缓存的成本)
- 当输入明显足够简单(单项、格式规整)时,跳过检测/分析阶段
- 把多个检查点/确认环节合并成一次综合评审
C. 写操作合并
- 把写操作批量合并成批量操作,而不是逐条创建
- 并行执行相互独立的工具调用
核心洞察
> 压缩一轮所省下的成本,胜过在一轮内节省 1000 个 token——因为每一轮都要背负完整的对话历史。
第三层:提示词缓存稳定性
问题
提示词缓存是按前缀匹配的。只要前缀(系统提示词 + 工具 + 消息历史)保持逐字节一致,后续轮次就能以很低的成本命中缓存。但有几种场景会打破缓存:
- 工具列表发生变化——分类器返回"不确定" → 兜底加载全部工具 → 前缀发生变化 → 缓存重建(代价高昂)
- 确认类回复被误分类——"是"、"否"、"继续"、"确认"这类回复被误判成打招呼 → 加载了错误的工具集,或者用寒暄来回应而不是真正处理请求
- 子智能体派发——子智能体使用独立的系统提示词 → 无法共享缓存
解决方法
-
不确定时 → 复用会话状态。 当分类器无法判断意图时,复用会话已积累的上下文(不要兜底加载全部工具)。既然对话上下文没变,工具集也不应该变。
-
决策性词语是动作,不是打招呼。 "是"、"否"、"好的"、"取消"、"停止"、"继续"、"确认"、"继续吧"这些词,是针对某个待处理动作做出的决策,绝不是在打招呼。把这条规则加进分类器里。
-
打招呼只发生在第一轮。 对话进行到中途出现的简短的"嗨"或"好的",是确认/后续回复,不是打招呼。
-
子智能体继承父智能体的工具。 让子智能体继承父智能体的工具列表(共享缓存前缀),而不是自己另外声明一套。
核心洞察
> 提示词缓存是隐藏的成本杀手。一次被误分类的确认回复,就可能触发一次 0.5 美元的缓存重建。在整个会话过程中保持前缀稳定——同样的工具、同样的系统提示词——是控制成本稳定的关键。
第四层:附件预处理
问题
当用户上传一个大文件时:
- 智能体去解析它(一次昂贵的工具调用)
- 解析出的内容(约 1 万到 5 万 token)会永久留在上下文里,后续每一轮都要背负一次
- 子智能体或下游步骤会重新解析同一份数据
解决方案
在分类之前就预先解析。 在主智能体启动之前,就把文件完整解析成一份缓存好的输出(CSV、Markdown 或结构化数据)。只保存一份清单(文件路径、类型、时间戳)——内容留在磁盘上,进入上下文的只有指针。
分类器读取解析后的内容,从而变得对内容有感知——它看到的是真实的列名和结构,而不只是"用户上传了一个文件"这种空泛信息。
主智能体在提示词里拿到预解析好的文件路径,直接读取文件——再也不会重新调用解析工具。这就消除了重复解析,以及内容永久堆积在上下文里的问题。
核心洞察
> 大文件应该只解析一次,之后靠路径引用来复用,而不是每一轮都重新解析。解析出来的内容,不管是谁在读,都是一样的——但工具调用和 token 成本却不是。
五条核心原则
-
轮次比 token 更重要。 压缩一轮胜过在一轮内节省 1000 个 token——因为每一轮都要背负完整的历史。
-
裁剪工具是唾手可得的成果。 大多数 token 浪费都来自不必要的工具 schema。先对任务分类,再做裁剪。
-
缓存是隐藏的成本杀手。 一次被误分类的确认回复,就可能触发一次 0.5 美元的缓存重建。保持前缀稳定。
-
工具设计决定智能体的行为方式。 如果一个工具需要预处理(日期转换、ID 生成、数据校验),就把这些逻辑做在工具内部——不要让 LLM 来做。让 LLM 做这些事既浪费轮次,又容易出错。
-
预解析 + 内容缓存。 大文件应该只解析一次,之后靠路径引用复用,而不是每一轮都重新解析。
反模式(不该做的事)
- ❌ 每次请求都加载全部工具——哪怕智能体可能只需要其中几个。应该按任务类型裁剪。
- ❌ 一轮只处理一个值——应该对重复性操作(日期、ID、列表处理)做批处理。
- ❌ 不确定时兜底加载全部工具——应该复用会话已有的上下文。
- ❌ 重复解析同一个文件——应该只预解析一次,之后靠路径复用。
- ❌ 简单任务也派发子智能体——内联处理更便宜,还能避免缓存重建。
- ❌ 让 LLM 来做预处理——日期转换、ID 生成、数据校验应该属于工具的职责,而不是提示词的职责。