
1. 跨轮传递里todo-state 为什么会被重复读跨轮传递 todo-state 时最典型的症状是每一轮都把完整任务清单、已完成项、失败原因和下一步重新塞进 promptClaude Code 或 Codex CLI 的上下文迅速膨胀。TaoToken 的入口可以先固定https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contenttodo_state_intro 先拿 Key再把请求地址设为 https://taotoken.net/api。很多长任务不是模型不会做而是状态传递方式把预算吃掉了越到后面真正重要的约束和下一步被历史复述淹没于是出现目标漂移、重复执行、工具调用来回打转。从 Agent 状态管理开发者的视角看todo-state 跨轮传递的 Token 消耗主要发生在两个地方一是“跨轮读取”也就是每一轮重新读取完整任务状态二是“复述调用”模型被要求把当前进度、已完成项、待办项再输出一遍作为下一轮输入。单轮看起来不多但长任务跑到 20 轮、50 轮后重复读会变成上下文里的固定税。本文不讨论抽象概念直接给出两样可复现产出一份增量传递配置一张重复读 Token 对照表。所有命令由读者在本地执行Key 统一用YOUR_API_KEY占位不要提交到仓库。长任务上下文工程里常见四类手段预算与卸载、压缩、todo-state 复述、跨会话记忆。本文把重点放在第三类但会让它和前三类配合预算决定哪些内容能留在当前轮卸载把大块材料挪到文件或外部记忆压缩把历史变成摘要todo-state 只传差量跨会话记忆只按需召回。这样做的目标不是让上下文“更短”这么简单而是让每一轮都清楚目标是什么、约束是什么、当前版本是什么、下一步只做什么。2. 接入 TaoToken 前先把 Key、Base URL、模型名分开管理在改任何客户端配置之前先把三件事分开Key、Base URL、模型名。Key 从官网入口获取建议先打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contenttodo_state_access 登录后创建 Key。Base URL 统一写https://taotoken.net/api注意这个地址在工具配置里不要加 UTM 参数UTM 只用于官网入口和文档入口。模型名则按你实际使用的客户端填写不要把一个客户端的模型名硬编码到另一个客户端里。本地建议只保存环境变量名不保存真实 Key。可以先用下面的方式确认当前 shell 里有哪些相关变量输出时把值打码export TAOTOKEN_API_KEYYOUR_API_KEY env | grep -E TAOTOKEN|ANTHROPIC|OPENAI | sed s/.*/***/如果你在团队里共享配置推荐使用 1Password、Vault、CI Secret 或系统钥匙串而不是把 Key 写进settings.json、config.toml、.env后提交。本文示例中的YOUR_API_KEY只是占位符复制后必须替换。接入完成后TaoToken 的价值不在于替你管理 todo-state而在于把请求入口、Key 管理和模型选择统一起来让你可以在客户端侧专注做增量传递、预算控制和对照实验。3. Claude Codesettings.json 与 ANTHROPIC_* 的最小改法Claude Code 侧使用settings.json和ANTHROPIC_*变量。用户级配置通常放在~/.claude/settings.json项目级配置放在项目内的.claude/settings.json。如果两者同时存在要确认优先级避免项目级覆盖用户级后你以为还在用旧配置。最小配置如下{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: claude-sonnet-4-20250514, ANTHROPIC_SMALL_FAST_MODEL: claude-3-5-haiku-20241022 } }如果你更喜欢用 shell 环境变量也可以这样export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODELclaude-sonnet-4-20250514 export ANTHROPIC_SMALL_FAST_MODELclaude-3-5-haiku-20241022改完后重启终端和 Claude Code避免旧进程继续读缓存配置。验证时不要只看“能不能启动”还要看长任务里每轮是否仍然把完整 todo 列表塞进 prompt。如果仍然出现大段重复复述问题通常不在 Base URL而在你的 todo-state 传递协议。Claude Code 的ANTHROPIC_*只用于 Claude Code不要把这些变量复制到 Codex 配置里Codex 使用单独的config.toml和 provider 配置。4. Codex CLIconfig.toml 独立 provider不要复用 ANTHROPIC_*Codex CLI 侧使用config.toml通常位于~/.codex/config.toml。它和 Claude Code 的配置体系不同不要把ANTHROPIC_*套到 Codex。下面是一个独立 provider 示例model 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、模型名或 provider 字段有差异以客户端实际要求为准但原则不变Codex 的 provider 只读TAOTOKEN_API_KEYBase URL 仍然是https://taotoken.net/api。如果你同时用 Claude Code 和 Codex建议用两个不同的 Key 或在 Key 名称上区分用途比如TAOTOKEN_CLAUDE_KEY、TAOTOKEN_CODEX_KEY排障时更容易定位是哪一个客户端在消耗。5. CC Switch 三件套profile、env、modelMap 的隔离写法CC Switch 场景下建议把配置拆成三件套profile、env、modelMap。profile 决定当前激活的是 Claude Code 还是 Codexenv 只引用环境变量名不写真实 KeymodelMap 把默认模型和快速模型映射到当前任务避免业务代码里到处硬编码模型名。下面是一个示意配置不同版本字段名可能不同核心结构可以照这个思路迁移{ profiles: [ { id: taotoken-claude, provider: claude, baseUrl: https://taotoken.net/api, apiKeyEnv: TAOTOKEN_API_KEY, modelMap: { default: claude-sonnet-4-20250514, fast: claude-3-5-haiku-20241022 } }, { id: taotoken-codex, provider: codex, baseUrl: https://taotoken.net/api, apiKeyEnv: TAOTOKEN_API_KEY, modelMap: { default: gpt-5-codex } } ] }三件套的意义在于减少串配置Claude Code 的 profile 只写 Claude 相关模型Codex 的 profile 只写 Codex 相关模型env 不泄露 KeymodelMap 可以按任务切换快速模型和默认模型。切换 profile 后建议执行一次本地检查确认目标客户端实际读到的 Base URL 是https://taotoken.net/api而不是旧供应商地址。长任务排障时先排除配置串用再排查上下文工程。6. todo-state 增量传递协议快照、差量、摘要、引用要减少 todo-state 重复读关键不是让模型“记住别复述”而是从协议上不提供需要复述的完整数据。可以把每轮状态拆成四层快照、差量、摘要、引用。快照只保留任务 ID、目标摘要、硬约束、下一步和版本号差量只保留新增、变更、删除、完成 ID、阻塞项摘要只保留历史轮次的压缩结论引用只保留外部记忆的路径或键不把全文塞进 prompt。下面是一份可运行的 Python 示例用来生成增量 payloadfrom dataclasses import dataclass, field, asdict from typing import Any import json import hashlib dataclass class TodoItem: id: str title: str status: str # pending / running / done / blocked owner: str agent evidence: str updated_at: str dataclass class TodoState: task_id: str goal: str todos: list[TodoItem] next_action: str constraints: list[str] field(default_factorylist) version: int 1 def stable_json(obj: Any) - str: return json.dumps(obj, ensure_asciiFalse, sort_keysTrue, separators(,, :)) def digest(obj: Any) - str: return hashlib.sha256(stable_json(obj).encode(utf-8)).hexdigest()[:16] def compact_snapshot(state: TodoState) - dict: return { task_id: state.task_id, goal_digest: digest(state.goal), goal: state.goal if len(state.goal) 120 else state.goal[:117] ..., constraints: state.constraints, next_action: state.next_action, version: state.version, } def incremental_payload(prev: TodoState | None, curr: TodoState) - dict: if prev is None: return { mode: full, snapshot: compact_snapshot(curr), todos: [asdict(t) for t in curr.todos], digest: digest(asdict(curr)), } prev_map {t.id: asdict(t) for t in prev.todos} curr_map {t.id: asdict(t) for t in curr.todos} added [v for k, v in curr_map.items() if k not in prev_map] changed [v for k, v in curr_map.items() if k in prev_map and v ! prev_map[k]] removed [k for k in prev_map if k not in curr_map] done_ids [t.id for t in curr.todos if t.status done] blocked [asdict(t) for t in curr.todos if t.status blocked] return { mode: delta, snapshot: compact_snapshot(curr), base_version: prev.version, current_version: curr.version, added: added, changed: changed, removed: removed, done_ids: done_ids, blocked: blocked, digest: digest(asdict(curr)), }接下来是把 payload 渲染成 prompt。注意这里明确要求模型不要复述完整 todo 列表def render_agent_turn(payload: dict, memory_refs: list[str], budget_remain: int) - str: delta_part { k: payload.get(k) for k in [added, changed, removed, done_ids, blocked] } return f你正在执行长任务。只依据以下增量状态继续不要复述完整 todo 列表。 状态模式{payload[mode]} 任务快照{json.dumps(payload[snapshot], ensure_asciiFalse)} 增量{json.dumps(delta_part, ensure_asciiFalse)} 外部记忆引用{json.dumps(memory_refs, ensure_asciiFalse)} 剩余预算{budget_remain} tokens 要求 1. 如果状态冲突以 current_version 和 digest 为准。 2. 只输出下一步动作、验证方式、需要更新的 todo id。 3. 不要重新抄写已完成项除非用户明确询问。 这个协议的关键点是compact_snapshot每轮都带但很短incremental_payload只在状态变化时追加完成项只传done_ids不传完整 evidence阻塞项单独传便于模型优先处理。外部记忆用memory_refs引用比如memory/task-001.md、notes/decision-003.md需要细节时再按需读取而不是每轮全文加载。7. 重复读 Token 对照全量复述与增量传递的本地实验为了判断增量传递是否真的减少了重复读可以做一个本地粗估。下面的脚本不依赖外部服务只比较两种 prompt 构造方式的字符量再按本地经验换算成粗略 token。它不适用于精确计费但足够看出趋势def rough_tokens(text: str) - int: # 本地粗估中文、代码、JSON 混合场景误差较大只用于对照趋势 return max(1, len(text) // 3) def render_full(state: TodoState) - str: return f任务目标{state.goal} 约束{json.dumps(state.constraints, ensure_asciiFalse)} 完整 todo{json.dumps([asdict(t) for t in state.todos], ensure_asciiFalse)} 下一步{state.next_action} 请复述当前进度、已完成项、待办项并给出下一步。 def build_demo_states() - list[TodoState]: states [] todos [ TodoItem(T1, 梳理上下文预算, done, evidencebudget.md), TodoItem(T2, 实现 todo 差量, running), TodoItem(T3, 接入 TaoToken, pending), TodoItem(T4, 跑长任务对照, pending), TodoItem(T5, 记录目标漂移, pending), TodoItem(T6, 整理排障清单, pending), ] for version in range(1, 9): if version 2: todos[1].status done if version 3: todos[2].status done if version 4: todos[3].status running if version 6: todos[4].status blocked todos[4].evidence 需要确认模型名 states.append(TodoState( task_idtask-001, goal在不丢目标的前提下降低长任务跨轮传递的重复读开销, todos[TodoItem(**asdict(t)) for t in todos], next_actionf处理第 {version} 轮变更, constraints[不要复述已完成项, 每轮必须保留下一步, 状态冲突以版本号为准], versionversion, )) return states states build_demo_states() prev None full_tokens 0 delta_tokens 0 for s in states: full_tokens rough_tokens(render_full(s)) payload incremental_payload(prev, s) delta_tokens rough_tokens(render_agent_turn(payload, [memory/task-001.md], 120000)) prev s print(全量复述粗略 token, full_tokens) print(增量传递粗略 token, delta_tokens)在本地示例数据里8 轮任务可能得到类似下面的对照趋势。实际数值会随 todo 数量、evidence 长度、模型和语言变化关键是看“随轮次增长”的曲线轮次全量复述粗略 token增量传递粗略 token状态版本第 1 轮980980v1第 2 轮1150320v2第 3 轮1250360v3第 4 轮1420420v4第 5 轮1600450v5第 6 轮1810560v6第 7 轮1980580v7第 8 轮2160610v8全量复述的问题是线性增长每多一轮就多一份完整 todo 和历史说明。增量传递虽然也有快照和下一步但增长更平缓因为已完成项只保留 ID阻塞项只在出现时传递历史细节被压缩到外部引用。对于长任务这能直接减少跨轮读取与复述调用的 Token 消耗也能降低模型被旧信息带偏的概率。8. 排障清单目标丢失、上下文溢出、Key 串用、状态冲突第一目标丢失。每轮必须带goal_digest、硬约束和next_action不要依赖完整历史。如果发现模型开始执行已经完成的任务先检查done_ids是否没有被更新再检查 prompt 是否要求它复述完整 todo。第二上下文溢出。不要把所有 evidence、日志、文件内容都放进 prompt。把大块材料写入外部记忆只在当前轮需要时读取引用。预算不是固定值可以按任务阶段调整探索阶段多一些执行阶段少一些验证阶段只保留失败证据和验收标准。第三Key 串用。Claude Code 的ANTHROPIC_*只给 Claude Code 用Codex 用config.toml里的 provider 和TAOTOKEN_API_KEY。如果出现 401 或模型不存在先检查当前客户端实际读取的是哪个 profile、哪个环境变量、哪个 Base URL。Base URL 应统一是https://taotoken.net/api不要在一个客户端里混入另一个客户端的变量名。第四状态冲突。增量传递必须带base_version、current_version和digest。如果模型收到的差量基于旧版本它可能覆盖新状态。处理方式很简单以current_version为准冲突时重新生成差量而不是让模型凭记忆猜。第五验证重复读是否下降。不要只看总 token还要看每轮 prompt 里 todo 列表的长度、已完成项是否被完整复述、阻塞项是否重复出现。可以用第 7 节的本地脚本做趋势对照再结合客户端实际用量观察。9. 落地顺序模型对话、Coding Plan、创建 Key、Claude Code 文档如果你还没开始接入建议按这个顺序走先通过模型对话验证 TaoToken 的连通性和模型返回是否符合预期地址是 https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contenttodo_state_chat 然后根据长任务和 Coding 场景选择 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contenttodo_state_plan 接着在控制台创建和管理 Key地址是 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contenttodo_state_keys 最后按 Claude Code 文档完成settings.json和ANTHROPIC_*配置地址是 https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contenttodo_state_claude_doc 。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contenttodo_state_cta 。回到 todo-state 跨轮传递本身减少重复读不靠一句“请简洁”而靠协议快照保留目标和约束差量只传变化摘要压缩历史引用按需召回。TaoToken 在这里的作用是统一请求入口和 Key 管理让你能把精力放在状态协议和 Token 对照上。先把 Base URL 固定为https://taotoken.net/api用YOUR_API_KEY占位跑通 Claude Code 或 Codex再逐步替换全量复述。长任务跑稳之后你会发现省下来的不只是 token还有被重复信息稀释掉的注意力。