夜雨聆风学习资料网

ARTICLE · 1176817

11 万 Star 的 Pi:把 AI 编程助手拆到源码层,终端 Agent 原来是这样工作的

11 万 Star 的 Pi:把 AI 编程助手拆到源码层,终端 Agent 原来是这样工作的

适合读者:AI 编程工具使用者、终端重度用户、Agent 应用开发者、想了解“AI 编程助手内部如何运转”的工程师。

阅读时长:约 18 分钟。

项目地址:https://github.com/earendil-works/pi

如果你最近关注 AI 编程工具,大概率已经见过类似产品:你在编辑器里提需求,它读代码、改文件、跑测试,最后给你一份修改结果。

但大多数工具把内部过程藏得很深。你知道它“能干活”,却不一定知道它为什么能干活:消息怎么组织?工具怎么调用?上下文怎么压缩?会话为什么能分叉?扩展为什么能改行为?

Pi 有意思的地方就在这里。

它不只是一个终端里的 AI 编程助手,也是一套拆得比较清楚的 Agent 运行框架。你可以把它当成日常写代码的工具,也可以顺着源码看到:一个现代 Coding Agent,究竟是怎么把 LLM、工具调用、会话状态、终端 UI 和扩展系统组装起来的。

截至 2026 年 10 月 13 日写稿核验时,这个项目在 GitHub 上已经有 114,798 个 Star、14,624 个 Fork,使用 MIT License,主要语言是 TypeScript。它热度很高,但更值得关注的是:它的代码结构足够清晰,适合拿来做 Agent 工程化学习样本。

这篇文章会按四个层次展开:

  1. Pi 到底能做什么;
  2. 如何安装、配置和日常使用;
  3. 源码架构和 Agent Loop 的实现原理;
  4. 它的优势、短板,以及适合什么人。

一、先说清楚:Pi 不是 Raspberry Pi

这里的 Pi,全称可以理解为 Pi Coding Agent。它跟树莓派没有关系。

一句话概括:

Pi 是一个运行在终端里的 AI 编程 Agent,也是一套可嵌入、可扩展的 Agent Harness。

“Agent Harness”这个词很重要。它不是在说“我接了一个大模型 API”,而是在说:工具提供了模型调用之外的一整套承载机制,包括:

  • 读写项目文件;
  • 执行 shell 命令;
  • 维护多轮对话状态;
  • 把工具结果重新送回模型;
  • 支持会话恢复、分叉和压缩;
  • 支持扩展、技能、MCP、SDK、RPC;
  • 用 TUI 把过程展示给用户。

所以,Pi 既可以直接使用,也可以被拆开研究。

普通用户看到的是:

cd /path/to/project
pi

开发者看到的是:

CLI 参数
  → 会话管理器
  → 模型与工具解析
  → AgentSession
  → Agent Loop
  → LLM Streaming
  → Tool Calling
  → Tool Result
  → 下一轮

这就是 Pi 的价值:把“AI 帮我写代码”这件事,从黑盒产品变成了可以阅读、可以改造、可以嵌入自己系统的工程结构。


二、它到底能做什么?

先看一个真实使用画面。

Pi 在终端中生成 FastAPI 项目代码

这张图里,Pi 正在终端中搭建一个 FastAPI 项目。它已经不是简单地补全几行代码,而是在连续执行“项目级动作”:

  • 创建 bbs_project/src/api/main.py;
  • 创建 bbs_project/src/api/agents.py;
  • 引入 FastAPI、CORS、数据库会话和多个 Agent 类;
  • 生成用户、帖子、线程、Agent 等 API 路由;
  • 把当前执行过程实时显示在终端里。

这类行为背后不是“单次问答”,而是一个典型 Agent Loop:

理解任务
  → 查看项目结构
  → 生成或修改文件
  → 执行命令验证
  → 根据结果继续修改
  → 直到任务完成或被用户中断

Pi 默认提供的编码工具很克制,主要有:

工具
作用
read
读取文件内容
bash
执行 shell 命令
edit
对已有文件做局部编辑
write
创建或覆盖文件
grep
搜索文本
find
查找文件
ls
列出目录

