09 Claude Code Checkpoint 实现原理深度解析

发布时间:2026/7/18 20:34:58

09 Claude Code Checkpoint 实现原理深度解析
Claude Code Checkpoint 实现原理深度解析从撤销代码修改到三层存储机制一次深入底层的技术之旅引言当你说撤销时发生了什么想象这样一个场景你正在用 Claude Code 写代码AI 帮你改了三个文件。你看了眼输出发现改动方向错了想回到修改前的状态。如果是在传统编辑器里你会按 CtrlZ撤销。但在对话式编程工具里撤销意味着什么你需要的不只是文件回滚还有对话上下文的回退。这就是 Claude Code Checkpoint 要解决的问题。但实现起来没那么简单。如果每次对话都保存所有文件的完整内容体积会爆炸如果只保存对话文件状态又恢复不了。Claude Code 的解决方案是——两个独立的系统三层存储机制。这篇文章会带你深入底层看看这个设计是怎么做到既轻量又完整的。一、整体架构三个独立的层Claude Code 的 Checkpoint 系统分三层每层都有自己的触发时机、作用和侵入方式。┌─────────────────────────────────────────────────────────────────┐ │ Layer 1: 追踪层 (Track Edit) │ │ 触发时机: 工具修改文件前 │ │ 作用: 创建单文件备份记录变更历史 │ │ 侵入方式: 同步前置钩子阻塞式 │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Layer 2: 快照层 (Make Snapshot) │ │ 触发时机: 用户消息处理完毕后 │ │ 作用: 为所有追踪文件创建完整快照建立恢复点 │ │ 侵入方式: 异步后置钩子非阻塞 │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Layer 3: 持久化层 (Record Snapshot) │ │ 触发时机: Layer 1 或 Layer 2 完成后 │ │ 作用: 将快照写入 JSONL transcript支持会话恢复 │ │ 侵入方式: 异步追加写入不阻塞主流程 │ └─────────────────────────────────────────────────────────────────┘看到这儿你可能会有疑问为什么需要三层一层不够吗答案是不够因为它们解决的是不同的问题。二、Layer 1追踪层——修改前的保险2.1 触发时机每次文件修改前Layer 1 的核心职责是在任何文件被修改前先备份它。比如你让 Claude Code 修改a.py系统会在实际修改前先调用fileHistoryTrackEdit()// FileEditTool.ts:416if(fileHistoryEnabled()){awaitfileHistoryTrackEdit(updateFileHistoryState,absoluteFilePath,parentMessage.uuid,// 绑定到当前对话消息);}// 之后才是实际的文件修改awaitfs.mkdir(dirname(absoluteFilePath))// ... read-modify-write ...注意这里的await——这意味着备份是同步阻塞的。如果备份失败后续的文件修改不会执行。这是为了保护数据一致性。2.2 为什么必须同步阻塞假设改成异步voidfileHistoryTrackEdit(...)// ❌ 不等待awaitmodifyFile(...)// 可能先于备份完成结果是什么备份可能捕获的是修改后的状态而不是修改前的状态。这就失去了备份的意义。所以 Layer 1 必须是同步阻塞的确保修改前的状态被正确捕获。2.3 备份流程Layer 1 的备份流程分四个阶段检查是否需要备份如果文件已在最近快照中追踪跳过避免重复备份创建物理备份~/.claude/backups/{file_hash}/v1更新内存状态FileHistoryState.backups[path] {...}持久化到 transcript写入 JSONLisSnapshotUpdatetrue关键设计幂等性如果文件已在快照中跳过内容哈希备份文件名包含内容哈希相同内容不重复备份版本号从 v1 开始每次修改递增三、Layer 2快照层——建立一致的恢复点3.1 触发时机每条用户消息后Layer 1 只是记录每次文件修改但如果你想恢复到某个对话点你需要的是所有文件的完整状态。这就是 Layer 2 的作用在每条用户消息处理完毕后为所有追踪文件创建完整快照。// handlePromptSubmit.ts:556if(fileHistoryEnabled()){newMessages.filter(selectableUserMessagesFilter).forEach(message{voidfileHistoryMakeSnapshot(// ← 异步不等待updateFileHistoryState,message.uuid,);});}注意这里的void——Layer 2 是异步非阻塞的。为什么3.2 为什么可以异步非阻塞因为 Layer 2 是锦上添花不影响数据一致性。即使 Layer 2 失败Layer 1 的备份已经存在每个文件都有独立的备份用户可以手动恢复到最近的 Layer 1 备份只是少了一个完整的恢复点所以权衡之下Layer 2 选择异步非阻塞不阻塞用户体验。3.3 完整快照流程Layer 2 的快照流程遍历所有追踪文件检查哪些文件自上次快照后发生了变化通过 mtime 判断为变化文件创建新备份版本号递增如 v1 → v2创建完整快照记录包含所有追踪文件的状态持久化到 transcript写入 JSONLisSnapshotUpdatefalse关键设计增量检测通过 mtime 判断文件是否变化避免重复备份完整快照包含所有追踪文件的状态形成一致的恢复点版本递增每个文件独立维护版本号四、Layer 3持久化层——把快照写入磁盘4.1 触发时机Layer 1/2 完成后Layer 3 的职责很简单把快照写入 JSONL transcript。// sessionStorage.ts:1098-1112asyncinsertFileHistorySnapshot(messageId:UUID,snapshot:FileHistorySnapshot,isSnapshotUpdate:boolean,// 区分 Layer 1 (true) vs Layer 2 (false)){awaitthis.appendEntry(fileHistoryMessage)// 追加写入 JSONL}注意Layer 1 和 Layer 2 都用void调用 Layer 3异步非阻塞。4.2 为什么选择追加写入如果改成重写整个文件awaitrewriteEntireFile(...)// ❌ 每次都重写结果文件越来越大时写入越来越慢每次修改都要等待 I/O 完成用户体验差追加写入的好处性能高只在文件末尾追加一行不阻塞主流程后台异步写入恢复友好重放 transcript 即可重建状态4.3 Transcript 格式Layer 3 写入的 JSONL 格式{type:file-history-snapshot,messageId:msg-002,snapshot:{...},isSnapshotUpdate:true} {type:file-history-snapshot,messageId:msg-003,snapshot:{...},isSnapshotUpdate:false}isSnapshotUpdate的含义trueLayer 1 的增量备份单文件falseLayer 2 的完整快照所有文件五、三层协作一个完整的时间线让我们看一个完整的场景用户说 “修改 a.py 和 b.py”。T0: 用户输入 修改 a.py 和 b.py │ T1: LLM 决定修改 a.py │ ├─ ★ Layer 1: Track Edit (a.py) ★ │ └─ 创建备份 a.pyv1 │ └─ ★ Layer 3: Record Snapshot (异步) ★ │ └─ 写入 JSONL: {isSnapshotUpdate:true, ...} │ T2: 实际修改 a.py │ T3: LLM 决定修改 b.py │ ├─ ★ Layer 1: Track Edit (b.py) ★ │ └─ 创建备份 b.pyv1 │ └─ ★ Layer 3: Record Snapshot (异步) ★ │ T4: 实际修改 b.py │ T5: LLM 响应完成 │ T6: 用户消息处理完毕 │ └─ ★ Layer 2: Make Snapshot (msg-001) ★ ├─ 检查 a.py 是否变化 → 是 → 创建 a.pyv2 ├─ 检查 b.py 是否变化 → 是 → 创建 b.pyv2 └─ ★ Layer 3: Record Snapshot (异步) ★ └─ 写入 JSONL: {isSnapshotUpdate:false, ...}六、Checkpoint vs Transcript两个独立的系统这是很多人容易混淆的地方Layer 2 保存工具调用信息吗答案❌ 不会。Layer 2 只保存文件快照文件路径 版本号 时间戳不保存工具调用信息。完整的对话记录包括工具调用由独立的 Transcript 持久化系统保存。6.1 为什么需要两个独立的系统File History System文件快照系统目标支持/rewind恢复文件状态粒度文件级物理备份数据文件路径 版本号 时间戳触发文件修改前 消息后Transcript System对话持久化系统目标支持会话恢复、历史重放、审计粒度消息级完整对话数据消息内容 工具调用 工具结果 父子链触发每条消息生成时6.2 如果合并会怎样如果 File History 也保存对话内容❌ 每次文件修改都要保存完整的对话 → 体积爆炸❌ 同一文件修改 10 次 → 10 个完整副本❌ 文件内容可能很大MB级→ Transcript 过大如果 Transcript 也保存文件内容❌ 每条消息都要保存所有文件的完整内容 → 体积爆炸❌ 对话历史被文件内容污染 → 难以阅读和审计分离设计File History只保存引用轻量 ~200 bytes支持文件恢复Transcript只保存对话适中 ~500 bytes支持会话恢复6.3 两者的协作messageId 的桥梁作用messageId在两个系统间起到桥梁作用恢复流程用户执行/rewind msg-002在 Transcript 找到 msg-002 → 确认对话位置在 File History 找到messageIdmsg-002的快照应用快照到文件系统 → 恢复文件状态可选在 Transcript 删除后续消息 → 恢复对话历史七、设计精髓总结7.1 三层设计的权衡层级侵入方式阻塞性为什么这样设计Layer 1同步前置钩子✅ 阻塞保证修改前状态被捕获Layer 2异步后置钩子⚠️ 非阻塞不影响用户体验“锦上添花”Layer 3异步追加写入⚠️ 非阻塞性能优先持久化是锦上添花7.2 两个系统的职责分离系统保存内容粒度目标File History文件备份路径 版本号文件级支持文件恢复 (/rewind)Transcript完整对话 工具调用消息级支持会话恢复、历史重放7.3 适用场景✅交互式编程助手Claude Code、Cursor 等✅对话式工具需要撤销栈的场景✅轻量级版本控制会话级非跨会话❌团队协作应用 Git❌长期历史管理应用 Git结语Claude Code 的 Checkpoint 定位很多人会问这和 Git 有什么区别定位差异Checkpoint对话上下文的撤销栈面向单用户、单会话Git版本控制的提交图面向团队、长期历史Checkpoint 的设计目标不是替代 Git而是为对话式编程提供轻量级的撤销能力。它解决的是刚才 AI 改错了我想回退的场景而不是三个月前的代码长什么样的问题。如果你正在构建类似的对话式工具这个三层存储机制 双系统分离的设计值得参考。附录最简 Python 实现如果你想快速理解 Checkpoint 的核心逻辑这里有一个最简实现~280 行classFileCheckpoint:deftrack_edit(self,file_path:Path,message_id:str)-None:编辑前追踪文件Layer 1# 检查是否需要备份iffile_pathinself.state.backups:return# 创建备份backup_pathself.backup_root/f{file_path.name}v1shutil.copy2(file_path,backup_path)# 更新内存状态self.state.backups[file_path]{version:1,backup_path:str(backup_path),}# 持久化到 transcriptself._record_snapshot(message_id,is_updateTrue)defmake_snapshot(self,message_id:str)-None:创建完整快照Layer 2tracked_file_backups{}forfile_pathinself.state.tracked_files:# 检查文件是否变化ifself._has_changed(file_path):# 创建新备份next_versionself.state.backups.get(file_path,{}).get(version,0)1backup_pathself.backup_root/f{file_path.name}v{next_version}shutil.copy2(file_path,backup_path)tracked_file_backups[file_path]{version:next_version,backup_path:str(backup_path),}# 持久化到 transcriptself._record_snapshot(message_id,is_updateFalse)defrewind(self,message_id:str)-dict:恢复到指定消息的快照target_snapshotself._find_snapshot(message_id)ifnottarget_snapshot:return{error:Snapshot not found}forfile_path,backupintarget_snapshot.backups.items():# 恢复文件shutil.copy2(backup[backup_path],file_path)return{success:True}参考资源Claude Code 源码src/utils/fileHistory.tsTranscript 持久化src/utils/sessionStorage.ts三篇分析文档见本文开头引用

