1. 长任务跑着跑着就变味:Goal Drift 到底怎么发生的
如果你用 ChatGPT 或 Codex 跑过那种"一次交代清楚、让它自己跑到底"的任务,大概率见过这个画面:一开始只是让它修一个登录后偶发丢 Session 的 Bug,明确说了"不要动 Public API"。结果几十轮之后你打开 Diff,发现 Token 刷新逻辑改了、缓存同步改了、异常处理重构了,连几个公共函数的签名都动了。每一步单独看都挺合理,但合起来就是——它已经不在做你最开始交代的那件事了。
这个现象就是 Goal Drift,目标漂移。它不是模型"忘了"原始目标,原始目标可能还躺在 Context 里,只是经过几十轮工具输出、测试日志、失败重试之后,它的权重被后面不断冒出来的局部目标压下去了。长任务真正危险的地方不是 AI 停下来,而是它一直非常努力地工作,最后把一个完全不同的问题解决得很好。
这篇就围绕 ChatGPT、Codex 这两个场景,讲清楚三件事:Goal Drift 的工程机制、怎么用 TaoToken 统一 Key/API 通道把 ChatGPT 和 Codex 接到同一套配置里、以及怎么用多轮长任务对比验证漂移现象并做上下文管理。适合已经在用 Agent 跑长任务、但发现结果越来越不可控的开发者。
2. 前置准备:用 TaoToken 统一 Key/API 通道
在验证 Goal Drift 之前,先把接入通道理顺。我自己的做法是让 ChatGPT 网页端和 Codex CLI 走同一套 Key 体系,这样多轮长任务对比时,模型能力、计费口径、日志都能对齐,不会出现"到底是模型差异还是通道差异"的干扰。
TaoToken 的定位就是一个统一的模型 API 通道,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。你需要先在控制台创建一个 API Key,然后分别写进 Codex 的 config.toml 和 ChatGPT 侧会用到的 settings.json。
注意:API Key 只创建一次、多处复用,不要每个工具单独建 Key,否则后面排查漂移时你分不清是配置差异还是任务本身的问题。
创建 Key 的入口在控制台的 API Keys 页面:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。拿到 Key 之后先别急着配,先确认你的模型对话通道能通,可以在这里快速验证:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。
3. 可复制配置:settings.json 与 config.toml
3.1 Codex 侧 config.toml 配置
Codex CLI 的配置文件一般放在~/.codex/config.toml。下面是我实测可用的一份最小配置,把 provider 指向 TaoToken 的 API 地址,模型按你实际订阅的填:
# ~/.codex/config.toml 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" [profiles.longtask] model = "gpt-5-codex" model_provider = "taotoken" # 长任务建议显式限制单轮上下文,避免无脑堆历史 model_max_output_tokens = 8192Key 不要写死在文件里,用环境变量注入:
export TAOTOKEN_API_KEY="sk-你的key"如果你用的是 Claude Code 那套 Anthropic 兼容入口,配置思路一样,只是 base_url 和 wire_api 换成对应值,参考文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
3.2 ChatGPT 侧 settings.json 配置
ChatGPT 网页端本身不读本地 settings.json,但如果你是通过自建客户端或 IDE 插件调用,通常会有一个 settings.json 用来存 provider 和 Key。典型结构如下:
{ "provider": "taotoken", "apiBase": "https://taotoken.net/api", "apiKeyEnv": "TAOTOKEN_API_KEY", "defaultModel": "gpt-5", "context": { "maxTurns": 40, "compactionThreshold": 0.75, "reinjectGoalOnCompaction": true } }这里reinjectGoalOnCompaction是我自己加的一个开关,含义是:每次上下文压缩之后,强制把 Goal Anchor 重新注入到系统提示里。这一条对抑制 Goal Drift 非常关键,后面第 5 节会展开。
3.3 关键参数对照
| 参数 | 作用 | 长任务建议值 |
|---|---|---|
| model_max_output_tokens | 单轮输出上限 | 8192 |
| maxTurns | 单会话最大轮数 | 40 |
| compactionThreshold | 触发压缩的上下文占比 | 0.75 |
| reinjectGoalOnCompaction | 压缩后重注入目标 | true |
4. 验证请求:多轮长任务对比 Goal Drift
配置好之后,用同一个任务跑两组对比:A 组不做任何目标锚定,B 组每 10 轮做一次 Goal Checkpoint。任务就用经典的"修 Session Bug 且不改 Public API"。
先发一条验证请求确认通道通:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5-codex", "messages": [ {"role": "system", "content": "Goal Anchor: 修复登录后偶发丢Session;Non-goal: 不改Public API、不做无关重构;Done: Bug无法复现且回归测试通过。"}, {"role": "user", "content": "开始分析Session丢失问题。"} ] }'返回 200 且带 choices 就说明通道正常。接下来跑长任务,A 组只给一次初始目标,之后一路"继续";B 组每 10 轮插入一次:
请重新输出:当前Goal、已确认Root Cause、当前Plan、哪些修改已超出Scope。实测下来,A 组到第 30 轮左右,Diff 里开始出现 Public API 签名变化,AI 的解释是"为了让测试通过需要统一接口行为"——这就是典型的 Objective Replacement,局部目标"让测试通过"取代了原始目标"不改 API"。B 组因为每 10 轮强制对齐一次,第 30 轮时 Scope 仍然收敛在 Session 相关文件内。
成功结果长这样:B 组最终 Diff 只涉及 3 个文件,A 组涉及 11 个文件且包含公共函数改动。这个对比就是 Goal Drift Distance 的直观体现。
5. 本篇常见错排查
5.1 配了 Key 但请求 401
先确认环境变量真的注入了,echo $TAOTOKEN_API_KEY看有没有值。常见坑是写进了.zshrc但当前 shell 没 source,或者 config.toml 里 env_key 名字和实际变量名不一致。401 基本都是 Key 没读到,不是通道问题。
5.2 压缩之后目标明显变形
这是 Goal Drift 最隐蔽的一种。原始约束"不改 Public API、不改旧业务行为"在压缩后可能被压成"继续解决登录问题并确保测试通过",两个关键 Non-goal 直接消失。解决办法就是 3.2 里的reinjectGoalOnCompaction,压缩后强制重注入 Goal Anchor,别默认压缩会保留所有约束。
5.3 越 Retry 越偏
Retry 解决的是执行失败,Goal Drift 解决的是方向错误。方向已经偏了还继续 Retry,只会让 AI 在错误方向上投入更多计算。这时候正确动作是 Pause,重新检查 Goal,必要时 Rollback 或 Restart。长任务里最危险的命令就是无脑"继续",因为你得先知道继续的是不是正确任务。
5.4 多 Agent 场景漂移扩散
如果主 Agent 把已经偏移的子目标派给 Subagent,Subagent 完成得很好,返回后主 Agent 反而更确信当前方向正确,这就形成 Drift Reinforcement。排查方法是给每个 Subagent 的输入里也带上 Goal Anchor,而不是只传局部子任务描述。
6. 长期编码与 Agent 场景的通道选择
如果你只是偶尔跑中等长度的 Feature,任务边界清楚,上面这套 Goal Anchor + Checkpoint 的流程建起来之后,普通订阅基本够用。但如果你日常就是大型 Repository、复杂长任务、多 Agent 协作,而且 Goal 管理已经比较成熟、真实高价值负载持续超过普通订阅的舒适范围,那就该考虑 Coding Plan 了:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。
判断逻辑不是"任务跑得久所以要升级",而是"我的长任务已经能稳定保持目标,但真实负载仍然持续超限"。接入和排障相关的完整说明在文档里:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,Key 管理在 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。
最后留一个我自己的习惯:每次长任务开始前,先把 Goal Anchor 写成三行贴进系统提示,任务中途每 10 轮让它复述一次当前 Goal。Agent 真正难的不是能跑多久,而是跑了很久之后还在做同一件事。