深入剖析 Earendil Works 开源的 Pi Agent Harness------一个基于 TypeScript Monorepo 构建的模块化交互式编程代理
项目简介
Pi Agent Harness 是由 Earendil Works 开发的开源 coding agent 项目。其核心定位是提供一个可扩展的交互式编程代理,能运行在终端中,通过多 LLM provider 驱动,完成代码编写、搜索、编辑等任务。
核心特点:
- 🔌 多 LLM Provider 支持:OpenAI、Anthropic、Google、GitHub Copilot、OpenRouter、xAI、Mistral 等 20+ 个 provider 的统一接口
- 🧩 自扩展:coding agent 可以通过 skill、extension、prompt template 等方式进行扩展
- 🖥️ 交互式终端 UI:支持差分渲染的 TUI、alt-screen 全屏模式、overlay、滚动视图等
- 📦 会话持久化与分支:支持 JSONL 格式的会话存储、会话 fork、恢复、continue
- 🔗 RPC 模式:支持通过 JSON 行进行程序化控制,可嵌入到其他应用中
- 🔐 供应链安全:pin 依赖版本、npm shrinkwrap、install lock、lifecycle script 审计
发布包:
| npm 包名 | 描述 |
|---|---|
@earendil-works/pi-coding-agent |
交互式 coding agent CLI |
@earendil-works/pi-agent-core |
Agent 运行时(工具调用、状态管理) |
@earendil-works/pi-ai |
统一多 provider LLM API |
@earendil-works/pi-tui |
终端 UI 库(差分渲染) |
整体架构
┌─────────────────────────────────────────────────────────────┐
│ 用户终端 │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ packages/coding-agent (交互式 CLI 主程序) │
│ ┌───────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ main.ts │→ │ AgentSession │→ │ SessionManager │ │
│ │ cli.ts │ │ Runtime │ │ (会话持久化) │ │
│ └───────────┘ └──────┬───────┘ └──────────────────┘ │
│ │ │
│ ┌───────────────────┐ │ ┌────────────────────────┐ │
│ │ Extensions / Skills│←─┘→│ SettingsManager │ │
│ │ (扩展与技能) │ │ (配置管理) │ │
│ └───────────────────┘ └────────────────────────┘ │
└────────────────────────┬────────────────────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────────┐ ┌────────────┐ ┌──────────────┐
│ packages/ │ │ packages/ │ │ packages/ │
│ agent │ │ tui │ │ protocol │
│ (Agent 运行时)│ │ (终端 UI) │ │ (RPC 协议) │
└──────┬───────┘ └────────────┘ └──────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ packages/ai (统一 LLM API 层) │
│ ┌────────────┐ ┌──────────┐ ┌────────────────┐ │
│ │ stream() │→ │ Provider │→ │ HTTP Client │ │
│ │ (统一入口) │ │ Registry │ │ (fetch/SSE) │ │
│ └────────────┘ └──────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ OpenAI / Anthropic / Google / Copilot / OpenRouter / xAI │
│ Mistral / Ollama / Groq / Cerebras / DeepSeek / ... │
└─────────────────────────────────────────────────────────────┘
分层结构(自底向上):
- AI 层 (
packages/ai):统一 LLM API,屏蔽不同 provider 的协议差异 - Agent 层 (
packages/agent):Agent 运行时,管理状态、工具调用、消息队列 - TUI 层 (
packages/tui):终端 UI 组件库,差分渲染、事件处理 - 协议层 (
packages/protocol):RPC 协议的 schema 定义 - Client 层 (
packages/client):RPC 客户端 SDK - Server 层 (
packages/server):RPC 服务端框架 - Coding Agent 层 (
packages/coding-agent):组装所有模块的 CLI 主程序
Monorepo 结构
pi-monorepo/
├── packages/
│ ├── ai/ # 统一多 provider LLM API
│ ├── agent/ # Agent 运行时核心
│ ├── coding-agent/ # 交互式 coding agent CLI(主程序)
│ ├── tui/ # 终端 UI 库
│ ├── protocol/ # RPC 协议定义
│ ├── client/ # RPC 客户端 SDK
│ ├── server/ # RPC 服务端框架
│ ├── evals/ # 评估基准
│ └── storage/ # 存储后端(sqlite-node 等)
├── scripts/ # 构建、发布、检查脚本
├── .pi/ # Pi 自身配置
├── package.json # Monorepo 根配置
├── tsconfig.json # TypeScript 配置
├── biome.json # Biome linter/formatter 配置
└── vitest.base.ts # 测试配置
构建顺序 (npm run build 的依赖链):
tui → ai → agent → storage/sqlite-node → protocol → client → coding-agent → server
这种依赖链设计非常清晰------底层模块不依赖上层,每一层只依赖其下的层。
核心模块详解
1. coding-agent:交互式 CLI 主程序
这是整个项目的主程序包,将 agent 运行时、TUI、会话管理、配置管理、工具系统等组合为一个完整的交互式 coding agent。
入口文件
src/main.ts --- main() 函数是整个 CLI 的入口点。
启动流程概览:
main()
├── 1. 初始化
│ ├── resetTimings() // 重置性能计时
│ ├── 合并 builtInExtensions // 注册内置扩展
│ ├── 检查 offline 模式 // --offline 或 PI_OFFLINE
│ └── applyHttpProxySettings() // 应用 HTTP 代理配置
│
├── 2. 命令处理(短路)
│ ├── handlePackageCommand() // pi install/uninstall/update/...
│ ├── handleConfigCommand() // pi config ...
│ └── runCredentialPrintCommand() // pi credential ...
│
├── 3. 参数解析
│ ├── parseArgs() // CLI 参数解析
│ └── resolveAppMode() // 确定运行模式(interactive/rpc/print)
│
├── 4. 迁移与设置
│ ├── runMigrations() // 执行配置迁移
│ └── SettingsManager.create() // 创建设置管理器
│
├── 5. 会话管理
│ ├── createSessionManager() // 创建/恢复/继续会话
│ └── getMissingSessionCwdIssue() // 检查会话 CWD 一致性
│
├── 6. 运行时创建
│ ├── createRuntime() // 创建 AgentSessionRuntime
│ │ ├── ModelRuntime // 模型运行时
│ │ ├── ResourceLoader // 资源加载器
│ │ ├── AuthStorage // 认证存储
│ │ ├── ExtensionManager // 扩展管理器
│ │ └── SkillRegistry // 技能注册表
│ └── createAgentSession() // 创建 Agent 会话
│
└── 7. 运行模式分派
├── runInteractiveMode() // 交互式 TUI 模式
├── runRpcMode() // RPC(JSON-lines)模式
└── runPrintMode() // 单次打印模式
核心子模块
AgentSession (src/core/agent-session.ts)
编码 agent 的会话对象,封装了:
- 底层
Agent(来自packages/agent)实例 - 系统提示词管理(system prompt)
- 工具注册(内置工具 + 扩展工具)
- 模型切换(Ctrl+P 循环)
- 自动压缩(auto-compaction,当 token 接近上限时自动摘要)
- 会话事件分发
AgentSessionRuntime (src/core/agent-session-runtime.ts)
与特定 CWD 绑定的运行时环境,包含:
ModelRuntime:模型查询、切换、API 兼容性处理SettingsManager:全局/项目/会话级设置ResourceLoader:资源文件(CLAUDE.md 等)加载AuthStorage:认证凭据存储ExtensionManager:扩展加载与管理SkillRegistry:技能(/command)注册
SessionManager (src/core/session-manager.ts)
会话的生命周期管理:
- 创建新会话(
create()) - 打开已有会话(
open()) - 继续最近会话(
continueRecent()) - Fork 会话(
forkSessionOrExit()) - 列出所有会话(
list()、listAll())
会话文件存储在 ~/.pi/agent/sessions/<encoded-cwd>/ 下,格式为 JSONL。
运行模式
交互式模式 (src/modes/interactive/):
- 使用
TuiMainScreen进行全屏或滚动渲染 - 支持键盘快捷键(Ctrl+C、Ctrl+P、Escape 等)
- 输入编辑器、自动补全、overlay 弹窗
- 流式输出渲染
RPC 模式 (src/modes/rpc/):
- 通过 stdin/stdout 交换 JSON-lines 消息
- 支持
get_state、prompt、steer、abort、set_model等命令 - 可嵌入到 IDE、web 应用等外部宿主
Print 模式 (src/modes/print/):
- 非交互式单次执行
- 输出到 stdout,适合脚本和管道
2. agent:Agent 运行时
Agent 运行时是与 UI 无关的核心引擎,负责:
- 管理 Agent 状态(消息、工具、模型)
- 驱动 Agent Loop(LLM 调用 → 工具执行 → 继续)
- 消息队列(steering queue、follow-up queue)
- 事件分发
- 会话持久化抽象
Agent 类
typescript
class Agent {
// 状态
state: AgentState; // 包含 systemPrompt, model, tools, messages, isStreaming 等
// 消息队列
steer(message: AgentMessage): void; // 在当前 turn 结束后注入消息
followUp(message: AgentMessage): void; // 在 agent 即将停止时注入消息
// 主入口
prompt(input: string | AgentMessage): Promise<void>; // 开始一个新 prompt
continue(): Promise<void>; // 继续运行(处理队列)
abort(): void; // 中止当前运行
// 事件
addListener(listener: (event: AgentEvent) => void): void;
// AgentEvent: assistant_start, text_delta, toolcall_start, tool_execution_end, agent_end, ...
}
状态模型(AgentState 接口):
typescript
interface AgentState {
systemPrompt: string; // 系统提示词
model: Model; // 当前模型
thinkingLevel: ThinkingLevel; // 推理等级(off/minimal/low/medium/high/xhigh/max)
tools: AgentTool[]; // 可用工具列表
messages: AgentMessage[]; // 对话历史
readonly isStreaming: boolean; // 是否正在流式输出
readonly streamingMessage?: AgentMessage; // 当前部分消息
readonly pendingToolCalls: ReadonlySet<string>; // 正在执行的工具调用 ID
readonly errorMessage?: string; // 最近的错误消息
}
Agent Loop
Agent Loop 是整个系统的核心循环 ,位于 src/agent-loop.ts:
runAgentLoop(context, streamFn, config)
│
├── 1. 准备上下文
│ ├── transformContext() // 消息转换(过滤自定义消息)
│ ├── convertToLlm() // AgentMessage → LLM Message
│ └── 构建 streamFn 参数
│
├── 2. 调用 LLM
│ ├── streamFn(model, context) // 调用 LLM API,返回流
│ └── 处理流事件:
│ ├── text_delta // 文本增量
│ ├── thinking_delta // 思考增量
│ ├── toolcall_start/delta/end // 工具调用
│ └── done / error // 结束/错误
│
├── 3. 工具执行
│ ├── 识别工具调用(从 assistant message 中)
│ ├── prepareToolCall() // 参数准备和验证
│ ├── beforeToolCall() // 工具执行前钩子
│ ├── 并行/串行执行工具 // 根据 executionMode
│ │ ├── tool.execute() // 执行工具
│ │ └── 收集 AgentToolResult
│ ├── afterToolCall() // 工具执行后钩子
│ └── 将工具结果追加到 messages
│
├── 4. 检查终止条件
│ ├── stopReason === "stop" // LLM 决定停止
│ ├── terminate === true // 工具请求终止
│ ├── 队列消息处理 // drain steering/followUp queue
│ └── prepareNextTurn() // 下一轮准备钩子
│
└── 5. 继续或结束
├── 有工具结果 → 继续循环
├── 有队列消息 → 注入消息继续
└── 否则 → 结束循环
关键设计决策:
- 工具执行模式 :支持
sequential(逐个执行)和parallel(并行执行)两种模式,每个工具可通过executionMode属性覆盖默认行为 - 消息队列 :
steering队列在每轮结束后注入,followUp队列在 agent 即将停止时注入,支持不同的QueueMode(all/one-at-a-time) - 流式处理 :LLM 返回的流通过
AssistantMessageEventStream处理,支持增量更新 UI
工具接口
typescript
interface AgentTool<TParameters, TDetails> {
name: string; // 工具名称
description: string; // 工具描述(给 LLM 看的)
parameters: TSchema; // JSON Schema 参数定义
label: string; // 人类可读标签(给 UI 看的)
executionMode?: ToolExecutionMode; // 执行模式
// 参数预处理(可选)
prepareArguments?: (args: unknown) => Static<TParameters>;
// 执行函数
execute: (
toolCallId: string,
params: Static<TParameters>,
signal?: AbortSignal,
onUpdate?: AgentToolUpdateCallback<TDetails>,
) => Promise<AgentToolResult<TDetails>>;
}
3. ai:统一多 Provider LLM 接口
AI 层是 LLM 调用的统一抽象层,将不同 provider 的协议差异屏蔽,为上层提供一致的 API。
核心 API
typescript
// 简化的流式调用
streamSimple(model, context, options): AssistantMessageEventStream
// 完整的流式调用
stream(model, context, options): AssistantMessageEventStream
API 实现
每种 API 协议有一个独立的实现文件:
| 文件 | 协议 | 使用的 Provider |
|---|---|---|
src/api/openai-completions.ts |
OpenAI Chat Completions | OpenAI、Azure、Mistral、Groq、Cerebras、DeepSeek、xAI 等 |
src/api/openai-responses.ts |
OpenAI Responses API | OpenAI (GPT-5 等) |
src/api/openai-codex-responses.ts |
OpenAI Codex Responses | Codex |
src/api/anthropic.ts |
Anthropic Messages | Anthropic (Claude) |
src/api/google.ts |
Google Gemini | |
src/api/bedrock.ts |
AWS Bedrock | Amazon |
src/api/ollama.ts |
Ollama | Ollama (本地) |
src/api/github-copilot.ts |
GitHub Copilot | GitHub Copilot |
流式处理流程(以 OpenAI Completions 为例)
stream(model, context, options)
│
├── 1. 初始化
│ ├── getClientApiKey() // 获取 API Key
│ ├── getCompat() // 获取 provider 兼容性配置
│ ├── createGrammarToolInputProperties() // 语法工具处理
│ ├── resolveCacheRetention() // 缓存保留策略
│ └── createClient() // 创建 OpenAI SDK 客户端
│
├── 2. 构建请求参数
│ └── buildParams()
│ ├── convertMessages() // 消息格式转换
│ ├── 设置 prompt_cache_key // 缓存键
│ ├── 设置 tools // 工具定义
│ └── 设置 reasoning_effort // 推理等级
│
├── 3. 发送请求
│ ├── retryProviderRequest() // 带重试的请求
│ └── client.chat.completions.create() // OpenAI SDK 调用
│
├── 4. 处理流式响应
│ └── for await (chunk of openaiStream)
│ ├── 解析 usage // token 用量
│ ├── 解析 finish_reason // 停止原因
│ ├── 解析 text delta // 文本增量
│ ├── 解析 reasoning delta // 推理增量(thinking)
│ ├── 解析 tool_calls delta // 工具调用增量
│ └── 解析 reasoning_details // 加密推理详情
│
└── 5. 完成/错误处理
├── finishBlock() // 完成每个内容块
├── stream.push(done) // 推送完成事件
└── stream.end() // 结束流
模型管理
typescript
interface Model<TApi> {
id: string; // 模型 ID
provider: string; // Provider ID
api: TApi; // API 类型
baseUrl?: string; // 自定义 base URL
headers?: Record<string, string>; // 自定义请求头
}
ModelsRuntime 负责:
- 内置模型数据(编译时生成)
- 远程 catalog 更新(运行时刷新)
- 模型查询和匹配
4. tui:终端 UI 库
TUI 是一个独立的终端 UI 库,提供类似 React 的组件化开发模型,支持差分渲染。
核心接口
typescript
interface Component {
// 渲染组件为行数组
render(width: number): string[];
// 处理键盘输入(当组件有焦点时)
handleInput?(data: string): void;
// 是否接收按键释放事件(Kitty 协议)
wantsKeyRelease?: boolean;
// 使缓存失效(主题变化等)
invalidate(): void;
}
interface TUI extends Component {
children: Component[]; // 子组件
terminal: Terminal; // 终端抽象
// 组件管理
addChild(component: Component): void;
removeChild(component: Component): void;
clear(): void;
// 焦点管理
setFocus(component: Component | null): void;
// Overlay(弹窗/浮层)
showOverlay(component: Component, options?: OverlayOptions): OverlayHandle;
hideOverlay(): void;
// 生命周期
start(): void;
stop(): void;
// 渲染
requestRender(force?: boolean): void;
// 输入监听
addInputListener(listener: TuiInputListener): () => void;
// 终端颜色
queryTerminalBackgroundColor(options): Promise<RgbColor | undefined>;
queryTerminalColorScheme(options): Promise<TerminalColorScheme | undefined>;
}
TUI 实现层次
TUI (interface)
└── TuiBase (abstract class, extends Container)
├── TuiMainScreen // 主屏幕模式(终端滚动缓冲区)
└── TuiAltScreen // 替代屏幕模式(全屏,应用控制视口)
TuiMainScreen:
- 使用终端的主屏幕(normal screen buffer)
- 输出追加到终端滚动缓冲区
- 支持差分渲染:只重绘变化的行
- 适用于常规 chat 模式
TuiAltScreen:
- 使用终端的替代屏幕 (alternate screen buffer,
ESC[?1049h) - 应用完全控制视口内容
- 支持滚动、鼠标事件、文本选择
- 适用于全屏模式
差分渲染机制
TUI 的核心性能优化是差分渲染:
requestRender(force?)
│
├── 1. 收集所有子组件的 render() 输出
│ └── 合并为完整的文档行数组
│
├── 2. 与上一帧对比
│ ├── 找到变化的行范围
│ └── 处理 Kitty 图片(特殊协议)
│
├── 3. 生成终端指令
│ ├── 移动光标到变化区域
│ ├── 输出变化的行
│ └── 清理多余行
│
└── 4. 更新缓存状态
├── lastDocument = newDocument
└── fullRedraws++(如果是强制重绘)
Overlay 系统
Overlay 是 TUI 的浮层/弹窗机制:
typescript
interface OverlayOptions {
width?: SizeValue; // 宽度(数字或百分比)
maxHeight?: SizeValue; // 最大高度
anchor?: OverlayAnchor; // 锚点(center/top-left/...)
offsetX?: number; // 水平偏移
offsetY?: number; // 垂直偏移
margin?: OverlayMargin | number; // 边距
visible?: (w, h) => boolean; // 可见性条件
nonCapturing?: boolean; // 是否不捕获焦点
}
interface OverlayHandle {
hide(): void; // 永久隐藏
setHidden(hidden: boolean): void; // 临时隐藏/显示
focus(): void; // 获取焦点
unfocus(options?): void; // 释放焦点
isFocused(): boolean; // 是否获得焦点
}
5. server / client / protocol
这三个包提供了 RPC(远程过程调用) 能力,允许外部程序通过 JSON-lines 协议控制 agent。
- protocol:定义了 RPC 协议的消息格式(请求、响应、事件)
- server :RPC 服务端框架(
PiSessionBackend、PiSessionRuntime、LiveSession) - client :RPC 客户端 SDK(
PiClient、RemoteSession,支持自动重连、状态同步)
工具系统
内置工具
coding-agent 提供了一组内置工具(packages/agent/src/harness/tools/):
| 工具 | 文件 | 功能 |
|---|---|---|
Bash |
bash.ts |
执行 shell 命令 |
Edit |
edit.ts |
文本替换编辑 |
Read |
read.ts |
读取文件内容 |
Write |
write.ts |
写入文件 |
Find |
find.ts |
文件搜索(按名称/glob) |
Grep |
grep.ts |
内容搜索(正则表达式) |
Ls |
ls.ts |
列出目录内容 |
NotebookEdit |
notebook-edit.ts |
Jupyter notebook 编辑 |
TodoRead |
todo-read.ts |
读取待办事项 |
TodoWrite |
todo-write.ts |
写入待办事项 |
Edit 工具的模糊匹配
Edit 工具(packages/agent/src/harness/tools/edit-diff.ts)实现了模糊匹配机制:
applyEditsToNormalizedContent(content, edits)
│
├── 1. 尝试精确匹配
│ └── content.indexOf(oldText)
│
├── 2. 如果失败,尝试模糊匹配
│ ├── normalizeForFuzzyMatch(content)
│ │ ├── 标准化 Unicode 引号
│ │ ├── 标准化 Unicode 破折号
│ │ ├── 标准化特殊空格
│ │ └── 去除行尾空白
│ └── fuzzyContent.indexOf(fuzzyOldText)
│
├── 3. 检查重复匹配
│ └── countOccurrences() > 1 → 报错
│
├── 4. 检查重叠
│ └── 排序后检查相邻 edit 是否重叠
│
└── 5. 应用替换
├── 精确匹配:直接替换
└── 模糊匹配:保留原始行的未变部分
这个设计很巧妙------AI 生成的代码经常会有 Unicode 引号(如 " 和 ")或行尾空白不一致的问题,模糊匹配能有效解决这些场景。
会话管理与持久化
会话文件格式
会话文件采用 JSONL 格式(每行一个 JSON 对象),存储在 ~/.pi/agent/sessions/<encoded-cwd>/ 下。
文件命名:<timestamp>_<session-id>.jsonl
jsonl
{"type":"session","version":3,"id":"<uuid>","timestamp":"2026-08-02T...","cwd":"/path/to/project"}
{"type":"message","id":"msg_001","message":{"role":"user","content":[{"type":"text","text":"Hello"}]}}
{"type":"message","id":"msg_002","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}]}}
{"type":"label","id":"lbl_001","targetId":"msg_002","label":"Greeting"}
{"type":"compaction","id":"cmp_001","usage":{"input":1000,"output":200,...}}
条目类型:
| 类型 | 说明 |
|---|---|
session |
会话头(必须第一行) |
message |
对话消息 |
label |
消息标签(可引用其他条目) |
compaction |
压缩摘要记录 |
branch_summary |
分支摘要 |
session_info |
会话元信息(名称等) |
自动压缩
当 token 使用接近上下文窗口限制时,系统会自动触发压缩:
检查 token 使用量
│
├── 如果超过阈值:
│ ├── 保留最近 N 条消息
│ ├── 将早期消息摘要为压缩文本
│ ├── 将摘要作为新的 system prompt 前缀
│ └── 记录 compaction 条目
│
└── 设置:
├── compaction.enabled(默认 true)
├── compaction.reserveTokens(默认 16384)
└── compaction.keepRecentTokens(默认 20000)
配置系统
三级配置合并
全局设置 (~/.pi/settings.json)
↓ 合并
项目设置 (<project>/.pi/settings.json)
↓ 合并
运行时覆盖 (CLI 参数 / 会话内设置)
合并策略:深度合并(deepMergeSettings),嵌套对象递归合并,后者覆盖前者。
主要配置项
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
defaultProvider |
string | - | 默认 LLM provider |
defaultModel |
string | - | 默认模型 |
defaultThinkingLevel |
ThinkingLevel | "off" | 默认推理等级 |
transport |
TransportSetting | "auto" | 传输协议 |
steeringMode |
"all" | "one-at-a-time" | - | 转向队列模式 |
followUpMode |
"all" | "one-at-a-time" | - | 后续队列模式 |
theme |
string | - | UI 主题 |
uiMode |
"regular" | "fullscreen" | "regular" | UI 模式 |
compaction.enabled |
boolean | true | 是否启用自动压缩 |
compaction.reserveTokens |
number | 16384 | 预留 token 数 |
compaction.keepRecentTokens |
number | 20000 | 保留最近消息的 token 数 |
terminal.showImages |
boolean | true | 是否显示图片 |
httpProxy |
string | - | HTTP 代理 URL |
packages |
PackageSource\[\] | - | npm/git 包源 |
extensions |
string\[\] | - | 扩展文件路径 |
skills |
string\[\] | - | 技能文件路径 |
enabledModels |
string\[\] | - | 可循环的模型模式 |
扩展机制
Extensions(扩展)
扩展是 TypeScript 文件,可以:
- 注册新工具
- 添加 UI 组件(overlay)
- 修改系统提示词
- 注册新的 LLM provider
- 添加自定义消息类型
typescript
interface Extension {
name: string;
tools?: AgentTool[];
uiComponents?: UIComponent[];
systemPrompt?: string;
providers?: Provider[];
}
扩展加载顺序:
- 内置扩展(
builtInExtensions) - Settings 中
extensions配置的扩展 - CLI
--extensions参数指定的扩展
Skills(技能)
技能是可以通过 /skill:name 命令调用的功能模块:
- 技能文件可以是 TypeScript 或 Markdown
- 支持参数传递
- 在
settings.skills中配置路径
Prompt Templates(提示词模板)
可复用的提示词模板:
- 存储在
settings.prompts指定的路径 - 支持变量替换
- 通过
/prompt命令或 API 调用
Themes(主题)
UI 主题定制:
- 定义颜色、样式
- 存储在
settings.themes指定的路径 - 通过
settings.theme激活
关键数据流
用户输入到 LLM 响应的完整流程
用户输入 "帮我写一个函数"
│
├── TUI 层
│ ├── 键盘事件 → InputHandler
│ ├── 输入编辑器收集文本
│ └── 按 Enter 提交
│
├── AgentSession
│ ├── 创建 UserMessage
│ ├── 追加到 state.messages
│ └── 调用 agent.prompt()
│
├── Agent
│ ├── 将消息放入队列
│ └── 启动 Agent Loop
│
├── Agent Loop
│ ├── convertToLlm(messages) // AgentMessage[] → Message[]
│ ├── transformContext() // 消息转换
│ └── streamFn(model, context) // 调用 LLM
│
├── AI 层
│ ├── 选择 API 实现(如 openai-completions)
│ ├── buildParams() // 构建请求参数
│ ├── client.chat.completions.create() // HTTP 请求
│ └── 返回 AssistantMessageEventStream
│
├── 流处理
│ ├── text_delta → AgentEvent → TUI 渲染
│ ├── toolcall_start → 工具准备
│ ├── tool_execution → 工具运行
│ ├── tool_result → 追加到 messages
│ └── done → 检查是否继续
│
└── 输出渲染
├── 文本增量 → 差分渲染到终端
├── 工具调用 → overlay/状态更新
└── 完成 → 恢复输入
关键文件索引
入口文件
| 文件 | 说明 |
|---|---|
packages/coding-agent/src/main.ts |
CLI 主入口(main() 函数) |
packages/coding-agent/src/cli.ts |
CLI 分派入口 |
packages/coding-agent/src/rpc-entry.ts |
RPC 模式入口 |
核心模块
| 文件 | 说明 |
|---|---|
packages/agent/src/agent.ts |
Agent 核心类 |
packages/agent/src/agent-loop.ts |
Agent Loop 实现 |
packages/agent/src/types.ts |
Agent 类型定义 |
packages/coding-agent/src/core/agent-session.ts |
AgentSession 实现 |
packages/coding-agent/src/core/agent-session-runtime.ts |
AgentSessionRuntime |
packages/coding-agent/src/core/session-manager.ts |
SessionManager |
packages/coding-agent/src/core/settings-manager.ts |
SettingsManager |
packages/coding-agent/src/core/model-runtime.ts |
ModelRuntime |
AI 层
| 文件 | 说明 |
|---|---|
packages/ai/src/index.ts |
AI 包入口 |
packages/ai/src/api/openai-completions.ts |
OpenAI Completions API |
packages/ai/src/api/anthropic.ts |
Anthropic Messages API |
packages/ai/src/api/google.ts |
Google Gemini API |
packages/ai/src/models.ts |
Model 类型和管理 |
packages/ai/src/models-runtime.ts |
ModelsRuntime |
TUI 层
| 文件 | 说明 |
|---|---|
packages/tui/src/tui.ts |
TUI 接口和基础类 |
packages/tui/src/tui-main-screen.ts |
主屏幕 TUI |
packages/tui/src/tui-alt-screen.ts |
替代屏幕 TUI |
packages/tui/src/terminal.ts |
终端抽象 |
工具
| 文件 | 说明 |
|---|---|
packages/agent/src/harness/tools/bash.ts |
Bash 工具 |
packages/agent/src/harness/tools/edit.ts |
Edit 工具 |
packages/agent/src/harness/tools/edit-diff.ts |
Edit 模糊匹配算法 |
packages/agent/src/harness/tools/read.ts |
Read 工具 |
packages/agent/src/harness/tools/find.ts |
Find 工具 |
packages/agent/src/harness/tools/grep.ts |
Grep 工具 |
总结
Pi Agent Harness 是一个工程质量极高的 TypeScript Monorepo 项目,几个亮点:
- 模块化极致:7 个独立 npm 包,每一层职责清晰
- 差分渲染 TUI:独立的终端 UI 库,性能优化到位
- 模糊匹配 Edit:解决 AI 生成代码的 Unicode 引号/空白问题
- RPC 模式:让 agent 可以被嵌入到 IDE、Web 应用等外部宿主
- 三级配置合并:全局 + 项目 + 运行时的灵活配置
- 会话 Fork 与分支:支持树形会话结构
- 供应链安全:pin 依赖、shrinkwrap、lifecycle script 审计
对于想要用 TypeScript 构建 AI Agent 工具的开发者来说,Pi 的架构设计(尤其是分层结构、Agent Loop、TUI 差分渲染、RPC 模式)非常值得学习。
项目地址 :https://github.com/earendil-works/pi-monorepo
许可证:开源