05 · 编码Agent-pi-coding-agent
05 — 编码 Agent (pi-coding-agent)
问题
pi-agent-core 提供了 Agent 运行时,但它不知道什么是”文件”、什么是”bash 命令”、什么是”系统提示”。它只是一个通用的 agent loop。
pi-coding-agent 的任务是:在 agent-core 之上组装一个可用的编码助手产品——定义具体工具、构建系统提示、管理会话、支持扩展、提供终端 UI。
启动流程
packages/coding-agent/src/main.ts(~982 行)
1 | 用户执行 `pi "帮我读一下 README"` |
config.ts 定义路径:~/.pi/agent/ 是全局配置目录,包含 auth.json、settings.json、sessions/。
AgentSession — 产品层核心
packages/coding-agent/src/core/agent-session.ts(~120K,最大文件)
AgentSession 类(line 310)封装 Agent(来自 agent-core),添加:
| 能力 | 说明 |
|---|---|
| 会话持久化 | 事件订阅 + 自动保存到 session 文件 |
| 模型管理 | 运行时切换模型和 thinking level |
| Compaction | 手动 + 自动压缩,含溢出恢复 |
| Bash 执行 | 内联 bash 命令处理 |
| 会话分支 | fork、导航会话树 |
| 扩展工具注册 | _toolDefinitions+_toolRegistry |
| 系统提示构建 | 每轮重建,纳入扩展 append |
prompt() 方法
agent-session.ts:1159 — 处理用户输入的入口:
1 | prompt(userInput) |
AgentSessionEvent
agent-session.ts:144 — 扩展 AgentEvent(agent-core 的事件),添加会话级事件:
| 事件 | 说明 |
|---|---|
compaction_start/end |
上下文压缩开始/结束 |
thinking_level_changed |
thinking level 变更 |
auto_retry_start/end |
自动重试 |
bash_execution_update |
bash 执行进度 |
session_info_changed |
会话信息变更 |
SDK 工厂
packages/coding-agent/src/core/sdk.ts:173 — createAgentSession()
这是组装 Agent 的核心函数:
1 | createAgentSession(options) |
关键设计:setDefaultStreamFn()(sdk.ts:37)将 pi-ai 的流式函数注入 agent-core。这样 agent-core 的 Agent 类在调用 LLM 时,实际上是通过 pi-ai 的 Models.stream() 路由到具体 provider。
工具定义
packages/coding-agent/src/core/tools/
8 个内置工具
| 工具 | 文件 | 说明 |
|---|---|---|
read |
read.ts |
读取文件(支持截断) |
bash |
bash.ts |
执行 shell 命令(spawn hooks) |
edit |
edit.ts+edit-diff.ts |
文件编辑 + diff 生成 |
write |
write.ts |
写文件 |
grep |
grep.ts |
内容搜索(使用rg) |
find |
find.ts |
文件查找(使用fd) |
ls |
ls.ts |
目录列出 |
powershell |
powershell.ts |
Windows PowerShell 执行 |
ToolDefinition → AgentTool 转换
每个工具有两层定义:
1 | ToolDefinition(扩展 API 层) |
工厂函数(tools/index.ts):
createCodingTools()→[read, bash, edit, write](默认集)createReadOnlyTools()→[read, grep, find, ls]createAllTools()→ 全部 8 个
系统提示构建
packages/coding-agent/src/core/system-prompt.ts:28 — buildSystemPrompt()
1 | buildSystemPrompt() |
关键设计:系统提示每轮重建。因为扩展可以在运行时添加工具或 append 内容,静态缓存的系统提示会过期。每次 LLM 调用前都重新构建,确保提示反映当前状态。
扩展系统
packages/coding-agent/src/core/extensions/
扩展类型定义
extensions/types.ts(~64K,第二大文件):
1 | interface ExtensionContext { |
扩展可以注册:
| 注册项 | 方法 | 说明 |
|---|---|---|
| 工具 | pi.registerTool() |
添加自定义工具 |
| 命令 | pi.registerCommand() |
添加/ 命令 |
| 快捷键 | pi.registerShortcut() |
添加键盘快捷键 |
| Provider | pi.registerProvider() |
添加自定义 LLM provider |
| 标志 | pi.registerFlag() |
添加 CLI 标志 |
扩展加载
extensions/loader.ts(~26K):
使用 jiti(运行时 TypeScript 转译)加载 .ts/.js 扩展文件。
关键设计 — VIRTUAL_MODULES(line 50):将 pi 包名映射到打包实例,让扩展即使在编译后的 Bun 二进制中也能 import pi 包。
加载来源:
- 文件扩展:从
extensions/目录发现 - 内联扩展:
InlineExtension+ factory 函数 - 包扩展:npm、git、本地路径安装的扩展包
扩展运行
extensions/runner.ts(~40K)— ExtensionRunner:
1 | 生命周期事件分发: |
ExtensionRunner 在每个生命周期节点调用已注册的处理器。before_provider_headers 和 before_provider_request 事件由 Agent 的 streamFn 触发——这是扩展修改 LLM 请求的入口。
内置扩展
extensions/index.ts:只有一个内置扩展——llama.cpp(extensions/llama/):
- 注册 llama.cpp provider 和
/llama命令 - HTTP 客户端连接 llama.cpp server
- HuggingFace 模型搜索/下载
- TUI 模型管理界面
运行模式
packages/coding-agent/src/modes/
Interactive 模式
modes/interactive/interactive-mode.ts(~229K,仓库最大文件)
InteractiveMode 类管理完整的 TUI 体验:
- 使用
pi-tui库渲染(TuiMainScreen、Container、Markdown、Text) - 管理聊天视口、编辑器、footer、模型选择器、会话选择器
- 处理键盘输入、slash 命令、自动补全、剪贴板、图片粘贴
- 订阅
AgentSession事件,事件驱动渲染 - 处理扩展 UI 上下文(对话框、组件、自定义编辑器、覆盖层)
components/(38 个文件):每个渲染需求一个组件——assistant-message.ts、bash-execution.ts、footer.ts、model-selector.ts、diff.ts、mermaid.ts、login-dialog.ts 等。
Print 模式
modes/print-mode.ts:单次模式,用于 pi -p "prompt"。发送 prompt → 输出结果 → 退出。用于脚本化。
RPC 模式
modes/rpc/:无头 JSON-over-stdin/stdout 协议,用于嵌入。runRpcMode() 监听 JSON 命令,输出事件和响应。
JSON Event 模式
modes/json-event.ts:toJsonEvent() 将 AgentSessionEvent 转为 JSON 可序列化对象,用于 --mode json 输出。
资源加载
packages/coding-agent/src/core/resource-loader.ts(~40K)
DefaultResourceLoader 从全局(~/.pi/agent/)和项目(.pi/)位置加载:
| 资源 | 位置 | 说明 |
|---|---|---|
| 扩展 | extensions/ |
.ts/.js 文件 |
| 技能 | skills/ |
SKILL.md 文件夹或 .md 文件 |
| 提示模板 | prompts/ |
.md 文件 |
| 主题 | themes/ |
.json 文件 |
| 上下文文件 | 根目录 | AGENTS.md、CLAUDE.md |
reload() 可以在信任决策后重新加载需要信任的资源。
其他核心组件
| 组件 | 文件 | 说明 |
|---|---|---|
| ModelRuntime | model-runtime.ts |
模型目录、认证、provider 注册、流式协调 |
| ModelResolver | model-resolver.ts |
解析provider/model:thinking 模式为 Model 对象 |
| SessionManager | session-manager.ts |
JSONL 会话持久化、分支、fork、会话树导航 |
| SettingsManager | settings-manager.ts |
全局~/.pi/agent/settings.json+ 项目 .pi/settings.json |
| Skills | skills.ts |
加载和格式化 skill 文件(markdown + frontmatter) |
| TrustManager | trust-manager.ts |
项目信任管理(不受信任项目的扩展需要审批) |
| Keybindings | keybindings.ts |
KeybindingsManager+DEFAULT_EDITOR_KEYBINDINGS/DEFAULT_APP_KEYBINDINGS |
| PackageManager | package-manager.ts |
安装/管理 npm、git、本地路径的 pi 包 |
AgentSession 如何扩展 Agent 核心
这是理解整个项目的关键——AgentSession 在不修改 Agent 类的前提下扩展其行为:
1 | Agent (agent-core) AgentSession (coding-agent) |
关键设计:所有扩展点都通过 AgentLoopConfig 的回调函数注入。Agent 类不需要知道扩展系统的存在——它只看到一组回调函数。这实现了开闭原则:对扩展开放,对修改关闭。
学习要点
- AgentSession 是组装层:它不实现 Agent 逻辑,而是通过回调函数将工具、系统提示、扩展、会话管理注入 agent-core
- 系统提示每轮重建:因为扩展可以动态添加工具和内容,静态缓存会过期
- ToolDefinition → AgentTool 的两层设计:ToolDefinition 面向扩展开发者(含 UI 渲染钩子),AgentTool 面向运行时(只含执行逻辑)
- 扩展系统通过生命周期事件解耦:扩展在
before_agent_start、tool_call、before_provider_request等节点被调用,不需要直接修改 Agent 代码 - streamFn 是 LLM 调用的唯一入口:通过
setDefaultStreamFn()注入,让 agent-core 保持 provider 无关 - VIRTUAL_MODULES 解决打包扩展的 import 问题:编译后的 Bun 二进制中,扩展仍然可以 import pi 包
上一篇:04-会话持久化-pi-protocol 与 session-backends
下一篇:06-终端 UI-pi-tui