预算耗尽前,TaoToken 该触发哪类 Agent 压缩
1. 预算烧穿那一刻压缩不是“省字”而是调度决策我在一个跑 40 分钟以上的 Agent 长任务里踩过一个很典型的坑第 27 步模型开始重复确认已经定下来的目录结构第 31 步把两轮前已经否掉的方案重新提出来第 35 步直接抛context_length_exceeded。这不是模型变笨是 harness 层缺少预算检查——它只知道往后追加消息不知道在哪个水位该卸载、在哪个水位该压缩、压缩一次又要花掉多少 Token。后来我把长任务的调用入口统一收拢到 TaoTokenhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_intro用同一个 Key 在 https://taotoken.net/api 上出请求才终于能把“预算检查”“压缩调用”“上下文回灌”三件事的 Token 账对齐到同一张表里。这篇就按 Agent 调度工程师的视角把长任务里四类上下文机制——预算与卸载、压缩、todo-state 复述、跨会话记忆——的触发阈值、Token 开销和切换配置拆开讲清楚最后给一份可以直接抄的阈值配置和不同压缩类型的 Token 对照。先说结论压缩不是等到报错才做的补救它是一次有成本的调度动作。压缩调用本身要读一遍剩下的上下文、再写出一份更短的内容这两头都在烧 Token。如果你的预算检查只盯着“当前上下文多长”不盯着“压缩这次要花多少”那大概率会出现一个尴尬场面——为了省 Token 而触发的压缩直接把剩余预算吃掉三分之一。所以本文所有阈值都围绕一个前提设计预算检查与压缩调用也是 Token 消耗方必须单独建账。2. 先把账本立起来预算检查本身要花多少 Token很多同学写 harness 的时候预算检查是这么写的每一步都把完整消息列表丢给模型问它“现在大概占了多少 Token”。这个做法在长任务里是自杀式的因为你每次都在为“统计”付一次全量上下文的价格。正确的拆分是三层第一层是本地估算。用对应模型的 tokenizer 或者社区近似算法对当前 transcript 做一次离线计数误差控制在 5% 以内就够用。这一步零 API 成本。第二层是周期性校准。每 N 步我一般设 3 步拿一次真实用量和本地估算对齐修正系数。真实用量可以从流式响应里累计input_tokens/output_tokens得到。第三层是压缩预算预留。在总预算里切出一块固定的“压缩专用额度”跟主任务额度分开记账。这样即使压缩调用比预期贵也不会把主任务的推理预算吃穿。账本字段建议至少包含这些{ window_tokens: 200000, reserve_output_tokens: 16000, compact_budget_tokens: 12000, compact_spent_tokens: 0, estimate_ratio: 1.0, last_calibrate_step: 0, calibrate_every_steps: 3 }window_tokens是模型窗口reserve_output_tokens是留给最终输出的compact_budget_tokens是这块任务允许在压缩上花掉的总额度。真正参与水位判断的可用额度是usable window_tokens - reserve_output_tokens ratio used_tokens / usable注意used_tokens里要包含 tool 返回的原始内容。Agent 长任务里最容易被忽略的一类膨胀源就是工具输出——一次目录树、一次全量日志、一次 diff都能瞬间把上下文推高几万 Token。这些内容不该长期驻留在 transcript 里它们属于“卸载”的范畴不是“压缩”的范畴。3. 四类压缩的触发阈值怎么定这一步是全文的核心。四类机制对应四类不同的问题触发水位也不同混着用就会出现“明明压缩了却还是丢失目标”的情况。第一类上下文预算与卸载offload。解决的是“工具原始输出太长”。做法是把大块内容写到外部存储本地文件、对象存储、记忆库transcript 里只留一个指针加一句摘要。触发水位我建议放在 0.55——也就是可用额度用掉一半多的时候就开始卸载而不是等满了再卸。卸载的成本几乎为零越早做越好。第二类压缩compaction。解决的是“历史对话太啰嗦”。把前 N 轮的消息合并成一份结构化摘要。触发水位放 0.70。这里有个关键点压缩一定要产出结构化内容不要产出自由散文。自由散文的摘要里模型最容易丢的就是“已经否决的方案”和“硬约束”而这两样恰恰是长任务跑偏的主要来源。结构化摘要的 schema 建议固定为五段goal: 当前任务的最终目标一句话 constraints: 不可违反的硬约束列表 decisions: 已确认的决策 被否决的方案及原因 artifacts: 已产出的文件路径 / 函数名 / 接口签名 open: 未决问题和下一步待办第三类todo-state 复述restate。解决的是“目标漂移”。做法是不做内容压缩而是周期性把当前目标、已完成项、待办项、验收条件重新回灌到上下文尾部。触发水位放 0.75并且每 K 步强制执行一次不管水位。我一般 K 取 8。复述内容的 Token 非常小通常几百个但对缓解目标丢失的效果立竿见影。第四类跨会话记忆memory。解决的是“任务跨进程 / 跨天重启后从零开始”。做法是把长尾事实写入外部记忆库需要时按查询召回。写入触发水位 0.55和卸载同步做召回触发水位 0.75。把四类放在一张表里对照机制解决的问题触发水位回灌 Token约单次调用开销卸载 offload工具输出膨胀0.5550~200接近 0本地写入压缩 compact历史对话啰嗦0.701.5k~3k读全量 写摘要todo 复述目标漂移0.75 / 每 8 步300~600接近 0本地拼装记忆召回跨会话断点0.75300~800单次检索 注入硬停 hard stop兜底0.88—中止并落盘再给一份不同压缩类型的 Token 对照。测试场景是一个已经跑到约 120k Token 的编码类长任务输出预留 16k可用额度 184k压缩类型回灌 Token约压缩调用开销约保留度适用水位不压缩全量回灌120k0100%仅作基线自由文本摘要2.5k~4k读 120k / 写 3k中约束易丢0.70结构化摘要1.2k~2k读 120k / 写 1.5k高0.70todo-state 复述300~600读 120k / 写 0.4k目标保真细节不保0.75卸载 指针50~200本地写入内容可回溯0.55记忆写入 按需召回300~800写 1k / 召回 0.6k长尾事实可检索0.55 写入 / 0.75 召回这张表的意义在于压缩类型不同Token 量级差一个数量级。用自由文本摘要去处理工具输出等于花高价买了一个还不一定记得住的结果用卸载去处理历史决策又等于什么都没压缩因为决策本来就不该被卸载掉。阈值配置可以直接落成一份 JSON{ budget: { window_tokens: 200000, reserve_output_tokens: 16000, compact_budget_tokens: 12000 }, triggers: { offload_ratio: 0.55, compact_ratio: 0.70, restate_ratio: 0.75, memory_recall_ratio: 0.75, hard_stop_ratio: 0.88, restate_every_steps: 8 }, compact_schema: [ goal, constraints, decisions, artifacts, open ] }4. 把 Base URL 切到 TaoTokenClaude Code / Codex / CC Switch阈值配好之后下一步是让压缩调用真的走同一条链路。如果你在同一个长任务里混用了多个供应商压缩调用的 Token 就不在同一个账本上前面做的预算对齐全白费。先去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_key 拿到 Key然后在 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_keys 创建一把长期 Key。后面所有配置里的YOUR_API_KEY都换成它。Claude Code用 settings.json 或 ANTHROPIC_环境变量。*~/.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: claude-sonnet-4-5, ANTHROPIC_SMALL_FAST_MODEL: claude-haiku-4-5 } }如果你更喜欢 shell 层注入export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODELclaude-sonnet-4-5 export ANTHROPIC_SMALL_FAST_MODELclaude-haiku-4-5ANTHROPIC_SMALL_FAST_MODEL建议单独指向一个便宜的小模型。todo-state 复述、轻量分类、摘要拼接这类低智力密度调用可以走它能明显压低压缩总开销。Codex用 config.toml不要套 ANTHROPIC_*。~/.codex/config.tomlmodel gpt-5-codex model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api responsesKey 单独放环境变量不要写进 tomlexport TAOTOKEN_API_KEYYOUR_API_KEY这里强调一遍Claude Code 用ANTHROPIC_*Codex 用config.tomlenv_key两套体系不能互相套用。把ANTHROPIC_BASE_URL写到 Codex 的配置里或者反过来只会得到一堆 401 和连接超时。CC Switch记住三件套 供应商名 Base URL API Key。CC Switch 在切换 Claude Code 供应商时实际改的就是三个字段供应商名: taotoken Base URL: https://taotoken.net/api API Key: YOUR_API_KEY切完之后一定做两件事一是重启终端里已有的 Claude Code 会话环境变量是进程级缓存的二是跑一次/status或等价命令确认当前生效的 Base URL。多供应商并存时最容易出的问题就是“配置改了但没生效”长任务跑到一半才发现压缩调用打到了旧地址。切换到 TaoToken 之后建议把官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_switch 上的模型列表和文档对照一遍确认你要用的压缩模型和目标模型都在可用范围内。5. 压缩后目标丢失的排障顺序压缩触发之后最常见的三类异常和对应的排查顺序如下。症状一压缩后立刻重复之前的动作。大概率是结构化摘要里decisions字段丢了。检查方法把压缩产出的摘要单独打印出来数一下里面有没有“被否决的方案”。如果只有结论没有过程模型就会重新走一遍已经被否掉的路。修法是让压缩调用产出的 schema 强制包含decisions并且要求每条决策后面必须跟一句否决理由。症状二压缩后整体跑偏开始做任务目标之外的事。这是 todo-state 没有复述的典型表现。压缩只保留内容不保留“我们现在在干什么”。修法是把复述频率提高到每 5 步一次并且把goal字段放在 transcript 最末尾——模型对上下文尾部的注意力最强。症状三压缩调用本身就超预算。表现为压缩之后剩余额度比压缩之前还紧张。原因是压缩调用没有被单独记账。修法是把compact_budget_tokens从主预算里切出来压缩调用前先检查这块额度还剩多少超了就直接降级到 todo-state 复述不做全量摘要。症状四跨会话恢复后重新探索目录结构。说明记忆写入做了但召回没做或者召回索引对不上。检查方法看恢复后的第一轮请求里有没有注入记忆召回结果。修法是给记忆库加一个按artifact path的索引恢复时用任务名做一次定向召回而不是泛召回。症状五直接抛context_length_exceeded。如果阈值都配了还报这个错通常是本地估算系数偏了。把estimate_ratio往上调 0.05 再跑一轮或者把calibrate_every_steps从 3 改到 1先确认估算和真实用量对齐。排障的时候有一个通用原则先看压缩产出的内容再看触发水位最后看配置链路。绝大多数“压缩没用”的案例问题都出在压缩产物质量上而不是阈值上。6. 把压缩闸门接进 harness 主循环阈值配置和供应商配置都准备好之后最后一步是把它接进主循环。下面是一个本地执行的守卫函数示例逻辑是“先校准、再判断、再决定动作、最后记账”BUDGET { window_tokens: 200_000, reserve_output_tokens: 16_000, compact_budget_tokens: 12_000, compact_spent_tokens: 0, calibrate_every_steps: 3, restate_every_steps: 8, } def guard(used_tokens: int, step: int, compact_cost_estimate: int): usable BUDGET[window_tokens] - BUDGET[reserve_output_tokens] ratio used_tokens / usable remain_compact BUDGET[compact_budget_tokens] - BUDGET[compact_spent_tokens] # 硬停兜底 if ratio 0.88: return {action: hard_stop, reason: budget_exhausted} # 目标复述低水位也按步数强制触发 if ratio 0.75 or step % BUDGET[restate_every_steps] 0: return {action: restate_todo} # 压缩额度不够就降级 if ratio 0.70: if remain_compact compact_cost_estimate: BUDGET[compact_spent_tokens] compact_cost_estimate return {action: compact_structured} return {action: restate_todo, reason: compact_budget_low} # 卸载最早触发成本最低 if ratio 0.55: return {action: offload_tool_output} return {action: continue}几个实现细节值得留意。第一compact_cost_estimate要按“输入全量 输出摘要”两头估只估输出会低估三到五倍。第二restate_todo的触发条件用了or意味着它既受水位驱动也受步数驱动。这个设计是为了防止一种情况任务在低水位区反复小步前进走了 30 步都没触发复述目标早就飘了。第三compact_budget_low的降级路径一定要有。压缩额度用完时退回到 todo-state 复述比强行压缩安全得多——前者最多丢细节后者可能把上下文挤出窗口。第四所有触发动作都要落日志。长任务出问题时日志里的触发序列比任何事后分析都直接。接好之后跑一个长任务验证一下。观察指标是三个压缩触发次数、压缩累计 Token、任务最终是否命中目标。理想状态下压缩触发 2 到 4 次累计压缩 Token 控制在总预算的 8% 以内任务目标不变。7. 一条可复现的验证路径把上面所有内容串起来一条完整的验证路径是第一步在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_flow 拿 Key在 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_flow_key 建一把长期 Key。第二步按第 4 节把 Claude Code 或 Codex 的 Base URL 统一指向 https://taotoken.net/api确认切换生效。第三步把第 3 节的阈值 JSON 落进 harness 配置把第 6 节的守卫函数接进主循环。第四步跑一个至少 20 步的长任务记录每次触发的动作类型和当步 Token 用量。第五步对照第 3 节的 Token 对照表看实际压缩开销落在预期区间没有。偏差超过一倍先查估算系数再查压缩 schema 是不是退化成自由文本了。第六步做一次跨会话恢复验证记忆召回是否生效。如果你还没决定用哪个模型来跑压缩调用可以先到 https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_chat 对比一下各模型在长上下文场景下的表现再决定主模型和小模型的搭配。长任务跑得多的话https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_plan 里的 Coding Plan 可以直接覆盖编码类工作流配合上面的阈值配置用起来更省心Claude Code 侧的具体接入细节在 https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentbudget_compact_doc 有完整说明。回到最开始那个坑Agent 长任务真正难的不是模型能力而是调度层愿不愿意把 Token 当成一种需要精算的资源。预算检查、卸载、压缩、todo-state 复述、跨会话记忆这五件事分开看都不复杂难的是把它们放进同一本账里并且在正确的水位触发正确的动作。阈值配错一次你可能要多花三倍预算才能跑完同一件事。