# 工具調度

一輪 tool call 如何檢查、執行與記錄：三段 pass、延遲載入的 schema、哪些工具並行，以及重複呼叫如何去重。

## Three-pass tool concurrency

`toolCall.go` 將每一輪的 tool call 拆成三個序列 pass；只有 Pass 2 會 fan out：

| Pass | 模式 | 工作 |
|---|---|---|
| 1 — pre-flight | 序列 | 將 `run_tool` 拆包為所指名的工具；直接呼叫未載入的工具時拒絕（並提示先以 `find_tools` 取 schema 再呼叫 `run_tool`）；重複呼叫去重（`tool\|args` hash）；尚未取得 schema 就呼叫的工具回傳其 schema 並要求重呼叫；存在檢查，沒有已註冊 handler 的名稱一律不執行（v1.2.0 起）；confirm gate；JSON-schema 驗證 |
| 2 — execute | 對標記 `IsConcurrent` 的 tool 並行，同時最多 `MAX_CONCURRENT_TOOLS` 個 —— v1.3.0 起為 CPU 核心數的一半、至少 3（v1.0.20 起原為固定 5 個）—— 其餘排隊；其餘 tool 序列 | `tools.Execute` |
| 3 — commit | 序列 | 落地 `sessionData.Tools` 與 `ToolHistories`、更新去重表、寫入結果 cache、發出 `EventToolResult` |

v1.2.0 起 tool schema 改為延遲載入。除 `claude-code` 外的所有 provider，請求的 tools 陣列只含 `find_tools`、`run_tool`、`reasoning_guide`、`write_todo`、`chat_history`、`ask_user` 與 `write_result`（另加 client tools；`reasoning_guide` 於 v1.2.1、`write_todo` 於 v1.2.3、後三者於 v1.3.5 加入）；其餘工具僅以名稱列在 turn context，以 `find_tools` 取得 schema 後透過 `run_tool(name, args)` 呼叫。`claude-code` 取得除 `run_tool` 外的全部工具，但除 `find_tools` 與 client tools 外一律先標為未取 schema，每個工具首次呼叫只回傳其 schema 而不執行。v1.3.5 起工具失敗的結果末尾附 `next:` 提示：錯誤指向憑證缺漏或被拒時指向 `store_secret`，其他情況提示改用別的方法成功後以 `write_error(outcome=resolved)` 記錄（v1.3.11 之前指向 `error_history(mode=write)`，該 mode 移除於 v1.3.11）。各工具的 `AlwaysLoad` 旗標已移除；改用 `find_tools` 載入 schema。移除於 v1.2.0。

標記為 concurrent 的內建 tool（21 個）：`read_files`、`find_files`、`find_tools`、`file_history`、`chat_history`、`error_history`、`write_error`（v1.3.11 起）、`fetch_page`、`search_web`、`http_request`、`public_api_list`、`download_file`、`test_tool`、`calculate`、`subagents`、`background_subagents`、`list_chatbot`、`send_to_chatbot`、`reasoning_guide`、`html_template`、`write_todo`（v1.0.26 起；`find_note` 已於 v1.0.23 隨工具移除而退出此集合）。`generate_audio` 與 `generate_image` 不帶此旗標。`script_*` / `ext_*` tool 僅在自身定義宣告時才並行；使用者自訂的 `api_*` tool 已移除（移除於 v1.3.2），請改用內建的 `public_api_list` 搭配 `http_request`。`edit_file`、`remove_file`、`restore_file`、`run_command`、`background_command`、互動類 tool 以及 MCP tool 一律序列執行 —— v1.0.23 新增的並行 `run_command_readonly` 工具已於 v1.0.25 移除。`run_command_background` 更名為 `background_command`（移除於 v1.3.2），`run_websocket` 則已移除（移除於 v1.3.2），沒有替代工具。

去重是「每次執行」而非「每個 session」：重複的 `tool|args` 組合直接回傳先前結果而不再執行，且成功的 `edit_file`、`remove_file` 或 `restore_file` 會使涵蓋其路徑的 `read_files` 項目失效。另有一層 30 分鐘的結果 cache 存於 ToriiDB（`db_0`），僅涵蓋 `fetch_page`、`search_web` 與 `http_request` 的 `GET` 呼叫。Pass 3 只替 `http_request` 寫入；v1.3.1 起 `fetch_page` 與 `search_web` 改由 handler 自行寫入未過濾的結果，每次回傳（不論來自快取或剛抓取）都再套用 decision model 過濾。`fetch_page` 的快取跨 session 共用。
