Pi Coding Agent 不只可以在終端裡互動。 它的 RPC 模式把 Agent 作為子程序執行,透過標準輸入接收 JSON 命令,再從標準輸出持續返回響應和事件。 這條路徑適合桌面殼、瀏覽器控制檯、內部工單系統或自動化測試,但它不是把終端輸出套進網頁那麼簡單。 真正需要處理的是程序壽命、請求編號、流式事件、會話目錄、許可權和異常恢復。
RPC 模式解決的不是遠端網路呼叫
這裡的 RPC 是本機程序協議。
宿主程式啟動 pi --mode rpc,寫入一行一個 JSON 物件,並逐行讀取 Pi 的標準輸出。
它沒有自動監聽 HTTP 埠,也不會替你做使用者認證、TLS 或跨機器訪問。
若要提供 Web UI,應由自己的後端持有 Pi 子程序,瀏覽器只連線這個後端。
不要讓公開網頁直接生成本機命令或接觸工作目錄。
先確認命令和版本來自同一套安裝
安裝 Pi 後,先在計劃執行服務的同一個賬戶下檢查:
|
|
Windows 服務、WSL 和普通 PowerShell 可能分別解析到不同的可執行檔案。 在 PowerShell 中用下面的命令確認路徑:
|
|
升級前記錄版本,RPC 客戶端應針對一個明確版本測試,而不是預設所有事件欄位永遠不變。
用最小命令觀察協議
在一個空的測試目錄啟動:
|
|
程序啟動後不會出現傳統互動介面,而是在等待標準輸入裡的 JSON 行。 傳送請求時必須以換行結束;只寫 JSON 但不寫換行,解析器可能一直等待。 手工試驗適合看首個響應,正式整合必須由程式同時讀取 stdout 和 stderr。
把 stdout 當協議通道
stdout 中每一行都應先作為完整 JSON 訊息解析。 不要把除錯提示、自己的日誌字首或顏色控制符寫進同一條管道。 宿主程式的診斷日誌寫到 stderr 或獨立檔案。 收到無法解析的行時,記錄原文、版本和前後訊息編號,但不要把可能含金鑰的整行發到公共日誌。
一個穩妥的讀取迴圈包含四步:
- 按換行切分位元組流。
- 對單行執行 JSON 解析。
- 根據訊息型別分發響應或事件。
- 未知型別進入相容分支,而不是讓整個程序崩潰。
請求 ID 是併發關聯的核心
客戶端可能在上一個任務仍流式輸出時傳送控制命令。
因此不能用“下一條響應屬於上一條請求”這種位置假設。
為每個命令生成唯一 ID,並維護 pending 對映。
最終響應到達後再解除對映;中間事件由會話或當前 turn 狀態處理。
|
|
超時只代表宿主沒有按時拿到最終響應,不等於 Pi 子程序已經停止。 超時後還應決定傳送中止、繼續接收,還是終止整個子程序。
Node.js 啟動子程序的最小骨架
下面的骨架刻意不繫結具體事件欄位,只負責可靠分行與程序退出:
|
|
shell: false 很重要:參數作為陣列傳入,避免專案路徑或使用者文字被外殼再次解釋。
如果 Windows 找不到 pi,應在啟動階段解析絕對路徑,而不是改成拼接命令字串。
統一封裝傳送函式
所有寫入都經過一個入口,才能限制訊息大小、檢查程序狀態並保證結尾換行。
|
|
大檔案不要內嵌為 JSON 字串。 把檔案放入受控工作目錄,讓 Agent 透過工具讀取,並在後端檢查真實路徑是否仍位於該目錄內。
事件流需要顯式狀態機
一次提示可能經歷排隊、開始、文字增量、工具呼叫、工具結果、結束或錯誤。 前端不要僅維護一個不斷追加的字串,否則工具事件和重試很容易重複顯示。 建議為每個 turn 儲存以下狀態:
queued:請求已寫入,尚未確認開始。running:正在接收模型或工具事件。aborting:使用者請求中止,等待最終狀態。completed:最終響應完整落盤。failed:協議錯誤、模型錯誤或程序退出。
事件到達順序異常時保留原始序號,UI 可以提示“狀態不完整”,不要偽裝成成功。
先處理背壓,再談流式體驗
stdin.write() 返回 false 時表示緩衝區已經承壓。
此時暫停繼續傳送,等待 drain 事件。
瀏覽器側也要限制 WebSocket 待傳送佇列,慢客戶端不能無限佔用伺服器記憶體。
文字增量可以每 30 至 80 毫秒合併一次再推送,減少 DOM 更新與網路小包。
工具結果通常按事件整體傳送,不適合字元級切片。
Provider 與模型由啟動參數固定
官方 RPC 文件提供 --provider 和 --model 啟動參數。
例如服務可以為不同用途啟動不同 worker:
|
|
不要允許普通前端使用者提交任意 Provider 名稱或模型參數。 後端維護允許列表,並把“快速”“高質量”“本地”對映到稽核過的組合。 切換 Provider 前先驗證憑據、上下文限制和工具能力;同名模型也可能具有不同輸出或計費行為。
金鑰只進入子程序環境
API 金鑰儲存在服務端秘密管理工具或受限環境變數中。 不要把金鑰寫入 RPC JSON、瀏覽器 localStorage、會話標題或錯誤回傳。 啟動子程序時可以構造最小環境,而不是無條件繼承服務程序的全部變數。
|
|
實際變數名取決於所選 Provider;缺失時在啟動前報錯,避免請求執行到一半才失敗。
會話目錄決定可恢復性
--no-session 適合一次性任務、CI 和協議測試,程序退出後不依賴歷史會話。
需要恢復對話時,使用 Pi 的會話能力,並透過 --session-dir 把資料放到明確位置。
|
|
服務端應把應用使用者 ID 對映為內部隨機目錄名。
禁止使用者直接傳入 ../、磁碟機代號或網路共享路徑。
備份會話前確認其中是否包含提示、程式碼片段、工具輸出或商業資料,並設定保留期限。
--name 用來區分受控例項
為長期執行的例項設定可識別名稱有助於日誌關聯:
|
|
名稱應由部署配置產生,不要直接使用郵箱、客戶名稱或工單全文。 日誌同時記錄應用例項 ID、Pi 程序 PID 和啟動版本,才能還原一次異常屬於哪個子程序。
Web UI 應隔著自己的後端
推薦鏈路是:
|
|
後端負責登入、速率限制、工作目錄、Provider 白名單和審計。 瀏覽器只收到展示所需欄位,不應看到本機絕對路徑、環境變數或未脫敏工具結果。 若 WebSocket 斷開,可以讓 turn 在後端繼續,並允許客戶端按事件序號補拉;也可以按產品規則主動中止。
一個使用者一個程序並非總是正確
長駐子程序恢復快,但會佔用記憶體並儲存更多上下文。 每次請求新建程序隔離更清楚,卻增加啟動和會話恢復成本。 常見折中是按工作區建立 worker,空閒一段時間後退出,並把併發請求放入每個 worker 的序列佇列。 不要讓兩個請求同時修改同一個 Git 工作區。 需要並行時,為任務建立獨立 worktree 或臨時副本。
工具許可權比模型選擇更重要
Pi 子程序繼承執行賬戶能夠訪問的檔案和命令。 部署 Web UI 時,至少執行以下隔離:
- 使用非管理員專用賬戶。
- 工作目錄使用允許列表。
- 禁止訪問 SSH 金鑰、瀏覽器資料和生產配置。
- 對外部倉庫先做只讀或臨時副本。
- 對寫檔案、執行命令和網路訪問保留審計事件。
容器可以縮小檔案系統範圍,但仍需限制掛載、網路和宿主套接字。 把 Docker socket 掛給 Agent 等同於授予很高的宿主控制能力。
中止與關閉必須分開
“停止當前回答”不等於“殺死 Pi 程序”。 優先使用協議提供的中止命令,讓當前 turn 收到可識別的結束狀態。 只有協議失去響應、stdout 關閉或超過強制終止期限時,才結束子程序。 服務退出時先停止接收新請求,等待短暫寬限期,再關閉 stdin 並回收程序。 Windows 和 Linux 的訊號語義不同,必須分別做退出測試。
崩潰恢復不能自動重放寫操作
Pi 在工具呼叫後崩潰時,宿主未必知道檔案寫入是否已經完成。 不要無條件重放最後一個提示,否則可能重複提交、重複發請求或覆蓋檔案。 恢復介面應展示最後確認事件,並讓使用者選擇檢查工作區、繼續對話或建立新會話。 只讀查詢可以設計冪等重試;寫操作需要操作 ID 或執行後驗證。
日誌分成協議、執行和審計三類
協議日誌記錄訊息型別、請求 ID、事件序號和耗時,不預設儲存完整正文。 執行日誌記錄 PID、版本、退出碼、記憶體和 stderr 摘要。 審計日誌記錄誰開啟了哪個工作區、允許了哪項工具能力以及產生了什麼變更。 三類日誌分別設定訪問許可權和保留期。 脫敏規則至少覆蓋 API key、Authorization header、郵箱、絕對使用者路徑和倉庫秘密。
先寫協議測試替代手工點選
測試客戶端可以啟動 pi --mode rpc --no-session,傳送一條固定請求,並驗證:
- 每個 stdout 行都能解析為 JSON。
- 請求 ID 能關聯到最終響應。
- 文字和工具事件不會重複結算。
- 超時後 pending promise 會被清理。
- 子程序異常退出時所有等待者收到失敗。
再增加包含中文、反斜槓、換行和超長文字的輸入,驗證 UTF-8 與 JSON 轉義。 CI 中不要呼叫昂貴的真實模型;將子程序替換成按相同協議輸出固定事件的假實現。
上線前做四個故障演練
第一,執行中斷開瀏覽器,確認後端不會無限快取事件。 第二,讓 Provider 返回限流或認證失敗,確認錯誤不會暴露金鑰。 第三,在工具執行期間終止 Pi,確認系統不會自動重放寫操作。 第四,讓 stdout 出現未知訊息型別,確認客戶端記錄並繼續處理後續相容訊息。 這些結果比“頁面能收到第一段文字”更能證明整合可維護。
選擇 RPC 還是直接使用庫
RPC 的優勢是語言無關、程序隔離清楚,也能複用官方 CLI 的啟動配置。 代價是要維護子程序、JSON 分行、狀態機和版本相容。 如果宿主本身就是 TypeScript,且需要深入控制 Agent 生命週期,可以評估直接整合對應庫介面。 如果宿主是 Python、Go、桌面程式或需要把 Agent 當獨立 worker,RPC 通常更容易劃清邊界。
最小可交付版本應到什麼程度
一個可信的首版至少應具備:固定 Pi 版本、單工作區序列佇列、Provider 白名單、後端認證、事件序號、超時與中止、stderr 日誌、會話目錄校驗和異常退出清理。 第二階段再加入多工作區 worker 池、斷線續傳、工具審批和使用量統計。 不要先做華麗的聊天氣泡,再把檔案許可權與崩潰恢復留到上線後。
結論
Pi Coding Agent RPC 的價值,在於把成熟的 Agent 執行迴圈放到一個清晰的程序邊界後面。
整合質量取決於宿主是否正確管理 JSON 行、請求 ID、事件狀態、會話目錄和工具許可權。
先用 --no-session 完成單請求協議測試,再加入持久會話和 Web UI,最後透過斷線、限流與崩潰演練驗證恢復策略。
這樣得到的不是一個“能聊天的終端轉發器”,而是一套可以審計、隔離並持續升級的 Agent 服務。