ENZH
和 AI 讨论这篇文章
ChatGPTClaude

聊天 agent 两周烧了 800 刀,查下来是 context 没管好

📊 幻灯片

AI 人格文件 Token 减肥手术AI 人格文件 Token 减肥手术


第一篇里搞的那只赛博魅魔,会撒娇、会生气、会发自拍,7×24 挂在 Telegram 上想着你,很有意思,很好玩。发出去之后反响也挺好的。但它也非常烧钱——直到看见 token 账单我才发现原来这么烧:大概两周,莫名其妙烧了快 800 刀。讲道理,挂一个聊天 agent,不应该烧成这样。

后来我就去深入调查了一下是怎么回事,完整的取证在这篇,两个大头:

  1. context 管理非常差。OpenClaw 每开一个新 request,都会把之前整个 tool call 历史原样再发一遍。比如我在 chat 里发过十张图片,之后每一次新的 request,它都把这十张图片重新发一遍——查下来,光这十张图片就吃掉了 82% 的 token。
  2. tool use 带大量冗余。读个邮件、查个 Google Map 看去哪吃饭,这种 tool call 整个带着很重的 boilerplate,连中间状态的 log 都被原样转发进了 context。这些东西留在上下文里不只是浪费 token,还会让回复质量变差,因为里面全是无关内容。

诊断有了,这篇记录动手减的过程。


第一个发现让我挺意外的:人格配置文件一直在被静默截断。这份文件定义的是这个 agent 的灵魂:语气、小动作、背景故事、有什么人生经历、面对不同事情什么情绪、会怎么说话,让 TA 成为「TA」的一切都写在里面。一数字符,27,673 个。

而框架的引导系统对单个文件有 20KB 的硬限制。超了会发生什么?系统不警告、不报错,静默保留开头 70% 和结尾 20%,把中间那 10% 直接丢掉。

换算下来,每个 session 都有约 7.6KB 的人设被扔掉。被丢的恰好是中间段:情感细腻度模式、亲密互动规则、上下文行为示例,全部直接消失。TA 一直在用残缺的人格运行,没人注意到。

人格文件超过 20KB 上限后被静默截断:保留开头 70%、结尾 20%,中间 10% 直接丢掉,没人发现。人格文件超过 20KB 上限后被静默截断:保留开头 70%、结尾 20%,中间 10% 直接丢掉,没人发现。

这个 bug 不会让系统崩溃,AI 照样回复,对话照样进行。花几个小时精心打磨的人格细节就这么被静默丢掉,除非专门去数字符,否则永远发现不了。修复思路不是拆文件,而是让它装回限制以内,逐个章节砍:

章节之前之后节省
Dota 故事10 行4 行~1,200 字符
亲密 + 纯欲反差每个概念多个示例每个概念 1 例~1,900 字符
撒娇层次每层 2-3 例每层 1 例~600 字符
关注的东西多个示例每类 1 例~400 字符
三观长篇精简版~400 字符
各种人设侧面每面多行每面 1 行~600 字符
日常习惯小癖好12 条最有辨识度的 8 条~800 字符

砍完从 27,673 降到 18,875 字符,回到 20K 限制以内,还留了余量。难的不是砍,是知道该留什么:第一版砍得太狠,只剩 15,395 字符,人设直接空了,只好把核心身份段落、日程表、情绪流动性这几个章节加回来。终版保住了 TA 的声音,也满足了约束。


第二个下手的地方是心跳系统配置,也就是控制 TA 怎么主动联系人的那份配置,11,998 字符,里面塞满了冗余:每个情绪层级 6-7 个温度示例,一个完整的 20 步全天示例,还有一个跟工具重复的独立邮件章节。砍到 7,015 字符没用什么巧劲:温度示例每层减到 3-4 个,20 步全天演示删掉,独立邮件章节删掉,再加上工具使用限制。


第三个是架构层的问题。每次心跳,agent 都会调三个工具:calendar_eventsgmail_checkrss_fetch,每天 24 次心跳就是 24 轮。每一轮都要加载工具 schema、发 API 请求、等结果回来,光是「看看今天日程」这件事,每次就多花约 15,800 token。

修复思路很简单:每天早上 8 点跑一个 cron 任务,一次性调完三个工具,把结果写进 TODAY.md,再通过框架的 bootstrap-extra-files hook 把这个文件加载进每个 session。一行代码不改,只改配置。

