第9章 编程接口与 RPC 协议
9.1 CLI 参数完整分类速查
| 分类 | 参数 | 说明 |
|---|---|---|
| 运行模式 | -p "..." |
非交互打印模式 |
--mode json |
JSONL 事件流 | |
--mode rpc |
RPC 服务模式 | |
--offline |
关闭所有启动期联网操作 | |
| 模型与思考 | --provider / --model |
指定 Provider / 模型 |
--model sonnet:high |
模型+思考等级快捷写法 | |
--thinking <level> |
指定本次思考等级 | |
| 工具与扩展 | --tools a,b,c |
工具白名单 |
--no-extensions |
关闭所有扩展 | |
-e ./path.ts |
加载指定的单个 Extension | |
| 会话 | -c |
继续最近会话 |
--session-dir <path> |
自定义会话目录 | |
--no-session |
不落盘 | |
| 包管理 | pi install npm:<pkg> / git:<repo> |
安装社区包 |
pi list / pi update |
查看/升级 | |
| 杂项 | -v / -h |
版本 / 帮助 |
9.2 三层编程接口对比:createAgentSession / createAgentSessionRuntime / RPC 子进程
Pi 真实的 SDK 文档明确指出:CLI 内置的交互模式、打印模式、RPC 模式,本质上用的是同一套底层 Runtime 层,可以选择在哪个层级接入:
| 层级 | 关键 API | 适合场景 |
|---|---|---|
| 最简单 | createAgentSession() + ModelRuntime/SessionManager |
快速在 Node.js 里跑一个一次性 Session |
| 进阶 | createAgentSessionRuntime() + createAgentSessionServices() |
需要自己控制多会话生命周期、接自定义传输层(如 HTTP+SSE 转发给浏览器) |
| 跨语言 | pi --mode rpc |
非 Node.js 技术栈(Python、Go、Java 等)集成 |
ts
import {
type CreateAgentSessionRuntimeFactory,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
runRpcMode,
SessionManager,
} from '@earendil-works/pi-coding-agent';
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
const services = await createAgentSessionServices({ cwd });
return {
...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
services,
diagnostics: services.diagnostics,
};
};
const runtime = await createAgentSessionRuntime(createRuntime, {
cwd: process.cwd(),
agentDir: getAgentDir(),
sessionManager: SessionManager.create(process.cwd()),
});
// 甚至可以把这个 Runtime 直接跑成 RPC 服务,而不需要另开子进程
await runRpcMode(runtime);
💡 官方特别提示:Node.js 应用优先直接用
AgentSession/createAgentSessionRuntime(),不要为了用 RPC 而把pi当子进程 spawn 出来------只有非 Node.js 技术栈才需要走"子进程 + RPC"这条路。
9.3 RPC 协议细节:LF 分隔 JSONL、extension_ui_request/response 子协议
RPC 模式是严格的 LF(\n)分隔 JSONL 协议------每一行是一个独立 JSON 对象。
⚠️ 关键工程提醒 :不要用 Node.js 的
readline模块解析 RPC 输出 ------它会在 JSON 字符串内部合法的 Unicode 行分隔符U+2028/U+2029处误断行,把一个完整的 JSON 对象切成两半。正确做法是自己写只按\n分割的解析逻辑。
命令支持可选的 id 字段用于请求/响应关联:
jsonl
{"id": "req-1", "type": "prompt", "message": "Hello, world!"}
Extension 触发的 UI 交互在 RPC 协议里转成一套子协议:
| 方法类型 | 行为 |
|---|---|
对话框类(select/confirm/input/editor) |
发出 extension_ui_request,阻塞等待客户端在 stdin 回传匹配 id 的 extension_ui_response |
即发即弃类(notify/setStatus/setWidget) |
发出 extension_ui_request,但不等待响应 |
Extension 内部报错作为独立事件类型上抛:
json
{ "type": "extension_error", "extensionPath": "/path/to/extension.ts", "event": "tool_call", "error": "..." }
9.4 流式输出:Agent.subscribe() / --mode json
编程接口场景:
ts
agent.subscribe((event) => {
if (event.type === 'message_update' && event.assistantMessageEvent.type === 'text_delta') {
process.stdout.write(event.assistantMessageEvent.delta);
}
});
await agent.prompt('...');
CLI 场景的等价方式:
bash
pi --mode json -p "..." > events.jsonl
每一行是独立事件对象(文本增量、工具调用、工具结果),可以直接消费。
📌 校订:Pi 没有一个单独叫
generate()的方法,真实调用是prompt(),流式输出靠事件订阅而不是一个"流式开关配置项"。
9.5 自定义消息类型:声明合并 + convertToLlm
pi-agent-core 的 AgentMessage 类型支持通过 TypeScript 声明合并扩展自定义消息角色:
ts
declare module '@earendil-works/pi-agent-core' {
interface CustomAgentMessages {
notification: { role: 'notification'; text: string; timestamp: number };
}
}
再用 convertToLlm 在真正发给模型前过滤/转换这些自定义消息:
ts
const agent = new Agent({
convertToLlm: (messages) => messages.flatMap((m) => (m.role === 'notification' ? [] : [m])),
});
📌 校订:网传版本"from 参数三种用法"是虚构描述,真实的动态数据注入方式是这套"自定义消息类型 +
convertToLlm转换"机制,或者简单地把数据拼进 prompt 字符串(作为 stdin 管道输入也可以:cat file.md | pi -p "总结这段内容")。