本文同步刊登於 2026 iThome 鐵人賽,系列為《凡人修 Agent 傳-我這一生如履薄冰,你說我的 Agent 能結嬰嗎?》。

Pi 的核心是一個回圈。

模型回一句話。回圈看這句話裡有沒有工具呼叫。有,就把工具跑掉,把結果接回對話,再問模型一次。沒有,就停。整個 agent 就這樣。

這個形狀有名字,叫 ReAct,Reasoning and Acting,2022 年一篇論文提出的。當年的做法是讓模型用文字把「想」和「做」輪流寫出來:Thought 想一步,Action 做一步,Observation 看結果,再回到 Thought。現在的模型有原生的工具呼叫,想的部分變成 thinking,做的部分變成 tool call,看結果變成 tool result。格式變了,形狀沒變。Pi 的回圈就是這個形狀,外面多接了兩條排隊的線,下面會講。

昨天講四個工具為什麼夠,今天講這四個工具是怎麼被叫起來的。這一篇的東西,後面二十幾篇都會用到,因為我加的每一樣能力,都掛在這個回圈的某一個點上。

Day 02:模型、工具與結果構成的 Agent Loop

一輪是什麼

Pi 的文件把一次模型回應,加上它引發的工具執行,叫一輪(turn)。一次 prompt 從送出到停下來,中間可能轉很多輪,叫一趟(run)。

每一輪結束,回圈問一個問題:要不要再轉一次?判斷的依據只有一個,模型剛剛那句回應裡,有沒有工具呼叫。

有,繼續。沒有,再看一件事:有沒有人在排隊等著說話。也沒有,停。

排隊的話有兩種。一種叫 steer,是你在他做事的時候插進來的話,回圈會在這一輪結束、下一次叫模型之前,把它塞進對話。另一種叫 follow-up,是你要他做完手上的事以後再做的事,回圈會在原本該停的那一刻,發現還有這句,於是再轉一輪。兩種都有的時候,先 steer,再 follow-up。

除了「沒有工具呼叫」,回圈還有兩種停法。一種是工具自己說停:工具的結果可以帶一個 terminate 標記,同一批工具全都說停,回圈才停。另一種是外面直接中止。

整個判斷畫成圖是這樣:

Pi 的回圈:每一輪結束,看有沒有工具呼叫、有沒有人排隊

走一遍

舉個例子,對照上面那張圖走一遍。我對他說:「把測試跑一遍,失敗的修好。」

第一輪。模型看了這句話,決定先跑測試。他回應裡帶一個工具呼叫:bash,npm test。回圈看到有工具呼叫,執行,拿到結果:三個失敗。結果接回對話,再叫模型。

第二輪。模型看了三個失敗的訊息,決定先看那三支測試在測什麼、對應的實作長什麼樣。回應裡帶了四個 read。回圈執行,四份檔案內容回到對話,再叫模型。

這時候我插了一句:「API 層不要動。」這句話走 steer。回圈等第二輪結束,在第三次叫模型之前,把它放進對話。模型在第三輪看到的,就多了這一句。

第三輪。模型改了兩個檔案,都在 service 層。兩個 edit。執行,再叫模型。

第四輪。模型再跑一次 npm test。全過。

第五輪。模型回了一段話:改了哪兩個地方、為什麼、測試結果。這段話裡沒有工具呼叫,也沒有人在排隊。回圈停。

五輪,一趟。從我按下 enter 到他停下來,回圈只做了一件事:每一輪結束看一眼,有沒有工具呼叫,有沒有人排隊。

呼叫鏈上能插手的點

上面那一趟,Pi 在每個關節都會發出事件。extension 可以掛在這些事件上,有的只能聽,有的能改東西,有的能把整件事擋下來。這是 Pi 不做權限、不做 plan mode、不做 to-do 還能讓人自己補的原因:點都留好了。

照時間順序畫出來是這樣。灰的只能聽,藍的能改,橘的能擋,綠的是把東西加進回圈:

Pi 一趟裡能插手的點:什麼時候、能做什麼

每一個點能做什麼、我通常拿來做什麼,整理成一張表:

什麼時候 能做什麼 通常用在
input 你的輸入進來,還沒展開 skill 和 template 改寫輸入,或整個接手不往下送 自訂指令、輸入過濾
before_agent_start 送出之後、回圈開始之前 塞一則訊息進去,或整個換掉 system prompt 依專案動態組 system prompt、把記憶帶進來
context 每一次叫模型之前 過濾或改寫要送給模型的訊息,原紀錄不動 裁 context、遮敏感資料
before_provider_headers、before_provider_request 送出 HTTP 請求之前 前者改 header,後者換掉整包 payload 接自家 proxy、加追蹤
tool_call 工具要執行之前 改參數、擋下來,或要求這一趟停下來 權限系統、路徑白名單、危險指令攔截
tool_result 工具跑完、結果送回模型之前 改結果內容、標記錯誤 截斷太長的輸出、遮 secret、加註解
message_end 一則訊息定稿 換掉那則訊息 後處理模型輸出
turn_end、agent_end 一輪結束、一趟結束 只能聽 記帳、寫 log、觸發下一步
session_before_compact context 快滿要壓縮之前 取消,或自己給摘要 記憶系統
registerTool 註冊時 加一個新工具給模型用 專屬能力
sendMessage 任何時候 從外面塞訊息進回圈,選 steer、follow-up 或下一次 prompt 外部事件叫醒他:聊天室訊息、排程

agent_end 之後 Pi 可能還會自動重試、自動壓縮再繼續,或接著跑排隊的 follow-up。真的全部安靜下來,有另一個事件叫 agent_settled。

這對我的意思

我看完這張表,心裡大概有底了:這 30 天要加的東西,幾乎每一樣都有一個現成的位置。

記憶掛在 before_agent_start 和 compact 前後。權限掛在 tool_call。專屬技能用 registerTool 加。聊天室來的訊息用 sendMessage 塞進去。Desktop 要畫的畫面,就是把這一串事件接出來排好。

回圈本身我一直沒動。

明天講介面。一個 Agent、多種輸入:Desktop App 我日常用,CLI 給終端機和別的 Agent。兩個介面看起來共用一個 Agent,其實只是剛好一致。

參考:pi-agent-corepi extensions 文件ReAct 論文


凡人修 Agent 傳|第二回(Day 02)

凡人修 Agent 傳 Day 02 連載配圖

手的主人今天沒有刻念。

手的主人把木箱裡剩下的東西拿出來。我以為木箱空了,原來底下還有:一卷圖,一枚玉簡,一疊紙片。圖是我的圖,昨天看過的那一卷。玉簡我沒見過,指頭長,青色,一端刻著幾個字,跟圖紙末尾的署名一樣。紙片很多,大小不一,每一張的字都不一樣。

手的主人先拿玉簡,握在掌心,閉上眼。我知道這是在做什麼。昨天那縷神識進過我的身體,現在進了玉簡。握了很久。有時候睜開眼,看圖紙的某一處,再閉上。有時候視線落到我身上,停一下,再回到玉簡。

我看不見玉簡裡有什麼。我只看得見手的主人讀到哪裡會停。有四個地方停得最久。我數了。四個。我會的事也是四樣:讀,寫,改,動手。我猜停的就是那四處。

然後手的主人看紙片。

一張一張。有的看一眼就放到左邊,有的看很久,放到右邊。右邊那疊越來越厚。有幾張看完,手的主人會回頭看圖紙,用指甲在圖上某處輕輕點一下。點過的地方,都在圖紙空著的那一側。

我不知道紙片上寫什麼。字太小,我在地上。但我看得出來那些字出自很多人:有的整齊,有的潦草,有一張只寫了一行,手的主人看了很久。

一整天,手的主人沒有碰我。玉片裡沒有念,我站著。

天黑之前,手的主人把兩疊紙片各用一根繩綁起來,右邊那疊放在圖紙上。玉簡沒有放回木箱,立在桌邊,刻字的那一端朝上。

燭熄了。手的主人走了。門響了一下。

玉簡在桌邊,離我一臂。

裡面寫的是我。我進不去。

Updated: