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

# 函数调用(tool use)

> 模型如何通过 RuAPI 调用你的函数:基于 OpenAI SDK 的完整 Python 示例

函数调用(又称 *tool use*)指的是:模型不再用文字回复,而是返回一个**结构化的调用请求**——要调用哪个函数、传什么参数。函数的实际代码由**你**在自己这边运行,再把结果交回给模型。之后模型才会把结果整理成一段正常的、给人看的回答。

听起来不起眼,但所有智能体(Agent)都建立在这个机制之上。模型本身不能查你的数据库、取天气、调别人的 API,但它能请你去做,并根据你返回的内容继续推理。

流程始终是同一套:

1. 你描述好可用的函数(它们的 schema),带上 `tools=` 发出一个普通请求。
2. 模型返回的不是文字,而是一个 `tool_calls` 数组——调用什么、传什么参数。
3. 你自己运行函数,然后发出**第二次**请求,把模型的回复和函数结果都追加进历史。
4. 模型返回最终文字。

## 完整 Python 示例

用经典的 `get_weather(city)` 举例。真实场景里它会去调天气服务;这里返回一个占位结果,让流程更清晰。

```python theme={null}
import json
from openai import OpenAI

client = OpenAI(
    api_key="sk-你的密钥",
    base_url="https://www.ruapi.ai/v1",
)

# 这是你自己的真实代码。模型并不会运行它,只会请求你去调用。
def get_weather(city: str) -> str:
    # 这里本该是一次真实的天气 API 调用
    return f"{city}当前 +18°C,局部多云"

# 给模型的函数描述:它能做什么、接收哪些参数
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "返回指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名,例如「北京」",
                    }
                },
                "required": ["city"],
            },
        },
    }
]

messages = [{"role": "user", "content": "北京现在天气怎么样?"}]

# 第 1 步。第一次请求——把问题和工具列表交给模型
response = client.chat.completions.create(
    model="claude-opus-4-8",  # 准确名称见主站定价页
    messages=messages,
    tools=tools,
)

message = response.choices[0].message

# 第 2 步。检查模型是否想调用函数
if message.tool_calls:
    # 重要:先把模型自己的回复(带 tool_calls 的那条)追加进历史
    messages.append(message)

    # 第 3 步。逐个处理调用(可能不止一个)
    for tool_call in message.tool_calls:
        # 参数是以 JSON 字符串形式传来的——必须先解析
        args = json.loads(tool_call.function.arguments)

        if tool_call.function.name == "get_weather":
            result = get_weather(args["city"])
        else:
            result = "未知函数"

        # 把调用结果作为单独的 role="tool" 消息发回去
        messages.append(
            {
                "role": "tool",
                "tool_call_id": tool_call.id,  # 与这次具体调用绑定
                "content": result,
            }
        )

    # 第 4 步。第二次请求——此时模型看到结果,给出最终回答
    final = client.chat.completions.create(
        model="claude-opus-4-8",
        messages=messages,
        tools=tools,
    )
    print(final.choices[0].message.content)
else:
    # 模型选择直接回答,没有调用函数
    print(message.content)
```

<Note>
  只有**能力较强的模型**才支持 tool use——比如 [Claude](/zh/claude-api) 和 [GPT](/zh/gpt-api) 系列。较老或轻量的模型
  可能直接忽略 `tools=`。哪个模型支持什么,请看主站\*\*「定价」\*\*页上的能力标记。
</Note>

## 常见坑

<AccordionGroup>
  <Accordion title="模型可能一次请求多个调用">
    一次回复里的 `message.tool_calls` 是一个**数组**,可能包含不止一个调用——比如同时查两个城市的天气。
    用循环遍历它,为**每一个** `tool_call` 都追加一条带自己 `tool_call_id` 的 `role="tool"` 消息。
    只要有一个调用没回结果,下一次请求就会报错。
  </Accordion>

  <Accordion title="先放模型回复,再放函数结果">
    `messages` 里的顺序是严格的:先追加**模型自己的回复**(带 `tool_calls` 的那个 `message` 对象),
    然后**才**追加带结果的 `role="tool"` 消息。如果在没有前置助手回复的情况下直接发结果,
    API 会因历史不一致而报错。
  </Accordion>

  <Accordion title="参数是字符串,不是对象">
    `tool_call.function.arguments` 是一个 **JSON 字符串**,不是现成的字典。使用前必须
    用 `json.loads(...)` 解析。直接对字符串按键取值会报错。
  </Accordion>
</AccordionGroup>

## 下一步

* [快速开始](/zh/quickstart) — 注册、密钥与第一次请求
* [错误与响应码](/zh/errors) — 请求失败时该怎么办