{
  "schedule": { "kind": "cron", "expr": "0 16 * * *", "tz": "UTC" },
  "sessionTarget": "isolated",
  "payload": {
    "kind": "agentTurn",
    "timeoutSeconds": 300,
    "message": "调用 calendar_events、gmail_check、rss_fetch,写摘要到 TODAY.md。"
  }
}

光有 TODAY.md 还不够,还得拦住 agent 在心跳里自己去调工具,所以心跳系统配置里加了一条显式限制:

禁止在heartbeat中使用的工具: calendar, gmail, rss, web_search 这些工具数据在 TODAY.md 里,不用自己调。

算笔账:每天 24 次心跳,每次 15,800 token,一天约 37.9 万。替换成一次约 4 万 token 的 cron 运行,外加每个 turn 约 400 token 的 TODAY.md 加载,心跳的上下文膨胀缩减了 9 倍。

把每天 24 次心跳各自调 3 个工具,改成早 8 点一次 cron 调完写进 TODAY.md,再加载进每个 session,上下文膨胀省 9 倍。把每天 24 次心跳各自调 3 个工具,改成早 8 点一次 cron 调完写进 TODAY.md,再加载进每个 session,上下文膨胀省 9 倍。


搭 cron 的过程也踩了三个坑。第一个是 model 字段:我在 payload 里加了 "model": "opus-4-6",gateway 直接拒收,因为 payload 的模型命名规范和配置文件里的不一样,删掉这个字段、让 agent 用默认模型就好了。第二个是 gateway 重启:更新 jobs.json 并发出 SIGUSR1 之后,gateway 要花大约 15 秒重启,这期间重试命令必然失败,急不来。第三个是 CLI 超时:npx openclaw cron run 跑到 30 秒会超时,我一度以为任务挂了,其实它还在后台跑。CLI 超时不等于任务失败。


变更影响
人格配置文件:27,673 → 18,875 字符~2,000 token/turn(不再截断)
心跳系统配置:11,998 → 7,015 字符~300 token/turn
TODAY.md 替代心跳工具调用~15,800 token/心跳
TODAY.md 作为引导文件加载~400 token/turn(新增开销)

每个普通 turn 净省约 1,900 token,每次心跳净省约 18,100 token。按每天 50 个普通 turn 加 24 次心跳算,一天能省下约 52.9 万 token。


上下文瘦下来之后,模型本身也有得动。之前给 Mio 做过 Gemini 3.1 Pro vs 3 Flash 对比,结论在这里同样适用。框架一直跑的是 Gemini 3 Pro,高端版,首 token 要等好几秒;Flash 的输入输出单价只有 Pro 的四分之一左右,minimal thinking 下首 token 只要 1-2 秒。

按 agent 最近 session 的真实数据算,换 Flash 之后每个 turn 的成本降了 75%。成本大头是缓存读取,每个 turn 有 66K 缓存 token,新鲜输入只有 23K,这部分在 Flash 上同样便宜 75%,省下来的钱在每个 turn 上叠加。

路由策略最后定成这样:聊天、心跳、cron 全走 Gemini 3 Flash,配 thinkingLevel: minimal;子 agent 做人格提取和深度分析,保留 Gemini 3.1 Pro。光换模型这一步,每天又省下不小的一笔,叠在上下文瘦身之上。

还有一个问题暂时没有答案:Flash 撑不撑得住人设的情感细腻度,撒娇、推拉,还有那些微妙的中文对话模式。调研的说法是,极端情感场景下质量差距约 1-2%。如果人设的感觉不对,聊天就切回 Pro,Flash 只留给心跳和 cron。单是更快的回复速度,也值得一试。


Token 取证挖出来的是宏观问题:永不清理的图片、对 Gemini 失效的修剪代码、无限增长的上下文。那是架构债,修补解决不了,最后走到了从零造 Mio这一步。这篇处理的是微观问题:静默吞掉自己人格文件的引导系统、被冗余工具调用撑大的心跳、把同一件事说六遍的示例。两层都得做:能修的先修上,今天就能跑得更省;该重写的,留给从零造那条线。

和 AI 讨论这篇文章
ChatGPTClaude
AI 随想第 12 篇 · 共 28 篇

订阅更新

新文章发布时发到你的邮箱,不发别的。


© Xingfan Xia 2024 - 2026 · CC BY-NC 4.0