how to implement checkpoint system like cursor in my own agent

Let me first describe this feature. You can use Cursor’s Withdraw function to easily roll back to a certain preview question and redesign your prompt. This is a feature that I use regularly, so I want to know how to implement it in my own three-agent workspace. Simply put, it is one workspace with three different responsibilities where the agents coordinate on one task.

So how do I implement this withdraw function in this architecture? This function is also needed when an agent produces something that you don’t want, allowing you to simply go back to the state where the wrong patch had not been generated. Basically, there are two things at the bottom level: the files should be moved back to the past state, and the conversation state must be restored to the previous state.


英文说太累了。

第一个就是 file 怎么 restore。这个非常简单:你只需要在 Agent 调用 write 工具写入一个 file,或者调用 edit 工具编辑一个 file 的时候,在这个 hook 前面去包装一个记录操作。相当于什么呢?就是你在做修改之前,先预判它可能会改坏你的文件。

那这个记录操作有几个问题:记录什么?怎么存储?怎么存储能够有利于 restore? 同时,write 和 edit 看起来又不太一样。比如 edit 是修改一个已经存在的文件,而 write 可能是在新建一个文件。所以我就在 write 和 edit 前面统一包一层 snapshot。

这个 snapshot 记录什么?记录的是某个 path 在本 round 开始前的状态。一个 round 就可以理解成一次工作的开始。如果这个文件本来存在,我就记录它的 path 和 content hash;如果这个文件是新建的,也就是修改前根本不存在,那我就记录这个 path,再加一个标志位,比如 absent=1

那具体的文件内容放哪里?文件内容本体还是完整文件,只是先算一个 SHA,然后存到这个项目独立的目录下面,比如 objects/<sha>。然后 path 对应哪个 SHA,或者它是不是 absent,这个索引就存在 SQLite 的 checkpoint_files 表里。

这样 restore 就很简单了:我只需要遍历这个 checkpoint 下的 checkpoint_files 记录。拿到 SHA 以后,就去 objects/<sha> 里把完整文件内容读出来,再把完整 bytes 写回原来的 path;如果看到 absent=1,说明这个文件在这一轮开始前根本不存在,那 restore 的时候就直接把它删掉。

所以 writer 和 editor 的记录机制实际上没有区别,都是写前 snapshot。这样做的好处是,虽然我们的 restore 操作有点像 Git,但我们完全没有碰 Git,也不需要做复杂的 diff、patch,或者处理 patch 合并失败的问题;同时我们又不是把整个项目完整地做一份 snapshot,而是只记录这一轮真正被 write/edit 碰过的文件。

第二个就是context怎么restore呢,context就是给模型看到的内容,我们已经回退了,模型就像是从未来回到现在 或者现在回到过去 面对现在的他 如果你不做手段 他可能会继续沿着一条推理/working Trajectory去重复做已经证明是我们不想要的patch操作。我们需要让他有一个可以从头来过的机会选择,我的操作是选择在这里给他注入一条system reminder

还有一种操作是,让他做多一次的总结。把失败过程通过另外一个Agent,做结构化的summary,比如记录他改了什么,为什么会失败,哪些约束比现在表达的信息更为丰富。但是实现起来更复杂,效果也不一定会好,用户不一定是因为他失败了(有失败原因)而可能就是不满意(所以总结不出来原因)。所以不仅我没有恢复原始的tool history,我还用一个最小的代价吧,就是告诉他,他接下来的这一条路是行不通的 这个system reminder 我会告诉他以失败路径禁止闯入。你在本轮已经尝试修改他是用的是edit file或write file,现在需要重新决策,你可以停下来问用户,不要再沿着同一假设小修,大概每一条失败的形式会是path+tool name和result

优化方面,比如就是一种很少见的情况,优化也不是很大,你前后两轮的一个文件,内容是一样的,在这个轮的进行过程中被touch过的,他只会存一份,另外,这也是一种按需处理,你只对这一轮touch过的文件做SQLite的记录,还有一个objects原文的记录,如果你真的回退了,他会把那个都删了,另外,如果他每一轮动过的话,他才会记录一个checkpoint,如果checkpoint超过40个,设置为40个,那么就会只保留近40个,前面的轮次就不让回退了

0%