默认启用的是 read、bash、edit、write。grep、find、ls 属于可用的只读工具,可以通过参数显式启用。

这个设计很像一把“瑞士军刀”:工具数量不多,但刚好覆盖 Coding Agent 最核心的动作——看、改、跑、再看结果。


三、安装和第一次运行

1. 环境要求

当前源码要求:

Node.js >= 22.19.0

可以先检查:

node-v

如果你的 Node 版本较低,建议先升级,否则可能连安装或启动都会失败。

2. 官方脚本安装

macOS / Linux:

curl-fsSL https://pi.dev/install.sh |sh

Windows PowerShell:

powershell -c "irm https://pi.dev/install.ps1 | iex"

3. npm 安装

也可以通过 npm 安装 CLI 包:

npm install -g--ignore-scripts @earendil-works/pi-coding-agent

这里的 --ignore-scripts 很值得注意。它体现了项目的一种供应链安全倾向:尽量减少安装脚本带来的额外执行面。项目 README 也提到了精确锁定直接依赖、以 package-lock.json 作为依赖事实来源、在 CI 中做 npm audit 和签名检查等实践。

4. 进入项目并启动

cd /path/to/project
pi

第一次使用时,通常需要先配置模型供应商。交互界面中输入:

/login

然后根据提示完成登录或 API Key 配置。

之后就可以输入自然语言任务,例如:

先阅读这个项目的目录结构,告诉我入口文件在哪里,不要修改任何文件。

或者:

帮我找出最近的登录相关改动,并解释可能引入的回归风险。

四、日常使用:把它当成“终端里的编程搭子”

Pi 的使用方式主要有四类。

1. 交互式 TUI

最典型方式:

cd your-project
pi

进入交互界面后,可以使用一些高频操作:

输入
作用
@路径
把指定文件加入上下文
!git status
执行命令,并把输出纳入对话
!!命令
只执行命令,不把输出发给模型
/model
查看或切换模型
/thinking
调整思考等级
/settings
查看设置
/export
导出会话
/share
分享会话
Ctrl+O
展开或收起工具输出
Ctrl+T
切换 thinking 显示

这套交互很符合终端用户习惯:你不用离开项目目录,也不用把代码反复复制到聊天窗口里。

2. Print 模式:问完就走

pi-p"List all .ts files in src/"

适合脚本化、一次性问答,或者你只想让 Pi 快速给出结论。

3. JSON 模式:给其他程序消费

pi--mode json -p"Summarize this repository"

JSON 模式会把 Agent 运行过程输出为结构化事件流,适合接入日志系统、可视化面板或自动化平台。

事件类型包括:

agent_start
turn_start
message_start
message_update
message_end
tool_execution_start
tool_execution_update
tool_execution_end
turn_end
agent_end

换句话说,Pi 不只告诉你“最后答案是什么”,还会把“它经历了哪些步骤”暴露出来。

4. RPC 模式:把它变成长期运行的服务

pi--mode rpc

RPC 模式通过 JSONL 的 stdin/stdout 通信。对平台型团队来说,这比“开一个终端给人用”更重要:你可以把 Pi 接到内部研发平台、代码审查机器人或任务调度系统里。


五、只读审查:一个更安全的入门姿势

第一次接触这类工具,不建议上来就让它自由改项目。

更稳妥的方式是只启用只读工具:

pi--tools read,grep,find,ls -p"Review this project"

这个命令只允许 Pi 读取、搜索和列目录,不启用 edit、write、bash。

你可以先让它做这些事:

阅读项目结构,输出模块关系图。
找出所有数据库查询入口,并检查是否有明显 N+1 问题。
分析最近的 git diff,列出潜在风险,但不要修改文件。

确认它理解项目之后,再逐步开放编辑和命令执行能力。


六、源码架构:Pi 不是一个单文件 CLI

从根目录 package.json 看,Pi 是一个 TypeScript monorepo,工作区包括:

{
"workspaces":["packages/*","apps/*"]
}

当前源码包版本为 1.2.0,核心包可以按职责这样理解:

模块
主要职责
packages/coding-agent
用户直接使用的 Coding Agent CLI,对应 @earendil-works/pi-coding-agent
packages/agent
通用 Agent 运行时,对应 @earendil-works/pi-agent-core
packages/ai
统一多供应商 LLM API,对应 @earendil-works/pi-ai
packages/tui
终端 UI 和差分渲染
packages/chord
类型化宿主运行时,支持插件契约、状态复制和 RPC
packages/durable
持久化会话、任务和文档运行时
packages/telemetry
遥测契约、适配器和 schema
packages/codemode
面向代码执行模式的扩展能力
packages/mcp
MCP 集成
packages/env
环境相关能力
packages/evals
评估与测试

这个拆法说明 Pi 的目标不只是“做一个 CLI”。它把几件事分开了:

  • 模型接入层
    :屏蔽不同 LLM 供应商差异;
  • Agent 核心层
    :处理消息、状态、工具调用和事件流;
  • 编码产品层
    :提供面向开发者的 CLI、TUI、会话和工具;
  • 扩展层
    :允许第三方注册工具、命令、快捷键、Provider、MCP Server;
  • 持久化层
    :让会话不是一次性的聊天记录,而是可恢复、可分叉、可压缩的数据结构。

如果你想学习 Agent 工程化,最值得看的不是某个单点算法,而是这些边界是怎么划出来的。


七、核心实现原理:Agent Loop 是怎么转起来的?

Pi 的核心循环可以用一条流水线理解:

AgentMessage[]
  → transformContext()
  → AgentMessage[]
  → convertToLlm()
  → Message[]
  → LLM Streaming
  → Tool Calls
  → Tool Results
  → 是否继续下一轮

这里有两个概念要分清。

1. AgentMessage 是 Pi 自己的内部消息模型

内部消息不直接等于某个供应商的 API 消息格式。

这样做的好处是:Agent 状态、工具调用结果、图片、thinking、使用统计等信息,可以先按 Pi 自己的模型保存;只有当真正要调用某个 LLM 时,才通过 convertToLlm() 转成对应供应商需要的格式。

这就是所谓的边界转换:

let messages = context.messages;
if (config.transformContext) {
  messages =await config.transformContext(messages, signal);
}
const llmMessages =await config.convertToLlm(messages);

这套设计避免了内部状态被某个供应商 API 绑死。

2. 工具调用不是“模型说了算”

模型返回工具调用请求之后,Pi 还会做工程校验:

收到 tool call
  → 校验参数是否符合 schema
  → 触发 beforeToolCall
  → 按配置顺序或并行执行
  → 触发 afterToolCall
  → 把 toolResult 写回消息历史

这很重要。因为模型输出本质上是文本和结构化片段,可能不完整、不合法,甚至危险。成熟的 Agent Runtime 必须在模型和真实系统之间加一层“执行闸门”。

agent-loop.ts 中还有一个细节:如果模型因为长度限制停止,某些工具调用可能被截断。Pi 会拒绝执行这种“可能不完整”的工具调用。这个判断看似小,却是很多玩具 Agent 和工程化 Agent 的差别。

3. 一轮结束后,不一定真的结束

如果工具执行产生了结果,或者用户插入了 steering / follow-up 消息,Agent 会进入下一轮:

第 1 轮:模型决定读取文件
第 2 轮:根据文件内容决定修改方案
第 3 轮:执行测试
第 4 轮:根据测试失败结果修复
第 5 轮:输出总结

这就是为什么它会给人一种“持续干活”的感觉。

本质不是魔法,而是:

模型输出动作 → 系统执行动作 → 动作结果重新成为模型上下文 → 模型继续决策。


八、CLI 启动时发生了什么?

packages/coding-agent/src/main.ts 的注释说得很直白:这个文件负责解析 CLI 参数,并把参数翻译成 createAgentSession() 的选项,真正重的活由 SDK 完成。

启动链路大致是:

