> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/api/preparation/client-tools.md).

# 客戶端工具（Client Tools）

讓 AI 助理呼叫由你的應用程式自行執行的工具（例如查詢只在內網可達的系統）：如何在對話 API 宣告客戶端工具、辨識模型的呼叫要求、把執行結果送回並讓模型繼續完成任務

客戶端工具讓 AI 助理可以呼叫「在你的應用程式裡執行」的工具。你在每次呼叫對話 API 時宣告工具清單；模型判斷需要用到某個工具時，MaiAgent 不會自己執行，而是把「要呼叫哪個工具、帶什麼參數」回傳給你，由你的應用程式在本機執行後，再把結果送回對話，模型會依結果繼續回答或呼叫下一個工具。

適合只有你的應用程式才能執行的動作，例如查詢只在公司內網可達的系統、瀏覽器擴充功能操作目前開啟的網頁、App 存取使用者裝置上的資源。

## 使用情境 <a href="#scenario" id="scenario"></a>

一家電商的工程師把 AI 助理接進公司內部的客服工作台，訂單系統只開放在內網。客服人員在工作台輸入「客人問訂單 A1024 什麼時候到貨」，工作台呼叫對話 API 時一併宣告了「查詢訂單」工具。模型回覆要查哪一筆訂單，工作台在內網查到出貨狀態後送回同一段對話，模型據此寫出回覆客人的說明。訂單系統不必對外開放，工作台只送出模型需要的資料。

## 使用條件 <a href="#requirements" id="requirements"></a>

客戶端工具已對所有組織開放，不需要另外申請。要讓模型能呼叫，需同時符合：

* AI 助理的 <mark style="color:blue;">回答模式</mark> 為 <mark style="color:blue;">Agent 模式</mark>
* AI 助理使用的模型支援工具呼叫

{% hint style="warning" %}
不符合上述條件時，API 不會回傳錯誤，而是**直接忽略**你宣告的客戶端工具，模型會在沒有這些工具的情況下回答。若模型始終不呼叫你的工具，請先檢查助理的回答模式與模型。
{% endhint %}

## 運作流程 <a href="#flow" id="flow"></a>

{% stepper %}
{% step %}

### 宣告工具並送出訊息 <a href="#declare" id="declare"></a>

呼叫 `POST /api/v1/chatbots/{chatbotId}/completions/`，在 `message.clientTools` 帶入工具定義。
{% endstep %}

{% step %}

### 收到工具呼叫要求 <a href="#receive-call" id="receive-call"></a>

模型決定呼叫你的工具時，回應的 `contentPayload.items` 會出現 `type` 為 `client_tool_call` 的項目，**本回合隨即結束**。
{% endstep %}

{% step %}

### 在你的應用程式執行工具 <a href="#run-tool" id="run-tool"></a>

依 `name` 與 `input` 執行對應的工具。
{% endstep %}

{% step %}

### 把結果送回同一段對話 <a href="#send-result" id="send-result"></a>

以同一個 `conversation` 再呼叫一次對話 API，把執行結果寫在 `message.content`，並**再次帶上 `clientTools`**。模型會接續回答；若還需要其他工具，會再回到步驟 2，直到回傳最終文字為止。
{% endstep %}
{% endstepper %}

## 宣告工具 <a href="#tool-definition" id="tool-definition"></a>

```json
{
  "conversation": "<對話 ID，第一則訊息可省略>",
  "isStreaming": false,
  "message": {
    "content": "客人問訂單 A1024 什麼時候到貨？",
    "clientTools": [
      {
        "name": "get_order",
        "description": "依訂單編號查詢訂單的出貨狀態與預計到貨日",
        "inputSchema": {
          "type": "object",
          "properties": {
            "order": { "type": "string", "description": "訂單編號" }
          },
          "required": ["order"]
        }
      }
    ]
  }
}
```

| 欄位          | 類型     | 說明                                           |
| ----------- | ------ | -------------------------------------------- |
| name        | string | 工具名稱，僅限英數字、底線 `_`、連字號 `-`，長度 1–64，同一次請求內不可重複 |
| description | string | 工具用途說明，模型依此判斷何時使用，請寫清楚                       |
| inputSchema | object | 工具參數的 JSON Schema，序列化後不可超過 4,000 字元          |

{% hint style="info" %}

