如果 Agent 是发动机,AgentSession 就是把发动机装进车里的那层:它处理会话、资源、认证、扩展、压缩、内置工具和产品行为。
Pi 的 SDK 文档也把 AgentSession 定义为管理生命周期、消息历史、模型状态、压缩和事件流的核心对象。
flowchart TB AS["AgentSession"] --> A["Agent"] AS --> SM["SessionManager"] AS --> Settings["SettingsManager"] AS --> RL["ResourceLoader"] AS --> MR["ModelRegistry"] AS --> ER["ExtensionRunner"] AS --> TD["Tool Definitions"]
用户调用 session.prompt(text) 时,并不是直接交给模型。Pi 会先做一串 preflight:
/mycommand,交给扩展执行。streamingBehavior 放进 steering 或 follow-up 队列。before_agent_start,允许扩展改系统提示词或注入消息。agent.prompt(messages)。flowchart TD
A["session.prompt(text)"] --> B{"extension command?"}
B -->|"yes"| C["execute command"]
B -->|"no"| D["input hooks"]
D --> E["expand skill/template"]
E --> F{"isStreaming?"}
F -->|"yes"| G["queue steer/followUp"]
F -->|"no"| H["check model/auth"]
H --> I["optional compaction"]
I --> J["build messages"]
J --> K["before_agent_start hooks"]
K --> L["agent.prompt(messages)"]底层 Agent 只维护内存状态。AgentSession 订阅 Agent 事件,在 message_end 时把消息 append 到 SessionManager。
这是一种很清晰的分层:
| 层 | 是否关心磁盘 |
|---|---|
Agent Loop |
不关心 |
Agent |
不关心,只管状态和事件 |
AgentSession |
关心,把事件转成持久化 |
SessionManager |
只关心 JSONL 和树结构 |
为什么还需要 AgentSessionRuntime?
因为有些操作不是“在当前会话追加消息”,而是替换整个运行上下文:
| 操作 | 为什么要重建 |
|---|---|
/new |
新 session file、新 leaf |
/resume |
切到另一个 session file |
/fork |
基于旧会话创建新文件 |
| 改 cwd | 资源加载、工具 cwd、项目设置都变了 |
这时只替换 messages 不够,工具、扩展、资源、设置都可能要按新 cwd 重新构造。
我们的教学版不会完整实现 AgentSessionRuntime,但会保留这个分层思想:
| Pi | 教学版 |
|---|---|
Agent |
runAgentLoop() + 内存 context |
AgentSession |
/api/prompt 里的 orchestrator |
SessionManager |
JsonlSessionStore |
ResourceLoader |
静态 system prompt + 工具注册表 |
ExtensionRunner |
简化 hooks |
当你理解这个映射,就不会被 Pi 的真实代码量吓到。它本质上是在把同一条链路做得更完整、更可扩展。