解析 CLI 参数
  → 处理 auth / package / config / MCP 等子命令
  → 创建 SettingsManager
  → 创建、打开或 fork SessionManager
  → 解析项目信任状态
  → 创建 AgentSessionServices
  → 解析模型与工具
  → 创建 AgentSession
  → 进入 interactive / print / json / rpc 模式

运行模式由参数和终端环境共同决定:

functionresolveAppMode(parsed: Args, stdinIsTTY:boolean, stdoutIsTTY:boolean): AppMode {
if (parsed.mode==="rpc") return"rpc";
if (parsed.mode==="json") return"json";
if (parsed.print||!stdinIsTTY ||!stdoutIsTTY) return"print";
return"interactive";
}

这段逻辑很能体现产品设计:

  • 人在终端里操作,就进入 TUI;
  • 输出被管道接走,就自动进入 print;
  • 明确指定 JSON/RPC,就进入程序化模式;
  • 同一个 Agent 核心,被不同前端复用。

九、会话不是聊天记录,而是一棵树

很多聊天工具的会话是线性的:一条消息接一条消息。

Pi 的会话更接近 Git 分支。官方文档把它描述为一棵树,每条记录有:

interface SessionEntryBase {
  type:string;
  id:string;
  parentId:string|null;
  timestamp:string;
}

一个会话文件是 JSONL,开头类似:

{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}

后续消息通过 id 和 parentId 组成树。当前激活分支决定模型看到哪些上下文。

因此它可以支持:

命令
用途
/tree
查看会话树
/fork
从当前位置分叉
/clone
克隆会话
/compact
压缩上下文
pi --continue
继续最近会话
pi --resume
恢复并选择历史会话

这个设计对真实开发很有意义。

比如你可以在一个节点分叉:

分支 A:让 Agent 用方案一重构
分支 B:让 Agent 用方案二重构

然后对比结果,而不是让两条思路互相污染。


十、上下文工程:Pi 不只是“把全部历史塞给模型”

长任务最大的敌人是上下文窗口。

Pi 的解决思路包括几层。

1. 结构化 System Prompt

system-prompt.ts 会把系统提示拆成多个部分:

  • preamble
    ;
  • tools
    ;
  • rules
    ;
  • docs
    ;
  • addendum
    ;
  • project_context
    ;
  • skills
    ;
  • cwd
    ;
  • 自定义 section。

这比把所有提示词拼成一大段字符串更清晰,也方便扩展系统按需插入内容。

2. 项目文档发现

Pi 会发现 AGENTS.md、CLAUDE.md 等项目文档,把它们作为项目上下文的一部分。

这意味着团队可以把编码规范、目录约定、测试命令、禁止事项写进仓库,让 Agent 在进入项目时自动吸收。

3. Skills 按需加载

Skill 是一个包含 SKILL.md 的目录:

---
name: pdf-tools
description: Extract text and tables from PDF files. Use when reading, converting, or inspecting PDFs.
---

Pi 不会把所有技能全文都塞进启动上下文,而是根据任务按需加载。这很像人类工程师:不是把所有手册背下来,而是知道什么时候去查哪本手册。

4. Prompt Templates 变成斜杠命令

一个 Markdown 模板可以变成可复用命令:

---
description: Review staged git changes
argument-hint: "[focus]"
---
Review the staged changes. Focus on ${1:-correctness, security, and error handling}.

团队可以把常见审查流程沉淀成模板,减少每个人重复写提示词。

5. Compaction 不删除历史

当上下文接近窗口上限时,Pi 会追加压缩摘要。判断逻辑可以概括为:

contextTokens > contextWindow - reserveTokens

默认值中:

reserveTokens = 16384
keepRecentTokens = 20000

重点是:压缩不会删掉原始记录,而是追加一条摘要,并改变后续模型看到的上下文。原始会话仍然可追踪。


十一、扩展系统:Pi 真正的想象力所在

Pi 的扩展是 TypeScript 模块,在进程内加载。一个最小扩展可以这样写:

importtype { ExtensionAPI } from"@earendil-works/pi-coding-agent";

exportdefaultfunction (pi: ExtensionAPI) {
  pi.registerCommand("hello", {
    description:"Show a greeting",
    handler:async (name, ctx) => {
      ctx.ui.notify(`Hello, ${name ||"world"}!`,"info");
    },
  });
}

