优化Token成本的7个工程技巧
Anthropic 的 Claude Code 工程师 Thariq Shafi 本周发布了一条帖子,任何使用 AI API 进行构建的人都应该阅读该帖子。
要点:Claude Code 是数百万人使用的人工智能编码代理,它处理提示缓存未命中的方式与大多数公司处理服务器中断的方式相同。他们运行警报。他们宣布事件。缓存未命中率几个百分点就会触发紧急响应。
在您了解经济学之前,这听起来很戏剧性。如果没有及时缓存,长时间人工智能对话中的每条消息都会从头开始重新处理整个对话历史记录。您发送 50 条消息的 100,000 个令牌对话意味着 API 处理 500 万个输入令牌。通过缓存,它可以处理新令牌并以 90% 的折扣从缓存中读取其余令牌。
缓存和未缓存的代理会话之间的差异就像成本为 0.50 美元的产品和成本为 5 美元的产品之间的差异。从规模上看,这就是一家可行的企业和一家破产企业之间的区别。
Manus(另一个人工智能代理)的创始人 Peak Ji 也发表了类似的发现。他的团队发现代理会话中输入令牌与输出令牌的比率为 100:1。当您的输入量如此不平衡时,缓存效率就不是一种优化。这是整个成本结构。
即使您不构建代理,这对您也很重要。如果您将 Claude API 用于具有多轮对话、长系统提示或重复上下文的任何内容,提示缓存可以大幅降低您的成本。 Anthropic 刚刚提供了自动缓存功能,这使得基本版本非常容易实现。
以下是它的工作原理、Claude Code 团队从惨痛的经历中学到的东西,以及如何应用他们的经验教训,而不需要构建像他们一样复杂的产品。
1、提示缓存简介
每次您向 Claude API 发送消息时,模型都需要处理所有输入令牌:您的系统提示、工具、对话历史记录,一切。处理令牌是昂贵的部分。这需要计算和时间。
提示缓存让 API 记住它之前见过的令牌的处理结果。在下一个请求中,如果输入的开头与缓存的内容匹配,则 API 会跳过重新处理这些令牌并读取缓存的结果。读取费用为正常输入价格的 10%。
关键机制:它通过前缀匹配来工作。 API 会缓存从请求开始到缓存断点的所有内容。如果下一个请求具有相同的前缀,则从缓存中读取这些令牌。如果该前缀中的任何内容发生变化,即使是一个字符,缓存也会失效,并且其后的所有内容都需要重新处理。
把它想象成共享通勤。如果您和您的同事都从同一个社区开车到同一个办公室,则可以拼车前往路线的共享部分。但如果你们中的一个人绕道离家三个街区,剩下的路你们就得分开开车了。路线的“共享前缀”是拼车的基础。提示缓存与应用于计算的想法相同。
2、数字
成本节省是显着的。
对于 Claude Sonnet 4.6,这意味着缓存读取每百万令牌的成本为 0.30 美元,而未缓存输入的成本为每百万美元 3.00 美元。对于 Opus 4.6,缓存读取的价格为 1.50 美元,而 15.00 美元。首次缓存令牌时,您需要支付少量费用(5 分钟缓存需额外支付 25%)。此后,每次阅读均可享受 90% 的折扣。
还有一个 1 小时的缓存层,其成本是基本速率的 2 倍,但可以使缓存保持更长时间,这对于消息之间可能存在间隙的会话很有用。
延迟的改善也很重要。缓存令牌的处理速度比未缓存的令牌快得多,这意味着随着对话时间的延长,您的代理或聊天机器人的响应速度明显更快。
3、实现的两个方法
Anthropic 提供了两种方法,其中较简单的一种是新方法。
3.1 自动缓存(简单)
将单个字段添加到您的 API 请求:
{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"cache_control": {"type": "ephemeral"},
"system": "Your system prompt here...",
"messages": [...]
}就是这样。 API 会自动将缓存断点放置在最后一个可缓存块的末尾,并随着会话的增长将其向前移动。对于大多数多轮对话用例,这就是您所需要的。
3.2 显式控制
如果您需要精确控制缓存的内容,您可以将“cache_control”放在特定的内容块上:
{
“系统”: [
{
“类型”:“文本”,
"text": "你的长系统提示符...",
“cache_control”:{“类型”:“短暂”}
}
]
}
这允许您在特定位置设置多个缓存断点。当您有不同的上下文部分(系统提示、参考文档、对话历史记录)并希望确保每个部分都独立缓存时非常有用。
4、Claude Code的经验教训
自动缓存功能处理基础知识。但是,如果您正在构建任何需要长时间运行的会话、大量工具或复杂状态管理的东西,那么 Claude Code 团队的课程值得理解。他们塑造了产品中的每一个设计决策。
4.1 像缓存层级一样排序你的提示
从请求开始向前提示缓存匹配项。这意味着提示中的内容顺序决定了跨请求缓存的内容量。
规则:静态内容在前,动态内容在后。
Claude Code 像这样构造每个请求:
- 静态系统提示和工具定义(每个用户、每个会话都相同)
- 项目级上下文,如 CLAUDE.md(项目内相同)
- 会话级上下文(会话内相同)
- 对话消息(每次都会更改)
这样,即使来自不同会话的请求也可以共享系统提示和工具上的缓存命中。同一项目内的请求也共享项目上下文中的点击。唯一未缓存的令牌是最新的消息。
如果你翻转这个顺序,将动态内容放在第一位,那么每个请求的缓存都会失效。共享前缀为零,您需要为所有商品支付全价。
4.2 前缀很脆弱
Claude Code 团队多次破坏了自己的缓存,所做的更改似乎无害。一些例子:
- 在系统提示符中放置详细的时间戳(每秒都会更改,使整个前缀无效)
- 以不确定的顺序打乱工具定义(工具相同,但顺序更改,因此前缀不匹配)*
- 动态更新工具的参数(例如,更改可用的子代理)
所有这些都会导致缓存命中率下降、成本飙升以及团队混乱。这就是为什么他们监控缓存命中率(例如正常运行时间)并将丢失视为事件。
结论:静态前缀中的任何内容都必须是真正静态的。如果发生变化,请将其从前缀移出并移至对话消息中。
4.3 使用消息而不是修改提示来进行更新
当信息在会话中发生变化(时间、用户编辑的文件、新配置)时,诱人的方法是更新系统提示。不要这样做。它破坏了缓存。
相反,应在下一个对话回合中将更新作为消息传递。 Claude Code 在用户消息或工具结果中使用“”标签来向模型传达更新。 “现在是星期三。” “用户更改了文件 X。” “新的上下文可用。”
模型从消息中读取更新。系统提示保持不变。缓存保持不变。
4.4 永远不要在会话中间改变工具
这是违反直觉的。您可能会认为只为模型提供它现在需要的工具会很有效。但由于工具定义是缓存前缀的一部分,因此添加或删除工具会使整个对话的缓存失效。
Claude Code 的解决方案:始终在每个请求中保留所有工具。对于仅有时需要的工具(例如数十个 MCP 服务器工具),他们使用“延迟加载”方法:发送仅包含工具名称和 defer_loading: true 标志的轻量级存根。当模型真正需要时,可以通过 ToolSearch 工具发现完整的工具模式。存根在前缀中保持稳定。缓存保持不变。
4.5 不要在会话中途切换模型
每个模型的提示缓存都是唯一的。如果您在与 Opus 的对话中花费了 100,000 个令牌,并且想问一个 Haiku 可以处理的快速问题,那么切换到 Haiku 实际上“更昂贵”,因为您需要从头开始重建 Haiku 的整个缓存。
更好的模式:使用子代理。主模型准备带有相关上下文的简明切换消息,并且在更便宜的模型上运行的子代理在其自己的会话中处理任务。 Claude Code 通过其 Explore 代理来实现这一点,该代理在 Haiku 上运行,具有重点上下文而不是完整的对话历史记录。
4.6 围绕缓存设计特性,而不是相反
Claude Code 中的计划模式就是一个完美的例子。明显的实现是:当用户进入计划模式时,将工具替换为只读工具。但交换工具会破坏缓存。
相反,Claude Code 在每个请求中保留所有工具,并将计划模式作为工具本身实现。当用户切换计划模式时,模型调用“EnterPlanMode”,接收一条解释约束的消息(探索代码库,不要编辑文件),并在完成后调用“ExitPlanMode”。工具定义永远不会改变。缓存永远不会中断。
这种设计的好处是:因为“EnterPlanMode”是模型可以自行调用的工具,所以当它检测到困难问题时,它可以自主进入计划模式。围绕缓存约束设计的功能最终产生了更好的行为。
4.7 压缩需要分享父级前缀
当对话达到上下文窗口限制时,Claude Code 会总结到目前为止的对话并继续进行总结。这称为压缩。
简单的实现:使用不同的系统提示进行单独的 API 调用,提示“总结此对话”。这会破坏缓存,因为压缩请求的前缀与父会话不匹配。您为所有这些代币支付全价。
Claude Code 的方法:压缩请求使用与父对话完全相同的系统提示、工具和上下文。包含父级的消息,并且压缩指令作为新的用户消息附加在末尾。从 API 的角度来看,该请求看起来与父级的最后一个请求几乎相同,因此缓存命中率保持较高。
Anthropic 将其作为压缩功能直接构建到 API 中,因此您无需自行设计。
5、这对你意味着什么
如果您使用 Claude API 进行多轮对话,请启用自动缓存。它只是请求正文中的一个字段,可以在长时间对话中将您的成本降低 80-90%。没有理由不这样做。
如果您正在构建代理或任何具有长时间运行会话的产品,那么设计课程比实施更重要:
- 将静态内容放在提示的顶部。底部的动态内容。
- 请勿在会话中修改系统提示。请改用消息。
- 请勿在会话中添加、删除或重新排序工具。
- 在不使用子代理的情况下,请勿在会话中切换模型。
- 监控缓存命中率。删除意味着您的前缀发生了变化。
Claude Code 团队从第一天起就围绕这些限制构建了整个产品。他们后来没有针对缓存进行优化。他们从一开始就围绕它进行设计。每一个架构决策,从计划模式如何工作到工具如何加载,再到压缩如何运行,都是由一个问题决定的:这会破坏缓存吗?
如果您基于 API 进行构建并忽略提示缓存,那么您就会浪费金钱和速度。
可能两者都有很多。
原文链接: The Engineering Trick That Makes AI Agents Affordable
汇智网翻译整理,转载请标明出处