pi-mono 架构设计 - JSONL 会话存储与树状结构
详解 pi-mono 框架的会话存储格式、树状结构、消息类型和版本演进
1.1 JSONL 会话存储格式
文件位置
pi-mono 的会话存储在用户主目录下,遵循分层结构:
~/.pi/agent/sessions/
├── --home--ubuntu--workspace--project/
│ ├── 20260307_140000_abc12345.jsonl
│ └── 20260307_150000_def67890.jsonl
├── --home--user--projects--another-project/
│ └── 20260306_090000_12345678.jsonl
路径转换规则:
- 工作目录的
/替换为- - 前后添加
--作为分隔符 - 避免与文件系统路径冲突
JSONL 格式特点
JSONL(JSON Lines)格式每行是一个独立的 JSON 对象:
{"type":"session","version":3,"id":"uuid-123","timestamp":"2026-03-07T14:00:00.000Z","cwd":"/home/user/project"}
{"type":"message","id":"a1b2c3d4","parentId":null,"timestamp":"2026-03-07T14:00:01.000Z","message":{"role":"user","content":"Hello"}}
{"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2026-03-07T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}]}}
优势:
- ✅ 流式读写:无需加载整个文件即可追加条目
- ✅ 损坏恢复:单行损坏不影响其他条目
- ✅ 版本控制友好:Git diff 可清晰显示变更
- ✅ 人类可读:可直接用文本编辑器检查
会话版本
当前会话格式为 v3,历史版本:
| 版本 | 特性 | 迁移方式 |
|---|---|---|
| v1 | 线性序列(无 id/parentId) | 加载时自动构建树 |
| v2 | 树状结构(id/parentId 链接) | 自动升级 |
| v3 | 重命名 hookMessage 为 custom | 向后兼容 |
1.2 树状会话结构
树形设计动机
传统聊天系统采用线性日志结构,存在以下局限:
- 分支困难:需要创建新文件才能尝试不同方向
- 回溯复杂:手动编辑日志易损坏结构
- 上下文污染:错误尝试无法隔离
pi-mono 采用树状结构解决这些问题:
[user msg] ─── [assistant] ─── [user msg] ─── [assistant] ─┬─ [user msg] ← 当前分支
│
└─ [branch_summary] ─── [user msg] ← 替代分支
树的遍历
buildSessionContext() 方法从当前 leaf 节点向 root 遍历:
function buildSessionContext(leafId: string): Context {
const path = [] // 从 root 到 leaf 的路径
let current = entries.get(leafId)
while (current) {
path.unshift(current)
current = entries.get(current.parentId)
}
// 过滤出需要发送给 LLM 的消息
return extractMessages(path)
}
关键行为:
- 仅遍历当前分支路径
- 跳过其他分支的条目
- 在分支点插入
BranchSummaryEntry
分支操作
# 从历史节点创建分支
pi --branch a1b2c3d4
# 带摘要的分支(推荐)
pi --branch a1b2c3d4 --summary "探索了方法 A,但遇到性能问题"
分支后:
- 创建
BranchSummaryEntry记录离开分支的上下文 - 新消息以分支点为
parentId - 原分支保持不变,可随时切回
1.3 消息类型详解
基础消息类型(pi-ai 包)
UserMessage
interface UserMessage {
role: "user"
content: string | (TextContent | ImageContent)[]
timestamp: number // Unix 毫秒
}
支持内容类型:
- 纯文本字符串
TextContent块数组ImageContent块数组(base64 编码)
AssistantMessage
interface AssistantMessage {
role: "assistant"
content: (TextContent | ThinkingContent | ToolCall)[]
provider: string // "anthropic", "openai", etc.
model: string // "claude-sonnet-4-5", "gpt-5.1-codex"
usage: Usage
stopReason: "stop" | "length" | "toolUse" | "error" | "aborted"
timestamp: number
}
ThinkingContent:
interface ThinkingContent {
type: "thinking"
thinking: string // 模型的推理过程
}
跨提供商传递时,thinking traces 转换为 <thinking> 标签包裹的文本。
ToolResultMessage
interface ToolResultMessage {
role: "toolResult"
toolCallId: string
toolName: string
content: (TextContent | ImageContent)[]
details?: any // 工具特定元数据(不发送给 LLM)
isError: boolean
timestamp: number
}
扩展消息类型(pi-coding-agent 包)
BashExecutionMessage
interface BashExecutionMessage {
role: "bashExecution"
command: string
output: string
exitCode: number | undefined
cancelled: boolean
truncated: boolean
fullOutputPath?: string // 长输出存储路径
excludeFromContext?: boolean // !! 前缀命令
timestamp: number
}
排除上下文:
- 使用
!!前缀的命令(如!! cat file.txt)设置excludeFromContext: true - 执行但不发送给 LLM,用于用户自行检查
CustomMessage
interface CustomMessage {
role: "custom"
customType: string // 扩展标识符
content: string | (TextContent | ImageContent)[]
display: boolean // 在 TUI 显示
details?: any // 扩展特定元数据
timestamp: number
}
用途:
- 扩展注入的消息(如检索到的记忆)
display: false时仅发送给 LLM
CompactionSummaryMessage
interface CompactionSummaryMessage {
role: "compactionSummary"
summary: string
tokensBefore: number
timestamp: number
}
元数据类型
CompactionEntry
上下文压缩记录:
{
"type": "compaction",
"id": "f6g7h8i9",
"parentId": "e5f6g7h8",
"timestamp": "2026-03-07T14:10:00.000Z",
"summary": "用户讨论了身份验证模块重构,决定采用 JWT 方案...",
"firstKeptEntryId": "c3d4e5f6",
"tokensBefore": 50000
}
LabelEntry
用户标记:
{
"type": "label",
"id": "j0k1l2m3",
"parentId": "i9j0k1l2",
"timestamp": "2026-03-07T14:30:00.000Z",
"targetId": "a1b2c3d4",
"label": "checkpoint-1"
}
1.4 版本演进历史
v0.1-v0.10:基础架构(2025 年 8 月 -9 月)
- 初始 JSONL 格式(v1,线性序列)
- 基础 4 工具:read, write, edit, bash
- 简单会话继续(
--continue)
v0.11-v0.30:树状结构(2025 年 10 月 -12 月)
- 引入
id/parentId(v2) - 分支支持(
--branch) - 上下文压缩(
CompactionEntry)
v0.31-v0.56:扩展生态(2026 年 1 月 - 至今)
- CustomEntry API(v3)
- Skills 系统
- Packages 分发机制
- RPC 模式(远程调用)
本章小结
pi-mono 的会话存储设计体现了其核心哲学:
- 文件化:JSONL 格式便于检查、备份、版本控制
- 树状结构:原生支持分支探索,无需创建新文件
- 类型丰富:区分消息、元数据、扩展内容
- 版本兼容:自动迁移旧格式,向后兼容
下一章将深入探讨记忆管理机制,包括上下文压缩、跨模型传递、会话恢复等核心功能。
参考资料
- pi-mono Session Documentation. https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/session.md
- pi-mono Source Code.
packages/coding-agent/src/core/session-manager.ts - Mario Zechner Blog. https://mariozechner.at/posts/2025-11-30-pi-coding-agent/