* 每次請求最多宣告 20 個客戶端工具。
* 工具清單只在**該次請求**有效，不會記在對話上。後續每一則訊息（包含送回工具結果的訊息）都要重新帶上 `clientTools`，否則模型在那一回合就無法再呼叫。
  {% endhint %}

## 工具呼叫要求的格式 <a href="#tool-call-format" id="tool-call-format"></a>

模型要求呼叫工具時，`contentPayload.items` 會包含如下項目，一次可能有多個：

```json
{
  "type": "client_tool_call",
  "toolCallId": "call_abc123",
  "name": "get_order",
  "input": { "order": "A1024" },
  "startTimestamp": "2026-10-01T08:30:00.000000+00:00"
}
```

| 欄位             | 類型             | 說明                           |
| -------------- | -------------- | ---------------------------- |
| type           | string         | 固定為 `client_tool_call`       |
| toolCallId     | string \| null | 模型產生的呼叫識別碼                   |
| name           | string         | 要執行的工具名稱，對應你宣告的 `name`       |
| input          | object         | 模型帶入的參數，對應你宣告的 `inputSchema` |
| startTimestamp | string \| null | 呼叫開始時間                       |

### 非串流與串流的差異 <a href="#streaming" id="streaming"></a>

* **非串流**（`isStreaming: false`）：工具呼叫項目在回應本體（`done: true`）的 `contentPayload.items` 中。
* **串流**（`isStreaming: true`）：工具呼叫項目會以獨立事件送出，該事件 `done` 為 `false`、`content` 為空字串、`contentPayload.items` 內含 `client_tool_call`。最後的 `done: true` 事件不會再重複這些項目，請在串流過程中收集。

{% hint style="warning" %}
回應的欄位名稱一律轉為 camelCase，`input` 內的參數名稱也會被轉換。例如你在 `inputSchema` 定義的 `order_id`，在 `input` 中會變成 `orderId`。建議參數名稱使用單一小寫英文單字（如 `order`、`keyword`），或在解析時同時接受兩種寫法。
{% endhint %}

## 送回執行結果 <a href="#return-result" id="return-result"></a>

目前沒有專用的「提交工具結果」端點。請在**同一段對話**送出下一則一般訊息，把結果寫在 `message.content`：

```json
{
  "conversation": "<同一個對話 ID>",
  "isStreaming": false,
  "message": {
    "content": "工具 get_order（toolCallId: call_abc123）執行結果：\n<查詢結果>",
    "clientTools": [ "...與前一次相同的工具定義..." ]
  }
}
```

* **一次多個工具呼叫**：全部執行完後，把各工具的結果合併在同一則訊息送回。
* **寫上工具名稱與 `toolCallId`**：後端不會用 `toolCallId` 比對，但寫在結果文字中能幫助模型對應是哪一次呼叫。
* **執行失敗或使用者拒絕**：同樣用文字說明，例如「get\_order 執行失敗：查無此訂單」或「使用者拒絕執行此操作」，模型會據此調整回答或改用其他方式。
* **結果內容長度**：送回的內容會計入模型輸入，資料量大時請先截取需要的部分。

## 連續呼叫多個工具 <a href="#multi-step" id="multi-step"></a>

每一次「模型要求呼叫 → 你送回結果」就是一個回合。模型可以在多個回合中連續呼叫工具，直到回應中不再出現 `client_tool_call`、改為回傳最終文字。建議在你的應用程式設定回合上限，避免異常情況下無限循環。

## 常見問題 <a href="#faq" id="faq"></a>

<details>

<summary>模型一直沒有呼叫我的工具</summary>

先確認助理的回答模式是 Agent 模式、模型支援工具呼叫（不符合時工具會被忽略，不會報錯）。再檢查 `description` 是否清楚描述用途與使用時機，以及該則訊息是否有帶上 `clientTools`。

</details>

<details>

<summary>送回結果後模型說無法使用工具</summary>

送回結果的那則訊息也必須帶上 `clientTools`。工具清單只對當次請求有效。

</details>

<details>

<summary>收到 400 錯誤</summary>

檢查工具定義：`name` 是否只含英數字、底線、連字號且不重複；`description` 是否為字串；`inputSchema` 是否為物件且序列化後不超過 4,000 字元；工具數量是否超過 20 個。

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.maiagent.ai/api/preparation/client-tools.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
