Codex 长任务跑到一半开始忘事、重复劳动,典型的上下文腐化(Context Rot)。Anthropic 的解法是 Context Reset。TaoToken 的 Key 从这拿:https://taotoken.net/?utm_source=taotoken_aicg_blog_end ,Base URL 填 https://taotoken.net/api。
第一次撞上这个问题的人,本能反应通常是三件事:把提示词写得更狠一点,换一个更大的模型,或者干脆让 Agent 把整个仓库重读一遍。这三招有时候看着有效,其实只是把症状往后推了十几轮。真正麻烦的点在于——上下文腐化不是"模型不会",而是"模型被自己前面写下的东西污染了"。窗口里堆积的中间结论、被否决的方案、早就过期的报错信息,在后半程全部变成干扰项。
这篇按排障顺序走:先确认你遇到的到底是不是上下文腐化,再讲 Context Reset 丢什么、从文件里读回什么,然后把 Codex 的模型通道接上统一 API,最后给一份能直接抄的 config.toml、progress.md 模板和 401/404 对照表。通道解决的是认证与稳定性,Context Reset 解决的是长链路跑偏,这两件事别混着谈。
1. Codex 跑到几十轮开始重复劳动,先分清是通道还是上下文
1.1 三种症状,对照你自己的会话记录
上下文腐化不会弹报错,它是"静悄悄地变糊"。判断标准可以看这三类表现。
第一类是重复劳动。第 30 轮又去读第 3 轮就读过的那个配置模块,或者把已经确认过的目录结构再列一遍。它自己不觉得重复,因为前面那段记忆已经被稀释得只剩影子。
第二类是目标漂移。任务开始时说得很清楚"只动 A 模块的接口层",跑到后半程开始顺手改 B 模块、C 模块,甚至去动构建脚本。这不是模型有想法,是它把最初那条约束的权重跑丢了。
第三类是规则失效。AGENTS.md 里写着"不要改 migrations 目录下的历史文件",前半程老老实实遵守,后半程直接伸手。这三类症状如果同时出现两项以上,基本可以判定是窗口里的历史在互相污染,而不是你的提示词写得不够礼貌。
1.2 Context Anxiety:模型会先自己"着急收尾"
Cognition 团队在用新模型重做 Devin 时观察到一个现象:模型好像能感觉到自己"快撑不住了",于是开始赶进度——突然简化方案、跳过验证步骤、急匆匆宣布任务完成。有意思的是,它对"自己还剩多少空间"的估计非常不准,经常以为快满了,实际还剩一大截。
这个现象带来一个实操上的提醒:别只盯着 token 用量条。如果你发现 Agent 的输出风格在某几轮之后突然变"急",方案粒度变粗、验证环节被省略、总结语句变多,这往往比用量条更早发出信号。此时继续硬喂任务,只会得到一份看起来很完整、实际没验证过的交付。
1.3 这一步落在 Harness 的哪一层
把等式 Agent = Model + Harness 摆出来看,除了模型本身,剩下所有决定 Agent 能不能稳定交付的东西,都归 Harness。它通常拆成六层:上下文精细化、工具系统、执行编排、记忆与状态、评估与观测、约束与恢复。
上下文腐化的主战场在第一层和第四层。第一层管"这一轮模型看到什么",第四层管"上一轮的事怎么流到下一轮"。很多人只修第一层,把历史压成摘要继续用;摘要腾出了空间,但错误的中间结论被完整地继承了下来,等于把污染源打包带走。这就是为什么压缩做完之后,问题还在。
2. Context Reset 到底丢什么、从文件里读回什么
2.1 历史压缩为什么不够用
压缩摘要和解的历史,是一种"有损但可控"的做法,它在短链路上很划算。到了长链路就有两个副作用:一是摘要会保真地保留错误结论,前面某个判断错了,后面十轮都在这个错判断上盖楼;二是模型那种"我已经干了很久"的疲惫感还在,它会继续赶工。
Context Reset 是更彻底的一步:整个旧窗口直接丢掉,换一个干净的窗口接手。新窗口不带任何历史情绪,它知道的东西全部来自磁盘。这个动作很像处理内存泄漏——不去死磕优化内存占用,直接重启进程,从持久化文件恢复状态。原则可以记成一句话:重启胜过修补,状态沉到文件里。
2.2 状态外化:progress.md 加 git history 的组合
要让新窗口"一秒接手",文件系统里得有三样东西。
第一样是进度日志,通常叫 progress.md 或 NOTES.md,写当前目标、已完成项、下一步、待确认问题、已知坑。第二样是 git history,每个提交的 message 要写清楚"为什么这么改",而不只是"改了什么",新窗口读提交记录就能重建决策链。第三样是启动脚本,一条命令把环境拉起来,避免新窗口花十几轮去摸索怎么跑测试。
这三样东西的价值在于:它们是给人看也成立的工程产物。哪怕你哪天不用 Agent 了,进度日志和提交记录照样有用。这也是状态外化比"把历史塞进上下文"更靠谱的原因——上下文窗口是易失的,磁盘不是。
2.3 记忆分三层,别混在一份文件里
常见的翻车写法是把所有东西都塞进一个 AGENTS.md:任务状态、临时结论、长期规范、某次调试的报错全文,全在一份文件里。跑上两周,这份文件变成一本谁都不敢删的流水账。
正确的做法是按生命周期分三层。任务状态写进 progress.md,任务结束就归档;会话中间结果只活在当轮,用完即弃,不值得落盘;长期规则写进 AGENTS.md 这类常驻配置,每次调用都注入,所以要短。三层混在一起,新窗口接手时根本分不清哪条是"这个任务的要求",哪条是"三周前某次调试的残留"。
3. 让 Codex 走 TaoToken:config.toml 里改三行
3.1 先去官网创建 API Key
打开 TaoToken 完成注册,进控制台创建一把 API Key,顺手在模型广场看一眼当前可用的模型 ID。Key 复制出来之后别贴进任何提交记录,放环境变量里;模型 ID 也照抄广场里的写法,别自己给模型加日期后缀——那是最常见的 404 来源。
这一步只解决一件事:Codex 拿到一个稳定的模型通道。它不负责 Context Reset,也不负责让你的 Agent 变聪明。把职责分清楚,排障的时候才不会互相甩锅。
3.2 ~/.codex/config.toml 里的 provider 写法
Codex 读的是 ~/.codex/config.toml。注意它的字段是 model_provider 和 base_url,跟 Claude Code 那套 ANTHROPIC_* 环境变量完全是两个体系,别把变量名套错。
# ~/.codex/config.toml model = "YOUR_MODEL_ID" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"model 这一行填模型广场里看到的 ID,用占位符顶着的这段时间里,Codex 会在启动时报"模型不存在",这是正常的。env_key 指的是环境变量名,不是 Key 本身,所以下面这条命令才是把钥匙递进去的动作:
export TAOTOKEN_API_KEY=YOUR_API_KEY codex exec "读 progress.md,按下一步执行,完成后更新该文件"3.3 Base URL 只能是 https://taotoken.net/api
这里有一条硬规则:填进工具的 Base URL 是 https://taotoken.net/api ,末尾不带 /v1,也不带任何查询参数。带上 UTM 的那种长地址是给人点的,只用来注册、创建 Key、看模型广场和看用量,不要往配置文件里塞。
带 /v1 的写法在某些 OpenAI 兼容客户端上凑巧能跑通,但换到 Codex 这类按 provider 拼接路径的工具上,就会出现路径重复,报 404 或者干脆返回一个空响应。这类问题排查起来最费时间,因为地址看起来"差不多对"。
4. 一次完整的 Context Reset 演练
4.1 把 AGENTS.md 从百科全书改成目录页
OpenAI 在做 Codex 早期踩过一个坑:把所有规范、约定、最佳实践统统塞进一份巨大的 AGENTS.md,以为规则越全越安全。结果恰恰相反——窗口是稀缺资源,文件越长,注意力被稀释得越厉害,Agent 反而更糊涂。
他们的改法是把主文件从"百科全书"改成"目录页",主文件只留核心索引,大约百来行量级,细节拆到子文档里按需加载。这就是渐进式披露:不是给得越多越好,而是该给的时候给,不该给的时候藏起来。如果你现在手上的 AGENTS.md 已经超过几百行,建议今天就拆。
# 项目约定索引 - 构建与测试命令:见 docs/build.md - 代码风格与命名:见 docs/style.md - 数据结构变更流程:见 docs/migrations.md - 硬性禁止:不要修改 migrations/ 下的历史文件 - 数据库相关:只允许生成或解释 SQL 语句; 诊断语句由人在本地客户端执行,再把结果贴回对话最后两条特别重要。AI 编程工具不该被当成能直连生产库的执行器,它能做的是生成语句、解释语义、对照结果。真正跑诊断 SQL、编译、注册组件这类动作,由你在本地或者对应客户端里执行,再把输出贴回来。
4.2 progress.md 该写哪五样东西
进度日志不用写长,五样东西够用:当前目标(一句话)、已完成(只列结论不列过程)、下一步(越具体越好)、待确认(需要人拍板的问题)、已知坑(踩过并且不打算再踩的)。
写"已完成"的时候克制一点。把过程写进去,等于把上下文腐化的原料提前塞进文件。写"下一步"的时候反过来,越细越好,因为新窗口接手后最先看的就是这一栏。
4.3 新窗口的第一条指令模板
重置之后的第一条指令,决定了新窗口会不会又立刻跑偏。可以照这个结构写。
先读 progress.md、AGENTS.md 和最近 10 条 git log, 用一段话复述:目标、已完成、下一步、待确认项。 复述完之后不要动手,等我确认。先复述、再动手,这一步不是形式主义。新窗口从文件里读回来的状态,跟你脑子里的状态之间一定有偏差,复述就是把这个偏差提前暴露出来的机会。偏差小就继续,偏差大就说明 progress.md 写得不够,当场补。
4.4 生产和验收别让同一个窗口干
让 Agent 自己给自己打分,永远偏乐观,尤其在没有标准答案的任务上。可行的做法是分工:一个窗口负责规划和实现,另一个全新窗口负责验收,验收方必须真的跑一遍——执行测试、看输出、核对结果,而不是只读代码。
危险的地方在于,"真的跑一遍"很容易被理解成"让 Agent 直连你的环境"。不是这个意思。验收窗口能做的是:写出该跑的命令、写出该执行的查询语句、根据你贴回来的输出判断对不对。真正的执行发生在你这一侧。这条边界守住,长任务才敢放手让它跑。
5. 401、404 和"能连上但越跑越偏"的区分排障
5.1 401:Key 与环境变量名
401 只跟认证有关,跟上下文一点关系都没有。按顺序查三件事:Key 是不是从控制台复制完整了(首尾空格很常见);env_key 里写的变量名,和你在 shell 里 export 的名字是否完全一致;这个 shell 会话是不是新开的(老终端里 export 的变量,新开的窗口读不到)。
如果 Key 用过一段时间突然开始 401,先回控制台确认它还在、额度还在,再检查是不是更换环境后忘了重新导出。通道不稳时不要盲目换模型 ID,那会把问题从认证层挪到配置层,更难查。
5.2 404:路径和模型 ID 各占一半
404 的来源基本是两类。一类是 Base URL 写错,最常见的两种写法是末尾加了 /v1,或者把给人点的长地址(带查询参数的那种)误填进了配置文件。另一类是模型 ID 不存在,自己拼了日期后缀、大小写不一致、或者抄了别的平台的模型名。
判断方法很简单:把模型 ID 换成"以 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 模型广场当时列表为准"的那一个,其他都不动。如果 404 消失,问题在模型 ID;如果还在,问题在路径。
5.3 通道正常但依然跑偏时的检查表
这是本篇最该记住的一段:能连上不等于跑得稳。当请求全部成功、没有任何报错,但 Agent 依然在第 40 轮开始重复劳动,那要查的是 Harness,不是通道。
检查表四项:一是 AGENTS.md 是不是又长回去了;二是 progress.md 是不是三天没更新了;三是这一轮是不是从头到尾没做过 reset,一直在同一个窗口里续命;四是验收是不是还由同一个窗口自己做。四项里有任意一项中招,先修那一项,别急着调模型参数。
6. 长任务收工后,去控制台对一下这次用了多少
6.1 用同一把 Key 发一条测试消息
配置改完、reset 跑通之后,先别急着开一个通宵任务。到 模型对话 里用同一把 Key 发一条测试消息,确认模型 ID 和 Base URL 都没填错。这一步花两分钟,能省掉后面半小时的困惑——尤其当你在 Codex 里看到的是"模型不存在",而实际上是 Key 复制漏了一位的时候。
6.2 长任务的账要对得上
跑完一个跨天任务,回控制台看一眼这次调用的记录:轮次、token 消耗、有没有异常中断。长任务最怕的不是花得多,而是花得不明不白——比如你以为 reset 了三次,实际上一整个通宵都在同一个窗口里续命,那用量曲线会明显偏高,而且中间没有任何断裂点。
对账的时候顺手做一次复盘:这次是哪一层出了问题?是 AGENTS.md 太长,是进度文件没写,还是验收被合并进了生产窗口。找到落点,下次就把它固化进环境里,而不是靠记忆。
6.3 接下来可以做的三件事
如果你准备把 Codex 长期挂在项目里跑长任务,可以先去 Coding Plan 看看套餐是否够用;还缺 Key 的话在 控制台 API Keys 创建;模型 ID 和可用列表,以 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 上的模型广场当时显示为准。
最后留一句个人体会:长链路 Agent 的稳定性,八成来自环境设计,两成来自模型能力。Context Reset 之所以值得认真做一遍,不只是因为它能治上下文腐化,而是因为它逼着你想清楚——这个任务的状态到底存在哪、由谁读、什么时候该丢。想清楚了这件事,换不换模型、走不走统一通道,都只是后面的选择题。