跳到主要内容

工具调用

工具调用(也称为函数调用)允许模型决定何时使用外部工具,并返回这些工具的结构化参数。当模型需要获取实时数据、查询内部系统、执行计算或在模型本身之外执行操作时,此功能非常有用。

工具调用通过 OpenAI 兼容的 /chat/completions API 支持,在 tools 字段中传递工具定义。

为什么使用工具调用

当模型需要其本身不具备的能力时,工具调用可以提供帮助,例如:

  • 检索实时信息
  • 调用内部 API 或数据库
  • 运行业务逻辑或工作流
  • 执行精确计算
  • 在外部系统中触发操作

与其让模型猜测,不如让它选择工具并提供结构化参数,由您的应用安全执行。

工作原理

典型的工具调用流程有四个步骤:

  1. 定义一个或多个工具,包括名称、描述和 JSON Schema 参数。
  2. 发送带有 tools 的聊天补全请求。
  3. 如果模型决定使用工具,它返回 tool_calls 而不是最终答案。
  4. 在您的应用中执行工具,将工具结果追加到 messages,并发送另一个请求以获取最终响应。

定义工具

每个工具必须定义为一个函数,包含:

  • name:简短、稳定的函数名称
  • description:清晰说明工具何时应被使用
  • parameters:描述允许参数的 JSON Schema 对象

示例:

[
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city to look up, such as San Francisco."
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to return."
}
},
"required": ["city"]
}
}
}
]

良好的工具定义很重要。模型依赖您的描述和 schema 来选择正确的工具并生成有效的参数。

基本示例

以下请求为模型提供了一个工具:

curl 'https://api.luchentech.com/inference/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <your_token_here>' \
--data '{
"model": "minimax/minimax-m2.5",
"messages": [
{
"role": "user",
"content": "What is the weather in Singapore right now?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city to look up."
}
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto",
"temperature": 0.1
}'

如果模型决定需要工具,响应通常会在助手消息中包含 tool_calls

{
"choices": [
{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Singapore\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}

完整工作流

完整的工具调用循环如下:

import json
from openai import OpenAI

client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.luchentech.com/inference/v1",
)

tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city to look up."
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit."
}
},
"required": ["city"]
}
}
}
]

def get_weather(city: str, unit: str = "celsius"):
return {
"city": city,
"temperature": 29,
"condition": "Partly cloudy",
"unit": unit
}

messages = [
{"role": "user", "content": "What's the weather in Singapore?"}
]

response = client.chat.completions.create(
model="minimax/minimax-m2.5",
messages=messages,
tools=tools,
tool_choice="auto",
temperature=0.1,
)

assistant_message = response.choices[0].message

if assistant_message.tool_calls:
tool_call = assistant_message.tool_calls[0]
tool_args = json.loads(tool_call.function.arguments)

tool_result = get_weather(**tool_args)

messages.append(assistant_message)
messages.append(
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(tool_result),
}
)

final_response = client.chat.completions.create(
model="minimax/minimax-m2.5",
messages=messages,
tools=tools,
temperature=0.1,
)

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

重要提示:通过应用中的显式函数映射执行工具。不要对模型生成的参数或函数名使用 eval

工具调用的消息模式

模型返回工具调用后,对话历史通常如下:

[
{
"role": "user",
"content": "What's the weather in Singapore?"
},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Singapore\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_123",
"content": "{\"city\":\"Singapore\",\"temperature\":29,\"condition\":\"Partly cloudy\",\"unit\":\"celsius\"}"
}
]

然后将更新后的 messages 数组发送回模型,使其能够生成最终的自然语言响应。

tool_choice

tool_choice 字段控制模型如何使用工具。

  • auto:模型决定是调用工具还是直接回答
  • none:不允许模型调用工具
  • required:模型必须至少调用一个工具

一些 OpenAI 兼容实现还支持强制特定函数:

{
"tool_choice": {
"type": "function",
"function": {
"name": "get_weather"
}
}
}

