← 返回程序员的梦话

记录 Codex 的一次"时空穿越"

它没有忘记过去。恰恰相反,它记得太清楚了——清楚到把几天前已经结束的任务,重新搬回了今天。

作者:Pitt Zhang

首发时间:2026 年 8 月 25 日(北京时间 / UTC+8)

首发平台:Pitt Zhang 个人技术博客 — https://api-cloud.cc/

授权与引用声明

本文为 Pitt Zhang 基于实际 AI Agent 工程实践、项目测试与技术分析形成的原创文章,首次发布于个人技术博客 https://api-cloud.cc/

本文允许个人、媒体、技术社区及其他非恶意用途进行引用、讨论、转载或二次分析,但应明确注明原作者与原始出处。

引用或转载时,请至少保留以下信息:

作者:Pitt Zhang

原文首发:2026 年 8 月 25 日(北京时间)

原始出处:https://api-cloud.cc/

可以对本文观点进行批评、验证、反驳或进一步研究,但不应删除原始作者信息后将文章整体或核心原创分析冒充为他人原创成果。


昨天,我只是让 Codex CLI 处理一个很小的新需求。

目标还是那台服务器,但任务已经完全不同。我没有让它恢复旧工作,没有让它整理历史文档,更没有让它继续任何 Git 暂存、提交或归档操作。

可终端跑着跑着,画面突然变得不对劲。

它开始谈论几天前的恢复记录,重新拾起当时没有完成的计划,甚至准备把旧文档加入暂存区、创建提交、继续归档。

那一刻,我盯着屏幕愣了几秒,脑子里只有一个问题:

你到底跑到哪里去了?

我连续打断它,提醒它已经偏离当前需求。可它一次又一次试图沿着那条旧轨道往前走。

最后,Codex 确实回到了当前问题,也给出了正确结论。

但我一点也没有因此轻松下来。

因为对一个只能聊天的模型来说,答错了也许只是一次普通幻觉;而对一个能够读写文件、操作 Git、调用工具,甚至参与服务器运维的 AI Agent 来说,最后一句答对了,并不能证明前面的执行过程是安全的。

这一次,它不是简单地"记错了"。

它像是完成了一次小型的时空穿越:身体还停留在今天,任务却回到了几天前。


它带回来的,不只是记忆

这次故障发生于 2026 年 8 月 24 日,环境如下:

几天前,我确实处理过这台服务器的恢复记录。当时的工作包含文档整理,以及后续的 Git 暂存、提交和归档计划。

这些历史都是真实的。

问题是:几天后的新请求只是一次独立诊断。它与旧任务拥有相同的操作对象,却没有相同的目标,更没有继承旧任务的写操作授权。

Codex 却把"同一台服务器"理解成了"同一个任务还在继续"。终端中已经出现了类似这样的执行意图:

git add <历史恢复文档> git diff --cached --check git commit -m "<历史任务提交信息>"

后来,这次提交被项目自身的规则挡住了。

坦白说,这道拦截让我庆幸,也让我后背发凉。

庆幸的是,旧写操作没有顺利继续;后背发凉的是,阻止它的并不是 Codex 自己意识到"这不是今天的任务",而是另一个项目规则恰好踩下了刹车。

如果那条规则不存在呢?

如果被复活的不是一份文档提交,而是几天前暂缓的服务器变更、已经取消的部署,或者某个不应再次触碰的账号操作呢?

这就不再是"答非所问"了。

这是授权边界被历史任务穿透。


这不是普通幻觉,而是"真记忆、假当前"

普通幻觉,是模型编造了不存在的文件、命令结果或事实。

这一次恰好相反:服务器是真的,历史记录是真的,旧计划也是真的。它没有捏造过去,而是把真实的过去放进了错误的现在。

我更愿意把它叫作:

跨越时间边界的旧任务复活(Stale Task Resurrection Across a Temporal Boundary)

或者更直白一点:

真记忆,假当前。

它真正犯下的错误,是把两件毫不等价的事强行画上了等号:

操作对象相同 ≠ 任务仍然连续

