Pi
Pi 是一个用 TypeScript 编写的开源 Agent harness 项目,也是其终端编码 Agent 的名称。它刻意只提供一组较小的默认能力,再让使用者通过 Extension、Agent Skills、Prompt Template、Theme 和 Pi Package 组合自己的工作流。项目没有稳定中文专名,中文社区通常仍直接称它为 Pi。
Pi 最鲜明的设计选择不是“功能少”,而是把默认核心与可选工作流分开:编码 Agent 默认给模型 read、write、edit、bash 四个工具,但不把 subagent、plan mode 等工作流固化进核心。需要这些能力时,可以让 Pi 编写 Extension,或安装第三方 Pi Package。这使它既是可直接使用的终端 Agent,也是研究 Harness Engineering(驾驭工程)接口边界的具体实现。
分层架构
Pi monorepo 的稳定核心由四个包构成:
| 包 | 职责 |
|---|---|
pi-ai | 统一多供应商、只收录支持 tool calling 的模型 API;处理认证、流式输出、token 与成本统计,并允许会话中切换模型 |
pi-agent-core | 有状态 Agent Loop(智能体循环)、工具执行、消息转换、事件流和较高层的 AgentHarness |
pi-coding-agent | 可直接运行的终端编码 Agent,加入文件工具、会话、上下文文件、压缩、扩展、技能和多种运行模式 |
pi-tui | 使用差分渲染和同步输出的终端 UI 库,支持 Markdown、编辑器、选择器、overlay 与终端内图片 |
flowchart TB
U["交互 TUI / Print / JSON / RPC / SDK"] --> C["pi-coding-agent\n会话、资源、扩展、内置工具"]
C --> H["pi-agent-core\nAgentHarness + Agent Loop"]
H --> A["pi-ai\n统一模型与供应商接口"]
C --> T["pi-tui\n差分终端渲染"]
H --> E["文件系统 / Shell / 自定义工具"]
pi-coding-agent 有四种入口:交互模式、一次性 Print/JSON 输出、供其他进程控制的 RPC,以及嵌入应用的 SDK。这里的重要边界是,同一套 Agent 能力不与单一 UI 绑定;终端只是一个客户端,状态、工具循环和模型接入都可以被程序化复用。
Agent Loop 与工具执行
pi-agent-core 把一次用户请求组织成持续到最终 assistant message 的工具循环。模型返回 tool call 后,runtime 校验参数、执行工具、生成 tool result,再启动下一次模型调用。它用事件流暴露 agent_start、turn_start、message 更新、tool execution 和 agent_end,因此 UI 可以在模型输出和工具运行期间增量更新。
工具调用默认并行执行,但工具可以声明必须串行;一个批次中只要出现串行工具,整个批次就按顺序执行。beforeToolCall 可以在参数解析后阻断动作,afterToolCall 可以审计或改写结果。这些 hook 提供的是构建权限门禁的接口,并不代表 Pi 自带完整权限策略。
消息层也明确区分了持久状态 与模型视图。应用可保存包含 UI 自定义类型的 AgentMessage,每次请求前再用 transformContext 做裁剪或注入,并通过 convertToLlm 投影成供应商理解的 user、assistant、tool result 消息。这是上下文工程中“完整记录与活跃上下文分离”的直接实现。
树形会话与上下文压缩
Pi 把会话保存为 JSONL,每个条目带 id 与 parentId,因此一份文件可以保存完整对话树。/tree 能跳回任意历史节点并从那里继续,原分支不会丢失;/fork 从历史用户消息创建新会话,/clone 则复制当前活动分支。这不是简单的 Undo,而是把探索路径作为一等会话状态保存。
上下文接近窗口上限时,Pi 会摘要较早历史、保留最近消息,并把 compaction entry 写入会话树;原始历史仍留在 JSONL 中。离开一个分支时还可以生成 branch summary,把该分支的目标、进展和文件变化带回新的活动路径。两者共同体现一个原则:模型输入可以压缩,审计历史不必删除。
仓库中的界面图补充了 README 正文没有完全展开的信息:交互页启动时会列出已加载的 Context、Skills、Prompts 和 Extensions;底栏同时显示工作目录、Git 分支、输入/输出 token、缓存读写、成本、上下文占用、供应商、模型与 thinking level。会话树图则显示 user、assistant、read/edit/bash 等工具节点共同参与分支结构,而不是只保存聊天文本。
自扩展资源模型
Pi Extension 是拥有 lifecycle event、工具注册、命令、快捷键、自定义 TUI 和会话持久化能力的 TypeScript 模块。它可以拦截工具调用、修改上下文、替换压缩逻辑,也可以接入外部系统。Skill 则遵循 Agent Skills 格式:启动时只把 name 和 description 放进系统提示,匹配任务后再读取完整 SKILL.md 与支持文件,实现渐进披露。
Pi Package 把 Extension、Skill、Prompt Template 和 Theme 一起通过 npm、Git 或本地路径分发。由此形成两种扩展层:
- Extension 改变 runtime 行为,能执行任意代码、注册工具和拦截事件
- Skill 主要提供按需加载的流程知识,也可能携带让模型执行的脚本
两者都会扩大能力与供应链风险,不能因为“只是配置”或“只是提示词”而跳过源码审查。
安全边界
Pi 明确声明:它没有内置的文件系统、进程、网络或凭证权限系统,默认继承启动它的用户与进程权限。项目信任机制会阻止未信任仓库自动加载项目级 Extension、Skill 和 Package,但一旦信任并加载,Extension 本身可以用完整主机权限执行任意代码。因此,project trust 是资源加载门,不是 Agent Sandbox(Agent 沙箱)。
官方文档给出三种外部隔离方式:
| 模式 | 隔离对象 | 关键边界 |
|---|---|---|
| Gondolin Extension | 内置工具与 ! 命令 | Pi 和模型认证留在 host,工具路由进本地 Linux microVM;其他 Extension 工具仍可能在 host 执行 |
| Plain Docker | 整个 Pi 进程 | 简单,但模型 API key 进入容器,workspace bind mount 仍会把修改写回 host |
| OpenShell | 整个 Pi 进程 | 由策略控制文件、进程、网络、凭证和推理访问,可使用 gateway 代注入模型凭证 |
这组方案说明,Pi 的可扩展性与安全性是同一枚硬币的两面:核心可以保持小而透明,但部署者必须明确选择权限门禁和执行隔离。不能把 beforeToolCall hook、项目 trust 与真正的 OS/VM sandbox 混为一谈。
项目沿革与边界
Pi 早期位于 badlogic/pi-mono,npm 包使用 @mariozechner/* scope;2026 年 5 月迁移到 Earendil Works,仓库改为 earendil-works/pi,包名改为 @earendil-works/*。迁移主要是所有权和命名变化,CLI 命令、配置目录与已有会话保持兼容。
Pi 适合希望自己组合模型、工具、会话和交互方式的开发者。其代价也很明确:subagent、计划、审批和 sandbox 等能力是否存在、质量如何,取决于选用的 Extension、Package 与外部运行环境;“可自行扩展”不等于这些生产能力已经由核心提供并验证。
相关概念
- Harness Engineering(驾驭工程) — Pi 是“极简核心 + 深扩展点”这一 harness 路线的实现案例
- Agent Loop(智能体循环) —
pi-agent-core实现模型、工具与观察结果之间的控制循环 - Agent Skills — Pi 支持的渐进披露技能格式
- Agent Sandbox(Agent 沙箱) — Pi 默认不提供,需通过 microVM、容器或策略沙箱补齐
- Context Engineering(上下文工程) — 树形会话、消息投影、compaction 和 branch summary 所属的工程面
- Codex — 另一种终端编码 Agent 及 harness 设计参照
- Claude Code — 将更多权限、subagent 与工作流能力内置进产品的参照