Sessions

元数据与落盘

理解 Active、Segment、累计 Summary、Assistant 草稿和中断恢复

元数据与落盘

SDK Session 默认保存在用户级 Workspace 数据目录中。应用应通过 Session API 读写,不要把这些文件当作自己的数据库。

~/.downcity/agents/<agent_id>/workspaces/<workspace_id>/sessions/<session_id>/
├── meta.json
├── instruction.md
└── messages/
    ├── active.jsonl
    ├── assistant_message.json
    └── segments/
        ├── 000000000001-000000000900.jsonl
        └── 000000000901-000000001500.jsonl

session_id 在一个 AgentWorkspace 内唯一。meta.json 同时记录 agent_idworkspace_id,物理目录也已经按这两个 ID 分区。不同 Agent 可以复用相同 session_id 而不会共享数据,项目目录不会创建 .downcity

instruction.md 是可选的完整 system 快照。Session 首次生成 system 后默认只在内存中固定;调用本地 session.snapshot() 会创建或覆盖该文件。session.syncshot() 会重新生成内存 system,并在文件已经存在时同步覆盖。恢复时若文件不存在,Session 会从 Agent 当前 instruction 和 plugin 重新生成;删除文件即可恢复这一行为。

Active 与 Segment

active.jsonl 只保存最近一次 Compact 之后仍处于活动窗口中的真实 SessionMessage。它没有固定条数上限,也不按 API 页大小切分。SDK 不在模型调用前估算 token;只有 Provider 返回的真实 usage.totalTokens(缺失时使用 inputTokens + outputTokens)达到模型 context_window 的 95% 时,才安排 Compact。

Compact 发生时,SDK 按 Active 中 User 和 Assistant Message 的数量选择最旧的 floor(n / 2) 条消息。摘要模型只调用一次,输入仅包含该前缀,以及存在时的上一版累计 Summary;较新的后 50% 不进入摘要输入并继续保留在 Active。Assistant 完成落盘后,选中的前缀被关闭为按 sequence 范围命名的不可变 Segment。Segment 每行先保存真实 Message,最后一行保存累计 Summary footer:

message sequence 1
...
message sequence 450
summary through sequence 450

下一次 Compact 会选择届时 Active 窗口中最旧的一半消息,其 footer Summary 合并旧 Summary 与本次选中的前缀,因此 Summary 始终累计覆盖已归档历史。Summary 不是 SessionMessage,不占用 sequence,也不放进 Active。

Compact 后的下一次真实 usage 用于验收:不超过 context_window 的 50% 即达标;高于 50% 时继续在下一 step 前 deep compact。Provider 直接返回 context-length error 且没有 usage 时,SDK 也会强制折叠当前模型消息并重试。

模型上下文

每轮模型输入只需要读取:

最新 Segment 的累计 Summary
+ Active 中的全部 User / Assistant Message

不需要扫描所有旧 Segment。旧 Segment 用于历史 UI、Fork 和审计;session.messages() 返回 Active 全量,传入 before_sequence 时返回紧邻的前一个完整 Segment。

推进泳道图

Composer 只读取快照并返回模型输入或压缩计划。Message、Assistant 草稿和 Segment 的实际写入统一由 SessionMessages 提交。

只有摘要生成成功后才能提交压缩计划。Provider 错误或空摘要会直接让 Compact 失败,不会创建 Segment,也不会修改 Active 历史。

assistant_message.json

流式 Assistant 在完成前不会把每个 Delta 追加到 Active,而是持续原子覆盖当前完整草稿。Assistant 完成后,最终快照追加到 active.jsonl,再删除草稿。同一时刻每个 Session 最多有一个流式草稿;Text、Tool、File Part 均按各自 sequence 保持模型生成顺序。

中断恢复与一致性

Compact 先原子创建 Segment,再原子覆盖 Active。若进程在两步之间退出,原始 Message 会短暂同时存在于 Segment 和 Active,但不会丢失;下次初始化会以最新 Segment 的结束 sequence 为边界,清理 Active 的重叠前缀。

启动或重新打开 Session 时,SDK 还会检查 Assistant 草稿。草稿中的文本、推理、工具和文件 Part 会保留并按原顺序恢复。

性能边界

  • 正常模型推进只读最新 Summary 与 Active,不随完整历史线性增长。
  • Compact 的触发只依赖 Provider 真实 usage,不执行调用前 token 预估。
  • 每次 Compact 只为选中的消息前缀调用一次摘要模型。
  • 历史向上加载每次只解析一个 Segment。
  • Segment 文件不可变,文件名就是 sequence 索引,不需要 manifest.json
  • fork() 明确复制完整历史,因此会读取所有 Segment;这是低频全量操作。

meta.json

meta.json 保存列表和详情所需的轻量属性,包括 session_idagent_id、标题、模型标签、时间戳、消息数、历史字节数和时区。运行时模型实例不会持久化。Session 属性或消息状态变化时,SDK 会同步更新这些摘要字段。

归档整个 Session 与 Compact Segment 是两件不同的事。archive() 会把整个 Session 移入 archived sessions 区域;Compact 只在当前 Session 的 segments/ 中关闭旧 Active 前缀。