/

Agent Loop 策略:為什麼有的 tool 要多輪、有的要單輪

一句話摘要:LLM 呼叫工具後要不要再 call 一次 LLM,是延遲與能力的取捨。這份筆記解釋這個專案是怎麼決定的。

先說結論


從一個日常情境開始

假設你請一個助理幫你「訂明天的日本料理餐廳」。他有兩種做法:

做法 A(直接行動)

做法 B(先研究再行動)

這就是 Agent loop 的核心問題:一個工具用完,要不要回去想想再做下一步?


技術背景

LLM 呼叫工具(tool calling)的基本流程:

使用者訊息 → LLM → (決定要用什麼工具) → 執行工具 → 拿到結果 → ???

最後那個 ??? 有兩條路:

每次 call LLM:

所以選哪條路直接影響使用者的等待時間


Option A vs Option B

Option A — 執行完就停

使用者:列出這台機器裝了什麼
LLM: (決定) 呼叫 exec_shell("ls /usr/local/bin")
→ 執行
→ 結果直接 echo 給使用者
完成。共 1 次 LLM call。

適合:使用者想看「原始資料」的場景。像 shell 輸出、檔案內容,LLM 翻譯反而是噪音。

Option B — 每次執行完再 call LLM

使用者:列出這台機器裝了什麼
LLM: 呼叫 exec_shell("ls /usr/local/bin")
→ 執行
→ 結果塞回 messages
→ LLM 第二次回覆:「你這台機器裝了 node、git、holeOpen... 看起來是開發機」
完成。共 2 次 LLM call。

適合:使用者要的是「解讀」、「摘要」、「判斷」,raw data 對他沒意義。


但 Skill 天生就是多輪的

Skills 採用漸進揭露(progressive disclosure):

問題來了:read_skill 拿到的只是「說明書」,不是最終答案。LLM 讀完說明書後還要:

  1. 判斷該用哪個指令
  2. 組出正確的參數
  3. 再呼叫 exec_shell 真的執行

這整段流程一定要多輪,不然 LLM 還沒看完說明書就被迫中止。

所以同一個 agent 裡:


解法:依「工具性質」分類,不是依「skill」

我們把工具分兩類:

類別工具特性呼叫後行為
讀取類read_skillweb_fetch拿資料,沒有副作用結果塞回 messages,繼續下一輪 LLM
執行類exec_shell會動到系統、使用者要的是輸出本身結果直接給使用者,立刻終止

這樣的好處:

  1. 純聊天:1 次 LLM,無工具 → 最快
  2. 執行單一指令(「列資料夾」、「重啟服務」):1 次 LLM + 1 次 exec_shell → 快
  3. 研究任務(「幫我看 XX 網站怎麼說」):N 次 web_fetch + 最後回一段文字 → 自動多輪
  4. 用 skill 部署:read_skill + exec_shell,恰好 2 次 LLM → 可接受
  5. 研究 + 執行混合:多次 web_fetch → 最後 exec_shell 終止 → 自然

為什麼不用 Option B 全多輪?

Option B 的代價是:每個 shell 指令都會被 LLM 多轉一次。對 90% 的「遠端跑指令」場景來說:

所以執行類工具走 Option A 更符合使用者直覺。


無限迴圈怎麼辦?

每次 LLM 都可能決定「再抓一個網頁」、「再讀一個 skill」。理論上能跑到天荒地老。

保險絲:MAX_ROUNDS = 5 + 強制總結


常見誤解

誤解 1:多輪一定比單輪好,因為 LLM 可以思考。 錯。使用者想看 ls 的原始輸出時,LLM 的「思考」是噪音。

誤解 2:Option A 犧牲品質。 不是,Option A 只在執行類工具後跳過 LLM 總結。純聊天、讀取類工具都還是會 call LLM。品質犧牲只發生在「使用者本來就想看原始輸出」的情境,那本來就不需要 LLM 美化。

誤解 3:skill 必須用某種特別的多輪模式。 不用。只要 skill 的「讀取說明」用讀取類工具(read_skill)、「執行」用執行類工具(exec_shell),既有的分類自動 cover。


延伸思考

  1. web_fetch 如果回的網頁很大怎麼辦? 目前截斷到 8000 字,LLM 可能漏關鍵資訊。未來可加「fetch 後自動摘要」層,但這又多一次 LLM call,又回到權衡。
  2. exec_shell 能不能也走多輪,但由 LLM 主動決定? 可以讓 LLM 用「tool_choice: none」在最後一輪做總結。這樣第一次 exec_shell 不終止,但下一輪 LLM 不能再呼叫工具、只能回文字。程式複雜度顯著上升,先不做。
  3. 如果有一個 skill 真的需要「exec → 判斷 → exec」呢? 例如先 git status 再根據輸出決定要不要 git stash。目前做法是寫成一行 shell 鏈(git status && ... && git stash),或把邏輯包進 CLI(讓 ni 自己判斷)。硬要在 LLM 層做,就要改成 Option B。

這個專案目前的決策

讀取類 (read_skill, web_fetch) → 多輪
執行類 (exec_shell)           → 單輪終止
MAX_ROUNDS = 5

實驗專案連結 實作位置:src/agent/index.js,在 handleReadSkill / handleWebFetch / handleExecShell 三個函式裡分別處理。

如果未來新增工具,依這個表判斷歸哪一類、照既有 pattern 實作即可。

分享