从ClaudeCode看如何Harness智能体(二)

从ClaudeCode看如何Harness智能体(二)

提示词缓存(Prompt Caching)是核心

工程领域有句常说的话:“缓存主宰一切”,这句话同样适用于智能体。像 Claude Code 这样的长周期智能体产品,之所以能够落地,核心在于提示词缓存——它允许我们复用之前对话轮次的计算结果,显著降低延迟和成本。什么是提示词缓存?它如何工作?如何从技术上实现?更多内容可参阅 @RLanceMartin 关于提示词缓存的文章,以及我们新推出的自动缓存功能。在 Claude Code 中,我们的整个工具框架都是围绕提示词缓存构建的。高提示词缓存命中率可以降低成本,并帮助我们为订阅计划提供更宽松的调用限额。因此,我们会对提示词缓存命中率进行告警,如果命中率过低,会将其视为严重线上故障(SEV)。以下是我们在大规模优化提示词缓存过程中,得出的一些(往往反直觉的)经验。

为缓存优化提示词排版

提示词缓存基于前缀匹配机制——API 会缓存从请求开头到每个缓存控制断点的所有内容。这意味着,内容的排列顺序至关重要,你需要让尽可能多的请求共享同一个前缀。实现这一点的最佳方式是:静态内容在前,动态内容在后。对于 Claude Code,具体顺序如下:静态系统提示 + 工具(全局缓存)Claude.MD(项目内缓存)会话上下文(会话内缓存)对话消息这样可以最大限度地让多个会话共享缓存命中。但这种排版方式意外地脆弱!我们曾经因为以下原因破坏过这种顺序:在静态系统提示中加入详细的时间戳、非确定性地打乱工具顺序定义、修改工具参数(例如,AgentTool 可以调用哪些智能体)等。

用消息传递更新内容

有时,你放入提示词中的信息会过时,例如时间变化或用户修改了文件。此时,你可能会想直接修改提示词,但这会导致缓存失效,最终可能会给用户带来较高的成本。不妨考虑在下一步对话中,通过消息传递这些更新信息。在 Claude Code 中,我们会在下一条用户消息或工具结果中添加 <system-reminder> 标签,将更新后的信息(例如“现在是周三”)传递给模型,这样有助于保留缓存。

会话中途不切换模型

提示词缓存是与模型绑定的,这使得提示词缓存的成本计算往往反直觉。假设你在与 Opus 进行一段 10 万个 tokens 的对话后,想询问一个相对简单的问题——此时,切换到 Haiku 反而比让 Opus 回答更昂贵,因为我们需要为 Haiku 重新构建提示词缓存。如果确实需要切换模型,最佳方式是使用子智能体:让 Opus 准备一条“交接”消息,将任务传递给另一个模型。我们在 Claude Code 的探索智能体(Explore agents)中经常这样做,这些探索智能体使用的是 Haiku。

会话中途绝不添加或删除工具

在对话中途修改工具集,是最常见的破坏提示词缓存的方式之一。直觉上,你可能会认为“只给模型当前需要的工具”是合理的,但由于工具是缓存前缀的一部分,添加或删除工具会导致整个对话的缓存失效。

计划模式——围绕缓存设计功能

计划模式是一个围绕缓存约束设计功能的典型例子。直觉上,当用户进入计划模式时,你可能会想替换工具集,只保留只读工具——但这会破坏缓存。相反,我们始终在请求中保留所有工具,并将 EnterPlanMode 和 ExitPlanMode 本身作为工具使用。当用户开启计划模式时,智能体会收到一条系统消息,说明它已进入计划模式,以及相关指令——探索代码库、不编辑文件、计划完成后调用 ExitPlanMode 工具。工具定义始终保持不变。这还有一个额外的好处:由于 EnterPlanMode 是模型可以自主调用的工具,当它检测到难题时,能够自主进入计划模式,而不会破坏缓存。

