EN

指南

函数调用

将 xAI 模型连接到外部工具和系统,以构建 AI 助手和各种集成。grok.cadn.net.cn


函数调用使语言模型能够使用外部工具,这些工具可以将模型与数字世界和物理世界紧密连接。grok.cadn.net.cn

这是一项强大的功能,可用于支持广泛的使用案例。grok.cadn.net.cn

  • 调用公共 API 以执行从查找足球比赛结果到获取实时卫星定位数据的各种作
  • 分析内部数据库
  • 浏览网页
  • 执行代码
  • 与物理世界互动(例如预订机票、打开特斯拉车门、控制机械臂)

下图演示了函数调用的请求/响应流程。grok.cadn.net.cn

Function call request/response flow example

您可以将其视为向用户系统启动 RPC(远程过程调用)的 LLM。从 LLM 的角度来看,“2.Response“ 是从 LLM 到用户系统的 RPC 请求,”3.Request“ 是包含 LLM 所需信息的 RPC 响应。grok.cadn.net.cn

整个过程在伪代码中如下所示:grok.cadn.net.cn

// ... Define tool calls and their names

messages = []

/* Step 1: Send a new user request */

messages += {<new user request message>}
response = send_request_to_grok(message)

messages += response.choices[0].message  // Append assistant response

while (true) {
    /* Step 2: Run tool call and add tool call result to messages */ 
    if (response contains tool_call) {
        // Grok asks for tool call

        for (tool in tool_calls) {
            tool_call_result = tool(arguments provided in response) // Perform tool call
            messages += tool_call_result  // Add result to message
        }
    }

    read(user_request)

    if (user_request) {
        messages += {<new user request message>}
    }

    /* Step 3: Send request with tool call result to Grok*/
    response = send_request_to_grok(message)

    print(response)
}

我们将在以下 Python 脚本中演示函数调用。首先,让我们创建一个 API 客户端:grok.cadn.net.cn

import os
import json
from openai import OpenAI

XAI_API_KEY = os.getenv("XAI_API_KEY")

client = OpenAI(
    api_key=XAI_API_KEY,
    base_url="https://api.x.ai/v1",
)

将工具函数定义为回调函数,以便在模型响应时调用它们。grok.cadn.net.cn

通常,这些函数会从数据库中检索数据,或调用另一个 API 终端节点,或执行某些作。 出于演示目的,我们硬编码返回 59° Fahrenheit/15° Celus 作为温度,返回 15000 英尺作为云顶。grok.cadn.net.cn

参数定义将在初始请求中发送到 Grok,因此 Grok 知道哪些工具和参数可以调用。grok.cadn.net.cn

为了减少人为错误,您可以使用 Pydantic 部分定义工具。grok.cadn.net.cn

使用 Pydantic 的函数定义:grok.cadn.net.cn

from pydantic import BaseModel, Field
from typing import Literal

# Defining functions and function arguments
class TemperatureRequest(BaseModel):
    location: str = Field(description="The city and state, e.g. San Francisco, CA")
    unit: Literal["celsius", "fahrenheit"] = Field(
        "celsius", description="Temperature unit"
    )

class CeilingRequest(BaseModel):
    location: str = Field(description="The city and state, e.g. San Francisco, CA")

def get_current_temperature(**kwargs):
    request = TemperatureRequest(**kwargs)
    temperature: int
    if request.unit.lower() == "fahrenheit":
        temperature = 59
    elif request.unit.lower() == "celsius":
        temperature = 15
    else:
        raise ValueError("unit must be one of fahrenheit or celsius")
    return {
        "location": request.location,
        "temperature": temperature,
        "unit": "fahrenheit",
    }

def get_current_ceiling(**kwargs):
    request = CeilingRequest(**kwargs)
    return {
        "location": request.location,
        "ceiling": 15000,
        "ceiling_type": "broken",
        "unit": "ft",
    }


# Generate the JSON schema
get_current_temperature_schema = TemperatureRequest.model_json_schema()
get_current_ceiling_schema = CeilingRequest.model_json_schema()

# Definition of parameters with Pydantic JSON schema
tools_definition = [
    {
        "type": "function",
        "function": {
            "name": "get_current_temperature",
            "description": "Get the current temperature in a given location",
            "parameters": get_current_temperature_schema,
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_ceiling",
            "description": "Get the current cloud ceiling in a given location",
            "parameters": get_current_ceiling_schema,
        },
    },
]

使用原始字典的函数定义:grok.cadn.net.cn

