第 2 章:会话与树——把时间掰成分叉

历史不是一条线,而是一棵树;你随时可以回到任何一个分岔口重新出发。

设计思想

大多数 coding agent 的历史是线性的:压缩有损且不可逆,走错了只能重开。Pi 把会话设计成单文件内的树结构——每次交互都是树上的节点,你可以回到任意历史节点原地分叉,探索不同方向而互不覆盖。配合自动压缩(compaction),长会话不炸上下文,且原始历史永远完整保留

原理

存储格式

  • 会话保存为 JSONL 文件,存于 ~/.pi/agent/sessions/,按工作目录组织,自动保存。
  • 每条记录有 idparentId,整个会话本质是存在单文件里的一棵树(格式详见 docs/session-format.md)。

三种"回到过去"的方式

操作 行为 文件
/tree 会话树内原地导航:选中任意历史点继续聊、在分支间切换 不新建文件,全部历史保留
/fork 从活跃分支上的某条历史 user message 开新会话文件,复制到该点为止的路径,并把该提示词放回编辑器供修改 新文件
/clone 当前活跃分支的当前位置完整复制成新会话文件,编辑器为空 新文件

CLI 侧:pi --fork <path|id> 可直接从既有会话或部分 UUID 分叉出新会话。

/tree 的操作

  • 直接输入文字即搜索;Ctrl+←/→(或 Alt+←/→)在分支间跳转,←/→ 翻页
  • Ctrl+O 切换过滤模式:default → no-tools → user-only → labeled-only → all
  • Ctrl+X 复制选中消息
  • Shift+L 给节点打标签做书签,Shift+T 切换标签时间戳显示

Compaction(上下文压缩)

  • 手动/compact/compact <自定义摘要指令>
  • 自动(默认开启):上下文溢出时触发(恢复并重试);接近上限时主动预防性压缩。可在 /settingssettings.json 配置。
  • 压缩有损(旧消息被摘要替代),但完整历史仍在 JSONL 文件里——/tree 随时可回看;压缩行为可通过 extensions 自定义(内部机制见 docs/compaction.md)。

用法

会话生命周期

pi -c                  # 继续最近一次会话
pi -r                  # 浏览历史会话并选择
pi --no-session        # 一次性模式,不保存
pi --name "my task"    # 启动时命名
pi --session <path|id> # 使用指定会话(部分 UUID 亦可)
pi --fork <path|id>    # 从既有会话分叉新会话

交互内用 /session 查看当前会话 ID,方便之后 --session <id> / --fork <id> 复用。

典型工作流

  • 方案 A/B 对比:聊到关键决策点,/tree 回到分岔口,先走方案 A;不满意再回同一节点走方案 B,两个分支互不干扰。
  • 危险操作前存档:执行大规模重构前,用 /clone 复制当前分支;翻车后直接回原会话继续。
  • 重写提示词/fork 选中某条历史提示词,新会话中修改后重发,比较两次结果。
  • 长任务防炸上下文:放着让自动 compaction 处理;发现质量下降时 /tree 回到压缩前的节点,换个问法继续。
  • 书签导航:给关键节点 Shift+L 打标签,之后在 labeled-only 过滤模式下快速定位。
  • 分享与归档/export 导出 HTML/JSONL,/import 恢复,/share 上传为私有 GitHub gist。