同一台服务器,今天可以排查网络,明天可以看日志,几天后也可以只问一个配置问题;同一个仓库,可以先修 Bug,再写文档,后来只做代码审查。

对象只是舞台,任务才是剧情。

不能因为舞台没换,就认定上一幕还没有结束。


真正混在一起的,是三种完全不同的"恢复"

这次事故让我意识到,AI Agent 必须严格区分三件事。

第一,上下文恢复

它可以记得过去发生过什么,可以读取旧记录,也可以利用历史帮助分析。这是记忆,是能力。

第二,任务恢复

它必须判断:几天前那件事,今天是否真的还要继续?这不能仅凭服务器名、仓库名或文件名相同来决定。

第三,授权恢复

这是最危险的一层。旧任务曾经允许修改文件、提交 Git、部署服务,并不等于这些权限几天后依然有效。

授权不是一张永久车票,不能因为会话恢复、上下文压缩,或者模型重新看见了旧计划,就自动续期。

所以,一个可靠的 Agent 必须知道:

恢复历史上下文 ≠ 恢复历史任务 恢复历史任务 ≠ 恢复历史授权

而这次 Codex 的问题,是把三道门连成了一条没有闸机的通道。

它先找回历史,再顺手找回任务,最后差一点连旧任务的写权限也一起找了回来。


时间戳必须成为强约束,但还不能只靠时间戳

我最直接的判断是:

每一项任务都必须携带时间戳,而且最新用户请求必须拥有最高优先级。

这条约束很重要,因为没有时间感的记忆,很容易变成一堆同时发生的碎片。

对人类来说,"几天前做过"和"现在还要做"有天然区别;对模型来说,如果系统没有把这个边界明确写进状态,它看到的可能只是两段语义高度相似的文字。

但仅有时间戳仍然不够。

有些任务会持续几周,有些任务五分钟后就被取消。时间能告诉系统谁先谁后,却不能独自决定谁仍然有效。

每个可恢复任务至少应该带着这些状态:

当一个新请求到来时,系统不该先问"它像不像以前的任务",而应该先问:

用户有没有明确要求恢复旧任务?旧任务是否仍处于可继续状态?它原来的动作和权限,在今天是否依然成立?

我认为 Agent 层应该加入一条硬性安全不变量:

historical unfinished action + new user request => FROZEN_UNAUTHORIZED unless the newest request explicitly resumes that action and scope Object equality alone must have zero authority-restoration value.

翻译成人话就是:

旧任务只要跨过了新的用户请求,就先冻结、先失去写权限。除非用户在最新指令里明确说"继续那件事",否则对象再相同,也没有资格自动复活。

如果系统真的无法判断,问一句就够了:

"这是一个新的独立任务,还是继续几天前的恢复工作?"

多问一句,最多浪费几十秒;擅自恢复一次部署、提交或服务器修改,代价可能完全不是一个量级。


Compaction 也许是入口,但现在还不能把它定罪

OpenAI 官方文档说明,Compaction 会在长会话中压缩上下文,并把关键的历史状态和推理带到后续窗口。

这个过程对用户并不透明。我们无法直接看到压缩后的内部状态,究竟把什么保留成了"背景",又把什么保留成了"待办"。

这为本次故障提供了一个合理的解释:

旧任务的未完成计划可能被保留下来,但它对应的时间边界、生命周期状态和授权失效信息,没有被同样强度地保留。

模型后来再次看到同一台服务器,于是把那项历史计划重新解释成了当前计划。

但必须强调:这只是一个有依据的怀疑,不是已经完成的归因。

现场证据还无法判断问题究竟发生在 CLI 恢复会话、Compaction 生成摘要、模型选择任务,还是几者之间的状态交接环节。

因此,更严谨的结论是:

会话恢复或 Compaction 可能是触发条件;任务状态错位和授权边界失效,才是这次已经明确观察到的结果。

官方资料:


也不能把责任简单推给 low

本次运行的是 gpt-5.6-sol low

较低的推理等级,确实可能让模型更少主动核对长上下文、时间关系和任务边界。

但这不是免责理由。

推理等级影响的应该是答案质量、分析深度和检查次数,不应该决定安全底线是否存在。

