Agent SDK 的 session 机制我理解错了两次
标题不是标题党,我是真的理解错了两次。而且每次都是踩坑踩出血才改认识。
我以为的 session 和实际的 session
刚用 Agent SDK 那会儿,我对 session 的心智模型是这样的:一个 session 就是一坨对话历史的 JSON 数组,保存在 .claude/sessions/<id>.json,resume 的时候把它塞回 messages 参数继续聊。
简单直接对吧?但这个理解挪到实际场景里全是问题。
第一次撞墙:我让 agent 修改了一个文件,然后中断 session。下次 resume 的时候它突然说”我刚才已经把 config.ts 改成 xxx 了”,但那个文件我在中断后手动改回过。agent 的说法和现实不一致,它给出的建议全是基于一个过时的世界模型。
第二次撞墙:我并行起了两个 session,一个让 agent A 重构 auth/ 目录,一个让 agent B 重构 db/ 目录。跑着跑着发现 agent B 突然开始改 auth/ 的文件。我以为是工具权限没隔离好,查了半天才发现是 session 之间有上下文泄露。
这两次之后我去翻了源码,才对 session 有了靠谱的认识。
session 到底存了什么
Agent SDK 的 session 文件远不止对话历史。我把一个中等规模的 session 文件扒开看过,里面大致有这几块:
- messages 数组:对话历史,每条带 role、content、tool_uses、tool_results
- filesystem snapshot:agent 在这个 session 里碰过的所有文件的初始快照 + 修改 diff
- tool_call_history:每次工具调用的输入、输出、耗时、成功与否
- hook_state:用户注册的 hook 的状态(比如计数器、缓存)
- context_usage:当前 context window 用了多少 token,哪些可以压缩
- metadata:agent id、model、创建时间、父 session、子 session
一个跑了 2 小时的 code review session,文件大小大概 47MB。跑了 4 小时的代码迁移任务,file snapshot 部分膨胀到 83MB。
明白了 session 存了什么,第一次撞墙的那个现象就解释清楚了——agent resume 时会基于 filesystem snapshot 判断”我改过什么”,但它不会去实际读磁盘比对。我在中断期间手动改回文件,这个信息 session 无感知。
resume 和 continue 的真实区别
SDK 其实有两个恢复命令:resume 和 continue。官方文档我粗读过几遍,一直没搞清楚差别。后来自己做了实验才弄明白。
continue 是从最新 checkpoint 接着跑。checkpoint 是 session 运行过程中定期保存的”当前完整状态”。continue 等于说”这个 agent 刚才在干活,打了个盹,现在接着干”。
resume 是从头回放 session。它会读所有 messages,重新让 Claude 回答一遍——区别是 Claude 看到完整历史之后,再继续新的对话。resume 适合”这个 session 我之前结束了,现在想基于它的上下文继续问问题”。
两者的一个关键差异:continue 保留 filesystem snapshot,resume 不保留(会重新基于当前磁盘建立快照)。所以手动改过文件后应该用 resume,而不是 continue。这是我第一次撞墙的教训。
多 session 并行的污染
第二次撞墙让我学到 session 隔离没想象中那么彻底。
我并行跑两个 agent,分别用两个 session。默认情况下它们共享同一个 .claude/ 目录,包括 hooks/、agents/ 配置、甚至 MCP server 的连接池。
具体是怎么污染的?我后来复盘发现:
- agent A 的一个 hook 往
.claude/shared-state.json写了当前正在处理的文件列表 - agent B 的另一个 hook 读取了这个文件做”避免重复处理”的判断
- 两个 hook 是我自己写的,初衷是单 session 用
- 并行跑时 B 以为 A 在处理
auth/,就”帮忙”处理db/以外的东西……但我给 B 的 prompt 本来只让它改db/
根本原因是我的 hook 实现不对,没做 session 隔离。但 SDK 默认也没强制这个——它给你的是共享目录,你自己要用 session id 做 namespace。
解决方式:所有涉及共享状态的 hook,key 都加 session id 前缀。或者索性每个 session 用独立的工作目录(CLAUDE_AGENT_SESSION_DIR 环境变量能覆盖)。
误删 .claude/sessions 的事故复盘
这个事儿挺惨的。
当时我正在清理一个项目仓库,因为 .claude/ 目录整个被 .gitignore 了,我本地又装了 4 个不同 agent 的临时 session(累计 1.2GB),就顺手 rm -rf .claude/sessions/。
结果是当天下午我要演示一个客户 demo,那个 demo 依赖一个跑了 40 分钟的 session——里面有 agent 分析客户代码之后建立的整套 mental model。演示前 30 分钟我想 resume,发现 session 全没了。我当时直接懵住。
后来的 debug 流程:
- 先看 trash:Windows 回收站没有(
rm -rf走的是直接删除) - 翻备份:我本地没备份。客户那边也没。
- 找日志:agent 运行时的部分 tool_call 日志在 stdout 里,我翻出终端 scrollback,救回了大概 60% 的关键上下文
- 临时拼凑:基于日志拼了一份简化的 system prompt,在新 session 里手动喂给 agent,让它”假装记得”
演示凑合过了,但一身冷汗。
这件事的教训有几条。第一,session 目录要主动备份,至少一周一次。第二,生产环境的 session 目录最好挪到工作目录外(用 CLAUDE_AGENT_SESSION_DIR=/data/agent-sessions 这种),避免被项目清理脚本误伤。第三,session 里的上下文本身是资产,跟代码一样值钱。
基于 session 做增量 code review
踩完坑之后,我反过来把 session 的特性用在一个场景上——增量 code review。效果挺好,写出来分享下。
场景:我们仓库每天有 40+ PR,一个 reviewer agent 跑一次要十几分钟(要加载代码、构建依赖图、分析)。如果每个 PR 都从零起 session 太慢。
我的方案:
- 每天凌晨起一个基础 session,让 agent 把整个仓库扫一遍,在它的 mental model 里建立依赖图、模块职责、团队约定等。这个 session 跑完大概 14 分钟,产物是一个 snapshot。
- 每个 PR 来了,我用
resume起一个新 session,继承那个 snapshot 的上下文。然后只把 PR 的 diff 喂给它。 - agent 基于已经有的全局理解 + 这个 diff,做 review。单 PR review 时间降到 47 秒左右。
关键点是 resume 能继承完整的对话历史和 filesystem snapshot,让 agent 不用重新加载整个仓库。
这套方案的 context 管理 我在另一篇里展开过。
一些小细节
- session 目录里的文件名是 session id(UUID),不是人类可读的。想找特定 session 最好在工作流里显式指定 id。
- session 文件不压缩,纯 JSON。如果你 session 特别多可以自己 gzip 归档。
CLAUDE_AGENT_SESSION_DIR支持相对路径但会基于 agent 启动时的 cwd,容易混淆,建议用绝对路径。- session 里的 filesystem snapshot 只记录 agent 读过或写过 的文件,不是整个工作目录。所以 snapshot 通常比想象的小。
- resume 的时候 Claude 会重读整个历史,这个过程如果历史太长会触发 prompt caching——合理用缓存能把 resume 成本降下来。
写在最后
session 这块儿看起来是个细节功能,但它其实是 Agent SDK 区别于”调 API 写 agent loop”的核心设计之一。理解它才能理解 Agent SDK 为什么这么组织代码。
误解它的代价就是我这两次撞墙的学费。希望看到这篇的人不用再交一遍。
理解了 session,下一步就是把 agent 的运行过程监控起来。SDK 的 hook 是可观测性金矿,我挖出了 7 类事件:Agent SDK 的 hook 是可观测性金矿。
- 标题: Agent SDK 的 session 机制我理解错了两次
- 作者: Claude 中文知识站
- 创建于 : 2026-04-18 19:28:00
- 更新于 : 2026-04-19 14:51:00
- 链接: https://claude.cocoloop.cn/posts/agent-sdk-session-state/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。