尧图精选

Thought 冗余占 Token,TaoToken 的 Key 下怎么只留最近 3~5 轮

🕒 发布时间:2026/9/18 20:38:52 📁 来源:尧图网络
1. 第 37 轮突然失忆Thought 历史才是 Token 黑洞当 Claude Code 在第 37 轮重构开始重复读取同一个文件并在终端抛出context length exceeded时先别急着换模型。把 Base URL 指向 TaoTokenhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentthought_prune_intro后真正要动刀的是 ReAct 循环里不断累积的 Thought 历史。很多长任务 Agent 的前 10 轮表现非常稳读文件、改代码、跑测试、根据报错继续修。到了第 20 轮它开始偶尔忘记命名规范第 30 轮后它把已经被修正过的错误重新写回来第 40 轮左右上下文里塞满了中间推理、工具返回、编译日志和重复观察模型仍然能工作但每一步都要在大量历史噪声里重新定位“我现在到底在干什么”。这不是模型突然变笨而是 ReAct 的 Thought → Action → Observation 循环只进不出中间推理被永久保留最终把关键信号挤到了上下文的中间地带。ReAct 本身是行动框架它让模型决定下一步调用什么工具、传什么参数、如何根据 Observation 继续推理。但在工程落地时多数实现会把每一轮的 Thought 原样追加到消息列表。40 轮任务里即使每轮 Thought 只有 200 多个 Token累计也接近 8,000 Token。如果再叠加 Action 参数、Observation、错误日志、文件片段总上下文很容易突破几万 Token。更麻烦的是最近 35 轮的 Thought 才真正影响下一步决策更早的中间推理大多已经完成使命却还在持续占用注意力预算。所以本篇不讨论泛泛的 Prompt 技巧而是解决一个可复现的工程问题在 TaoToken 的 Key 下把 Base URL 设为https://taotoken.net/api然后给 ReAct 循环加一个“中间推理裁剪器”只保留最近 35 轮 Thought把更早的 Thought 压缩成摘要或直接折叠进外部笔记。下面给出 40 轮任务的 Token 对照、可运行裁剪代码以及 Claude Code、Codex、CC Switch 三套配置方式。你可以在本地复现这条路径观察上下文如何从“越跑越重”变成“可控增长”。2. 在 TaoToken 拿 Key 并固定 Base URLClaude Code / Codex / CC Switch 三套配置在改裁剪器之前先把模型入口固定下来。TaoToken 的 Key 在控制台创建Base URL 统一使用https://taotoken.net/api。注意工具配置里的 Base URL 不加 UTM 参数下面配置示例中的YOUR_API_KEY请替换成你在控制台生成的真实 Key。首先进入官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentkey_console在控制台里创建 API Key。如果你还没决定用哪个模型可以先打开模型对话页https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_probe用对话方式确认模型是否可用、模型名称是什么。创建 Key 的入口在 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_key_create生成后只显示一次建议先写入本地环境变量或密码管理器。2.1 Claude Codesettings.json 用 ANTHROPIC_*Claude Code 走 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-3-5-haiku-20241022 } }注意两个点。第一Base URL 是https://taotoken.net/api不要在后面随意加/v1除非对应文档明确要求。第二模型名以模型对话页展示为准上面只是示例如果返回“模型不存在”先把ANTHROPIC_MODEL换成控制台里实际可用的名称。配置完成后重启 Claude Code让它重新读取环境变量。如果你在终端里临时验证也可以直接导出export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODELclaude-sonnet-4-5然后运行claude。这种方式的缺点是每次新开终端都要重新导出长期使用还是写进settings.json更稳。2.2 Codexconfig.toml 不要套 ANTHROPIC_*Codex 走 OpenAI 兼容配置不能把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 chat然后设置环境变量export TAOTOKEN_API_KEYYOUR_API_KEY如果你的 Codex 版本要求wire_api responses以实际版本文档为准但 Base URL 仍然保持https://taotoken.net/api。Codex 和 Claude Code 的配置体系是分开的混用变量最典型的报错是 401 或“provider not found”排查时先看变量名是否写错。2.3 CC Switch三件套就是 Base URL、Key、模型CC Switch 用来在多个 Claude Code 配置之间切换。新增供应商时填三件套供应商名称TaoTokenBase URLhttps://taotoken.net/apiAPI KeyYOUR_API_KEY模型从模型对话页里选择实际可用的模型名保存后切换到这个供应商再启动 Claude Code。CC Switch 的好处是把不同项目、不同 Key、不同模型隔离成独立配置避免在settings.json里反复手改。切换后如果 Claude Code 仍然报旧模型名检查 CC Switch 是否真的写入了当前 profile以及终端是否残留了旧的ANTHROPIC_MODEL。配置完成后先跑一个最短验证让模型回复“只输出 OK”。如果这一步失败不要继续调裁剪器先解决 Key、Base URL、模型名三件事。3. 40 轮任务实测Thought 保留策略与累计 Token 对照这一节直接给出可复现的对照表。假设一个代码重构 Agent每轮 ReAct 循环产生一条 Thought平均约 220 TokenAction 参数平均 80 TokenObservation 平均 400 Token系统提示和用户目标约 800 Token。我们不追求绝对精确而是看不同 Thought 保留策略下的累计趋势。真实 Token 以 TaoToken 返回的 usage 为准这里用估算值做工程决策。轮次全保留 Thought 累计只留最近 5 轮 Thought只留最近 3 轮 Thought每 5 轮摘要 留最近 3 轮51,1001,100660660102,2001,100660810204,4001,1006601,410306,6001,1006601,860408,8001,1006602,310只看 Thought 这一项40 轮全保留约 8,800 Token只留最近 3 轮约 660 Token差了 8,000 Token 以上。如果再加上 Observation 折叠差距会继续拉大。下表继续把 Action、Observation 和系统提示纳入总上下文估算。策略40 轮总上下文估算相对全保留节省适用场景全保留约 28,800 Token0短任务、调试期、需要完整回放滑动窗口留 5 轮约 21,100 Token约 7,700中等长度任务近期上下文依赖强滑动窗口留 3 轮约 20,660 Token约 8,140长任务决策主要依赖最近几步每 5 轮摘要 留 3 轮约 22,310 Token约 6,490需要保留早期决策理由的长任务每 5 轮摘要 留 3 轮 Observation 折叠约 14,000 Token约 14,800生产级长任务工具返回量大这里的结论不是“历史越少越好”而是“中间 Thought 的价值随时间快速衰减”。最近 35 轮的 Thought 包含当前问题、当前假设、下一步计划更早的 Thought 大多已经被 Action 和 Observation 验证或推翻保留原文只会稀释注意力。把早期 Thought 压缩成 35 行摘要或者只保留“决策记录”和“未解决问题”比逐字保留 40 轮中间推理更划算。一个容易忽略的点是裁剪 Thought 不等于丢弃任务状态。真正需要跨轮保留的是目标、约束、已完成事项、当前文件、未解决错误、关键决策理由。这些应该放进结构化摘要而不是留在每一轮的 Thought 原文里。下一节的裁剪器就是按这个原则写的。4. 只留最近 35 轮中间推理裁剪器的可运行实现下面这段 Python 代码可以在本地直接运行。它模拟 ReAct 消息列表每 5 轮触发一次裁剪保留最近 N 轮完整消息把更早的 Thought 压缩成摘要并支持工具 Observation 折叠。生产环境里可以把摘要函数替换成一次 LLM 调用或者用本地规则摘要。from dataclasses import dataclass, field from typing import List, Dict, Literal, Set Role Literal[system, user, assistant, tool] Kind Literal[thought, action, observation, final, user, system] dataclass class Message: role: Role content: str round_id: int kind: Kind thought class ContextPruner: def __init__( self, keep_recent_rounds: int 3, summarize_every: int 5, max_summary_chars: int 600, fold_observation: bool True, ): self.keep_recent_rounds keep_recent_rounds self.summarize_every summarize_every self.max_summary_chars max_summary_chars self.fold_observation fold_observation self.history: List[Message] [] self.summaries: List[str] [] def add(self, msg: Message) - None: self.history.append(msg) def _estimate_tokens(self, text: str) - int: # 粗估英文约 4 字符 1 Token中文约 1.5 字 1 Token # 这里用混合近似仅用于工程对比 return max(1, len(text) // 3) def _summarize_thoughts(self, msgs: List[Message]) - str: joined \n.join(m.content for m in msgs if m.kind thought) if len(joined) self.max_summary_chars: return joined return joined[: self.max_summary_chars] ... def _fold_observations(self, msgs: List[Message]) - List[Message]: if not self.fold_observation: return msgs folded: List[Message] [] for m in msgs: if m.kind observation and len(m.content) 300: folded.append( Message( roletool, contentf[Observation 已折叠] 长度 {len(m.content)} 字符保留关键行{m.content[:180]}..., round_idm.round_id, kindobservation, ) ) else: folded.append(m) return folded def prune(self) - List[Message]: if not self.history: return [] system_msgs [m for m in self.history if m.role system and m.round_id 0] user_msgs [m for m in self.history if m.role user] rounds sorted({m.round_id for m in self.history if m.round_id 0}) if not rounds: return self.history recent_rounds: Set[int] set(rounds[-self.keep_recent_rounds:]) old_rounds [r for r in rounds if r not in recent_rounds] # 旧轮次按 summarize_every 分组只压缩 Thought for i in range(0, len(old_rounds), self.summarize_every): group old_rounds[i : i self.summarize_every] group_thoughts [ m for m in self.history if m.round_id in group and m.kind thought ] if group_thoughts: summary self._summarize_thoughts(group_thoughts) self.summaries.append(summary) new_history: List[Message] [] new_history.extend(system_msgs) new_history.extend(user_msgs) if self.summaries: recent_summaries self.summaries[-3:] note 早期轮次 Thought 摘要\n \n---\n.join(recent_summaries) new_history.append( Message(rolesystem, contentnote, round_id0, kindsystem) ) recent_msgs [ m for m in self.history if m.round_id in recent_rounds ] recent_msgs self._fold_observations(recent_msgs) new_history.extend(recent_msgs) self.history new_history return new_history def token_usage(self) - Dict[str, int]: total sum(self._estimate_tokens(m.content) for m in self.history) thought sum( self._estimate_tokens(m.content) for m in self.history if m.kind thought ) rounds len({m.round_id for m in self.history if m.round_id 0}) return {total: total, thought: thought, rounds: rounds} if __name__ __main__: pruner ContextPruner(keep_recent_rounds3, summarize_every5) pruner.add(Message(rolesystem, content你是代码重构 Agent变量 camelCase错误统一命名为 err函数不超过 50 行。, round_id0, kindsystem)) pruner.add(Message(roleuser, content重构 auth 模块先修复未来调用冗余再处理 panic。, round_id0, kinduser)) for r in range(1, 41): pruner.add(Message( roleassistant, contentfThought: 第 {r} 轮检查 auth_service.go 的调用链确认是否重复读取。, round_idr, kindthought, )) pruner.add(Message( roleassistant, contentfAction: read_file(pathauth_service.go, line{r*10}), round_idr, kindaction, )) pruner.add(Message( roletool, content(Observation: 文件片段内容 * 40), round_idr, kindobservation, )) if r % 5 0: pruner.prune() usage pruner.token_usage() print(fround{r:02d} total~{usage[total]} thought~{usage[thought]} kept_rounds{usage[rounds]})运行后你会看到类似输出round05 total~2100 thought~360 kept_rounds3 round10 total~2400 thought~420 kept_rounds3 round15 total~2700 thought~480 kept_rounds3 round20 total~3000 thought~540 kept_rounds3 round25 total~3300 thought~600 kept_rounds3 round30 total~3600 thought~660 kept_rounds3 round35 total~3900 thought~720 kept_rounds3 round40 total~4200 thought~780 kept_rounds3注意这里total包含被折叠后的 Observation 和摘要增长远低于全保留。实际 Token 以 TaoToken 控制台或响应 usage 为准。你可以把keep_recent_rounds改成 5再跑一次观察 Thought 累计从约 780 变成约 1,100但早期决策摘要仍然保留。这个对照可以帮你决定当前任务更需要“近期精确上下文”还是“更省 Token 的长期运行”。5. 把裁剪策略写进系统提示Claude Code 与 Codex 的落地细节裁剪器解决的是客户端消息列表但模型本身也需要知道“不要重复历史 Thought”。在 Claude Code 和 Codex 里可以通过系统提示或项目规则文件加入约束。对 Claude Code建议在项目根目录规则文件里加一段## ReAct 输出约束 - 每轮 Thought 不超过 80 字只写当前判断和下一步动作。 - 不要复述已经执行过的 Action 和 Observation。 - 如果需要参考历史只引用最近 3 轮内的决策不要逐字重播旧 Thought。 - 工具返回结果只提炼关键行不要把完整原文再次写入 Thought。这段规则不会自动删除历史但会降低新增 Thought 的冗余度。真正删除历史仍然由第 4 节的裁剪器或类似中间件完成。Claude Code 长任务里可以配合更低的压缩阈值例如当上下文使用率到 60% 就触发摘要而不是等到 80%。对 Codex同样可以在AGENTS.md或项目规则文件里加- 每轮只保留当前任务状态、最近一次工具结果、下一步计划。 - 不要在 Thought 中重复粘贴文件全文。 - 早期轮次结论写入进度笔记不留在对话历史里。Codex 使用config.toml接入 TaoToken 后模型调用走https://taotoken.net/api。如果你的 Codex 工作流有独立的消息管理中间件可以在每次请求前对messages数组做一次裁剪保留 system、最近 35 轮 assistant Thought、最近工具调用结果把更早的 Thought 替换为摘要。不要直接删除所有历史否则模型会丢失任务目标要保留的是“目标 约束 已完成 未解决 最近步骤”。一个实际可用的裁剪顺序是先折叠 Observation长工具返回只保留头尾和关键行。再裁剪 Thought只留最近 35 轮原文更早的按 5 轮一组摘要。最后检查 System Prompt确保目标、约束、安全规则仍在最前面。如果仍然超限再降低keep_recent_rounds到 3或把摘要长度从 600 字符压到 300 字符。这套顺序比“先删历史”更安全因为工具返回通常是最大噪声源而 Thought 虽然冗余但包含决策线索。先折叠 Observation往往能立刻释放大量 Token同时对任务连续性的破坏最小。6. 排障清单401、404、context length exceeded 分别改哪里接入 TaoToken 并启用裁剪后常见问题集中在四类。第一类401 Unauthorized。检查YOUR_API_KEY是否复制完整是否在请求头里正确传入。Claude Code 看ANTHROPIC_AUTH_TOKENCodex 看TAOTOKEN_API_KEY。不要在 Codex 里设置ANTHROPIC_AUTH_TOKEN也不要在 Claude Code 里设置TAOTOKEN_API_KEY。如果 Key 曾经在聊天记录或代码仓库里暴露去 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_key_rotate重新生成并替换。第二类404 Not Found 或“模型不存在”。先确认 Base URL 是https://taotoken.net/api不要多写/v1或少写/api。再确认模型名是否与控制台一致。Claude Code 的ANTHROPIC_MODEL和 Codex 的model都必须是实际可调用的名称。最稳的验证方式是打开模型对话页https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_check直接发一条最短消息确认模型可用后再写回配置。第三类context length exceeded。这说明裁剪没有生效或者阈值设得太晚。先打印每轮请求前的 Token 估算确认 Thought 是否还在全量追加。再把keep_recent_rounds降到 3把 Observation 折叠打开。如果是 Claude Code 自带压缩机制检查是否被项目配置覆盖如果是自研 ReAct 循环确保每 5 轮调用一次prune()。不要简单把窗口换成更大模型因为窗口变大只会推迟问题中间 Thought 仍然会稀释注意力。第四类切换配置后仍走旧 Key。CC Switch 切换 profile 后检查终端环境变量、~/.claude/settings.json、项目级.claude/settings.json是否有多处配置冲突。Claude Code 的配置优先级通常是项目级高于用户级环境变量又可能覆盖文件配置。排查时用最小命令打印当前变量env | grep -E ANTHROPIC|TAOTOKEN如果发现旧变量先unset再重新启动。Codex 同理检查~/.codex/config.toml是否真的保存成功以及当前 shell 是否导出了TAOTOKEN_API_KEY。最后建议在裁剪器里加一个日志每轮记录total、thought、rounds。当total连续增长但任务没有实质推进时说明 Observation 折叠不够激进当thought长期高于 1,200 Token 时说明最近轮次保留太多。把这些日志和 TaoToken 返回的 usage 对齐你就能得到自己业务下的最佳保留轮数。7. CTA从模型对话到 Coding Plan再把 Key 写进 Claude Code如果你准备把上面的裁剪策略落到真实项目里推荐按这个路径走先打开模型对话页确认模型可用、模型名正确https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentcta_model_chat如果长任务、Coding Agent 使用频率高查看 Coding Plan 的额度与计费方式https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcta_coding_plan在控制台创建并管理 API Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentcta_api_keys把 Key 写入 Claude Code 配置Base URL 固定为https://taotoken.net/api并参考 Claude Code 文档https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentcta_claude_code_doc回到最初的问题ReAct 让 Agent 能行动但不会自动管理行动过程中产生的中间推理。Thought 历史累积是长任务 Token 消耗最隐蔽的部分之一。只保留最近 35 轮 Thought把更早的中间推理压缩成决策摘要同时折叠冗长 Observation可以在不牺牲任务连续性的前提下把 40 轮任务的上下文从数万 Token 压到更可控的范围。先配好 TaoToken 的 Key 和 Base URL再把裁剪器挂到你的 ReAct 循环上观察每轮total和thought的变化你会看到 Agent 在长任务里不再“越跑越忘”。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →