自动化与程序集成
Pi 提供四个不同层级的程序入口。最小的入口通常最可靠:只需要最终文本就用 Print,需要同进程控制才用 SDK。
先做选择
| 目标 | 推荐入口 | 进程关系 | 输出 |
|---|---|---|---|
| shell 脚本只需要最终回复 | pi -p | 子进程 | 文本 |
| 日志、评测或 UI 需要完整事件 | pi --mode json | 子进程 | 单向 JSONL |
| IDE 或服务需要持续双向控制 | pi --mode rpc | 长驻子进程 | 双向 JSONL |
| Node.js 应用需要完整会话对象 | coding-agent SDK | 同进程 | TypeScript API |
| 只需要模型或自定义 Agent 循环 | pi-ai / pi-agent-core | 同进程 | 更底层 API |
不要用 RPC 解决一次性文本任务,也不要为了“未来可能需要”把完整 coding-agent SDK 嵌入第一版服务。
Print:最短的自动化路径
Print 模式输出最终回复后退出:
pi -p "概览当前项目,并列出应该运行的检查命令"它也会合并管道输入:
git diff --stat HEAD |
pi -p --no-session --no-approve --no-tools \
--no-context-files --no-extensions --no-skills \
--no-prompt-templates --system-prompt "" \
--append-system-prompt "" \
"根据输入生成一段简洁的变更摘要"这个例子显式选择:
--no-session:不保存一次性任务。--no-approve:不加载项目级受保护资源和设置。--no-tools:不向模型暴露可调用工具。--no-context-files与三个--no-*资源参数:忽略上下文文件,以及全局和项目级 Extensions、Skills、Prompt Templates。- 两个空系统提示参数:忽略用户级
SYSTEM.md和APPEND_SYSTEM.md;Pi 内置系统提示仍然存在。
这些参数组合后,除 Pi 内置系统提示外,模型只消费管道内容和显式提示。单独使用 --no-approve 并不会禁用 AGENTS.md、CLAUDE.md 或用户级 Extension;单独使用 --no-tools 也不会阻止 Extension 工厂在启动时执行。
CI 中不要依赖交互式信任提示,因为 Print、JSON 和 RPC 模式不会显示它。需要项目 Skill 或 Extension 时,审查后使用 --approve;需要真正的最小资源基线时,像上面一样显式关闭各类资源。
获取稳定退出结果
脚本至少区分:
- 进程是否成功启动。
- 模型运行是否返回错误。
- 输出是否满足你的业务格式。
- 超时或取消是否生效。
不要仅以“stdout 非空”判断任务成功。对于机器消费结果,优先使用结构化工具、JSON 模式或应用自己的 schema 校验。
JSON:消费完整事件流
JSON 模式把会话头和所有事件按 JSON Lines 输出到 stdout:
pi --mode json --no-session --no-approve \
"读取 README 并概览项目" \
2>pi-error.log |
jq -c 'select(.type == "message_end")'输出中的每一行都是独立 JSON 对象。下面为便于阅读,把完整消息对象缩写成 {...}:
{"type":"session","version":3,"id":"...","timestamp":"...","cwd":"..."}
{"type":"agent_start"}
{"type":"message_update","message":{...},"assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"...","partial":{...}}}
{"type":"message_end","message":{...}}
{"type":"agent_end","messages":[...]}需要处理的主要事件:
| 事件 | 用途 |
|---|---|
message_update | 文本或思考内容增量 |
tool_execution_start/update/end | 工具生命周期 |
message_end | 一条完整消息 |
turn_end | 一次模型回复及其工具结果 |
agent_end | 当前 Agent 运行结束 |
compaction_start/end | 上下文压缩 |
auto_retry_start/end | 自动重试 |
queue_update | steering 与 follow-up 队列变化 |
把 stderr 和 stdout 分开。协议事件在 stdout,诊断信息应单独记录,避免污染 JSONL。
RPC:控制一个长驻 Pi 子进程
RPC 模式通过 stdin 接收命令,通过 stdout 返回响应和异步事件:
pi --mode rpc --no-session --no-approve每条命令占一行:
{"id":"state-1","type":"get_state"}
{"id":"run-1","type":"prompt","message":"概览当前项目"}
{"id":"abort-1","type":"abort"}响应复用请求 ID:
{"id":"run-1","type":"response","command":"prompt","success":true}success: true 只表示 prompt 已接受、入队或立即处理。之后的模型失败通过事件流报告,不会为同一个请求再发第二个失败响应。
严格处理 JSONL 分帧
RPC 只使用 LF,也就是 \n,作为记录分隔符:
- 按
\n拆分记录。 - 输入可以接受
\r\n,解析前移除末尾\r。 - 保留最后一个尚未出现换行的半包。
- 不要使用会把
U+2028或U+2029当换行的通用 line reader。
Pi 官方明确指出 Node.js readline 不符合这个 RPC 分帧要求。TypeScript 子进程客户端优先复用 coding-agent 包中的 RpcClient 实现,或自己按字节缓存并只寻找 0x0A。
运行中发送消息
Agent 正在运行时,普通 prompt 必须指定投递方式:
{
"id": "steer-1",
"type": "prompt",
"message": "先停下写操作,只汇报当前发现",
"streamingBehavior": "steer"
}steer:当前 assistant turn 的工具执行结束后、下一次模型调用前送达。followUp:Agent 完成现有工作后送达。
缺少 streamingBehavior 时,运行中的 prompt 会被拒绝。也可以直接发送 steer 或 follow_up 命令。
SDK:在 Node.js 中直接控制会话
安装并固定版本:
npm install --save-exact @earendil-works/pi-coding-agent@0.83.0最小内存会话:
import {
createAgentSession,
SessionManager,
} from "@earendil-works/pi-coding-agent";
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
tools: ["read", "grep", "find", "ls"],
});
const unsubscribe = session.subscribe((event) => {
if (
event.type === "message_update" &&
event.assistantMessageEvent.type === "text_delta"
) {
process.stdout.write(event.assistantMessageEvent.delta);
}
});
try {
await session.prompt("概览当前目录,只读取文件");
} finally {
unsubscribe();
session.dispose();
}几个重要细节:
SessionManager.inMemory()不写会话文件。tools是 allowlist。示例只启用只读工具。session.prompt()等待当前 prompt 的 Agent 运行结束。- 无论成功、失败还是取消,都调用
session.dispose()。
Pi 0.83 的模型运行时
旧版 SDK 示例可能还传入 AuthStorage 和 ModelRegistry。从 Pi 0.80.8 开始,编码 Agent SDK 使用异步 ModelRuntime:
import {
createAgentSession,
ModelRuntime,
SessionManager,
} from "@earendil-works/pi-coding-agent";
const modelRuntime = await ModelRuntime.create();
const { session } = await createAgentSession({
modelRuntime,
sessionManager: SessionManager.inMemory(),
});不要把旧版认证对象和新版 modelRuntime 混用。升级时对照与你安装版本一致的 SDK 文档与 changelog。
Web 应用的推荐边界
浏览器 / Vue
│ 只理解应用自己的事件协议
▼
应用后端
├── 用户认证、限流、审计
├── 模型与工具白名单
├── Pi SDK 或受控 RPC 子进程
└── 供应商凭据浏览器不要直接持有供应商密钥,也不要把完整 coding-agent SDK 打进客户端 bundle。Vue 侧只需要稳定事件和停止接口,具体实现见 SDK 与 Vue 最小集成。
生产检查清单
- [ ] Pi 与 Node.js 版本固定并记录。
- [ ] 非交互模式显式选择
--approve或--no-approve。 - [ ] 工具使用 allowlist,默认不开放写入和 shell。
- [ ] stdout 协议流与 stderr 诊断分离。
- [ ] RPC 按 LF 做半包和粘包测试。
- [ ] 请求、响应和工具调用使用稳定 ID。
- [ ] 取消信号传到模型和工具。
- [ ] 超时、输出大小、并发和重试有上限。
- [ ] 日志中的 prompt、工具参数和凭据经过脱敏。
- [ ] 真实模型测试之外,还有确定性的假事件测试。
什么时候不要使用 Pi coding-agent
如果产品只需要:
- 调用一个模型并流式输出文本:直接使用
@earendil-works/pi-ai。 - 自己定义工具循环和消息状态:使用
@earendil-works/pi-agent-core。 - 一个固定、无工具的后台任务:供应商 SDK 可能更小。
coding-agent 的价值是完整会话、编码工具、资源加载和 Pi 工作流。没有这些需求时,少一层通常更容易运维。
相关入口: