数据截至 (上游 commit 0a27a45390b4)
消息模型与会话持久化:Log、TOML 与事件日志
30 秒导读: gptme 的每一条对话消息都是一个冻结的数据类
Message,一整段对话是一个不可变的Log——所有"修改"都返回新对象。落盘时按会话目录写成conversation.jsonl(一行一条消息),另有一份events.jsonl事件流做崩溃恢复的兜底。这一章只讲"数据长什么样、怎么存、怎么恢复",不碰主循环和工具(那是 01、02)。
本章是 gptme 的数据底座。上层的主循环(01)、提示词准备(04)都建立在这个模型上:它们操作的每一个对象,都是这里定义的 Message 和 Log。
1. 这是什么(零基础也能懂)
一句话定义: 这是 gptme 存"聊天记录"的那一层——把一次人和 AI 的对话,变成内存里能操作、磁盘上能恢复的数据。
它解决什么问题: 假设你在终端里和 gptme 聊了两小时,改了十几个文件,中途机器崩了。重开后你希望对话还在、能接着聊——甚至希望 AI 直接从"你刚问的问题"继续回答,而不是让你重打一遍。这一层就是为此存在的:让对话既能可靠落盘,又能在崩溃后精确复原到"该谁说话"的那一刻。
它由三样东西组成:
| 部件 | 白话 | 文件 |
|---|---|---|
Message | 一条消息(谁说的、说了什么、什么时候) | gptme/message.py |
Log | 一串消息组成的对话,不可变 | gptme/logmanager/manager.py |
LogManager | 管家:负责追加、落盘、加载、恢复 | gptme/logmanager/manager.py |
用起来什么样: 你几乎不直接碰它——你在终端里打字、AI 回话,背后每一轮都在往同一个 LogManager 里 append 消息,同时静默写盘。你能感知它的地方是:Ctrl-C 打断后重开,对话还在;或者你想手改历史时,把日志导成 TOML、编辑、再读回来。
一句话直觉: 把 Log 当成一本只能往后翻、不能涂改的记事本——你永远只能在末尾添一页;想"改历史",就是誊一本新的记事本出来。这种"不可变 + 追加"的设计,是它能安全恢复、能分叉(branch)的根本原因。
2. 顶层全景(一条消息的一生)
先看一条消息从"生成"到"落盘"再到"崩溃后复活"走的完整路径。这张图从上到下是时间顺序,左边是内存对象,右边是磁盘文件。
内存(Python 对象) 磁盘(<logdir>/)
─────────────────── ────────────────────
Message(role, content, …) ← frozen,不可变
│
▼ manager.append(msg)
┌─────────────────────────┐
│ Log = Log + [msg] │ ← 追加返回新 Log
│ (不可变,整个替换) │
└───────────┬─────────────┘
│ write()
├───────────────────────────► conversation.jsonl (主分支,一行一条)
│ branches/*.jsonl (其它分支)
│ views/*.jsonl (压缩视图)
│ _write_event_log()
└───────────────────────────► events.jsonl (追加事件 + 每50条一个checkpoint)
── 崩溃后重开 ──
LogManager.load(logdir) ◄─────────────── 读 conversation.jsonl → Log
│
▼
_should_prompt_for_input(log) ← 看最后一条消息的角色
│
├─ 最后是 assistant → 等用户输入
└─ 最后是 user → 不问,直接让模型接着答(崩溃恢复)
怎么读这张图: 中间那根竖线是"一次 append"。它做两件事——把消息塞进不可变 Log(内存)、把 Log 和事件都写盘(磁盘)。图最下方是恢复:重开时读回 Log,再靠最后一条消息的角色决定接下来该谁说话。
各部件一句话职责:
| 部件 | 干什么 | 符号 · 位置 |
|---|---|---|
Message | 一条消息的不可变载体 | message.py:250 Message |
Log | 不可变消息序列,append 返回新 Log | manager.py:73 Log |
LogManager | 追加 / 落盘 / 加载 / 分叉的管家 | manager.py:124 LogManager |
| TOML 序列化 | 把日志导成人可手改的格式再读回 | message.py:688 msgs_to_toml |
| 事件日志 | 追加式事件流 + checkpoint,崩溃恢复兜底 | eventlog.py:137 recover_messages |
| 会话查询 | 列会话、算 token/成本、取预览 | conversations.py:441 get_conversations |