工具

高级用法

查看 Markdown

本节介绍智能体工具调用的高级用法,包括:

  • 使用客户端工具 - 将服务端智能体工具与你自己的客户端工具结合,实现需要本地执行的专用功能。

  • 多轮对话 - 在启用智能体工具的对话中跨多个轮次保留上下文,让模型基于之前的研究和工具结果,迭代解决更复杂的问题

  • 启用多个工具的请求 - 发送同时启用多个服务端工具的请求,让 Web Search、X Search 和 Code Execution 协同完成综合分析

  • 图像集成 - 在启用工具的对话中加入图像,用于视觉分析和理解上下文的搜索


混合使用服务端和客户端工具

你可以将服务端智能体工具(例如 Web Search 和 Code Execution)与自定义客户端工具结合,构建强大的混合工作流。这样既能利用模型通过服务端工具进行推理,又能加入在应用本地运行的专用功能。

工作原理

混合使用服务端和客户端工具时,关键区别在于服务端工具由 xAI 自动执行,而客户端工具需要开发者介入

  1. 按照标准函数调用模式定义客户端工具

  2. 在请求中同时包含服务端和客户端工具

  3. xAI 会自动执行所有服务端工具,只要模型决定使用它们(例如 Web Search、Code Execution)

  4. 模型调用客户端工具时,执行会暂停 - xAI 会将工具调用返回给你,而不会代为执行

  5. 自行检测并执行客户端工具调用,然后追加执行结果以继续对话

  6. 重复此过程,直到模型生成最终响应且不再发起客户端工具调用

理解 max_turns 与客户端工具

使用这个max_turns 参数 与服务端、客户端工具混合使用时,需要注意max_turns仅限制单个请求中的 Assistant/服务端工具调用轮次

当模型决定调用客户端工具时,智能体执行会暂停并将控制权交还给你的应用。这意味着:

  • 当前请求结束,你会收到需要执行的客户端工具调用

  • 执行客户端工具并追加结果后,你需要发起一个新的后续请求

  • 该后续请求会重新开始计算 max_turns 次数

换言之,客户端工具调用相当于重置轮次计数器的“检查点”。如果设置 max_turns=5,且智能体在请求客户端工具前执行了 3 轮服务端工具调用,那么在你提供客户端工具结果后,后续请求仍可再次执行最多 5 轮服务端工具调用。

实际示例

假设有一个本地客户端函数 get_weather,用于获取指定城市的天气。模型可以结合此客户端工具与 Web Search 工具,查出 2025 年 NBA 总冠军球队所在城市的天气。

使用 xAI SDK

你可以使用 xai_sdk.tools.get_tool_call_type 检查 response.tool_calls 列表中的工具调用,判断其是否为客户端工具调用。 更多详情请参阅识别工具调用类型

  1. 导入依赖并定义客户端工具。

    Python

    import 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.7"
  2. 执行工具循环并延续对话:

    • 你可以使用 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}")

你会看到类似以下内容的输出:

Text

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

你可以检查 type 字段;该字段位于 response.output 列表的输出项中,由此判断某个工具调用是否为客户端工具调用。 更多详情请参阅识别工具调用类型

  1. 导入依赖并定义客户端工具。

    Python (OpenAI)

    import 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.7"
    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",
        },
    ]
  2. 执行工具循环:

    • 你可以使用 previous_response_id

      Python (OpenAI)

      response = 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)
    • 或者使用加密内容

      Python (OpenAI)

      input_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)

保留智能体状态的多轮对话

使用智能体工具时,你可能希望后续 prompt 在多轮对话中保留全部智能体状态,包括完整的推理、工具调用和工具响应历史。Stateful API 可以跨多次交互保留对话上下文。下面介绍两种方案。

远程存储对话历史

你可以选择将对话历史远程存储在 xAI 服务器上。每次继续对话时,都可以从希望恢复的上一条响应接续。

只需额外执行两个步骤:

  1. 首次发起智能体请求时添加参数 store_messages=True。这会指示服务在 xAI 服务器上存储完整对话历史,包括模型推理、服务端工具调用及相应结果。

  2. 创建后续对话时传入 previous_response_id=response.id,其中 response 是要继续的对话中由 chat.sample()chat.stream() 返回的响应。

请注意,后续对话无需使用与初始对话相同的工具、模型参数或其他配置,它仍会完整恢复上一次交互的全部智能体状态。

Python

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.7",  # 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.7",  # 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(Zero Data Retention)用户或不希望使用上述方案的用户,还有另一种选择:让 xAI 服务器除最终内容外,同时向客户端返回加密的推理和工具输出,并在下一轮对话中将这些加密内容作为上下文的一部分。

此方案需要额外执行以下步骤:

  1. 首次发起智能体请求时添加参数 use_encrypted_content=True。这会指示服务向客户端返回完整对话历史,包括模型推理(已加密)、服务端工具调用和相应结果(已加密)。

  2. 将响应追加到要继续的对话,然后调用 chat.sample()chat.stream()

Python

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.7",  # 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)

有关 Stateful Response 的更多详情,请参阅此指南


工具组合

为请求配置多个工具很简单,只需将要启用的工具放入请求的 tools 数组。模型会根据当前任务智能协调这些工具。

推荐的工具组合

以下是针对不同使用场景的常见工具组合:

如果你想……可以启用……原因
研究并分析数据Web Search + Code ExecutionWeb Search 收集信息,Code Execution 对其进行分析和可视化
汇总新闻与社交媒体信息Web Search + X Search同时覆盖传统网站与社交平台,获取更全面的信息
从多个来源中提取洞察Web Search + X Search + Code Execution从不同来源收集数据,再计算相关性和趋势
监测实时讨论X Search + Web Search结合权威信息追踪社交舆情
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()]

在不同场景中使用工具组合

  1. 需要在网络上搜索新闻时,可以启用全部搜索工具:

    • Web Search 工具

    • X Search 工具

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.7",  # 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)
  1. 需要从 Internet 收集最新数据并据此计算时,可以选择启用:

    • Web Search 工具

    • Code Execution 工具

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.7",  # 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...

在上下文中使用图像

你可以使用包含图像的初始对话上下文来启动请求。

在下面的代码示例中,我们先将一张图像传入对话上下文,再发起智能体请求。

Python

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.7",  # 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)

最后更新:2026 年 7 月 14 日