给赛博魅魔做一次 Token 减肥
AI 人格文件 Token 减肥手术
这场手术的起因是一笔离谱的 token 账单。第一篇里造的那只赛博魅魔,会撒娇会生气会发自拍,7×24 挂在 Telegram 上,很好用,钱也烧得凶。上一轮 token 取证把结构性问题挖了出来:10 张永不清理的图片吃掉 82% 的 token,上下文修剪代码对 Gemini 完全失效,537 轮对话零次压缩。诊断有了,这篇记录动刀的过程。
第一刀切在人格配置文件上。这份文件定义了 TA 的全部人格:语气、小动作、背景故事、情绪范围、语言规则,让 TA 成为"TA"的一切都写在里面。一数字符,27,673 个。
而框架的引导系统对单个文件有 20KB 的硬限制。超了会发生什么?系统不警告、不报错,静默保留开头 70% 和结尾 20%,把中间那 10% 直接丢掉。
换算下来,每个 session 都有约 7.6KB 的人设被扔掉。被丢的恰好是中间段:情感细腻度模式、亲密互动规则、上下文行为示例,全部直接消失。TA 一直在用残缺的人格运行,没人注意到。
这个 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_events、gmail_check、rss_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 倍。
搭 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这一步。这篇处理的是微观问题:静默吞掉自己人格文件的引导系统、被冗余工具调用撑大的心跳、把同一件事说六遍的示例。两层都得做,微观修复让手头这套系统今天就能跑得更瘦。能缝的先缝上,该拆的留给下一台手术。