RPC 模式:把 pi 嵌进程序
本教程共 30 篇 · 第 20 篇 · 更新于 2026-08-10 · 约 7 分钟阅读
本节目标:理解 RPC 模式的运行原理,掌握用 JSON 协议控制 pi 的方法,能用 curl 或脚本发送指令并接收结果。学完你就能把 pi 接进 CI/CD 流水线、聊天机器人或者自定义的 Web 前端。
不是只有 TUI:pi 的四种面孔
pi 一共有四种运行模式:交互模式(TUI)、print 单次模式、JSON 模式、RPC 模式(第 1 章提过,第 6 章有详细对比表)。交互模式适合你坐在终端前跟 pi 对话,但如果你想把 pi 嵌进另一个程序里——比如一个 VS Code 插件、一个 Slack 机器人、或者一个 CI 流水线——交互模式就不够用了。
RPC 模式就是为此准备的;另外把 pi 当库直接调用(SDK 模式)也能达到类似效果,但那是库 API 而不是 CLI 运行模式。这一章讲 RPC 模式。
RPC 模式让 pi 变成一个”后台服务”:它通过 stdin/stdout 收发 JSON 消息,你的程序启动一个 pi 子进程,往 stdin 写命令,从 stdout 读事件。不依赖终端,不需要交互,纯数据流。
Note如果你是在 Node.js 项目里集成 pi,直接引用
@earendil-works/pi-coding-agent的AgentSessionAPI 会更方便。SDK 方式在下一章讲。RPC 模式适合不用 TypeScript 的场景——比如 Python、Go、Rust 或者 CI 脚本。
启动 RPC 模式
一行命令:
pi --mode rpc
常用的附加参数:
| 参数 | 作用 |
|---|---|
--provider <name> | 指定 LLM 提供商(anthropic、openai、google 等) |
--model <pattern> | 指定模型,支持 provider/id 格式和 :thinkingLevel 后缀 |
--name <name> / -n <name> | 设置会话显示名称 |
--no-session | 不持久化会话,退出即丢弃 |
--session-dir <path> | 自定义会话存储目录 |
一个典型的启动命令:
pi --mode rpc --provider anthropic --model claude-sonnet-4-20250514 --no-session
启动后 pi 不再显示 TUI,而是静静等 stdin 上的 JSON 命令。stdout 输出 JSON 事件。
协议格式:一行一条 JSON
RPC 协议是 JSONL(JSON Lines)格式,用 \n 做记录分隔符。两条规则:
- 你的程序往 stdin 写命令:一条 JSON 占一行,写完立刻 flush
- pi 从 stdout 吐事件和响应:同样一行一个 JSON 对象
命令和响应通过可选的 id 字段关联。你在命令里设了 id,响应里就有同样的 id:
{"id": "req-1", "type": "prompt", "message": "你好"}
收到响应:
{"id": "req-1", "type": "response", "command": "prompt", "success": true}
所有响应都有 "type": "response",success 字段告诉你命令是否被接受。注意:success: true 只表示命令被接受了,不代表 Agent 执行完成了——Agent 的执行结果通过后续事件流异步返回。
RPC 命令速览
pi 的 RPC 协议提供了丰富的命令集。以下按功能分类列出最常用的。
发送消息
| 命令 | JSON 示例 | 说明 |
|---|---|---|
prompt | {"type":"prompt","message":"帮我改个bug"} | 发一条用户消息给 Agent |
steer | {"type":"steer","message":"停,换个思路"} | Agent 运行时插队纠正方向 |
follow_up | {"type":"follow_up","message":"做完后再检查一遍"} | 等 Agent 空闲后追加任务 |
abort | {"type":"abort"} | 中止当前 Agent 操作 |
prompt 是最常用的命令。如果 Agent 正在流式输出(isStreaming: true),直接发 prompt 会报错。这时候需要指定 streamingBehavior:
{"type": "prompt", "message": "换个方案", "streamingBehavior": "steer"}
两个可选值:"steer" 在当前轮工具执行完后插入,"followUp" 等 Agent 完全空闲后插入。
会话管理
| 命令 | JSON 示例 | 说明 |
|---|---|---|
new_session | {"type":"new_session"} | 开始全新会话 |
switch_session | {"type":"switch_session","sessionPath":"/path/to/session.jsonl"} | 切换到已有会话 |
fork | {"type":"fork","entryId":"abc123"} | 从某条用户消息处开分支 |
clone | {"type":"clone"} | 复制当前分支到新会话 |
export_html | {"type":"export_html"} | 导出会话为 HTML |
状态查询
| 命令 | JSON 示例 | 说明 |
|---|---|---|
get_state | {"type":"get_state"} | 获取当前会话状态(模型、流式状态等) |
get_messages | {"type":"get_messages"} | 获取全部对话消息 |
get_session_stats | {"type":"get_session_stats"} | Token 用量和费用统计 |
get_last_assistant_text | {"type":"get_last_assistant_text"} | 取最后一条助手回复的文本 |
get_state 返回的信息很全,包含当前模型、思考级别、是否在流式输出、是否为压缩中、消息数量等:
{
"type": "response",
"command": "get_state",
"success": true,
"data": {
"model": {"id": "claude-sonnet-4-20250514", "name": "Claude Sonnet 4", "provider": "anthropic"},
"thinkingLevel": "medium",
"isStreaming": false,
"isCompacting": false,
"sessionFile": "/path/to/session.jsonl",
"sessionId": "abc123",
"messageCount": 8,
"pendingMessageCount": 0
}
}
模型控制
| 命令 | JSON 示例 | 说明 |
|---|---|---|
set_model | {"type":"set_model","provider":"anthropic","modelId":"claude-sonnet-4-20250514"} | 切换模型 |
cycle_model | {"type":"cycle_model"} | 循环到下一个可用模型 |
get_available_models | {"type":"get_available_models"} | 列出所有已配置的模型 |
思考级别
| 命令 | JSON 示例 | 说明 |
|---|---|---|
set_thinking_level | {"type":"set_thinking_level","level":"high"} | 设置思考深度 |
cycle_thinking_level | {"type":"cycle_thinking_level"} | 循环切换思考级别 |
可用级别:off、minimal、low、medium、high。部分模型还支持 xhigh 和 max。
压缩和重试
| 命令 | JSON 示例 | 说明 |
|---|---|---|
compact | {"type":"compact"} | 手动压缩上下文 |
set_auto_compaction | {"type":"set_auto_compaction","enabled":true} | 开关自动压缩 |
set_auto_retry | {"type":"set_auto_retry","enabled":true} | 开关自动重试 |
abort_retry | {"type":"abort_retry"} | 中止正在进行的重试 |
执行命令
| 命令 | JSON 示例 | 说明 |
|---|---|---|
bash | {"id":"req-1","type":"bash","command":"ls -la"} | 直接执行 shell 命令并捕获输出 |
abort_bash | {"type":"abort_bash"} | 中止正在运行的 bash 命令 |
bash 的结果不会立即送给 LLM,而是在下一条 prompt 时一并发送。你可以连续执行多条 bash,它们的输出会在下次对话时一起出现。
事件流:Agent 在干什么
命令发出去之后,pi 通过事件流告诉你进展情况。事件也从 stdout 以 JSONL 格式输出。核心事件:
| 事件 | 触发时机 |
|---|---|
agent_start | Agent 开始处理任务 |
agent_end | 一次 Agent 运行结束(可能还有重试或后续) |
agent_settled | Agent 彻底安静(不会再有自动重试或排队消息) |
turn_start / turn_end | 每一轮(一次 LLM 调用 + 工具执行)的开始和结束 |
message_start / message_end | 每条消息的开始和结束 |
message_update | 流式输出增量(文本/思考/工具调用) |
tool_execution_start / tool_execution_end | 工具开始和完成执行 |
compaction_start / compaction_end | 上下文压缩 |
queue_update | 排队消息(steer/followUp)发生变化 |
最重要的是 message_update,它承载着 Agent 的流式输出。里面的 assistantMessageEvent 分好几种:
{"type":"message_update","assistantMessageEvent":{"type":"text_start","contentIndex":0}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"我来"}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"看看"}}
{"type":"message_update","assistantMessageEvent":{"type":"text_end","contentIndex":0,"content":"我来看看"}}
thinking_start/thinking_delta/thinking_end 对应模型的思考过程(如果开启了 thinking)。toolcall_start/toolcall_delta/toolcall_end 对应工具调用。
实战一:用 curl 发一条消息
RPC 模式走的是 stdin/stdout,curl 没法直接连。但你可以写一个小脚本把 stdin 管道连起来。以 Linux/macOS 为例:
# 启动 pi 的 RPC 模式,把命令通过管道送进去
echo '{"type":"prompt","message":"当前目录下有哪些文件?"}' | pi --mode rpc --no-session
上面这条只是把命令扔进去了,你还需要读 stdout 上的输出。一个更完整的做法是用命名管道或子进程。
在 Bash 中这样写:
#!/bin/bash
# 启动 pi RPC 模式,捕获 stdout
coproc PI { pi --mode rpc --no-session; }
# 发送命令
echo '{"type":"prompt","message":"用一句话介绍你自己"}' >&"${PI[1]}"
# 读取事件
while IFS= read -r line <&"${PI[0]}"; do
event=$(echo "$line" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('type',''))")
if echo "$line" | python3 -c "
import sys,json
d=json.load(sys.stdin)
if d.get('type')=='message_update':
e=d.get('assistantMessageEvent',{})
if e.get('type')=='text_delta': print(e['delta'],end='')
" 2>/dev/null; then :; fi
if [ "$event" = "agent_settled" ]; then break; fi
done
TipWindows 上用 PowerShell 也能跑,但注意
Start-Process的-RedirectStandardInput只能重定向到文件,拿不到可写的流对象。要读写 stdin/stdout,得用System.Diagnostics.Process并开启RedirectStandardInput=true/RedirectStandardOutput=true,之后用$p.StandardInput.WriteLine()写命令、读$p.StandardOutput。
实战二:curl 风格的命令发送器
如果你要在 CI 脚本里用,可以写个简单的 Node.js 封装。下面这个脚本接收命令行参数,启动 pi、发一条消息、等 Agent 静默后退出:
// rpc-send.js - 向 pi RPC 发送一条消息并流式输出回复
const { spawn } = require("child_process");
const message = process.argv.slice(2).join(" ") || "Hello";
const pi = spawn("pi", ["--mode", "rpc", "--no-session"]);
let buffer = "";
pi.stdout.on("data", (chunk) => {
buffer += chunk.toString();
const lines = buffer.split("\n");
buffer = lines.pop(); // 保留不完整行
for (const line of lines) {
if (!line.trim()) continue;
try {
const event = JSON.parse(line);
if (event.type === "message_update") {
const e = event.assistantMessageEvent;
if (e.type === "text_delta") process.stdout.write(e.delta);
}
if (event.type === "agent_settled") {
process.stdout.write("\n");
pi.stdin.end();
process.exit(0);
}
} catch (_) {}
}
});
pi.stdin.write(JSON.stringify({ type: "prompt", message }) + "\n");
用法:
node rpc-send.js "帮我写一个冒泡排序的 Python 实现"
RPC 适合什么场景
RPC 模式的核心价值在于进程隔离和语言无关。不管你的主程序用什么语言写,只要能启动子进程、读写 stdin/stdout,就能集成 pi。
典型场景:
- CI/CD 流水线:代码合并后自动跑 pi 做代码审查、生成 changelog、检查安全问题
- 聊天机器人:Slack/Discord/飞书机器人把用户消息转给 pi,pi 回复后再发回群里
- 自定义前端:用 Electron、Tauri 或 Web 前端包一个 pi 的后台进程,做出自己的 AI 编程界面
- 编辑器插件:VS Code 或 Neovim 插件通过 RPC 调用 pi,在编辑器侧边栏显示 pi 的建议
Note如果你的主程序也是 Node.js/TypeScript 写的,直接引用
@earendil-works/pi-coding-agent的 SDK API 会更简洁——类型安全、无需子进程通信开销。下一章讲 SDK 方式。
扩展 UI 协议:远程弹窗
RPC 模式下还能处理 Extension 发起的用户交互请求。当 Extension 调用 ctx.ui.select() 或 ctx.ui.confirm() 时,pi 不会尝试画 TUI 界面,而是从 stdout 发出 extension_ui_request 事件:
{
"type": "extension_ui_request",
"id": "uuid-1",
"method": "confirm",
"title": "允许执行危险命令?",
"message": "pi 想执行 rm -rf /tmp/build",
"timeout": 10000
}
你的程序收到后展示给用户(比如弹个对话框),然后通过 stdin 回复:
{"type": "extension_ui_response", "id": "uuid-1", "confirmed": true}
支持的交互方法有 select(选择列表)、confirm(确认框)、input(文本输入)、editor(多行编辑器)。另外还有 notify(通知)、setStatus(状态栏)等无需回复的 fire-and-forget 方法。
这套协议让你能在完全不依赖 TUI 的情况下,构建出完整的 AI 编程交互体验。