
1. 任务中断后为什么“继续”可能是最差的选择用 Codex 跑多步任务最容易踩的坑不是模型不够聪明而是恢复策略选错了。你让 Codex 修一个跨模块的 Bug第一轮分析完它判断问题出在缓存失效改了两个文件跑测试部分通过还剩一个 Case 失败。这时候你因为额度用完或者要开会任务中断了。第二天回来你打开 ChatGPT 或者 Codex CLI很自然地敲一句“继续昨天的任务”。问题就出在这里。Agent 恢复任务时面对的不是一个断点而是一整套可能已经变化的状态代码可能被你自己或同事改过依赖版本可能升级了测试结果可能过期了Context 可能经过压缩只保留了部分历史甚至最初那个“缓存失效”的判断本身可能已经不成立。如果 Codex 仍然沿着昨天的假设往下跑它不是没有上下文而是继承了一个已经过期的状态。这就是 Resume 和 Checkpoint 需要分开讨论的原因。Resume 是“接着跑”Checkpoint 是“回到一个已知正确的状态快照再决定方向”。两者不是替代关系而是适用边界完全不同。这篇文章面向用 ChatGPT、Codex 做多步任务编排的开发者给出 config.toml 里 Resume/Checkpoint 相关配置骨架复现一次中断场景帮你判断什么时候该接着跑、什么时候该回退重来。2. 前置准备TaoToken 接入与 Codex 环境确认在讨论恢复策略之前先把接入层跑通。我用的方式是 TaoToken 提供的统一 API 入口它兼容 OpenAI 风格的接口Codex CLI 和 ChatGPT 类客户端都能直接对接。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 注意 API 地址不带 UTM 参数。你需要先拿到一个 API Key。登录后进入控制台在 API Keys 页面创建一个新 Key复制保存。这个 Key 后面会写进 Codex 的 config.toml 里。如果你还没创建过可以直接访问 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 这个 deep link 快速跳转。环境侧确认三件事Codex CLI 版本、config.toml 路径、当前项目 Git 状态。Codex CLI 一般安装在~/.codex/目录下配置文件是~/.codex/config.toml。先跑一下版本确认codex --version # 输出示例codex-cli 0.9.x然后确认 Git 状态这一步很关键因为后面判断 Resume 还是 Checkpoint第一件事就是看代码有没有变git status --short git log --oneline -5如果git status有未提交的改动说明上次任务留下的 Diff 还在工作区。如果git log显示有新的 commit说明代码状态已经和暂停时不一样了。这两条信息直接决定你的恢复策略。3. config.toml 中 Resume 与 Checkpoint 的配置骨架Codex 的 config.toml 支持配置模型端点、超时、上下文窗口等参数。下面是一个可用的骨架重点标注了和恢复策略相关的字段# ~/.codex/config.toml # 模型接入配置 model gpt-4o model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY # 会话与上下文 [history] max_tokens 128000 # 上下文压缩阈值超过后触发 compaction compact_threshold 100000 # 是否保留工具调用历史 retain_tool_calls true # 任务恢复相关 [resume] # 恢复时是否自动校验 Git 状态 verify_git_state true # 恢复时是否重新读取当前工作区 Diff reload_diff true # 恢复前是否要求确认目标未变 confirm_goal true # Checkpoint 配置 [checkpoint] # 是否启用自动 checkpoint enabled true # checkpoint 存储目录 dir ~/.codex/checkpoints # 每个 checkpoint 保留的最大数量 max_checkpoints 20 # 触发 checkpoint 的时机manual / on_pause / on_compact trigger on_pause这里有几个字段值得展开说。compact_threshold控制上下文压缩的触发点一旦触发压缩历史对话会被摘要化部分细节会丢失这正是 Resume 风险变高的信号之一。resume.verify_git_state打开后Codex 在恢复任务时会先跑一次git status和git diff如果发现代码变了会提示你。checkpoint.trigger设为on_pause表示每次任务暂停时自动落一个快照这样你后面可以选择回滚到这个点。API Key 通过环境变量注入不要写死在配置文件里export TAOTOKEN_API_KEY你的Key如果你用的是 ChatGPT 网页端做任务编排接入方式略有不同可以在模型对话页面直接选择对应模型地址是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。但 config.toml 这套配置主要针对 Codex CLI 场景网页端没有本地 checkpoint 能力恢复策略要靠手动管理对话历史。4. 复现一次中断Resume 与 Checkpoint 的验证请求现在动手复现一次中断场景看看两种策略的实际差异。准备一个测试项目故意制造一个需要多步排查的 Bugmkdir codex-resume-test cd codex-resume-test git init cat cache.py EOF import time _cache {} def get_data(key): if key in _cache: return _cache[key] # 模拟数据获取 time.sleep(0.1) data fdata-{key} _cache[key] data return data def invalidate(key): if key in _cache: del _cache[key] EOF git add . git commit -m init启动 Codex 任务让它排查一个“缓存失效后仍返回旧数据”的问题codex 分析 cache.py 中 invalidate 后 get_data 仍返回旧值的原因给出修复方案并修改代码Codex 会开始多步分析读文件、判断问题、修改代码、跑测试。在它改完第一个文件但还没跑完测试时按 CtrlC 中断任务。这时候工作区里已经有未提交的 Diffgit diff --stat # cache.py | 5 --现在模拟“第二天回来”的场景。先制造一个状态变化模拟同事提交了新代码cat cache.py EOF def get_data_v2(key): return _cache.get(key, fdefault-{key}) EOF git add cache.py git commit -m add get_data_v2此时如果你直接 ResumeCodex 会带着昨天的假设继续跑。用 Resume 方式恢复codex resume --last它会加载上一次会话历史看到昨天的分析和 Diff然后继续围绕“缓存失效”往下排查。但注意get_data_v2是新加的昨天的分析里完全没有这个函数Codex 的旧 Plan 可能已经失效。换 Checkpoint 方式先看有哪些快照codex checkpoint list # 输出示例 # [0] 2025-01-15 14:30 on_pause 分析 cache.py 缓存失效问题 # [1] 2025-01-15 14:10 manual 初始任务启动回滚到任务开始前的干净状态codex checkpoint restore 1恢复后工作区回到初始 commit没有昨天的半成品 Diff也没有新加的get_data_v2干扰。这时候重新发起任务Codex 会基于当前真实代码状态重新分析而不是继承过期假设。两种方式的差异在验证请求里体现得很明显。Resume 后你可以问 Codexcodex 当前任务的目标是什么上次确认了哪些事实Repository 和上次相比有什么变化如果它回答的目标还是“修复缓存失效”但代码里已经多了get_data_v2说明状态校验没通过应该转向 Checkpoint 或 Replan。Checkpoint 恢复后同样问一遍它会基于干净状态重新给出判断。5. 常见错误排查Resume 失效与 Checkpoint 冲突实际用下来几类报错和异常状态出现频率最高。第一类是 Resume 后 Codex 反复修改同一个文件却跑不通测试。这通常是因为旧 Diff 和新代码产生了语义冲突。比如昨天改的是get_data的缓存逻辑今天有人重构了_cache的数据结构旧修改直接失效。排查方式是先看git diff里有没有冲突标记再让 Codex 做一次 Resume Checkcodex 不要继续执行先检查当前工作区 Diff 是否与最新 commit 兼容列出所有可能冲突的文件第二类是 Checkpoint restore 后报checkpoint not found或dirty working tree。前者是 checkpoint 目录被清理了检查~/.codex/checkpoints是否存在以及max_checkpoints是否设得太小导致旧快照被覆盖。后者是工作区有未提交改动restore 前需要先 stashgit stash push -m before checkpoint restore codex checkpoint restore id第三类是上下文压缩后 Resume 丢失关键信息。表现是 Codex 忘记了之前确认过的 Root Cause重新开始猜测。这时候看 config.toml 里的compact_threshold如果设得太低长任务很容易触发压缩。可以适当调高或者养成手动落 Checkpoint 的习惯把关键结论写进 checkpoint 的备注里。第四类是 API 请求超时导致任务中断。检查base_url是否写成了带 UTM 的地址API 端点应该是https://taotoken.net/api不带任何查询参数。如果超时频繁可以在 config.toml 里加超时配置[request] timeout_seconds 120 max_retries 3第五类是 Resume 后 Codex 看起来特别顺畅一路往下跑。这反而要警惕。流畅不代表正确它可能继承了昨天的错误假设在高速执行。复杂任务恢复后的第一轮重点不是执行速度而是状态正确性。先让它做 Resume Check确认 Goal、Evidence、Repository 变化、下一步是否成立四个答案都稳定再继续。6. 恢复策略选择与长期编码工作流判断该 Resume 还是 Checkpoint核心指标是恢复成本。任务只改一个函数、目标明确、Diff 很少、没有外部依赖、暂停时间短恢复成本低直接 Resume 没问题。任务跨多个模块、运行很久、几十个文件变化、Root Cause 还没确认、中间发生过压缩、代码又有更新恢复成本很高这时候直接继续通常不是最优策略应该先 Checkpoint 回滚或者 Clean Restart。一个实用的自测方法是问五个问题暂停后代码有没有变化Root Cause 是否已经确认当前 Diff 是否干净下一步是否非常明确你能不能用 100 字以内准确描述现在的任务状态五个答案都好Resume三四项不确定先恢复状态再决定。如果你长期跑 Codex 多步任务建议把 Coding Plan 用起来它针对长任务和连续工作负载做了优化配合 checkpoint 机制能显著降低恢复成本。具体可以看 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有 config.toml 各字段的完整说明和更多恢复策略示例。真正成熟的 Agent 工作流不会只有一句“继续”。它会先判断 Goal 还对不对、State 还有效吗、Plan 还能不能执行、Resume Cost 有多高。有时候最好的动作是 Resume有时候是 Replan有时候应该 Rollback甚至直接 Restart。停下来以后知道该从哪里重新开始比永远不停下来更重要。