> ## Documentation Index
> Fetch the complete documentation index at: https://apione.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 工具调用

> 定义工具、执行函数并将结果安全返回给模型。

工具调用让模型选择由你的应用执行的函数。模型只生成调用意图和参数，不会替你访问数据库、发送邮件或执行命令。

## 调用流程

1. 在请求中发送工具名称、说明和 JSON 参数结构。
2. 模型返回工具调用及参数。
3. 应用校验参数、权限和用户意图。
4. 应用执行工具并记录审计信息。
5. 将工具结果作为后续消息返回模型，获取最终答复。

## 请求示例

```json theme={null}
{
  "model": "YOUR_APIONE_MODEL_ID",
  "messages": [{"role": "user", "content": "查询上海今天的天气"}],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "查询指定城市的天气",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"],
          "additionalProperties": false
        }
      }
    }
  ],
  "tool_choice": "auto"
}
```

发送到 `POST https://api.apione.ai/v1/chat/completions`。只有目标模型和线路支持工具调用时才使用这些字段；先查询模型参数。

## 解析与校验

回复正文可能为空，工具请求通常位于 `choices[].message.tool_calls`。不要直接执行模型生成的字符串：先解析 JSON，验证工具名、字段类型、枚举、长度和权限。

## 安全边界

* 工具名使用白名单，未知工具直接拒绝。
* 文件、命令、支付、删除和外部消息属于高风险操作，应要求额外确认。
* 服务端重新计算用户权限，不信任模型传入的用户 ID 或金额。
* 设置执行超时、最大返回大小和速率限制。
* 工具结果中移除密钥、内部错误栈和无关个人数据。

## 常见问题

| 现象 | 处理 |
| - | - |
| `content` 为空 | 检查是否返回 `tool_calls` |
| 参数不是合法 JSON | 让应用捕获解析错误并要求模型重试 |
| 工具被反复调用 | 设置最大轮次并检测相同参数循环 |
| 模型调用不存在的工具 | 强制白名单并返回可理解的错误 |
| 工具结果过长 | 摘要、分页或只返回必要字段 |

## 完整 Python 示例

示例由程序执行温度换算，再将结果回传给模型。先安装依赖，并按 [快速开始](https://apione.ai/docs/quickstart.md) 设置 `APIONE_API_KEY` 和支持工具调用的 `APIONE_MODEL`。保存代码为 `tool_demo.py`，运行 `python tool_demo.py`。

```bash theme={null}
python -m pip install openai
```

```python theme={null}
import json
import math
import os
from openai import OpenAI, APIStatusError, APIConnectionError

tools = [{"type": "function", "function": {
    "name": "celsius_to_fahrenheit",
    "description": "Convert Celsius to Fahrenheit.",
    "parameters": {"type": "object", "properties": {
        "celsius": {"type": "number", "minimum": -273.15, "maximum": 10000}
    }, "required": ["celsius"], "additionalProperties": False}
}}]

def execute(name, arguments):
    if name != "celsius_to_fahrenheit":
        return {"error": "Unknown tool"}
    try:
        args = json.loads(arguments)
        if not isinstance(args, dict) or set(args) != {"celsius"}:
            raise ValueError("Expected celsius only")
        value = args["celsius"]
        if isinstance(value, bool) or not isinstance(value, (int, float)):
            raise ValueError("Expected a number")
        if not math.isfinite(value) or not -273.15 <= value <= 10000:
            raise ValueError("Temperature out of range")
        return {"celsius": value, "fahrenheit": round(value * 9 / 5 + 32, 6)}
    except (ValueError, TypeError):
        return {"error": "Invalid tool arguments"}

messages = [{"role": "user", "content": "Use the tool to convert 25 Celsius to Fahrenheit."}]
try:
    with OpenAI(base_url="https://api.apione.ai/v1",
                api_key=os.environ["APIONE_API_KEY"],
                timeout=60, max_retries=0) as client:
        for round_number in range(4):
            raw = client.chat.completions.with_raw_response.create(
                model=os.environ["APIONE_MODEL"], messages=messages,
                tools=tools, tool_choice="auto")
            ids = {name: raw.headers[name] for name in
                   ("X-Request-ID", "X-Maxx-Trace-Id", "X-Maxx-Request-ID")
                   if name in raw.headers}
            print("request identifiers:", ids)
            result = raw.parse()
            if not result.choices:
                raise RuntimeError("No choices returned")
            message = result.choices[0].message
            calls = message.tool_calls or []
            if not calls:
                print(message.content)
                break
            if len(calls) > 8:
                raise RuntimeError("Too many tool calls")
            messages.append(message.model_dump(exclude_none=True))
            for call in calls:
                output = execute(call.function.name, call.function.arguments)
                messages.append({"role": "tool", "tool_call_id": call.id,
                                 "content": json.dumps(output)})
        else:
            raise RuntimeError("Tool-call round limit reached")
except APIStatusError as error:
    print("HTTP status:", error.status_code)
    headers = error.response.headers
    print("request identifiers:", {name: headers[name] for name in
          ("X-Request-ID", "X-Maxx-Trace-Id", "X-Maxx-Request-ID") if name in headers})
    raise SystemExit(1) from None
except APIConnectionError:
    print("Connection failed: no HTTP response or server request ID received.")
    raise SystemExit(1) from None
```

本地函数对 25°C 返回 77°F；模型的最终措辞由实际响应决定。每条工具结果的 `tool_call_id` 对应原调用的 `id`，必须保留工具调用消息与全部结果。示例最多执行 4 轮、每轮 8 个工具调用，并关闭 SDK 自动重试。费用以实际请求记录为准。

下一步：[结构化输出](https://apione.ai/docs/api/structured-output.md)、[模型参数](https://apione.ai/docs/models/parameters.md)、[错误排查](https://apione.ai/docs/support/errors.md)、[请求跟踪、超时与重试](https://apione.ai/docs/api/request-reliability.md)。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.