# 工具調度

一輪 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 並行，同時最多 5 個（`MAX_CONCURRENT_TOOLS`，v1.0.20），其餘排隊；其餘 tool 序列 | `tools.Execute` |
| 3 — commit | 序列 | 落地 `sessionData.Tools` 與 `ToolHistories`、更新去重表、寫入結果 cache、發出 `EventToolResult` |

v1.2.0 起 tool schema 改為延遲載入。除 `claude-code` 外的所有 provider，請求的 tools 陣列只含 `find_tools` 與 `run_tool`（另加 client tools）；其餘工具僅以名稱列在 turn context，以 `find_tools` 取得 schema 後透過 `run_tool(name, args)` 呼叫。`claude-code` 取得除 `run_tool` 外的全部工具，但除 `find_tools` 與 client tools 外一律先標為未取 schema，每個工具首次呼叫只回傳其 schema 而不執行。各工具的 `AlwaysLoad` 旗標已移除；改用 `find_tools` 載入 schema。移除於 v1.2.0。

標記為 concurrent 的內建 tool（18 個）：`read_files`、`find_files`、`find_tools`、`file_history`、`chat_history`、`error_history`、`fetch_page`、`search_web`、`http_request`、`download_file`、`test_tool`、`calculate`、`subagents`、`list_chatbot`、`send_to_chatbot`、`reasoning_guide`、`html_template`、`write_todo`（v1.0.26 起；`find_note` 已於 v1.0.23 隨工具移除而退出此集合）。`generate_audio` 與 `generate_image` 不帶此旗標。`api_*` / `script_*` / `ext_*` tool 僅在自身定義宣告時才並行。`edit_file`、`run_command`、互動類 tool 以及 MCP tool 一律序列執行 —— v1.0.23 新增的並行 `run_command_readonly` 工具已於 v1.0.25 移除。

去重是「每次執行」而非「每個 session」：重複的 `tool|args` 組合直接回傳先前結果而不再執行，且 `edit_file` 會使涵蓋其寫入路徑的 `read_files` 項目失效。另有一層 30 分鐘的結果 cache 存於 ToriiDB（`db_0`），僅涵蓋 `fetch_page`、`search_web` 與 `http_request` 的 `GET` 呼叫。
