首页 / pi-agent 入门教程 / RPC 模式:把 pi 嵌进程序

pi-agent 入门教程

RPC 模式:把 pi 嵌进程序

本教程共 30 篇 · 第 20 篇 · 更新于 2026-08-10 · 约 7 分钟阅读

pi-agentRPC嵌入Server协议

本节目标:理解 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-agentAgentSession API 会更方便。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 做记录分隔符。两条规则:

  1. 你的程序往 stdin 写命令:一条 JSON 占一行,写完立刻 flush
  2. 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"}循环切换思考级别

可用级别:offminimallowmediumhigh。部分模型还支持 xhighmax

压缩和重试

命令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_startAgent 开始处理任务
agent_end一次 Agent 运行结束(可能还有重试或后续)
agent_settledAgent 彻底安静(不会再有自动重试或排队消息)
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
Tip

Windows 上用 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 编程交互体验。