1. Codex 越用越慢的真实场景:从 MEMORY.md 膨胀说起
Codex 用久了变慢,很多人第一反应是模型波动或者网络问题。我一开始也这么想,直到发现一个更隐蔽的原因:本地记忆文件在悄悄膨胀。Codex 这类 Coding Agent 会把长期偏好、工作区规则、历史任务摘要写进MEMORY.md和memory_summary.md,每次新会话默认加载。文件越大,默认塞进上下文的内容越多,模型判断越容易跑偏。
具体表现很难用单个 Bug 解释:任务还没开始,先重复一大段旧规则;已完成的项目被当成当前背景;仓库已经升级,回答里还引用旧提交号;简单要求需要多次确认,长任务越来越容易跑偏。这些现象叠加在一起,体感就是"越用越慢、越用越啰嗦"。
我实测下来,当时MEMORY.md已经到 1,875 行、201,111 字节,包含 40 组历史任务,整个记忆目录约 2.0 MB。清理后主文件剩 150 行、8,698 字节,按字节减少 95.7%,摘要从 99 行降到 60 行,目录降到 976 KB,对话和 62 份任务回放摘要全部保留。
这篇复盘聚焦一件事:怎么把"每轮默认加载的知识"和"需要时再查的历史证据"分开。排查顺序是先做只读盘点,再识别膨胀来源,然后建立分层目录、备份重写、逐项确认清理对象、同步摘要、用 Git 验收,最后用真实任务测量效果。适合长期使用 Codex 或类似 Coding Agent、感觉响应变慢的开发者跟做。
2. TaoToken 前置准备:把 settings 改到 TaoToken 的完整配置
在动手清理记忆之前,先把模型接入层固定下来,否则你无法判断变慢是记忆膨胀还是接入配置问题。我用的方案是把 Codex 的 settings 指向 TaoToken,Base URL、Key、Model ID 三件套一次配齐,后续排查才有稳定基线。
TaoToken 官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。你需要先去控制台生成 API Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到字段不确定时对照文档最稳。
Codex 的配置通常落在~/.codex/config.toml或项目级 settings 里。下面是一份可复制的 TOML 片段,路径和字段按你本地实际文件调整:
# ~/.codex/config.toml model = "claude-sonnet-4-5" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"对应的环境变量在 shell 里导出,不要写进仓库:
export TAOTOKEN_API_KEY="sk-你的Key"如果你用的是 Claude Code 或 Cline 这类工具,配置思路一致:Base URL 填https://taotoken.net/api,Key 填控制台生成的密钥,Model ID 填你实际要用的模型名。三件套缺一不可,只填 Base URL 不填 Model ID 是最常见的半配置状态,表现为请求能发出但返回异常。
配好之后先别急着清理记忆,跑一个最小请求确认接入层是通的。这一步的意义是把变量隔离:如果接入层本身有问题,后面所有"变慢"的观察都不可信。
3. 可复制配置与排查脚本:只读盘点加分层目录
接入层固定后,进入记忆排查。第一步是只读盘点,不要拿到目录就递归删除。先确认主文件、摘要、日志、更新单和回放分别占多少空间:
set -euo pipefail MEM_DIR="$HOME/.codex/memories" wc -l -c "$MEM_DIR/MEMORY.md" "$MEM_DIR/memory_summary.md" du -sh "$MEM_DIR" find "$MEM_DIR" -maxdepth 3 -type f -print | sort如果目录由 Git 管理,再补两条:
git -C "$MEM_DIR" status --short git -C "$MEM_DIR" log -5 --oneline这一步要回答三个问题:哪些文件会默认进入新会话,哪些只是按需证据,目录里是否已有未提交修改。第三点很重要,你自己的改动不能被清理脚本覆盖。
识别膨胀来源时,重点看四类内容:重复规则(同一约束同时写在摘要、主记忆和工作区说明中)、陈旧动态事实(提交号、版本号、当前部署、临时地址)、任务流水账(某天做了什么、跑了哪些命令)、冲突结论(旧技术栈与新技术栈都被写成当前命令)。模型不是 SQL 数据库,不会按时间戳只选最新一行,相互矛盾的自然语言同时出现时它只能猜,猜错就表现为答非所问。
我采用三层目录边界。活跃索引层MEMORY.md和memory_summary.md只保留长期有效信息:稳定偏好和验收口径、架构与安全边界、工作区定位、冲突时的决策原则。按日日志层放当天进展和临时笔记,例如memory/2026-08-25.md,可追溯但不要求每个新任务读取。证据归档层放完整排障记录、发布回读、旧版本快照和任务回放,进入memory/archive/,仍可搜索但不再充当当前指令。
凭据、私钥、签名材料和客户私有文件内容不能靠分层解决,它们根本不应进入记忆目录。
备份和重写主文件:
set -euo pipefail MEM_DIR="$HOME/.codex/memories" STAMP="$(date +%Y%m%d-%H%M%S)" BACKUP_DIR="$MEM_DIR/archive/pre-slim-$STAMP" mkdir -p "$BACKUP_DIR" cp -p "$MEM_DIR/MEMORY.md" "$BACKUP_DIR/" cp -p "$MEM_DIR/memory_summary.md" "$BACKUP_DIR/"旧主文件出现几十个任务章节时,建议新建精简版本而不是逐段删除。新文件先写稳定偏好、硬边界和工作区索引,再逐条对照旧文件补漏。我给活跃层设了两个上限:MEMORY.md不超过 150 行或 15 KiB,memory_summary.md不超过 60 行或 4 KiB。上限不是通用标准,关键是默认层必须有预算。
清理对象逐项确认,本次盘点结果如下:
| 对象 | 体积或数量 | 处理 |
|---|---|---|
| 旧 MEMORY.md | 201,111 B | 备份后用精简索引替换 |
| raw_memories.md | 289,360 B | 确认是阶段性聚合后清理 |
| 已处理更新单 | 39 份 / 79,721 B | 确认已合并后移除 |
| 重复归档 | 201,111 B | 校验内容重复后清理 |
| .DS_Store | 3 个 / 24,588 B | 清理 |
| rollout summary | 62 份 / 334,646 B | 保留 |
不要只看扩展名判断。名为 archive 的文件也可能是唯一证据,名为 raw 的文件也可能尚未消费。删除前至少比较内容、修改时间和引用关系。
4. 验证请求与成功结果:Git 验收加真实任务测量
只瘦身MEMORY.md很容易出现"删了但仍然记得"的错觉。如果新会话先加载memory_summary.md,而摘要仍包含旧项目和旧版本,问题并没有消失。顺序应当是:确定新的主记忆,从新主记忆提炼摘要,清点已消费更新单,新建干净任务验证。旧线程已经读过旧内容,磁盘修改不会让现有上下文倒带,验收一定要包含新任务。
用 Git 做可回读验收。我的自动脚本曾经中途失败:macOS awk 对路径正则的处理与预期不同,同时 Markdown 还有两处尾部空白,文件虽然改了但提交并未完成。因此至少执行:
git status --short git diff --cached --stat git diff --cached --check这三条命令分别回答:改了什么、改了多少、格式是否可提交。它们比脚本最后打印的 done 可靠得多。最终提交包含 41 个文件变化,新增 208 行、删除 6,447 行,仓库压缩后只有一个约 402 KiB 的 pack,工作区干净。
测量是否真的有效,不要只比较目录大小,也不要只靠主观体感。准备 3 到 5 个真实任务,固定模型、仓库状态和提示词,记录:第一次有效工具调用耗时、输入与输出 Token、引用过期事实的次数、重复确认次数、最终验收是否一次通过。清理前后的冷缓存、热缓存也要区分,新任务用于观察记忆层,旧长线程还会受当前对话长度和 compaction 影响。
验证接入层是否正常,可以用模型对话页发一个最小请求: https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 。如果这里返回正常,说明 Base URL、Key、Model ID 三件套没问题,变慢的锅就不在接入层。
5. 本篇常见错排查:401、local proxy failed 与 reading choices
排查过程中最容易撞上的几类报错,逐个对照。
401 Unauthorized 通常有三种原因:Key 没导出到当前 shell、Key 复制时带了空格或换行、环境变量名和配置里的env_key不一致。检查方式是echo $TAOTOKEN_API_KEY | head -c 8,确认前缀正确且无多余字符。如果配置里写的是env_key = "TAOTOKEN_API_KEY",环境变量就必须叫这个名字,大小写敏感。
local proxy failed 多出现在你本地还挂着其他转发工具时。先确认没有残留进程占用端口,再确认base_url没有被本地代理改写。配置里base_url应直接是https://taotoken.net/api,不要指向127.0.0.1之类的本地地址。
reading choices 报错一般是响应结构不符合预期,常见于wire_api字段和实际接口不匹配。如果你用的是 chat 类接口,wire_api = "chat";如果工具走的是另一套协议,对照接入文档调整。这个错误和记忆膨胀无关,但会伪装成"变慢",因为请求在重试。
OAuth 相关报错出现在用 Claude Code 或类似工具时,通常是登录态和 API Key 混用。用 Key 接入就不要再走 OAuth 流程,两者选其一。Claude Code 的接入配置参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=ClaudeCodeAnthropic&utm_campaign=rewrite ,里面有完整的 Base URL、Key、Model ID 填写位置。
还有一个隐蔽的坑:清理脚本跑完显示 done,但 Git 提交没成功。表现是下次新会话仍然加载旧记忆。回到第 4 节的git status --short确认工作区状态,别信脚本的输出。
如果排查完接入层和记忆层还是慢,再考虑是不是当前线程太长。长线程受对话长度和 compaction 影响,和记忆层是两回事。开一个新任务对比,能快速区分。
6. 长期编码与 Agent 场景的稳定接入
把 settings 改到 TaoToken 之后,长期编码和 Agent 场景的稳定性主要取决于两件事:接入层固定,记忆层有预算。接入层固定意味着 Base URL、Key、Model ID 三件套不随意变动,排查时变量可控。记忆层有预算意味着默认加载的内容有上限,历史证据移出默认层但保持可检索。
如果你经常跑长任务或 Agent 工作流,可以考虑用 Coding Plan 把额度和管理集中起来: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。配合前面说的分层目录,活跃索引保持精简,按日日志和证据归档按需检索,Codex 的响应会稳定很多。
这次清理确认了两件事:活跃主记忆可以从 201KB 降到 8.7KB,同时保留核心规则;历史也不必删除,只要移出默认加载层并保持可检索。它没有证明所有 Codex 任务都会固定提速多少,这个结论要靠同一套任务长期对比。如果你的 Coding Agent 也开始慢、啰嗦、爱引用旧结论,排查顺序可以是:先看默认上下文,再看规则冲突,然后核验磁盘现场,最后才讨论模型是否变化。