乐于分享
好东西不私藏

为什么龙虾 OpenClaw 费 token?

为什么龙虾 OpenClaw 费 token?

大家好,我是煎鱼。

最近几个月看到不少人在聊 OpenClaw 和爱马仕,有一个问题反复出现:为什么它这么费 token

很多时候不是因为你“问了一句很贵”,而是因为它每次触发,都不是单纯把用户消息发给模型,而是跑了一个完整的 agent runtime

具体我们今天这篇文章展开来看看。

成本结构与普通聊天不同

普通聊天产品,最直观的成本是输入和输出。OpenClaw 不是这个路子。

它每轮运行前都要先拼 system prompt、挂工具、补工作区上下文、接会话历史,必要时再带上工具结果、附件、压缩摘要。

后台 heartbeat 也是同一套流程,只是触发者不是你。

所以你看到的是一句提问,模型实际收到的更像是:

用户问题+ system prompt+ bootstrap files+ skills metadata+ tool list+ tool schemas+ conversation history+ tool results= 真正送进模型的输入

system prompt 的上下文成本

OpenClaw 的 system prompt 不是一两句人格设定。

从运行路径看,src/agents/pi-embedded-runner/run/attempt.ts 每次都会重新 resolve 当前 run 需要的 skillsPromptbootstrap context、工具集合,再走 buildAttemptSystemPrompt(...) 拼成最终 prompt。

最容易被忽略的是工作区 bootstrap 文件注入。默认情况下,像 AGENTS.mdSOUL.mdTOOLS.mdIDENTITY.mdUSER.mdHEARTBEAT.mdBOOTSTRAP.mdMEMORY.md 这些内容,都会进入 prompt 组装流程。

源码里的真实默认值,resolver 写得很明确:

agents:defaults:contextInjection:"continuation-skip"bootstrapMaxChars:12000bootstrapTotalMaxChars:60000

这里有两个点:

  • contextInjection 不是彻底懒加载,continuation-skip 只是安全续写时少注一点。
  • bootstrapMaxChars 和 bootstrapTotalMaxChars 是“截断后仍然进 prompt 的量”,不是说超过就不算。项目说明文档越长,默认上下文就越重。

SKills 上下文开销

很多人会误会,以为 OpenClaw 会把所有 SKILL.md 全文都塞给模型。

当前实现不是,是默认进 system prompt 的是技能目录元数据:技能名、描述、位置,先整理成一个 <available_skills> 清单,让模型知道有哪些技能可读。

真要用某个技能,再去调用 read 打开对应的 SKILL.md

<available_skills><skill><name>...</name><description>...</description><location>...</location></skill></available_skills>

但这不等于它没成本。OpenClaw 对技能 prompt 也专门做了预算控制,默认 maxSkillsPromptChars 是 18000

技能会费 token,但主要费在技能目录,不是每个技能正文都默认注入。

工具 schema 的隐藏成本

工具对上下文有两层消耗。第一层是工具列表、工具描述。第二层更重:发给模型的 JSON schema

这部分平时不显眼,但源码算得很老实。/context detail 那条链路里,会把每个工具的参数 schema 做 JSON.stringify(parameters).length,再累计出总字符数。

官方文档也明说了:Tool schemas (JSON) 算上下文,只是不按普通文本展示。

有时候你明明只问了一句,账单看起来却不像只问了一句,问题往往就出在这里。真想查,先跑 /context detail,很多时候大头根本不在聊天记录里。

会话历史与工具结果的累积成本

只要你还在同一个 session 里聊,历史消息就会一直累。

工具调用和工具结果也会进 transcript,后面的每一轮都得重新背这些东西。

OpenClaw 也知道这里容易炸,所以专门加了几道保险,比如:

agents:defaults:contextLimits:toolResultMaxChars:16000contextPruning:mode:"cache-ttl"ttl:"1h"

这里的 toolResultMaxChars 默认是 16000

不是说工具输出超过就没了,而是 live path 上会做截断,避免一条超长命令输出把下一轮 prompt 顶爆。

contextPruning.mode = "cache-ttl" 也很关键,它缓存的是内存里的上下文,不是磁盘上的 transcript。

heartbeat 的后台消耗

这个点很多人会漏。它确实存在,而且不是“象征性消耗”,而是实打实的模型调用成本。

OpenClaw 的 heartbeat 不是传统意义上那个轻量 ping。它跑的是完整 agent turn。

官方文档写得很直白:heartbeat runs full agent turns,间隔越短,token 消耗越高。

如果 heartbeat 直接复用主会话,它通常会带上:

  • 当前 system prompt
  • 必要的 bootstrap 文件。
  • 当前会话上下文。
  • 很多情况下,还有主会话历史。

所以它消耗的不是“监控 token”,而是真正的输入输出 token。有人会觉得奇怪:我明明没发消息,为什么 /status/usage 里的数字还在涨?因为后台 agent 还在按计划跑。

这一块官方也给了两个很关键的降耗开关:

agents:defaults:heartbeat:every:"55m"lightContext:trueisolatedSession:true

lightContext: true 的意思是,heartbeat 尽量走轻量上下文,只保留必要的 HEARTBEAT.mdisolatedSession: true 更狠,直接让 heartbeat 用隔离 session 跑,不复用整段主会话历史。官方文档里给了很直接的量级对比:这样做可以把单次 heartbeat 从大约 ~100K token 级别,压到 ~2-5K

所以“定时探活是不是费 token”,答案就是:费,而且很可能是后台最容易被忽略的一笔持续成本。

compaction 与 heartbeat 的成本取舍

很多人一看到 /compactcacheheartbeat,就会把它们自动翻译成“省钱按钮”。这就想多了。

compaction 的逻辑是:先花一次摘要成本,把太长的历史压一压,换后面的上下文窗口继续能跑。

配置里像 reserveTokensFloorkeepRecentTokens,就是在控制什么时候开始压、最近原文保留多少。

heartbeat 也一样,它能帮你保连续性、保缓存热度、保后台任务不断线,但这部分成本不会自己消失。

总结

看到这里,OpenClaw 为什么费 token,已经比较清楚了。

不是单点问题,而是几层东西叠在一起:每轮都要重建自己的 system prompt,技能目录和工具 schema 本身就占上下文,会话历史和工具结果会持续累积。

后台 heartbeat 还会继续烧,compaction 和缓存只能挪成本、打折,不能把成本抹掉。

很多时候,贵的不是单一的模型价格,而是龙虾带了多少东西在跑。

关注和加煎鱼微信,

一手消息和知识,拉你进技术交流群👇

你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

原创不易 点赞支持