跳至內容
English
Chat

工具呼叫 ​

將 Chat Completions 串接至您的應用程式函式。

POST /v1/chat/completions

在 tools 中宣告函式,由應用程式執行模型提出的呼叫,並回傳結果以繼續對話。

工具定義 ​

json
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "lookup_stock",
        "description": "Look up inventory for a product SKU.",
        "parameters": {
          "type": "object",
          "properties": {
            "sku": {
              "type": "string"
            }
          },
          "required": [
            "sku"
          ],
          "additionalProperties": false
        }
      }
    }
  ]
}

Chat 的定義放在 function 物件內。讀取 choices[0].message.tool_calls,將 function.arguments 解析為 JSON,保留 assistant 訊息,再為每個呼叫加入 role: "tool" 訊息,並將呼叫的 id 填入 tool_call_id。

多語言首輪請求 ​

這組範例會取得模型的工具呼叫要求,尚未執行工具。讀取下方完整流程,以真實呼叫 ID 回傳工具結果並繼續對話。

執行方式見多語言範例說明。先設定 API_KEY,並替換模型、檔案網址及 ID 占位值;四種方式會顯示相同請求的原始回應。

bash
curl --fail-with-body --silent --show-error --max-time 180 \
  --request POST \
  --url "https://api.tokatlas.ai/v1/chat/completions" \
  --header "Authorization: Bearer $API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
  "model": "gpt-4o",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "lookup_stock",
        "description": "Look up inventory for a product SKU.",
        "parameters": {
          "type": "object",
          "properties": {
            "sku": {
              "type": "string"
            }
          },
          "required": [
            "sku"
          ],
          "additionalProperties": false
        }
      }
    }
  ],
  "stream": false,
  "messages": [
    {
      "role": "user",
      "content": "Check stock for DEMO-001."
    }
  ]
}'
python
import os
import requests

headers = {
    'Authorization': 'Bearer ' + os.environ["API_KEY"],
    'Content-Type': 'application/json',
}
payload = {'model': 'gpt-4o',
 'tools': [{'type': 'function',
            'function': {'name': 'lookup_stock',
                         'description': 'Look up inventory for a product SKU.',
                         'parameters': {'type': 'object',
                                        'properties': {'sku': {'type': 'string'}},
                                        'required': ['sku'],
                                        'additionalProperties': False}}}],
 'stream': False,
 'messages': [{'role': 'user', 'content': 'Check stock for DEMO-001.'}]}
response = requests.request(
    'POST', 'https://api.tokatlas.ai/v1/chat/completions', headers=headers,
    json=payload,
    timeout=180,
)
response.raise_for_status()
print(response.text)
js
if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const response = await fetch("https://api.tokatlas.ai/v1/chat/completions", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "model": "gpt-4o",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "lookup_stock",
        "description": "Look up inventory for a product SKU.",
        "parameters": {
          "type": "object",
          "properties": {
            "sku": {
              "type": "string"
            }
          },
          "required": [
            "sku"
          ],
          "additionalProperties": false
        }
      }
    }
  ],
  "stream": false,
  "messages": [
    {
      "role": "user",
      "content": "Check stock for DEMO-001."
    }
  ]
}),
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(await response.text());
java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

public class Example {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalArgumentException("Set API_KEY first.");
        }
        String payload = String.join("\n",
            "{",
            "  \"model\": \"gpt-4o\",",
            "  \"tools\": [",
            "    {",
            "      \"type\": \"function\",",
            "      \"function\": {",
            "        \"name\": \"lookup_stock\",",
            "        \"description\": \"Look up inventory for a product SKU.\",",
            "        \"parameters\": {",
            "          \"type\": \"object\",",
            "          \"properties\": {",
            "            \"sku\": {",
            "              \"type\": \"string\"",
            "            }",
            "          },",
            "          \"required\": [",
            "            \"sku\"",
            "          ],",
            "          \"additionalProperties\": false",
            "        }",
            "      }",
            "    }",
            "  ],",
            "  \"stream\": false,",
            "  \"messages\": [",
            "    {",
            "      \"role\": \"user\",",
            "      \"content\": \"Check stock for DEMO-001.\"",
            "    }",
            "  ]",
            "}"
        );
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tokatlas.ai/v1/chat/completions"))
            .timeout(Duration.ofSeconds(180))
            .header("Authorization", "Bearer " + apiKey)
            .header("Content-Type", "application/json")
            .method("POST", HttpRequest.BodyPublishers.ofString(payload))
            .build();
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": "
                + response.body());
        }
        System.out.println(response.body());
    }
}

完整 Python 範例 ​

安裝 requests、設定 API_KEY,並選用已開通的模型。庫存數值為示範資料;請將 execute 替換為業務服務。

python
import json
import os
import requests

URL = "https://api.tokatlas.ai/v1/chat/completions"
HEADERS = {'Authorization': "Bearer " + os.environ["API_KEY"], 'Content-Type': 'application/json'}
TOOLS = [{'type': 'function',
  'function': {'name': 'lookup_stock',
               'description': 'Look up inventory for a product SKU.',
               'parameters': {'type': 'object',
                              'properties': {'sku': {'type': 'string'}},
                              'required': ['sku'],
                              'additionalProperties': False}}}]


def post(payload):
    response = requests.post(URL, headers=HEADERS, json=payload, timeout=60)
    response.raise_for_status()
    body = response.json()
    # Accept the documented gateway envelope or a direct protocol response.
    data = body.get("data", body)
    if not isinstance(data, dict):
        raise RuntimeError("Unexpected API response")
    if data.get("error"):
        raise RuntimeError(data["error"])
    return data


def execute(name, args):
    if name != "lookup_stock":
        return {"error": "Unknown tool"}
    if not isinstance(args, dict) or set(args) != {"sku"}:
        return {"error": "Expected exactly one sku argument"}
    if not isinstance(args["sku"], str) or not args["sku"].strip():
        return {"error": "sku must be a non-empty string"}
    # Demo fixture only; replace with your inventory service.
    stock = {"DEMO-001": 18}
    if args["sku"] not in stock:
        return {"error": "SKU not found"}
    return {"sku": args["sku"], "available": stock[args["sku"]]}

messages = [{"role": "user", "content": "Check stock for DEMO-001."}]
for _ in range(6):
    reply = post({"model": "gpt-4o", "stream": False,
                  "tools": TOOLS, "messages": messages})
    choice = reply["choices"][0]
    if choice.get("finish_reason") in ("length", "content_filter"):
        raise RuntimeError("Response was not completed")
    message = choice["message"]
    calls = message.get("tool_calls") or []
    if not calls:
        print(message.get("content") or "")
        break
    messages.append(message)
    for call in calls:
        try:
            args = json.loads(call["function"]["arguments"])
            result = execute(call["function"]["name"], args)
        except (ValueError, TypeError):
            result = {"error": "Invalid JSON arguments"}
        messages.append({"role": "tool", "tool_call_id": call["id"],
                         "content": json.dumps(result)})
else:
    raise RuntimeError("Tool round limit reached")

工具選擇與串流 ​

使用 tool_choice: "auto" 自動選擇、"required" 要求呼叫,或 "none" 停用呼叫。指定函式時使用 {"type":"function","function":{"name":"lookup_stock"}}。避免每一輪都強制呼叫工具。

串流時,依 choice 與工具呼叫索引收集 delta.tool_calls,合併參數片段,完整接收後才執行。每個呼叫都須回傳結果,並驗證參數、限制重試次數。

相關主題 ​