仅当您的工作流需要时才使用强制工具选择。

多个工具

您可以在同一请求中提供多个工具。例如,旅行助手可以提供:

  • get_weather
  • search_hotels
  • search_restaurants
  • get_exchange_rate

模型根据您的工具描述和参数 schema 选择最匹配用户请求的工具。

当多个工具有重叠时,请仔细编写描述以便模型能够区分。

流式工具调用

工具调用也支持 stream=true。在流式模式下,工具调用参数可能会逐步返回,需要在执行前组装。

import json
from openai import OpenAI

client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.luchentech.com/inference/v1",
)

tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "The city to look up."}
},
"required": ["city"]
}
}
}
]

tool_calls = {}

with client.chat.completions.create(
model="minimax/minimax-m2.5",
messages=[{"role": "user", "content": "What's the weather in Singapore?"}],
tools=tools,
stream=True,
temperature=0.1,
) as stream:
for chunk in stream:
choices = getattr(chunk, "choices", None)
if not choices:
continue

choice = choices[0]
delta = getattr(choice, "delta", None)

if delta and getattr(delta, "tool_calls", None):
for tc in delta.tool_calls:
index = tc.index
if index not in tool_calls:
tool_calls[index] = {"id": "", "name": "", "arguments": ""}

if getattr(tc, "id", None):
tool_calls[index]["id"] = tc.id

fn = getattr(tc, "function", None)
if fn and getattr(fn, "name", None):
tool_calls[index]["name"] = fn.name
if fn and getattr(fn, "arguments", None):
tool_calls[index]["arguments"] += fn.arguments

finish_reason = getattr(choice, "finish_reason", None)
if finish_reason == "tool_calls":
break

for tc in tool_calls.values():
try:
args = json.loads(tc["arguments"])
print(f"Call {tc['name']} with {args}")
except json.JSONDecodeError:
print(f"Incomplete tool args for {tc['name']}: {tc['arguments']}")

当模型可能生成大型工具参数或您希望降低感知延迟时,流式输出很有帮助。

工具 Schema 最佳实践

设计良好的 schema 能提高工具准确性。

  • 使用具体的函数名称
  • 为工具和每个参数编写清晰的描述
  • 正确标记必填字段
  • 当值来自固定集合时使用 enum
  • 保持 schema 尽可能简单
  • 避免职责模糊的重叠工具

示例:

{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, for example San Francisco."
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit."
}
},
"required": ["location"]
}

参数建议

工具调用通常在低随机性下效果最佳:

{
"temperature": 0.0,
"top_p": 1.0
}

较低的温度减少了参数值的幻觉,使工具选择更加确定。

安全建议

工具调用会增加现实世界的副作用,因此请将工具执行视为应用安全边界的一部分。

  • 执行前验证工具参数
  • 将工具限制在所需的最小权限内
  • 永远不要直接执行模型输出的任意 shell 命令
  • 使用支持的工具名称允许列表
  • 在传递给下游系统之前清理外部输入
  • 记录工具请求和工具输出以用于调试和审计

常见问题

模型不调用工具

尝试:

  • 改进工具描述
  • 使用户请求更明确
  • tool_choice 设置为 required
  • 降低 temperature
  • 确认所选模型支持工具调用

工具参数缺失或不正确

尝试:

  • 添加更好的参数描述
  • 对受限值使用 enum
  • 使必填字段明确
  • 降低 temperature

模型直接回答而不是等待工具结果

这通常发生在以下情况:

  • 工具描述太模糊
  • 模型认为可以从先验知识回答
  • 工作流没有正确追加 tool 角色消息

确保在请求最终答案之前,助手工具调用和对应的 tool 消息都已包含。

多轮流程中的工具调用变得混乱

保持对话状态清晰:

  • 保留包含 tool_calls 的助手消息
  • 每个工具结果追加一条 tool 消息
  • 在后续请求中发送完整的更新消息历史

相关指南