Token 用量分析
OpenClaw 为什么消耗那么多 token?优化空间在哪里?
每次 LLM 调用的 token 预算
每次 LLM 调用发送的内容:
默认上下文窗口:200,000 tokens(src/agents/defaults.ts:6 — DEFAULT_CONTEXT_TOKENS = 200_000)。
Token 消耗来源排名
1. 会话历史(最大且持续增长)
源码:~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl
每一轮(用户消息 + 助手回复 + 工具调用 + 工具结果)都追加到 JSONL 转录文件。下一轮时,整个历史被加载并发送给 LLM。
典型对话中:
- 每次工具调用来回约 ~500-2000 tokens(工具输入 + 输出)
- Agent 每条用户消息可能做 5-10 次工具调用
- 10 条用户消息带工具使用后,历史可轻松达到 50,000-100,000 tokens
压缩触发条件(src/agents/compaction.ts):
- 当历史超过
contextWindow - reserveTokensFloor时,触发压缩 - Reserve 值可通过
agents.defaults.compaction.reserveTokensFloor配置 - 压缩将历史分成多个块(默认 2 部分),逐个总结,再合并摘要
- 总结开销:预留 4,096 tokens(
SUMMARIZATION_OVERHEAD_TOKENS) - 使用 1.2x 安全余量,因为
estimateTokens()用 chars/4 启发式算法会低估
关键洞察:在压缩触发之前,每轮都要支付完整历史的代价。一个 20 轮带工具使用的对话,轻松每次调用烧掉 100k+ 输入 tokens。
2. System prompt(约 2,000-5,000 tokens,每次调用固定)
源码:src/agents/system-prompt.ts — buildAgentSystemPrompt()
System prompt 包含以下章节(按顺序):
System prompt 总量:约 2,000 tokens(最小)到 5,000+ tokens(完整模式含 skills + 上下文文件)。
子 Agent 优化(PromptMode):
"full":所有章节(约 5,000 tokens)"minimal":精简章节 — 跳过记忆、消息、语音、回复标签、静默回复、心跳(约 1,500 tokens)"none":仅身份行(约 15 tokens)
3. Skills 目录(约 1,000-5,000 tokens,每次调用固定)
源码:src/agents/skills/workspace.ts
Skills 从三个来源加载:
- 内置 skills(
skills/)— 52 个 - 工作空间 skills(
~/.openclaw/agents/<id>/workspace/skills/) - 插件 skills(来自 extensions)
硬限制(workspace.ts:95-98):
Skills prompt 不包含完整的 SKILL.md 内容。它包含的是一个目录:每个 skill 的名称 + 描述 + 文件位置。Agent 只有在选中某个 skill 后才会读取完整的 SKILL.md。
已有优化:compactSkillPaths() 将 home 目录路径替换为 ~,共节省约 400-600 tokens(源码注释:"Saves ~5-6 tokens per skill path × N skills")。
但是:52+ 个 skill 每个约 20-50 tokens(名称 + 描述 + 路径),仅目录就有约 1,500-3,000 tokens。
4. Tool 定义(约 2,000-4,000 tokens,每次调用固定)
源码:伴随每次 LLM 调用发送的工具 JSON schema
Pi Agent SDK 将工具定义作为 JSON schema 对象发送。每个工具包含:
- 名称
- 描述
- 参数 schema(properties、types、descriptions、required 字段)
24 个核心工具 + 插件工具 + skill 工具,典型总量约 2,000-4,000 tokens 的 JSON schema。
待确认:system prompt 中的工具摘要和 API 调用中的工具定义之间是否有重复。这可能是一个冗余点。
5. Memory/RAG 上下文(每轮约 500-2,000 tokens)
源码:src/memory/manager.ts
记忆搜索结果被注入对话中。混合搜索(BM25 + 向量 + MMR 重排序)从 SQLite 数据库返回最佳匹配。
待确认:注入的 chunk 数量上限和每次注入的最大 token 数。需要更深入阅读 src/memory/manager-search.ts。
6. 上下文文件(0-5,000+ tokens,每次调用固定)
源码:buildAgentSystemPrompt() 第 588-608 行
用户可编辑文件(如 SOUL.md、RULES.md、工作空间文档)被原样加载注入 system prompt 的"Project Context"下。没有截断 — 完整文件内容都包含在内。
一个较大的 SOUL.md 或工作空间文档可以轻松增加 2,000-5,000 tokens 每次调用。
7. 心跳开销(每次心跳约完整上下文)
源码:src/cron/service/timer.ts
默认:当 cron.heartbeat.enabled: true 时每 600 秒(10 分钟)一次。
每次心跳发送完整 system prompt + 会话历史来做一次 LLM 调用,只是为了得到"HEARTBEAT_OK"的回复。如果会话历史很长,这很昂贵。
成本估算:200k 上下文 × $3/M 输入 tokens(Claude)× 6 次/小时 × 24h = 约 $86/天(上下文满载时)。实际会话通常更短,但心跳成本随会话大小线性增长。
汇总:token 去了哪里
30 轮重度工具使用后:轻松达到 100,000-150,000 tokens 每次调用。
优化机会
高影响
中等影响
低影响(已优化)
贡献者关键文件
我的认知盲区
- Tool JSON schema 的精确 token 数 — 需要序列化后测量
- Memory 注入限制(
maxChunks、token 上限)— 需要更深入阅读manager-search.ts - 是否在使用 prompt caching(Anthropic)— 这会大幅降低固定 system prompt 的实际成本
- Pi Agent SDK 中
estimateTokens()的实现方式 — 提到了 chars/4 启发式但精度未知 - 跨轮次是否有重复工具调用的批处理或缓存