相关新闻

Codex 配置 OpenAI 兼容 Base URL:Windows 下从安装到跑通项目分析

Codex 配置 OpenAI 兼容 Base URL:Windows 下从安装到跑通项目分析

2026/7/8 3:24:57

Codex 配置 OpenAI 兼容 Base URL:Windows 下从安装到跑通项目分析 最近在 Windows 上重新整理了一遍 Codex 的配置流程,发现新手最容易卡住的不是提示词,而是本地环境、配置文件和 base_url。 这篇文章按实操顺序记录一遍: 安装 …

智能车极速越野组:BLDC六步换相 vs FOC 电机控制方案3大性能对比

智能车极速越野组:BLDC六步换相 vs FOC 电机控制方案3大性能对比

2026/7/7 1:35:52

智能车极速越野组:BLDC六步换相与FOC电机控制方案的深度性能对比在智能车竞赛的极速越野组别中,电机控制方案的选择直接决定了车辆的动力性能和赛道表现。面对复杂的室外地形和高速行驶需求,工程师们常常在BLDC六步换相方波控制与FOC&#xf…

kail_dnn_adapter最新特性解析:2025.12.30接口更新与功能增强详解

kail_dnn_adapter最新特性解析:2025.12.30接口更新与功能增强详解

2026/7/8 5:24:30