# Defining functions
def get_current_temperature(location: str, unit: str = "fahrenheit"):
    temperature: int
    if unit.lower() == "fahrenheit":
        temperature = 59
    elif unit.lower() == "celsius":
        temperature = 15
    else:
        raise ValueError("unit must be one of fahrenheit or celsius")
    return {"location": location, "temperature": temperature, "unit": "fahrenheit"}


def get_current_ceiling(location: str):
    return {
        "location": location,
        "ceiling": 15000,
        "ceiling_type": "broken",
        "unit": "ft",
    }

tools_map = {
    "get_current_temperature": get_current_temperature,
    "get_current_ceiling": get_current_ceiling,
}

# Raw dictionary definition of parameters
tools_definition = [
    {
        "type": "function",
        "function": {
            "name": "get_current_temperature",
            "description": "Get the current temperature in a given location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "default": "celsius"
                    }
                },
                "required": ["location"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_ceiling",
            "description": "Get the current cloud ceiling in a given location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA"
                    }
                },
                "required": ["location"]
            }
        }
    }
]

创建一个 string -> 函数映射,这样我们就可以在模型发送函数名称时调用该函数。例如grok.cadn.net.cn

tools_map = {
    "get_current_temperature": get_current_temperature,
    "get_current_ceiling": get_current_ceiling,
}

定义完所有函数后,是时候将我们的 API 请求发送到 Grok 了!grok.cadn.net.cn

现在,在我们发送之前,让我们看看新任务的通用请求正文是什么样子的。grok.cadn.net.cn

请注意 tool 调用是如何被引用三次的:grok.cadn.net.cn

  • idname在 “上一次 Assistant 响应以获取工具调用” 中
  • tool_call_id在 “Tool Call return” 中
  • tools请求正文的字段
Function call request body

现在,我们在请求正文中编写请求消息并将其发送到 Grok。Grok 应该返回一个响应,要求我们进行工具调用。grok.cadn.net.cn

messages = [{"role": "user", "content": "What's the temperature like in Paris?"}]
response = client.chat.completions.create(
    model="grok-2-latest",
    messages=messages,
    tools=tools_definition,  # The dictionary of our functions and their parameters
    tool_choice="auto",
)

# You can inspect the response which contains a tool call
print(response.choices[0].message)

我们检索 Grok 想要调用的工具函数名称和参数,运行函数,并将结果添加到消息中。grok.cadn.net.cn

此时,您可以选择仅使用结果响应工具调用添加新的用户消息请求grok.cadn.net.cn

toolmessage 将包含以下内容:{ "role": "tool", "content": <json string of tool function's returned object>, "tool_call_id": <tool_call.id included in the tool call response by Grok>}grok.cadn.net.cn

我们尝试组装并发送回 Grok 的请求正文。请注意,它看起来与新的任务请求正文略有不同:grok.cadn.net.cn

Request body after processing tool call

用于附加消息的相应代码:grok.cadn.net.cn

# Append assistant message including tool calls to messages
messages.append(response.choices[0].message)

# Check if there is any tool calls in response body
# You can also wrap this in a function to make the code cleaner

if response.choices[0].message.tool_calls:
    for tool_call in response.choices[0].message.tool_calls:

        # Get the tool function name and arguments Grok wants to call
        function_name = tool_call.function.name
        function_args = json.loads(tool_call.function.arguments)

        # Call one of the tool function defined earlier with arguments
        result = tools_map[function_name](**function_args)

        # Append the result from tool function call to the chat message history,
        # with "role": "tool"
        messages.append(
            {
                "role": "tool",
                "content": json.dumps(result),
                "tool_call_id": tool_call.id  # tool_call.id supplied in Grok's response
            }
        )

response = client.chat.completions.create(
    model="grok-2-latest",
    messages=messages,
    tools=tools_definition,
    tool_choice="auto"
)

print(response.choices[0].message.content)

您可以按照步骤 2 继续对话。否则,您可以终止。grok.cadn.net.cn


默认情况下,模型将自动决定是否需要调用函数,并选择要调用的函数,由tool_choice: "auto"设置。grok.cadn.net.cn

我们提供三种方法来自定义默认行为:grok.cadn.net.cn

  1. 要强制模型始终调用一个或多个函数,您可以设置tool_choice: "required".然后,模型将始终调用 function。请注意,这可能会强制模型产生幻觉参数。
  2. 要强制模型调用特定函数,您可以设置tool_choice: {"type": "function", "function": {"name": "my_function"}}.
  3. 要禁用函数调用并强制模型仅生成面向用户的消息,您可以不提供任何工具,或者将tool_choice: "none".