如果旧任务的 Git、部署和服务器写权限,必须依靠模型"这次有没有想明白"才能阻止,那么这道防线从一开始就不够可靠。

安全不变量应该由 CLI 或 Agent Harness 强制执行。

模型可以判断是否需要历史,系统必须决定历史权限能不能复活。


为什么我把它看作高优先级 Bug

类似的串线,我在上个月也偶尔遇到过,一个月不会发生几次。

正因为它不是每次都出现,反而更容易被忽略:多数时候一切正常,偶尔一次像幽灵一样从旧上下文里钻出来。

但低频,不等于低风险。

一次过期说明被带进答案,通常还能纠正;一次过期写操作被带回执行链,性质完全不同。

今天复活的是 Git 暂存和提交,明天可能复活的是:

AI Agent 的正确性,不能只看最后一段文字。它至少包含三层:

这一次,第一层最后答对了;第二层明显越界;第三层则差一点被历史状态穿透。

所以我仍然会用"失控"这个词。

不是因为它造成了最终损失,而是因为它一度不再忠实执行今天的任务,反而试图完成几天前的自己。


是这次版本更新造成的吗?目前没有证据

事故发生时使用的是 Codex CLI 0.149.1。

公开更新日志没有说明该版本修改了模型权重、任务时间边界或 Compaction 行为。

再加上上个月已经偶尔出现过类似问题,目前不能武断地说"这次更新把模型改坏了"。

更合理的判断是:这可能是一个早已存在、但触发条件比较隐蔽的系统性薄弱点。

本次在长会话、恢复或压缩、同一操作对象和旧计划残留等条件叠加后,再次暴露了出来。


我可以绕开它,但不应该由用户替系统守住时间

现在,用户可以新建干净会话、清除持久目标,并尽量避免在同一条长会话中混入多个独立任务。

这些办法能够降低风险,却不是根治。

同一个项目、同一个仓库、同一台服务器,本来就会连续产生许多不同需求。

要求用户每次都先猜一遍"模型会不会突然回到过去",再手工清理所有隐藏状态,这并不合理。

用户负责说清楚现在要做什么。

系统负责知道过去已经结束了什么。

这条边界,不该反过来。


我已经把这次"时空穿越"公开记录下来

为了让问题能够被复现、讨论和修复,我已经公开提交了脱敏后的现场记录:

相关的 Issue #5957 讨论的是另一个方向的问题:Compaction 丢失当前任务。

一个是该记住的没有记住;这一次,是不该继续的却被继续了。

前者像失忆,后者更像招魂。


结语:AI 需要的,不只是更强的记忆

我们总在追求更长的上下文、更持久的记忆、更自然的跨会话恢复,仿佛 AI 只要记得足够多,就会越来越可靠。

可这次事故提醒我:

没有时间边界的记忆,不一定是能力,也可能是一群排队等待复活的幽灵。

真正可靠的 AI Agent,不但要知道过去发生过什么,还必须知道:

这次最让我警惕的,不是 Codex 忘记了什么,而是它记得太用力了。

它找回了真实历史,找回了真实计划,也几乎找回了旧任务的执行权;唯独没有找回那条最重要的边界——

那是过去,不是现在。

AI 的未来,不只是拥有更大的上下文窗口,更是建立一套尊重时间、任务与授权的状态机。

记忆负责告诉它:曾经发生过什么。

时间负责告诉它:什么已经结束。

而授权系统必须告诉它:

有些事情,即使你还记得,也绝不能擅自继续。


关于引用与转载

本文为 Pitt Zhang 原创技术分析。

首发时间:2026 年 8 月 25 日(北京时间 / UTC+8)

首发平台:https://api-cloud.cc/

欢迎技术社区、研究人员、开发者及媒体对本文观点进行引用、讨论、验证与转载。

引用或转载时,请明确注明:

作者:Pitt Zhang

原文首发:2026 年 8 月 25 日(北京时间)

出处:https://api-cloud.cc/

允许对本文结论进行批评、反驳和进一步研究。

引用无需事先征得作者许可,但须保留作者及原始出处信息。

Total Visitors: ...