ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

atlas-checkpoint实现深潜:Blob、锁与时间线的存储架构全解

atlas-checkpoint实现深潜:Blob、锁与时间线的存储架构全解 atlas-checkpoint实现深潜Blob、锁与时间线的存储架构全解【免费下载链接】atlasSource control for agents. Use multiple coding agents, track their changes and query them in one place项目地址: https://gitcode.com/GitHub_Trending/atlas115/atlasAtlas是一款面向 AI 编程代理的源代码管理工具让你在一个界面里使用多个编码 Agent、追踪它们的每一次改动。它内部的核心记录模块atlas-checkpoint负责把 Agent 会话、工具调用与 Git 提交持久化为一份可回溯的「Agent 行为时间线」。本文用通俗的方式带你看懂它的三块存储基石Blob 溢出存储、WriterLock 写锁以及 Timeline 时间线读模型。为什么 Agent 也需要检查点Agent 会话是短暂的对话滚动消失后这段代码为什么这么写的答案就跟着丢失了。atlas-checkpoint 的模块说明把问题讲得很直白——六周后没有人能回答它当时为什么这么改包括写代码的那个人。它把整个工作区的历史记录到一个文件里.atlas/sessions.db一个 SQLite 数据库大文件则溢出到旁边的内容寻址目录中。模块文档列出了三条值得记住的设计承诺见 lib.rs脱敏发生在落盘之前而不是上传之前——本地记录本身永远不会泄露密钥全程不碰网络——离线是常态而非降级模式提交是被观察的不是被拦截的——不装 git hooks只观察引用refs移动所以即使你在终端里提交、或 Atlas 没开时提交也能事后关联到对应的会话。Blob 存储64KB 是那道分水岭为什么大内容要溢出这不是理论上的预防措施而是实测数据逼出来的在一个真实开发者的语料里294 个会话共705 MB最大的单条消息达2.02 MB——已经超过服务端单行上限。行内存储这种数据今天就必然失败来源blobs.rs 顶部注释。因此存储策略是常量值作用SPILL_THRESHOLD_BYTES64 KB超过此大小的消息体溢出为独立文件PREVIEW_BYTES2 KB溢出后留在行内的预览片段用于列表渲染绝大多数轮次只有几 KB留在数据库行内只需一次查询少数超大内容才付出一次查询 一次文件读取的代价。内容寻址用 SHA-256 当文件名溢出的文件存放在.atlas/blobs/下文件名就是内容的 SHA-256 十六进制哈希见 key_for。这带来两个好处天然去重相同内容写两次只占一个文件可校验读取时重新计算哈希发现内容对不上文件名就判定为损坏文件并删除下次写入同名内容会自动修复。写入过程也经过精心设计先写临时文件 →fsync→ 原子重命名。注释里点明了fsync是承重墙——断电时如果只重命名不刷盘内容寻址键下会留下一个永远不会被修复的半截文件因为put看到文件已存在就会直接短路返回。另外目录按哈希前两位十六进制做了分片path_for一位开发者一年会产生几十万个小文件不分片的话任何文件系统枚举它都很慢。WriterLock为什么多窗口应用必须单写者问题WAL 只让并发可能不保证一致Atlas 是多窗口应用两个窗口可以同时打开同一个工作区。SQLite 的 WAL 模式让读写并发成为可能但两个独立的写入进程会互相踩踏同步队列outbox的状态机——一行记录可能被 A 进程标记为已发送而 B 进程还在尝试发送它。解法一个永远不提交的独占事务锁的实现出人意料地简单lock.rs在一个旁边的小型 sidecar 数据库sessions.lock上持有一个BEGIN EXCLUSIVE事务直到进程退出。这个未提交的事务本身就是操作系统级的锁绝不阻塞抢不到锁的第二个窗口立刻降级为只读而不是挂起等待——等待中的窗口和卡死的窗口从外面看完全一样没有过期时间注释一针见血——超时恰恰是产生两个写入者的方式崩溃即释放进程崩溃时 OS 关闭句柄、事务回滚下一个窗口自然接管。主库sessions.db则按WAL synchronousNORMAL配置store.rs读时间线浏览从不阻塞写捕获NORMAL 比 FULL 少一次 fsync省下的那点持久性可以由从 Agent 原始转录重建兜底。Timeline把数据表读回人能看的故事时间线模块timeline.rs是整个 crate 唯一的读模型——一个没有查看器的记录器与一个坏掉的记录器没有区别。它提供两种形状SessionSummary每个会话一行列表视图用统计全部来自覆盖索引绝不读取消息正文SessionDetail单个会话的完整时间线把提示词、回复、思考、工具调用、检查点混排成一条有序列表。正文是条件内联的一条消息可能是 40 MB 的粘贴日志。时间线的规则是正文 ≤ 64 KBINLINE_LIMIT_BYTES就完整内联否则只发 2 KB 预览并打上truncated标记同时携带body_ref——那是指向 Blob 的钥匙查看器可以按需取回全文。这正是 Blob 溢出策略在读侧的镜像。排序键轮次优先于时间时间线最精妙的地方在排序函数order主排序键是turn_seq而不是时间戳。因为检查点是在提交被观察到时创建的可能比写入文件的那轮晚几分钟——只按时间排提交就会从产生它的工作旁边漂走。排序规则可以概括为轮次在前 → 同轮内按 提示词 → 思考 → 工具调用 → 回复 →检查点收尾→ 最后才看时间戳。无法归因到任何轮次的孤儿检查点用turn -1排到最顶部——属于任何轮次都不属于的提交恰恰是最需要开发者注意的情况把它沉底反而是错的。Agent 时间与墙钟时间是两回事会话统计里的active_secondsAgent 实际工作时间与wall_seconds首尾跨度刻意分开并配有两个上限保护IDLE_CAP_SECONDS消息间隔超过 300 秒算人走开了单轮超过 30 分钟视为休眠/崩溃后的事件不计入工作时间。V8 迁移脚本里还留了一个真实案例一份六月运行、七月导入的转录曾用updated_at - started_at报告出1395 小时的工作时长把一整年历史全塞进了今天分组见 schema.rs。检查点如何把提交认回会话提交与会话的关联规则是不对称的checkpoint.rs 顶部注释提交中改动过已存在的文件 → 仅凭文件路径就建立链接人工审改 Agent 产物再提交是正常流程文件是新建的 → 提交内容必须与 Agent 写入时的哈希完全一致否则不算 Agent 的功劳防止Agent 建了文件、人删掉重写了被记到 Agent 头上。每次扫描从上次游标走到 HEADwalk_new_commits游标丢失时做一次最多 200 个提交的有界重扫宁可重放也不允许静默失明。合并提交本身不产生检查点——那会把合进来的每个改动记第二次账——但会评估合并带入的侧分支提交保证每份工作在真正产生它的那个提交上被记录恰好一次。从哪些文件开始读如果你想亲手验证本文的每个结论crate 刻意不依赖 Tauri测试直接驱动真实临时目录、真实 SQLite 和真实 git 仓库是极佳的阅读入口模块路径看点模块总纲与设计承诺lib.rs三句话讲清定位Blob 溢出与内容寻址blobs.rsfsync、去重、分片单写者锁lock.rs99 行读完一个分布式锁本地存储与 WAL 配置store.rs打开、只读降级、事务粒度表结构与迁移策略schema.rs索引即 schema 的一部分时间线读模型timeline.rs排序规则与统计口径会话捕获与脱敏capture.rs唯一的写入入口提交观察与关联checkpoint.rs非对称链接规则端到端测试tests/捕获、时间线、绑定、健康检查小结atlas-checkpoint 用三件朴素而扎实的工具解决了 Agent 记录的难题Blob64 KB 阈值 SHA-256 内容寻址让 705 MB 的语料也能稳定落盘锁一个永不提交的独占事务换来无依赖、自动释放、崩溃安全的单写者保证时间线轮次优先的排序与条件内联的正文让提交紧跟在产生它的工作之后成为默认视图。它们共同守护的是模块文档开篇那句承诺无论六个月后谁来打开这个工作区当时为什么这么写的答案都还在。【免费下载链接】atlasSource control for agents. Use multiple coding agents, track their changes and query them in one place项目地址: https://gitcode.com/GitHub_Trending/atlas115/atlas创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进