跳转到正文

自动化与程序集成

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 模式输出最终回复后退出:

bash
pi -p "概览当前项目,并列出应该运行的检查命令"

它也会合并管道输入:

bash
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.mdAPPEND_SYSTEM.md;Pi 内置系统提示仍然存在。

这些参数组合后,除 Pi 内置系统提示外,模型只消费管道内容和显式提示。单独使用 --no-approve 并不会禁用 AGENTS.mdCLAUDE.md 或用户级 Extension;单独使用 --no-tools 也不会阻止 Extension 工厂在启动时执行。

CI 中不要依赖交互式信任提示,因为 Print、JSON 和 RPC 模式不会显示它。需要项目 Skill 或 Extension 时,审查后使用 --approve;需要真正的最小资源基线时,像上面一样显式关闭各类资源。

获取稳定退出结果

脚本至少区分:

  1. 进程是否成功启动。
  2. 模型运行是否返回错误。
  3. 输出是否满足你的业务格式。
  4. 超时或取消是否生效。

不要仅以“stdout 非空”判断任务成功。对于机器消费结果,优先使用结构化工具、JSON 模式或应用自己的 schema 校验。

JSON:消费完整事件流

JSON 模式把会话头和所有事件按 JSON Lines 输出到 stdout:

bash
pi --mode json --no-session --no-approve \
  "读取 README 并概览项目" \
  2>pi-error.log |
  jq -c 'select(.type == "message_end")'

输出中的每一行都是独立 JSON 对象。下面为便于阅读,把完整消息对象缩写成 {...}

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_updatesteering 与 follow-up 队列变化

把 stderr 和 stdout 分开。协议事件在 stdout,诊断信息应单独记录,避免污染 JSONL。

RPC:控制一个长驻 Pi 子进程

RPC 模式通过 stdin 接收命令,通过 stdout 返回响应和异步事件:

bash
pi --mode rpc --no-session --no-approve

每条命令占一行:

json
{"id":"state-1","type":"get_state"}
{"id":"run-1","type":"prompt","message":"概览当前项目"}
{"id":"abort-1","type":"abort"}

响应复用请求 ID:

json
{"id":"run-1","type":"response","command":"prompt","success":true}

success: true 只表示 prompt 已接受、入队或立即处理。之后的模型失败通过事件流报告,不会为同一个请求再发第二个失败响应。

严格处理 JSONL 分帧

RPC 只使用 LF,也就是 \n,作为记录分隔符:

  • \n 拆分记录。
  • 输入可以接受 \r\n,解析前移除末尾 \r
  • 保留最后一个尚未出现换行的半包。
  • 不要使用会把 U+2028U+2029 当换行的通用 line reader。

Pi 官方明确指出 Node.js readline 不符合这个 RPC 分帧要求。TypeScript 子进程客户端优先复用 coding-agent 包中的 RpcClient 实现,或自己按字节缓存并只寻找 0x0A

运行中发送消息

Agent 正在运行时,普通 prompt 必须指定投递方式:

json
{
  "id": "steer-1",
  "type": "prompt",
  "message": "先停下写操作,只汇报当前发现",
  "streamingBehavior": "steer"
}
  • steer:当前 assistant turn 的工具执行结束后、下一次模型调用前送达。
  • followUp:Agent 完成现有工作后送达。

缺少 streamingBehavior 时,运行中的 prompt 会被拒绝。也可以直接发送 steerfollow_up 命令。

SDK:在 Node.js 中直接控制会话

安装并固定版本:

bash
npm install --save-exact @earendil-works/[email protected]

最小内存会话:

ts
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 示例可能还传入 AuthStorageModelRegistry。从 Pi 0.80.8 开始,编码 Agent SDK 使用异步 ModelRuntime

ts
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 应用的推荐边界

text
浏览器 / 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 工作流。没有这些需求时,少一层通常更容易运维。

相关入口:

非官方中文工程指南,内容以 Pi 上游文档与源码为准。