工具
高级用法
在本节中,我们将探讨代理工具调用的高级用法模式,包括:
- 使用客户端工具 - 将服务器端代理工具与您自己的客户端工具结合,实现需要本地执行的专门功能。
- 多轮对话 - 在启用代理工具的对话中保持多轮之间的上下文,使模型能够基于先前的研究和工具结果进行更复杂的迭代问题解决。
- 使用多个活动工具的请求 - 同时激活多个服务器端工具发送请求,实现网络搜索、X 搜索和代码执行工具协同工作的全面分析。
- 图像集成 - 在您的工具启用对话中包含图像,用于视觉分析和上下文感知搜索。
NOTE
高级工具使用模式尚未在 Vercel AI SDK 中支持。请使用 xAI SDK 或 OpenAI SDK 实现此功能。
混合服务器端和客户端工具
您可以将服务器端代理工具(如网络搜索和代码执行)与自定义客户端工具结合,创建强大的混合工作流。这种方法让您能够利用模型的推理能力与服务器端工具,同时添加在应用程序本地运行的专门功能。
工作原理
混合服务器端和客户端工具时的主要区别在于:服务器端工具由 xAI 自动执行,而客户端工具需要开发者干预:
- 使用标准函数调用模式定义您的客户端工具
- 在请求中同时包含服务器端和客户端工具
- xAI 自动执行模型决定使用的任何服务器端工具(网络搜索、代码执行等)
- 当模型调用客户端工具时,执行暂停 - xAI 返回工具调用给您而不是执行它们
- 您自己检测并执行客户端工具调用,然后将结果附加回去以继续对话
- 重复此过程,直到模型生成不再有额外客户端工具调用的最终响应
理解带有客户端工具的 max_turns
当在混合服务器端和客户端工具的场景中使用max_turns 参数时,重要的是要理解 max_turns 仅限制单个请求中助手/服务器端工具调用的轮次。
当模型决定调用客户端工具时,代理执行暂停并将控制权返回给您的应用程序。这意味着:
- 当前请求完成,您收到要执行的客户端工具调用
- 在您执行客户端工具并附加结果后,您发出新的后续请求
- 此后续请求从新的
max_turns计数开始
换句话说,客户端工具调用充当"检查点",会重置计数器。如果您设置 max_turns=5,代理在请求客户端工具之前执行了 3 次服务器端工具调用,那么后续请求(在您提供客户端工具结果后)将再次允许最多 5 次服务器端工具轮次。
实际示例
给定一个本地客户端函数 get_weather 来获取指定城市的天气,模型可以使用这个客户端工具和网络搜索工具来确定 2025 年 NBA 冠军队主场的天气。
使用 xAI SDK
您可以使用 xai_sdk.tools.get_tool_call_type 针对 response.tool_calls 列表中的工具调用来确定工具调用是否为客户端工具调用。 有关更多详细信息,请查看识别工具调用类型。
导入依赖项,并定义客户端工具。
pythonimport os import json from xai_sdk import Client from xai_sdk.chat import user, tool, tool_result from xai_sdk.tools import web_search, get_tool_call_type client = Client(api_key=os.getenv("XAI_API_KEY")) # Define client-side tool def get_weather(city: str) -> str: """Get the weather for a given city.""" # In a real app, this would query your database return f"The weather in {city} is sunny." # Tools array with both server-side and client-side tools tools = [ web_search(), tool( name="get_weather", description="Get the weather for a given city.", parameters={ "type": "object", "properties": { "city": { "type": "string", "description": "The name of the city", } }, "required": ["city"] }, ), ] model = "grok-4.5"使用对话继续执行工具循环:
您可以使用
previous_response_id从上一个响应继续对话。python# Create chat with both server-side and client-side tools chat = client.chat.create( model=model, tools=tools, store_messages=True, ) chat.append( user( "What is the weather in the base city of the team that won the " "2025 NBA championship?" ) ) while True: client_side_tool_calls = [] for response, chunk in chat.stream(): for tool_call in chunk.tool_calls: if get_tool_call_type(tool_call) == "client_side_tool": client_side_tool_calls.append(tool_call) else: print( f"Server-side tool call: {tool_call.function.name} " f"with arguments: {tool_call.function.arguments}" ) if not client_side_tool_calls: break chat = client.chat.create( model=model, tools=tools, store_messages=True, previous_response_id=response.id, ) for tool_call in client_side_tool_calls: print( f"Client-side tool call: {tool_call.function.name} " f"with arguments: {tool_call.function.arguments}" ) args = json.loads(tool_call.function.arguments) result = get_weather(args["city"]) chat.append(tool_result(result)) print(f"Final response: {response.content}")或者,您可以使用加密内容继续对话。
python# Create chat with both server-side and client-side tools chat = client.chat.create( model=model, tools=tools, use_encrypted_content=True, ) chat.append( user( "What is the weather in the base city of the team that won the " "2025 NBA championship?" ) ) while True: client_side_tool_calls = [] for response, chunk in chat.stream(): for tool_call in chunk.tool_calls: if get_tool_call_type(tool_call) == "client_side_tool": client_side_tool_calls.append(tool_call) else: print( f"Server-side tool call: {tool_call.function.name} " f"with arguments: {tool_call.function.arguments}" ) chat.append(response) if not client_side_tool_calls: break for tool_call in client_side_tool_calls: print( f"Client-side tool call: {tool_call.function.name} " f"with arguments: {tool_call.function.arguments}" ) args = json.loads(tool_call.function.arguments) result = get_weather(args["city"]) chat.append(tool_result(result)) print(f"Final response: {response.content}")
您将看到类似于以下的输出:
Server-side tool call: web_search with arguments: {"query":"Who won the 2025 NBA championship?","num_results":5}
Client-side tool call: get_weather with arguments: {"city":"Oklahoma City"}
Final response: The Oklahoma City Thunder won the 2025 NBA championship. The current weather in Oklahoma City is sunny.使用 OpenAI SDK
您可以通过检查 response.output 列表中输出条目的 type 字段来确定工具调用是否为客户端工具调用。 有关更多详细信息,请参阅识别工具调用类型。
导入依赖项,并定义客户端工具。
pythonimport os import json from openai import OpenAI client = OpenAI( api_key=os.getenv("XAI_API_KEY"), base_url="https://api.x.ai/v1", ) # Define client-side tool def get_weather(city: str) -> str: """Get the weather for a given city.""" # In a real app, this would query your database return f"The weather in {city} is sunny." model = "grok-4.5" tools = [ { "type": "function", "name": "get_weather", "description": "Get the weather for a given city.", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "The name of the city", }, }, "required": ["city"], }, }, { "type": "web_search", }, ]执行工具循环:
您可以使用
previous_response_id。pythonresponse = client.responses.create( model=model, input=( "What is the weather in the base city of the team that won the " "2025 NBA championship?" ), tools=tools, ) while True: tool_outputs = [] for item in response.output: if item.type == "function_call": print(f"Client-side tool call: {item.name} with arguments: {item.arguments}") args = json.loads(item.arguments) weather = get_weather(args["city"]) tool_outputs.append( { "type": "function_call_output", "call_id": item.call_id, "output": weather, } ) elif item.type in ( "web_search_call", "x_search_call", "code_interpreter_call", "file_search_call", "mcp_call", ): # Server-side items expose type-specific fields (e.g. action), # not name/arguments like client-side function_call items. details = getattr(item, "action", None) or item.model_dump( exclude={"id", "type", "status"}, exclude_none=True ) print(f"Server-side tool call: {item.type} {details}") if not tool_outputs: break response = client.responses.create( model=model, tools=tools, input=tool_outputs, previous_response_id=response.id, ) print("Final response:", response.output[-1].content[0].text)或使用加密内容
pythoninput_list = [ { "role": "user", "content": ( "What is the weather in the base city of the team that won the " "2025 NBA championship?" ), } ] response = client.responses.create( model=model, input=input_list, tools=tools, include=["reasoning.encrypted_content"], ) while True: input_list.extend(response.output) tool_outputs = [] for item in response.output: if item.type == "function_call": print(f"Client-side tool call: {item.name} with arguments: {item.arguments}") args = json.loads(item.arguments) weather = get_weather(args["city"]) tool_outputs.append( { "type": "function_call_output", "call_id": item.call_id, "output": weather, } ) elif item.type in ( "web_search_call", "x_search_call", "code_interpreter_call", "file_search_call", "mcp_call", ): # Server-side items expose type-specific fields (e.g. action), # not name/arguments like client-side function_call items. details = getattr(item, "action", None) or item.model_dump( exclude={"id", "type", "status"}, exclude_none=True ) print(f"Server-side tool call: {item.type} {details}") if not tool_outputs: break input_list.extend(tool_outputs) response = client.responses.create( model=model, input=input_list, tools=tools, include=["reasoning.encrypted_content"], ) print("Final response:", response.output[-1].content[0].text)
保留代理状态的多轮对话
使用代理工具时,您可能希望进行多轮对话,其中后续提示保持所有代理状态,包括推理、工具调用和工具响应的完整历史记录。有状态 API 通过在多次交互中保留对话上下文实现了这一点。下面概述了两种选项。
远程存储对话历史
您可以选择将对话历史远程存储在 xAI 服务器上,每次想要继续对话时,可以从您希望恢复的最后一个响应处继续。
只有 2 个额外步骤:
- 在进行首次代理请求时添加参数
store_messages=True。这告诉服务将完整的对话历史(包括模型的推理、服务器端工具调用和相应响应)存储在 xAI 服务器上。 - 在创建后续对话时传递
previous_response_id=response.id,其中response是您希望继续的对话中由chat.sample()或chat.stream()返回的响应。
请注意,后续对话不需要使用与初始对话相同的工具、模型参数或任何其他配置 - 它仍然会从先前的交互中完全填充完整的代理状态。
import os
from xai_sdk import Client
from xai_sdk.chat import user
from xai_sdk.tools import web_search, x_search
client = Client(api_key=os.getenv("XAI_API_KEY"))
# First turn.
chat = client.chat.create(
model="grok-4.5", # reasoning model
tools=[web_search(), x_search()],
store_messages=True,
)
chat.append(user("What is xAI?"))
print("\\n\\n##### First turn #####\\n")
for response, chunk in chat.stream():
print(chunk.content, end="", flush=True)
print("\\n\\nUsage for first turn:", response.server_side_tool_usage)
# Second turn.
chat = client.chat.create(
model="grok-4.5", # reasoning model
tools=[web_search(), x_search()],
# pass the response id of the first turn to continue the conversation
previous_response_id=response.id,
)
chat.append(user("What is its latest mission?"))
print("\\n\\n##### Second turn #####\\n")
for response, chunk in chat.stream():
print(chunk.content, end="", flush=True)
print("\\n\\nUsage for second turn:", response.server_side_tool_usage)附加加密的代理工具调用状态
对于 ZDR(零数据保留)用户或不想使用上述选项的用户,还有另一种选择,即让 xAI 服务器除了最终内容外,还将加密的推理和加密的工具输出返回给客户端,这些加密内容可以作为下一轮对话上下文的一部分包含在内。
为此选项,您需要采取以下额外步骤:
- 在进行首次代理请求时添加参数
use_encrypted_content=True。这告诉服务将完整的对话历史返回给客户端,包括模型的推理(加密)、服务器端工具调用和相应响应(加密)。 - 在调用
chat.sample()或chat.stream()之前,将响应附加到您希望继续的对话中。
import os
from xai_sdk import Client
from xai_sdk.chat import user
from xai_sdk.tools import web_search, x_search
client = Client(api_key=os.getenv("XAI_API_KEY"))
# First turn.
chat = client.chat.create(
model="grok-4.5", # reasoning model
tools=[web_search(), x_search()],
use_encrypted_content=True,
)
chat.append(user("What is xAI?"))
print("\\n\\n##### First turn #####\\n")
for response, chunk in chat.stream():
print(chunk.content, end="", flush=True)
print("\\n\\nUsage for first turn:", response.server_side_tool_usage)
chat.append(response)
print("\\n\\n##### Second turn #####\\n")
chat.append(user("What is its latest mission?"))
# Second turn.
for response, chunk in chat.stream():
print(chunk.content, end="", flush=True)
print("\\n\\nUsage for second turn:", response.server_side_tool_usage)有关有状态响应的更多详细信息,请查看本指南。
工具组合
为您的请求配备多个工具很简单 - 只需在请求的 tools 数组中包含您想要激活的工具即可。模型将根据手头的任务智能地在它们之间协调。
建议的工具组合
根据您的用例,以下是一些常见的工具组合模式:
| 如果您想要... | 考虑激活... | 因为... |
|---|---|---|
| 研究与分析数据 | 网络搜索 + 代码执行 | 网络搜索收集信息,代码执行分析和可视化它 |
| 聚合新闻和社交媒体 | 网络搜索 + X 搜索 | 从传统网络和社交平台获得全面覆盖 |
| 从多个来源提取见解 | 网络搜索 + X 搜索 + 代码执行 | 从各种来源收集数据,然后计算关联和趋势 |
| 监控实时讨论 | X 搜索 + 网络搜索 | 跟踪社交情绪以及权威信息 |
from xai_sdk.tools import web_search, x_search, code_execution
# Example tool combinations for different scenarios
research_setup = [web_search(), code_execution()]
news_setup = [web_search(), x_search()]
comprehensive_setup = [web_search(), x_search(), code_execution()]research_setup = {
"tools": [
{"type": "web_search"},
{"type": "code_interpreter"}
]
}
news_setup = {
"tools": [
{"type": "web_search"},
{"type": "x_search"}
]
}
comprehensive_setup = {
"tools": [
{"type": "web_search"},
{"type": "x_search"},
{"type": "code_interpreter"}
]
}在不同场景中使用工具组合
- 当您想要搜索互联网上的新闻时,可以激活所有搜索工具:
- 网络搜索工具
- X 搜索工具
import os
from xai_sdk import Client
from xai_sdk.chat import user
from xai_sdk.tools import web_search, x_search
client = Client(api_key=os.getenv("XAI_API_KEY"))
chat = client.chat.create(
model="grok-4.5", # reasoning model
tools=[
web_search(),
x_search(),
],
include=["verbose_streaming"],
)
chat.append(user("what is the latest update from xAI?"))
is_thinking = True
for response, chunk in chat.stream():
# View the server-side tool calls as they are being made in real-time
for tool_call in chunk.tool_calls:
print(f"\\nCalling tool: {tool_call.function.name} with arguments: {tool_call.function.arguments}")
if response.usage.reasoning_tokens and is_thinking:
print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)
if chunk.content and is_thinking:
print("\\n\\nFinal Response:")
is_thinking = False
if chunk.content and not is_thinking:
print(chunk.content, end="", flush=True)
print("\\n\\nCitations:")
print(response.citations)
print("\\n\\nUsage:")
print(response.usage)
print(response.server_side_tool_usage)
print("\\n\\nServer Side Tool Calls:")
print(response.tool_calls)import os
from openai import OpenAI
api_key = os.getenv("XAI_API_KEY")
client = OpenAI(
api_key=api_key,
base_url="https://api.x.ai/v1",
)
response = client.responses.create(
model="grok-4.5",
input=[
{
"role": "user",
"content": "what is the latest update from xAI?",
},
],
tools=[
{
"type": "web_search",
},
{
"type": "x_search",
},
],
)
print(response)import os
import requests
url = "https://api.x.ai/v1/responses"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"
}
payload = {
"model": "grok-4.5",
"input": [
{
"role": "user",
"content": "what is the latest update from xAI?"
}
],
"tools": [
{
"type": "web_search",
},
{
"type": "x_search",
}
]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())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 latest update from xAI?"
}
],
"tools": [
{
"type": "web_search"
},
{
"type": "x_search"
}
]
}'- 当您想要从互联网收集最新数据并基于互联网数据进行计算时,可以选择激活:
- 网络搜索工具
- 代码执行工具
import os
from xai_sdk import Client
from xai_sdk.chat import user
from xai_sdk.tools import web_search, code_execution
client = Client(api_key=os.getenv("XAI_API_KEY"))
chat = client.chat.create(
model="grok-4.5", # reasoning model
# research_tools
tools=[
web_search(),
code_execution(),
],
include=["verbose_streaming"],
)
chat.append(user("What is the average market cap of the companies with the top 5 market cap in the US stock market today?"))
# sample or stream the response...import os
from openai import OpenAI
api_key = os.getenv("XAI_API_KEY")
client = OpenAI(
api_key=api_key,
base_url="https://api.x.ai/v1",
)
response = client.responses.create(
model="grok-4.5",
input=[
{
"role": "user",
"content": "What is the average market cap of the companies with the top 5 market cap in the US stock market today?",
},
],
# research_tools
tools=[
{
"type": "web_search",
},
{
"type": "code_interpreter",
},
],
)
print(response)import os
import requests
url = "https://api.x.ai/v1/responses"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {os.getenv('XAI_API_KEY')}"
}
payload = {
"model": "grok-4.5",
"input": [
{
"role": "user",
"content": "What is the average market cap of the companies with the top 5 market cap in the US stock market today?"
}
],
# research_tools
"tools": [
{
"type": "web_search",
},
{
"type": "code_interpreter",
},
]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())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 average market cap of the companies with the top 5 market cap in the US stock market today?"
}
],
"tools": [
{
"type": "web_search"
},
{
"type": "code_interpreter"
}
]
}'在上下文中使用图像
您可以使用包含图像的初始对话上下文来引导您的请求。
在下面的代码示例中,我们在发起代理请求之前,将图像传递到对话的上下文中。
import os
from xai_sdk import Client
from xai_sdk.chat import image, user
from xai_sdk.tools import web_search, x_search
# Create the client and define the server-side tools to use
client = Client(api_key=os.getenv("XAI_API_KEY"))
chat = client.chat.create(
model="grok-4.5", # reasoning model
tools=[web_search(), x_search()],
include=["verbose_streaming"],
)
# Add an image to the conversation
chat.append(
user(
"Search the internet and tell me what kind of dog is in the image below.",
"And what is the typical lifespan of this dog breed?",
image(
"https://pbs.twimg.com/media/G3B7SweXsAAgv5N?format=jpg&name=900x900"
),
)
)
is_thinking = True
for response, chunk in chat.stream():
# View the server-side tool calls as they are being made in real-time
for tool_call in chunk.tool_calls:
print(f"\\nCalling tool: {tool_call.function.name} with arguments: {tool_call.function.arguments}")
if response.usage.reasoning_tokens and is_thinking:
print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)
if chunk.content and is_thinking:
print("\\n\\nFinal Response:")
is_thinking = False
if chunk.content and not is_thinking:
print(chunk.content, end="", flush=True)
print("\\n\\nCitations:")
print(response.citations)
print("\\n\\nUsage:")
print(response.usage)
print(response.server_side_tool_usage)
print("\\n\\nServer Side Tool Calls:")
print(response.tool_calls)