工具搜索——延迟加载而非删除

同样的原则也适用于我们的工具搜索功能。Claude Code 可能会加载几十个 MCP 工具,如果每次请求都包含所有工具,成本会很高。但在对话中途删除工具,又会破坏缓存。我们的解决方案是:延迟加载(defer_loading)。我们不删除工具,而是发送轻量级的占位符——只包含工具名称,并设置 defer_loading: true——模型需要时,可以通过 ToolSearch 工具“发现”这些工具。只有当模型选择某个工具时,才会加载其完整的工具 schema。这样可以保持缓存前缀的稳定性:相同的占位符始终以相同的顺序存在。幸运的是,你可以通过我们的 API 使用工具搜索工具,简化这一过程。

上下文分叉——压缩(Compaction)

当上下文窗口耗尽时,就需要进行压缩:我们总结迄今为止的对话,然后在新的会话中继续。令人意外的是,压缩在提示词缓存方面有很多反直觉的边缘情况。特别是,当我们进行压缩时,需要将整个对话发送给模型以生成摘要。如果这是一个使用不同系统提示、不包含任何工具的独立 API 调用(这是最简单的实现方式),那么主对话的缓存前缀将完全不匹配。你需要为所有输入 tokens 支付全额费用,这会大幅增加用户的成本。

解决方案——缓存安全分叉

当我们进行压缩时,会使用与父对话完全相同的系统提示、用户上下文、系统上下文和工具定义。我们将父对话的消息前置,然后在末尾添加压缩提示作为新的用户消息。从 API 的角度来看,这个请求与父对话的最后一个请求几乎完全相同——相同的前缀、相同的工具、相同的历史记录——因此缓存前缀会被复用,新增的 tokens 只有压缩提示本身。但这也意味着,我们需要预留一个“压缩缓冲区”,以便在上下文窗口中留出足够的空间,容纳压缩提示和摘要输出 tokens。压缩虽然复杂,但幸运的是,你不必亲自摸索这些经验——基于我们在 Claude Code 中的实践,我们已将压缩功能直接集成到 API 中,你可以在自己的应用中直接应用这些模式。

经验总结

提示词缓存基于前缀匹配:前缀中的任何一处修改,都会导致后续所有内容的缓存失效。整个系统的设计都必须围绕这一约束。只要排版顺序正确,大部分缓存功能都会自动生效。用消息传递替代修改系统提示:你可能会想通过编辑系统提示来实现进入计划模式、修改日期等操作,但实际上,将这些内容插入对话中的消息,效果会更好。会话中途不修改工具或切换模型:使用工具来实现状态转换(如计划模式),而非修改工具集;采用延迟加载而非删除工具。像监控系统可用性一样监控缓存命中率:我们会对缓存破坏进行告警,并将其视为故障。几个百分点的缓存未命中率,就可能对成本和延迟产生巨大影响。分叉操作需要共享父对话的前缀:如果需要执行辅助计算(压缩、摘要、技能执行),请使用与父对话完全相同的缓存安全参数,以确保能够命中父对话的缓存。Claude Code 从第一天起就围绕提示词缓存构建,如果你正在构建智能体,也应该这样做。

 

原文作者:从ClaudeCode看如何Harness智能体(二)

本文作者:ZKCOI

文章名称:从ClaudeCode看如何Harness智能体(二)

文章链接:https://www.zkcoi.com/365up/ai-agent/4531.html

本站资源仅供个人学习和交流,如若转载,请注明出处,详见《免责声明》

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
ZKCOIZKCOI
从ClaudeCode看如何Harness智能体(一)
上一篇 2026年4月3日 下午4:59
从ClaudeCode看如何Harness智能体(三)
下一篇 2026年4月3日 下午5:07

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系博主

立即联系
一般有空就回复

qrcode_web

微信扫码联系我

insert_link 友情链接
分享本页
返回顶部