扩展能注册的不只是命令,还包括:

  • 自定义工具;
  • 斜杠命令;
  • 快捷键;
  • CLI flag;
  • 模型 Provider;
  • MCP Server;
  • 虚拟模型;
  • 事件处理器;
  • 终端 UI 渲染器。

这意味着 Pi 可以从“一个工具”长成“一个平台”。

比如团队可以写内部扩展:

  • 接入公司私有模型网关;
  • 注册代码规范检查工具;
  • 把测试平台、需求平台接进来;
  • 在 Agent 修改文件前后自动做安全扫描;
  • 把会话事件同步到内部观测系统。

当然,能力越大,风险也越大。扩展在进程内运行,这意味着你必须像对待依赖包一样审查扩展来源。


十二、SDK:把 Agent 嵌进你自己的应用

如果你不想用 CLI,也可以用 SDK。

最小示例:

import { createAgentSession } from"@earendil-works/pi-coding-agent";

const { session } =awaitcreateAgentSession();

try {
await session.prompt("What files are in the current directory?");
console.log(session.getLastAssistantText());
} finally {
  session.dispose();
}

更底层一点,可以直接使用 pi-agent-core:

import { Agent } from"@earendil-works/pi-agent-core";
import { createModels } from"@earendil-works/pi-ai";
import { anthropicProvider } from"@earendil-works/pi-ai/providers/anthropic";

const models =createModels();
models.setProvider(anthropicProvider());
const model = models.getModel("anthropic","claude-sonnet-4-6");

const agent =newAgent({
  initialState: {
    systemPrompt:"You are a helpful assistant.",
    model,
  },
  streamFn: models.streamSimple.bind(models),
});

await agent.prompt("Hello!");

这时候,Pi 不再是一个终端命令,而是你应用里的 Agent Runtime。


十三、优点:为什么它值得关注?

1. 架构边界清楚

模型、Agent 核心、CLI、TUI、会话、扩展、遥测都拆开了。阅读源码时,你能清楚看到每一层的职责。

2. 工程细节比普通 Demo 完整

比如:

  • 工具参数 schema 校验;
  • 截断工具调用保护;
  • 事件流;
  • steering / follow-up;
  • 会话树;
  • compaction;
  • SDK / JSON / RPC 多入口;
  • 供应链安装加固。

这些内容决定了一个 Agent 能不能从 Demo 走向真实使用。

3. 终端体验足够自然

对习惯命令行的人来说,pi、!git status、@文件、/model、/tree 比不断切换网页或 IDE 面板更顺滑。

4. 可扩展性强

工具、Provider、MCP、Skills、Prompt Templates、Extensions 都给了入口。它既可以个人使用,也可以被团队改造成内部平台组件。

5. MIT License

对学习和二次开发都比较友好。


十四、缺点和风险:这部分一定要认真看

1. 它不是沙箱

官方安全文档说得很明确:Pi 没有内置一套用于限制文件系统、进程、网络或凭证访问的权限系统。默认情况下,它以启动用户和启动进程的权限运行。

这句话翻译成人话就是:

你能删什么、改什么、访问什么,它理论上也可能删什么、改什么、访问什么。

项目信任机制可以阻止不受信项目自动加载资源,但它不是沙箱。

2. 不要无人值守地跑在敏感目录

不建议直接在以下环境开放完整工具能力:

  • 生产服务器;
  • 含私钥、凭证、客户数据的目录;
  • 未审查的第三方仓库;
  • 无人值守的自动化任务。

如果一定要做,优先使用容器、虚拟机或操作系统级隔离。

官方给出的 Docker 思路大致是:

