Skip to content

print 與 rpc 模式

源码版本v0.73.1

pi 的非互動模式有兩種:print(單次,文字或 JSON 輸出)和 rpc(長連接,JSON-RPC over stdin/stdout)。print 模式用於腳本化呼叫 pi -p "fix this bug",rpc 模式用於把 pi 嵌到其他 GUI 應用(VS Code 擴充、網頁前端)裡。兩者共用 AgentSessionRuntime,但訂閱事件、輸出格式、擴充 UI 整合的方式完全不同。

職責

  1. print 模式:runPrintModemode: "text" | "json",text 模式只輸出最終 assistant 訊息文字,json 模式串流輸出所有 AgentSessionEvent。見 packages/coding-agent/src/modes/print-mode.ts:32-66
  2. rpc 模式:runRpcModeattachJsonlLineReader 從 stdin 讀 JSON 命令,透過 handleCommand 分派 29 種命令類型,事件和回應寫到 stdout。見 packages/coding-agent/src/modes/rpc/rpc-mode.ts:48-70packages/coding-agent/src/modes/rpc/rpc-mode.ts:371-625
  3. 訊號處理:兩種模式都註冊 SIGTERM / SIGHUP handler,先 killTrackedDetachedChildrendisposeRuntime,保證 bash 子行程不殘留。見 packages/coding-agent/src/modes/print-mode.ts:47-63packages/coding-agent/src/modes/rpc/rpc-mode.ts:351-365
  4. 擴充 UI 橋接:rpc 模式實作 createExtensionUIContext,把擴充的 dialog / widget 請求透過 extension_ui_request 事件發到客戶端,客戶端回應 extension_ui_response 回來。見 packages/coding-agent/src/modes/rpc/rpc-mode.ts:83-130
  5. session 替換鉤子:兩種模式都透過 runtimeHost.setRebindSession 註冊 rebind 回呼,session 切換後重新 bindExtensions、重訂閱事件。見 packages/coding-agent/src/modes/print-mode.ts:67-108packages/coding-agent/src/modes/rpc/rpc-mode.ts:306-349

設計動機

為什麼 print 和 rpc 不共用程式碼?因為輸出契約不同:print 模式 stdout 是單向的(要麼文字要麼 JSON 行),rpc 模式 stdin/stdout 是雙向的(命令進、事件+回應出)。rpc 還要處理擴充 UI 請求的 pending Promise map(等客戶端回應),這套機制 print 模式完全不需要。共用會引入不必要的複雜度,所以兩個檔案各自實作自己的 rebindSessionhandleEvent

rpc 模式的 29 個命令類型(prompt、steer、follow_up、abort、new_session、get_state、set_model、cycle_model、bash、fork、clone、export_html 等)是 AgentSession 公共 API 的完整映射。客戶端可以透過 RPC 呼叫 AgentSession 暴露的所有方法,這讓 pi 可以被嵌入到任何語言寫的 GUI 裡,只要能講 JSON-RPC。

print 模式的 mode: "text" 路徑只看最後一條 assistant 訊息:成功就寫 content.text 到 stdout,失敗(stopReason === "error" || "aborted")就寫 stderr 並回傳 exitCode 1。簡單粗暴,適合 shell 腳本 pi -p "..." | jq 這種用法。

關鍵檔案

print 模式 text 路徑,只輸出最後一條 assistant 訊息的 text content:

typescript
// packages/coding-agent/src/modes/print-mode.ts:128-145
if (mode === "text") {
	const state = session.state;
	const lastMessage = state.messages[state.messages.length - 1];

	if (lastMessage?.role === "assistant") {
		const assistantMsg = lastMessage as AssistantMessage;
		if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
			console.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
			exitCode = 1;
		} else {
			for (const content of assistantMsg.content) {
				if (content.type === "text") {
					writeRawStdout(`${content.text}\n`);
				}
			}
		}
	}
}

rpc 模式 prompt 命令,用 preflightResult 在 prompt 接受後立刻回 response:

typescript
// packages/coding-agent/src/modes/rpc/rpc-mode.ts:379-401
case "prompt": {
	let preflightSucceeded = false;
	void session
		.prompt(command.message, {
			images: command.images,
			streamingBehavior: command.streamingBehavior,
			source: "rpc",
			preflightResult: (didSucceed) => {
				if (didSucceed) {
					preflightSucceeded = true;
					output(success(id, "prompt"));
				}
			},
		})
		.catch((e) => {
			if (!preflightSucceeded) {
				output(error(id, "prompt", e.message));
			}
		});

資料流

兩種模式的輸入輸出對照:

邊界與失敗

小結

print 和 rpc 是 pi 的非互動模式,共用 AgentSessionRuntime 但輸出契約不同。print 單向輸出文字或 JSON 事件串流,rpc 是雙向 JSON-RPC 協定,把 AgentSession 所有方法暴露給外部客戶端,並橋接擴充 UI 請求。組裝入口看 CLI 入口與分派,runtime 替換看 會話 switch/fork/import