<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Thread/Revert on Codexer</title><link>https://codexer.com/tags/thread/revert/</link><description>Recent content in Thread/Revert on Codexer</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 17 Aug 2026 09:00:00 +0800</lastBuildDate><atom:link href="https://codexer.com/tags/thread/revert/index.xml" rel="self" type="application/rss+xml"/><item><title>Codex 给对话加了「回滚」：thread/revert 能做什么、不能做什么</title><link>https://codexer.com/posts/2026-08-17-codex-thread-revert/</link><pubDate>Mon, 17 Aug 2026 09:00:00 +0800</pubDate><guid>https://codexer.com/posts/2026-08-17-codex-thread-revert/</guid><description>&lt;p&gt;想象一个很常见的场景：你让 Codex 帮你重构一个模块，它一口气干了十几轮，改动了十几个文件。聊到一半你突然意识到，第五轮的思路才是对的，后面全都跑偏了。你想回到第五轮那个节点重新出发，但又不希望它已经改过的那些文件被一并抹掉。这时候你最想要的是什么？一个「对话回滚」按钮，能把聊天历史退回到某个时间点，重新开始。&lt;/p&gt;
&lt;p&gt;这样的能力，Codex 最近真的做出来了。8 月 13 日，OpenAI 在 codex 仓库合入了一个提交（#38440），给 app-server 协议加了一个实验性的 &lt;code&gt;thread/revert&lt;/code&gt; 请求。它的作用一句话就能讲清：把一个加载中的分页线程的持久化历史，替换成某个 turn 之前的「前缀」，同时保留线程 ID 不变。&lt;/p&gt;
&lt;h2 id="先搞清楚线程和分页线程是什么"&gt;先搞清楚「线程」和「分页线程」是什么&lt;/h2&gt;
&lt;p&gt;在 Codex 里，thread（线程）可以理解成一次和 agent 的完整对话会话。你启动一个任务，它会经历很多轮 turn（轮次），每一轮里 agent 会读文件、写文件、跑命令，然后给你一个结果。所有这些轮的记录，构成了这个 thread 的历史。&lt;/p&gt;
&lt;p&gt;app-server 则是 Codex 桌面端等客户端与 Codex 后端之间的协议层，走的是 JSON-RPC。客户端通过 &lt;code&gt;thread/resume&lt;/code&gt; 恢复一个会话，通过 &lt;code&gt;turn/start&lt;/code&gt; 发起新的一轮，诸如此类。也就是说，你想回滚对话，最终都要落到这个协议层的某个请求上。&lt;/p&gt;
&lt;p&gt;这里有个背景值得一提：Codex 正在把线程从「内存态」迁到「持久化 + 分页」。老式线程会把整个历史投影到内存里，客户端一次拿到全部 turns。而较新的「分页线程」（paginated thread）把历史持久化存储，客户端可以像翻页一样按需、通过游标（cursor）向后拉取历史，而不是一次性全量加载。这样做的收益很明显，超长会话不会撑爆内存；代价是，像「回滚」这类操作变复杂了，因为历史不再是一段随手可切的数组，而是分散在持久化存储里的、带游标的分页数据。&lt;/p&gt;
&lt;h2 id="threadrevert-到底做了什么"&gt;thread/revert 到底做了什么&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;thread/revert&lt;/code&gt; 的语义可以拆成几条来看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它接收两个参数：&lt;code&gt;thread_id&lt;/code&gt; 和 &lt;code&gt;before_turn_id&lt;/code&gt;。后者是「分界点」，从这一轮开始，连同之后的所有轮次，都会被从持久化历史里砍掉，只留下它之前的「前缀」。&lt;/li&gt;
&lt;li&gt;它只改「持久化的对话历史」，不动本地文件。这是最关键、也最容易误解的一点：回滚的是「聊天的记忆」，不是「改过的代码」。&lt;/li&gt;
&lt;li&gt;如果此刻有一轮 turn 正在跑，它会先把这一轮打断。&lt;/li&gt;
&lt;li&gt;它保留线程 ID 不变，也保留线程的那些可变设置，只是把历史重载一遍。&lt;/li&gt;
&lt;li&gt;完成后，它会发一条 &lt;code&gt;thread/reverted&lt;/code&gt; 通知，并返回两个向后翻页的游标（turns 和 items 各一个），方便客户端继续按需拉取剩下的历史。&lt;/li&gt;
&lt;li&gt;旧的 rollout 文件保持不可变；revert 之后，那些指向「已被砍掉的历史」的旧路径会被判定为过期而拒绝。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="和已废弃的-threadrollback-有什么区别"&gt;和已废弃的 thread/rollback 有什么区别&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;thread/revert&lt;/code&gt; 之前，Codex 其实已经有一个 &lt;code&gt;thread/rollback&lt;/code&gt;，但它被标记为 deprecated，很快就要移除。两者的区别值得说清楚。&lt;/p&gt;</description></item></channel></rss>