kail_dnn_adapter最新特性解析:2025.12.30接口更新与功能增强详解 【免费下载链接】kail_dnn_adapter Adapter for Kunpeng Deep Neural Network Library 项目地址: https://gitcode.com/openeuler/kail_dnn_adapter 前往项目官网免费下载:https:…

使用PLUS过程中遇到的问题

使用PLUS过程中遇到的问题

2026/7/18 20:33:40

GPT Plus 对开发人员非常友好,但是中途会遇到一些问题,接下来盘点一下可能会遇到的问题,帮助大家避坑!! 1、保持网络环境稳定,这一点很重要 2、想用PLUS但是苦于没有国外的卡,这部分我后期补充…

从业心得:一次性涌入上万条线索,企业最稳的获客打法是什么?

从业心得:一次性涌入上万条线索,企业最稳的获客打法是什么?

2026/7/18 20:33:40

做企业营销和拓客这么多年,我发现一个非常扎心的行业通病:绝大多数公司的获客困境,都卡在“线索和人力不匹配”上。 线索少的时候,销售没客户跟进,团队不稳定、业绩上不去;可一旦渠道投放起量、活动引流爆单…

本地进程缓存

本地进程缓存

2026/7/18 20:33:40

本地进程缓存时使用本地的JVM资源去存储数数,减少数据库和redis等中间件的压力,并且远程中间件与进程之间传输数据会经过网络传输,会影响一定的性能,为了加快数据响应提高用户使用体感,我们可以引入本地进程缓存来加快…

2026 烘箱烤炉传感器怎么选 M50稳定运行3年零故障案例

2026 烘箱烤炉传感器怎么选 M50稳定运行3年零故障案例

2026/7/18 20:33:40

深圳某锂电池模组制造企业,主要为国内 top5 新能源汽车厂商供应电池 Pack。## 前言 在锂电池制造过程中,烘箱产线扮演着至关重要的角色。电池模组在组装后需要经过高温烘烤工序,以固化胶水、去除水分、确保电芯之间的紧密粘合,这对…

【桁架】基于matlab三维桁架优化以实现最大刚度【含Matlab源码 15893期】含报告

【桁架】基于matlab三维桁架优化以实现最大刚度【含Matlab源码 15893期】含报告

2026/7/18 20:33:40

💥💥💥💥💥💥💞💞💞💞💞💞💞💞欢迎来到海神之光博客之家💞💞💞&#x1f49…

黑灯工厂中AI视觉检测的实现条件——AI检测的检出边界与工程原理

黑灯工厂中AI视觉检测的实现条件——AI检测的检出边界与工程原理

2026/7/18 20:23:40

黑灯工厂中检测工位的无人化依赖设备的自动亮度监测、自动相机校准和次品收集的自动切换三个条件。AI检测的检出边界由光源方案决定,只能检表面可见的缺陷。以下以磁材企业和密封件集团的设备方案为样本做分析。黑灯运行的光源自动监测LED光源在数万小时工作后亮度衰…

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

2026/7/18 14:07:00

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator作为Unity游戏社区中最成熟的文本翻译解决方…

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

2026/7/17 17:34:09

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持 【免费下载链接】raspberrypi-kernel It provides openEuler kernel source for Raspberry Pi 项目地址: https://gitcode.com/openeuler/raspberrypi-kernel 前往项目官网免费下载&…

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

2026/7/18 5:51:23

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧 【免费下载链接】integration-test The repo contains test suits for system integration test 项目地址: https://gitcode.com/openeuler/integration-test 前往项目官网免费下载:…

从模糊意图到可执行指令:Claude PRD中Prompt Engineering与需求颗粒度的5级映射法则

从模糊意图到可执行指令:Claude PRD中Prompt Engineering与需求颗粒度的5级映射法则

2026/7/18 0:02:02

更多请点击: https://kaifayun.com 第一章:从模糊意图到可执行指令:Claude PRD中Prompt Engineering与需求颗粒度的5级映射法则 在Claude驱动的产品需求文档(PRD)生成实践中,原始业务意图往往以自然语言片…

Cursor配置生成失效?3大隐藏陷阱+4行修复代码,资深工程师连夜整理的紧急补救清单

Cursor配置生成失效?3大隐藏陷阱+4行修复代码,资深工程师连夜整理的紧急补救清单

2026/7/18 0:02:02

更多请点击: https://codechina.net 第一章:Cursor配置生成失效?3大隐藏陷阱4行修复代码,资深工程师连夜整理的紧急补救清单 Cursor 配置生成突然失效,是近期高频报障场景。表面看是 cursor.config.json 未更新或 LSP…

某智驾大牛创业

某智驾大牛创业

2026/7/18 0:02:02

作者:钟声编辑:Mark出品:红色星际头图:智能驾驶图片据悉,国内某头部智驾公司端到端模型技术大牛Z投身创业,并且已经拿到融资。Z不仅是该头部公司内部最年轻的对标阿里P10级别技术负责⼈,更是业内…