Learn Pi Agent
s09

RPC 集成

委派与集成

用 JSONL 把 pi 接进任何程序

55 LOC0 个工具JSONL RPC
stdin 发命令,stdout 收事件。
机制定位
RPC 子进程边界(stdin / stdout)
用户输入组装上下文模型推理tool_call 闸门执行工具tool_result 回传会话 / 持久化

RPC 在进程边界之外驱动整条循环。

s08 → s09 → s10 → ...

"用 JSONL 把 pi 接进任何程序" — stdin 发命令,stdout 收事件。

Harness 层:进程边界 — 让 harness 可被任意语言驱动。


问题

SDK(s01–s08)解决的是「在同一个 Node 进程里用 pi」。但很多场景要求 pi 作为一个独立子进程:

  • 你的主程序是 Python / Go / Rust,不想为它引入 Node。
  • 你需要进程隔离:pi 崩了不能带崩宿主。
  • 你要做一个自己的 IDE / Web UI,后端和 agent 分开部署。

解决方案

pi --mode rpc 把 agent 变成一个 JSONL 服务:stdin 收命令,stdout 吐事件。

pi --mode rpc --no-session

发一条命令(每行一个 JSON):

{"id": "req-1", "type": "prompt", "message": "列出当前目录"}

收一串事件(每行一个 JSON):

{"id":"req-1","type":"response","command":"prompt","success":true}
{"type":"agent_start"}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","delta":"当前"}}
{"type":"agent_end","messages":[...]}

工作原理

三个概念

概念方向说明
命令stdin → piprompt、steer、follow_up、abort、compact、set_model…
响应pi → stdout{type:"response", command, success, id?},确认命令是否被接受
事件pi → stdoutagent 生命周期与流式输出,不需要请求

id 可选,用于请求/响应关联。success: true 只表示命令被接受(可能已排队),后续失败通过事件流报告,不会为同一个 id 再发一次 response。

关键:帧协议是严格的 JSONL

  • 只用 \n 作分隔符。
  • 输入允许 \r\n,解析时去掉末尾 \r。
  • 不要用 Node 的 readline:它会把 JSON 字符串里合法的 U+2028/U+2029 也当换行,破坏协议。用 StringDecoder 手动按 \n 切。

本章的 code.ts 就是这个最小客户端的完整实现。

RPC vs SDK

SDKRPC
语言Node/TypeScript任意
隔离同进程独立进程
类型安全强靠协议约定
直接访问 agent.state可以只能靠事件
适用同进程集成、自定义工具/扩展跨语言、IDE、进程隔离

别忘了扩展 UI 协议

扩展在 RPC 模式下也会调用 ctx.ui.confirm() 等对话框。这时 pi 会通过 stdout 发 extension_ui_request,客户端必须在 stdin 回 extension_ui_response。ctx.hasUI 在 RPC 模式是 true —— 所以 s03 的权限闸门在 RPC 下会真的弹出确认请求,而不是 fail-closed。协议细节见 pi 文档 docs/rpc.md#extension-ui-protocol。

变更内容

组件s08s09
驱动方式同进程 SDK独立子进程 RPC
语言Node / TypeScript任意
通道函数调用stdin / stdout JSONL
隔离共享进程进程级隔离
读 agent.state可以只能靠事件
循环不变不变

试一下

npm run s09
npm run s09 -- "用 bash 数一数当前目录有多少个 .ts 文件"

你会看到客户端打印出命令响应、流式文本、最终 agent_end。整个过程 pi 是一个独立子进程,宿主只通过管道通信。

换成任何语言都一样:把 spawn 换成 subprocess.Popen / exec.Command,把 JSON.parse 换成对应语言的解析,协议不变。

再试几个:

  • npm run s09 -- "读一下 package.json,告诉我项目名" —— 观察 tool_execution_start 事件
  • 在客户端里把 abort 绑到一个 3 秒定时器,发一条「从 1 数到 100000」的 prompt,观察 agent_end 之前的中断

本章要点

  • RPC = stdin 命令 + stdout 事件 + JSONL 帧。
  • 严格按 \n 切帧,别用 readline。
  • response 表示命令被接受,不表示任务成功;结果看事件。
  • 跨语言、要隔离就 RPC;同进程、要类型就 SDK。

下一章

pi 内置支持一堆模型供应商,但你可能想接自己的推理服务。s10:自定义 Provider。