Pi Agent 的 Memory 机制:会话树、上下文与压缩如何协作

Pi Agent 的 Memory 机制

Pi Agent 的记忆机制更像一套上下文管理系统:完整历史保存在会话树里,当前模型只看到被挑选、压缩、转换后的上下文。

换句话说,Pi 的 memory 不是“模型自己记住了什么”,而是 Pi 在每次调用模型前,决定把哪些历史、规则、工具结果、摘要和动态上下文放到这一次请求里。

本文脉络:
一 Pi Agent Memory 的五层结构
二 工作记忆:当前 Context
三 情景记忆:JSONL Session Tree
四 摘要记忆:Compaction
五 程序性记忆:AGENTS、Skills 与 Prompt Templates
六 可插拔记忆:Extension context hook
七 一次调用前,Memory 如何变成 Context
八 Pi 这套设计的取舍
九 常见问题

一、Pi Agent Memory 的五层结构

Pi 的 memory 不是单个模块,而是几层机制叠在一起。

Pi Agent Memory 的五层

可以这样理解:

层级 在 Pi 里的形态 解决的问题
工作记忆 当前 AgentMessage[] / LLM messages 本轮模型能看到什么
情景记忆 JSONL Session Tree 保存对话、工具结果、模型切换、分支历史
摘要记忆 Compaction entry 长上下文下保留早期历史要点
程序性记忆 AGENTS.md、Skills、Prompt Templates 告诉 Agent 遇到某类任务该怎么做
可插拔记忆 Extension 的 context / before_agent_start 事件 注入外部记忆、RAG、团队策略或动态状态

这五层里,只有第一层“工作记忆”会直接进入模型。其他层都要经过加载、选择、压缩、转换,最后变成本轮上下文的一部分。

这也是理解 Pi memory 的关键:记忆不等于存储,记忆等于“能在正确时机被带回上下文”。

二、工作记忆:当前 Context

Pi 每次调用模型时,都会构建一份上下文。它通常来自:

  • 当前用户输入;
  • 当前 session branch 上的历史消息;
  • 工具返回的 ToolResultMessage
  • system prompt;
  • 被加载的项目规则、Skills 或模板;
  • 压缩后的摘要;
  • Extension 动态注入的内容。

这份上下文就是 Pi 的工作记忆。

它有两个特点。

第一,工作记忆是短暂的。模型调用结束后,模型不会“天然记住”这次内容。Pi 必须把消息和事件写回 session,后续调用才有机会重新取出来。

第二,工作记忆是有边界的。上下文窗口再大也会满,工具结果、长文件、连续对话都会把窗口撑爆。所以 Pi 不可能把完整历史永远塞给模型,而要在“完整历史”和“可执行上下文”之间做取舍。

三、情景记忆:JSONL Session Tree

Pi 的长期会话记忆主要落在 session 文件里。它不是一个简单的线性聊天记录,而是 append-only JSONL tree。

每个 entry 有自己的 id,并通过 parentId 指向父节点。这样一份 session 可以长成树,而不是只能一条线往下写。

它保存的不只是 user/assistant message,还包括:

Entry 类型 记住了什么
message 用户输入、模型输出、工具结果
model switch 模型切换
thinking level switch 推理强度变化
compaction 压缩摘要
branch summary 会话树跳转时的分支摘要
custom extension data Extension 写入的自定义状态
session info session 名称等元信息

这套设计带来一个很实用的能力:失败路径不需要被覆盖。

编码任务经常要试错。模型改了 A 方案,发现不对,再 fork 到 B 方案,后来又回到某个中间节点。线性日志只能一路往下追加,很难表达“从这里分叉”;JSONL tree 可以保留探索过程。

所以 Pi 的情景记忆不是只为了“能继续聊天”,它还支撑 /tree/fork/clone/export 这类会话操作。

四、摘要记忆:Compaction

当历史越来越长,Pi 不能把所有 entry 原样塞进上下文。这时就需要 Compaction。

Compaction 的直觉很简单:

完整历史太长
-> 总结早期历史
-> 追加一个 compaction entry
-> 后续模型从摘要 + 新消息继续

注意这里的细节:Compaction 不会删除原始历史。

原始 JSONL 还在,会话的审计历史还在。变化的是后续构建模型上下文时,Pi 可以从压缩后的摘要节点继续,而不是每次都带上早期全部消息。

Pi 的 Compaction 常见触发方式有两类:

触发方式 含义
proactive compaction 接近上下文上限时提前压缩
overflow recovery Provider 报上下文溢出后,压缩并重试

这个设计体现了 Pi 的一个边界意识:

会话文件:保存完整历史,方便追溯
模型上下文:只放本轮需要的信息,方便执行

这两个东西不要混为一谈。完整历史适合保存,完整历史不一定适合每次都喂给模型。

五、程序性记忆:AGENTS、Skills 与 Prompt Templates

Pi 的 memory 里还有一类很容易被忽略:程序性记忆。

它不是“上次聊过什么”,而是“遇到某类任务应该怎么做”。

在 Pi 里,这类记忆主要来自:

  • AGENTS.md:项目级或目录级的长期约定;
  • Skills:一类任务的操作手册、参考资料和脚本;
  • Prompt Templates:固定格式的提示词模板;
  • themes / resources:交互或输出层面的可复用资源。

这些内容通常由 ResourceLoader 在启动或 reload 时发现,再参与 system prompt 或上下文构建。

举个例子:你让 Pi 修改博客文章。如果项目里有写作 Skill,它就不需要每次从零猜 front matter、<!-- more -->、图片路径和构建命令。Skill 就像 Pi 的“操作手册记忆”。

所以,Pi 的 memory 不是只有“对话历史”。对编码 Agent 来说,项目规范和工作流记忆同样重要

六、可插拔记忆:Extension context hook

Pi 没有把所有 memory 形态都硬编码进核心,这一点很重要。

如果你想接入外部长期记忆、RAG、团队知识库、用户偏好,比较自然的位置是 Extension。

比如 Extension 可以监听:

事件 可以做什么
before_agent_start 修改 system prompt,加入本轮规则
context 替换或扩展 messages,注入检索结果
tool_result 对工具结果脱敏、压缩或补充解释
session_start 从 session branch 恢复扩展状态
session_before_compact 自定义压缩策略

一个最小的“外部记忆注入”思路可能是:

pi.on("context", async (event) => {
const memory = await searchTeamMemory(event.messages);

return {
messages: [
{
role: "user",
content: [{ type: "text", text: `Relevant team memory:\n${memory}` }],
},
...event.messages,
],
};
});

这段代码的重点不是具体 API,而是插入位置:在模型调用前,Extension 可以把外部记忆转成 messages,让它真正进入本轮工作记忆。

这也是 Pi 设计比较克制的地方:核心提供稳定的 session、context、compaction 和事件插槽;具体要不要接向量库、数据库、知识图谱,交给扩展层。

七、一次调用前,Memory 如何变成 Context

把前面几层串起来,一次模型调用前大概是这样:

Pi Agent Context 构建流程

简化成文字:

当前 session leaf
-> 沿 parentId 回溯当前 branch
-> 读取 message / compaction / custom entry
-> 合并 system prompt、资源、Skills、模板
-> transformContext()
-> Extension context hook 可注入或替换 messages
-> convertToLlm()
-> Provider request

这里有两个边界很重要。

第一,transformContext() 面向 Agent 消息,适合做裁剪、动态上下文、压缩、RAG 或长期记忆注入。

第二,convertToLlm() 面向 Provider 协议,负责把 Pi 内部的 AgentMessage 转成模型真正能理解的 user、assistant、tool result 等消息。

也就是说,“记忆选择”和“协议转换”是两件事。前者决定模型该看什么,后者决定这些内容怎么发给模型。

八、Pi 这套设计的取舍

Pi 的 memory 机制有几个明显优点。

优点 说明
可追溯 JSONL append-only 保存完整历史
支持分支 tree 结构适合探索、fork、回退
不强塞历史 Compaction 让上下文保持可用
可扩展 Extension 可以接入外部 memory
适合编码任务 工具结果、模型切换、项目规则都能进入同一套会话体系

但它也有取舍。

第一,Pi 默认不是一个“自动长期记住所有用户偏好”的系统。想做跨项目、跨会话、可检索的用户长期记忆,需要额外扩展。

第二,Compaction 本质是摘要,摘要就会损失细节。它适合保留任务目标、关键决策、文件变化和未完成事项,不适合替代完整审计历史。

第三,Extension 注入外部记忆时要克制。记忆越多不代表越好,塞太多反而会污染工作上下文,让模型抓不住当前任务。

我的建议是:先把 Pi 内建的 session tree、compaction、AGENTS、Skills 用好,再考虑接外部长期记忆。

九、常见问题

问题 回答要点
Pi Agent 有“长期记忆”吗? 有会话级长期历史,也就是 JSONL session tree;但默认不是向量库式的跨会话语义记忆。
Pi 的 memory 和普通聊天记录有什么区别? Pi 保存的是可分支的 session tree,不只是线性对话;还包含工具结果、模型切换、压缩、扩展状态等 entry。
Compaction 会删除历史吗? 不会。它追加摘要 entry,改变后续上下文构建方式;原始 JSONL 历史仍然保留。
模型每次都会看到完整历史吗? 不会。模型只看到本轮 Provider 请求里的上下文,完整历史需要经过选择、压缩和转换。
Skills 算 memory 吗? 算程序性记忆。它不是记住某次对话,而是记住“这类任务应该怎么做”。
想接入向量记忆怎么办? 更适合通过 Extension 的 context 事件做检索注入,而不是把向量记忆硬塞进核心流程。

最后用一句话收束:Pi Agent 的 memory 机制,本质是把“完整历史可追溯”和“当前上下文可执行”分开管理。