工具
函数调用
定义自定义工具,使模型在对话过程中可以调用。模型请求调用,您在本地执行,然后返回结果。这 enables 与数据库、API 和任何外部系统的集成。
WARNING
使用流式传输时,函数调用作为一个完整的块返回,而不是跨多个块流式传输。
- 使用名称、描述和参数的 JSON schema 定义工具
- 在请求中包含工具
- 当模型需要外部数据时,返回一个
tool_call - 在本地执行函数并返回结果
- 模型根据您的结果继续处理
快速开始
bash
curl https://api.x.ai/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{
"model": "grok-4.5",
"input": [
{"role": "user", "content": "What is the temperature in San Francisco?"}
],
"tools": [
{
"type": "function",
"name": "get_temperature",
"description": "Get current temperature for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "fahrenheit"}
},
"required": ["location"]
}
}
]
}'python
import os
import json
from xai_sdk import Client
from xai_sdk.chat import user, tool, tool_result
client = Client(api_key=os.getenv("XAI_API_KEY"))
# Define tools
tools = [
tool(
name="get_temperature",
description="Get current temperature for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "fahrenheit"}
},
"required": ["location"]
},
),
]
chat = client.chat.create(
model="grok-4.5",
tools=tools,
)
chat.append(user("What is the temperature in San Francisco?"))
response = chat.sample()
# Handle tool calls
if response.tool_calls:
chat.append(response)
for tc in response.tool_calls:
args = json.loads(tc.function.arguments)
# Execute your function
result = {"location": args["location"], "temperature": 59, "unit": args.get("unit", "fahrenheit")}
chat.append(tool_result(json.dumps(result)))
response = chat.sample()
print(response.content)python
import os
import json
from openai import OpenAI
client = OpenAI(
api_key=os.getenv("XAI_API_KEY"),
base_url="https://api.x.ai/v1",
)
tools = [
{
"type": "function",
"name": "get_temperature",
"description": "Get current temperature for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "fahrenheit"}
},
"required": ["location"]
},
},
]
response = client.responses.create(
model="grok-4.5",
input=[{"role": "user", "content": "What is the temperature in San Francisco?"}],
tools=tools,
)
# Handle function calls
for item in response.output:
if item.type == "function_call":
args = json.loads(item.arguments)
result = {"location": args["location"], "temperature": 59, "unit": args.get("unit", "fahrenheit")}
response = client.responses.create(
model="grok-4.5",
input=[{"type": "function_call_output", "call_id": item.call_id, "output": json.dumps(result)}],
tools=tools,
previous_response_id=response.id,
)
for item in response.output:
if item.type == "message":
print(item.content[0].text)javascript
import { xai } from '@ai-sdk/xai';
import { streamText, tool, stepCountIs } from 'ai';
import { z } from 'zod';
const result = streamText({
model: xai.responses('grok-4.5'),
tools: {
getTemperature: tool({
description: 'Get current temperature for a location',
inputSchema: z.object({
location: z.string().describe('City name'),
unit: z.enum(['celsius', 'fahrenheit']).default('fahrenheit'),
}),
execute: async ({ location, unit }) => ({
location,
temperature: unit === 'fahrenheit' ? 59 : 15,
unit,
}),
}),
},
stopWhen: stepCountIs(5),
prompt: 'What is the temperature in San Francisco?',
});
for await (const chunk of result.fullStream) {
if (chunk.type === 'text-delta') {
process.stdout.write(chunk.text);
}
}使用 Pydantic 定义工具
使用 Pydantic 模型实现类型安全的参数 schema:
python
from typing import Literal
from pydantic import BaseModel, Field
from xai_sdk.chat import tool
class TemperatureRequest(BaseModel):
location: str = Field(description="City and state, e.g. San Francisco, CA")
unit: Literal["celsius", "fahrenheit"] = Field("fahrenheit", description="Temperature unit")
class CeilingRequest(BaseModel):
location: str = Field(description="City and state, e.g. San Francisco, CA")
# Generate JSON schema from Pydantic models
tools = [
tool(
name="get_temperature",
description="Get current temperature for a location",
parameters=TemperatureRequest.model_json_schema(),
),
tool(
name="get_ceiling",
description="Get current cloud ceiling for a location",
parameters=CeilingRequest.model_json_schema(),
),
]python
from typing import Literal
from pydantic import BaseModel, Field
class TemperatureRequest(BaseModel):
location: str = Field(description="City and state, e.g. San Francisco, CA")
unit: Literal["celsius", "fahrenheit"] = Field("fahrenheit", description="Temperature unit")
class CeilingRequest(BaseModel):
location: str = Field(description="City and state, e.g. San Francisco, CA")
tools = [
{
"type": "function",
"name": "get_temperature",
"description": "Get current temperature for a location",
"parameters": TemperatureRequest.model_json_schema(),
},
{
"type": "function",
"name": "get_ceiling",
"description": "Get current cloud ceiling for a location",
"parameters": CeilingRequest.model_json_schema(),
},
]处理工具调用
当模型想要使用您的工具时,执行函数并返回结果:
python
import json
def get_temperature(location: str, unit: str = "fahrenheit") -> dict:
# In production, call a real weather API
temp = 59 if unit == "fahrenheit" else 15
return {"location": location, "temperature": temp, "unit": unit}
def get_ceiling(location: str) -> dict:
return {"location": location, "ceiling": 15000, "unit": "ft"}
tools_map = {
"get_temperature": get_temperature,
"get_ceiling": get_ceiling,
}
chat.append(user("What's the weather in Denver?"))
response = chat.sample()
# Process tool calls
if response.tool_calls:
chat.append(response)
for tool_call in response.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
result = tools_map[name](**args)
chat.append(tool_result(json.dumps(result)))
response = chat.sample()
print(response.content)python
import json
def get_temperature(location: str, unit: str = "fahrenheit") -> dict:
temp = 59 if unit == "fahrenheit" else 15
return {"location": location, "temperature": temp, "unit": unit}
tools_map = {"get_temperature": get_temperature}
# Process function calls
for item in response.output:
if item.type == "function_call":
name = item.name
args = json.loads(item.arguments)
if name not in tools_map:
output = json.dumps({"error": f"Unknown function: {name}"})
else:
output = json.dumps(tools_map[name](**args))
response = client.responses.create(
model="grok-4.5",
input=[{"type": "function_call_output", "call_id": item.call_id, "output": output}],
tools=tools,
previous_response_id=response.id,
)
for item in response.output:
if item.type == "message":
print(item.content[0].text)与内置工具结合使用
函数调用与内置的智能体工具协同工作。模型可以使用网络搜索,然后调用您的自定义函数:
python
from xai_sdk.chat import tool
from xai_sdk.tools import web_search, x_search
tools = [
web_search(), # Built-in: runs on xAI servers
x_search(), # Built-in: runs on xAI servers
tool( # Custom: runs on your side
name="save_to_database",
description="Save research results to the database",
parameters={
"type": "object",
"properties": {
"data": {"type": "string", "description": "Data to save"}
},
"required": ["data"]
},
),
]
chat = client.chat.create(
model="grok-4.5",
tools=tools,
)python
tools = [
{"type": "web_search"}, # Built-in
{"type": "x_search"}, # Built-in
{ # Custom
"type": "function",
"name": "save_to_database",
"description": "Save research results to the database",
"parameters": {
"type": "object",
"properties": {
"data": {"type": "string", "description": "Data to save"}
},
"required": ["data"]
},
},
]当混合使用工具时:
- 内置工具 在 xAI 服务器上自动执行
- 自定义工具 暂停执行并返回给您处理
有关带工具循环的完整示例,请参阅高级用法。
工具选择
控制模型使用工具的时机:
| 值 | 行为 |
|---|---|
"auto" | 模型决定是否调用工具(默认) |
"required" | 模型必须至少调用一个工具 |
"none" | 禁用工具调用 |
{"type": "function", "function": {"name": "..."}} | 强制使用特定工具 |
并行函数调用
默认情况下启用并行函数调用 — 模型可以在单个响应中请求多个工具调用。在继续处理之前,请处理所有调用:
python
# response.tool_calls may contain multiple calls
for tool_call in response.tool_calls:
result = tools_map[tool_call.function.name](**json.loads(tool_call.function.arguments))
# Append each result...在请求中使用 parallel_tool_calls: false 禁用。
工具 Schema 参考
| 字段 | 必需 | 描述 |
|---|---|---|
name | 是 | 唯一标识符(每个请求最多 200 个工具) |
description | 是 | 工具的功能 — 帮助模型决定何时使用它 |
parameters | 是 | 定义函数输入的 JSON Schema |
参数 Schema
json
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"default": "celsius"
}
},
"required": ["location"]
}parameters schema 的根必须是一个对象 ("type": "object");将任何其他类型嵌套在 properties 内。当每个分支本身就是一个对象时,根 anyOf 或 oneOf 也适用,让您可以定义接受几种对象变体之一的工具:
json
{
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "const": "email" },
"address": { "type": "string" }
},
"required": ["kind", "address"]
},
{
"type": "object",
"properties": {
"kind": { "const": "sms" },
"phone": { "type": "string" }
},
"required": ["kind", "phone"]
}
]
}WARNING
如果工具的 parameters 根既不是对象也不是对象的联合(例如,标量、数组,或包含非对象分支的 anyOf/oneOf),则无法将其编译为工具调用语法,并且会收到一个命名该工具的 400 错误。
完整的 Vercel AI SDK 示例
Vercel AI SDK 自动处理工具定义、执行以及请求/响应循环:
javascript
import { xai } from '@ai-sdk/xai';
import { streamText, tool, stepCountIs } from 'ai';
import { z } from 'zod';
const result = streamText({
model: xai.responses('grok-4.5'),
tools: {
getCurrentTemperature: tool({
description: 'Get current temperature for a location',
inputSchema: z.object({
location: z.string().describe('City and state, e.g. San Francisco, CA'),
unit: z.enum(['celsius', 'fahrenheit']).default('fahrenheit'),
}),
execute: async ({ location, unit }) => ({
location,
temperature: unit === 'fahrenheit' ? 59 : 15,
unit,
}),
}),
getCurrentCeiling: tool({
description: 'Get current cloud ceiling for a location',
inputSchema: z.object({
location: z.string().describe('City and state'),
}),
execute: async ({ location }) => ({
location,
ceiling: 15000,
ceiling_type: 'broken',
unit: 'ft',
}),
}),
},
stopWhen: stepCountIs(5),
prompt: "What's the temperature and cloud ceiling in San Francisco?",
});
for await (const chunk of result.fullStream) {
switch (chunk.type) {
case 'text-delta':
process.stdout.write(chunk.text);
break;
case 'tool-call':
console.log(`Tool call: ${chunk.toolName}`, chunk.input);
break;
case 'tool-result':
console.log(`Tool result: ${chunk.toolName}`, chunk.output);
break;
}
}