FROM node:24-bookworm-slim
RUNapt-get update \
&&apt-get install -y--no-install-recommends bash ca-certificates git ripgrep \
&&rm-rf /var/lib/apt/lists/*
RUNnpm install -g--ignore-scripts @earendil-works/pi-coding-agent
WORKDIR /workspace
ENTRYPOINT ["pi"]

运行时只挂载必要目录:

docker run --rm-it\
-e ANTHROPIC_API_KEY \
-v"$PWD:/workspace"\
-v pi-agent-home:/root/.pi/agent \
  pi-sandbox

3. 成本与效果取决于模型

Pi 的框架再好,也不能让弱模型凭空变强。复杂任务的效果仍然取决于:

  • 模型能力;
  • 上下文质量;
  • 工具设计;
  • 项目文档;
  • 用户拆解任务的方式。

长任务还可能产生不低 token 成本。

4. 项目迭代很快

高活跃度是优点,也意味着 CLI 参数、SDK API、扩展接口和文档可能持续变化。用于生产系统时,建议锁定版本并保留回归测试。

5. 学习曲线存在

如果只是想“问一句、改一行”,Pi 可能显得比普通聊天机器人复杂。它更适合真正需要项目级操作、自动化和扩展的人。


十五、推荐的上手路线

我建议按四步走。

第一步:只读模式

pi--tools read,grep,find,ls -p"Review this project"

先让它读项目、讲架构、找风险,不给写权限。

第二步:小范围修改

在一个干净 Git 分支上运行:

git checkout -b pi-test
pi

然后给它一个小任务:

把 src/utils 下重复的时间格式化逻辑抽成公共函数,并补充测试。

要求它先给计划,再修改。

第三步:引入项目规范

在仓库里写 AGENTS.md,例如:

# Project Rules

- 修改代码前先阅读相关模块。
- 不修改 generated/ 目录。
- 完成后运行 `npm test`。
- API 变更必须同步更新 docs。

让项目规范进入上下文,而不是每次口头重复。

第四步:需要自动化时再接 SDK / RPC

当你确认它在自己的工作流里稳定后,再考虑:

  • 用 SDK 嵌入内部工具;
  • 用 JSON 模式做日志和审计;
  • 用 RPC 模式接入平台;
  • 用扩展系统接入私有模型和内部工具。

十六、它适合谁?

适合:

  • 经常在终端里工作的开发者;
  • 想研究 Coding Agent 内部实现的人;
  • 想把 Agent 嵌入自己产品的人;
  • 需要会话分叉、恢复、导出和审计的人;
  • 想基于 MCP / Skills / Extensions 做内部平台的人。

不太适合:

  • 完全不想接触命令行的人;
  • 只需要简单代码补全的人;
  • 无法承担模型 API 成本的人;
  • 希望在生产环境“不设防”运行 Agent 的人。

结语:Pi 最值得学的,不是“它会写代码”

AI 编程工具越来越多,单看“能不能生成代码”,差异正在缩小。

真正拉开差距的是:

  • 上下文怎么组织;
  • 工具调用怎么校验;
  • 会话状态怎么持久化;
  • 长任务怎么压缩;
  • 人机介入怎么设计;
  • 扩展系统怎么控制边界;
  • 安全风险怎么被诚实说明。

Pi 的价值就在于,它把这些关键部分都摆在了源码里。

你可以只把它当成一个终端 AI 编程助手;也可以更进一步,把它当成一份正在快速演进的 Agent 工程教材。

如果你最近想认真理解“AI 编程工具到底怎么做出来的”,这个项目值得 clone 下来,从 coding-agent、agent、ai 三个包开始读。


参考资料

  1. Pi GitHub 仓库:https://github.com/earendil-works/pi
  2. 项目 README:https://github.com/earendil-works/pi/blob/main/README.md
  3. CLI 文档:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/cli.md
  4. 使用文档:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/usage.md
  5. 会话格式:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/session-format.md
  6. Compaction 文档:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/compaction.md
  7. 扩展系统:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md
  8. Skills 文档:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/skills.md
  9. 安全说明:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/security.md
  10. 容器化运行:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/containerization.md
  11. SDK 文档:https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/sdk.md

写在最后:如果这篇文章对你有帮助,欢迎转发给正在研究 AI 编程、Agent 架构或终端工作流的朋友。工具会不断更新,但这些 Agent 工程化的底层问题,值得我们反复拆开看。

相关学习资料