函数调用(也称为工具调用或工具使用)是让模型访问你的代码的方式。你可以描述“查询订单”或“报价运费”等函数。模型决定调用哪个函数以及使用哪些参数;你的代码执行该函数并返回结果。从商店客服机器人到 Claude Code,每个 agent 都建立在这个循环之上。
在 GetVibe API 中,函数调用通过 OpenAI 格式的标准 /v1/chat/completions 端点,以及 Anthropic 格式的 /v1/messages 端点实现。下面是一个完整的循环示例,我们在来自三家不同厂商的三个模型上运行了这段代码。
循环的工作方式
- 你发送消息以及一个
tools列表。每个工具都有名称、描述,以及用于定义参数的 JSON Schema。 - 模型返回的不是文本,而是
tool_calls:要调用哪个函数以及使用哪些参数。finish_reason的值为tool_calls。 - 你的代码执行这些函数,并为每次调用追加一条角色为
tool的消息,其中携带相同的tool_call_id。 - 你再次发送整个历史记录。模型要么请求更多调用,要么以文本形式回答用户。
模型本身不会执行任何操作,它只会请求调用。实际执行什么操作以及使用哪些权限,都由你的代码决定。
完整示例:商店客服助手
这里有两个函数:查询订单状态和获取运费报价。用户同时询问这两项内容,模型在同一轮中调用了这两个函数。
import json
import os
from openai import OpenAI
client = OpenAI(base_url="https://getvibeapi.com/v1", api_key=os.environ["GETVIBE_API_KEY"])
MODEL = "gpt-6-luna"
def get_order_status(order_id: str) -> dict:
# 数据库或 CRM 查询逻辑写在这里
return {"order_id": order_id, "status": "out for delivery", "eta": "2026-10-24"}
def get_shipping_quote(zip_code: str) -> dict:
return {"zip_code": zip_code, "price_usd": 7.5, "days": 2}
TOOLS = [
{"type": "function", "function": {
"name": "get_order_status",
"description": "Look up an order by its ID",
"parameters": {"type": "object",
"properties": {"order_id": {"type": "string", "description": "Order ID, e.g. A-1042"}},
"required": ["order_id"]}}},
{"type": "function", "function": {
"name": "get_shipping_quote",
"description": "Courier shipping price and delivery time to a US ZIP code",
"parameters": {"type": "object",
"properties": {"zip_code": {"type": "string"}},
"required": ["zip_code"]}}},
]
FUNCTIONS = {"get_order_status": get_order_status, "get_shipping_quote": get_shipping_quote}
messages = [
{"role": "system", "content": "You are a store support assistant. Be brief."},
{"role": "user", "content": "Where is my order A-1042? And how much is shipping to 94110?"},
]
for _ in range(5): # 限制循环轮数
resp = client.chat.completions.create(model=MODEL, messages=messages, tools=TOOLS)
msg = resp.choices[0].message
if not msg.tool_calls:
print(msg.content)
break
messages.append(msg.model_dump(exclude_none=True))
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = FUNCTIONS[call.function.name](**args)
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
我们在 gpt-6-luna、deepseek-v4.1-flash 和 claude-haiku-5.5 上原样运行了这段代码,只修改了 MODEL。在第一轮中,三个模型都调用了两个函数:使用 A-1042 调用 get_order_status,使用 94110 调用 get_shipping_quote。在第二轮中,三个模型都以文本形式给出了配送状态、预计送达时间、7.50 的价格和两天的配送时长。
这段代码中的关键点
- 将模型的工具调用消息完整追加到历史记录中。如果不这样做,你的
tool消息回答的是模型看不到的调用,许多模型会直接再次调用同一个函数,导致循环无法结束。 - 每次调用对应一条
tool消息,并携带匹配的tool_call_id。模型可能会一次请求多个调用,正如本例所示。 - 参数以字符串形式返回。
arguments是字符串中的 JSON。请解析并验证它,因为模型可能会生成错误的值。 - 限制循环轮数。如果没有限制,函数中的 bug 可能会让循环无限执行,也会导致 token 费用无限增长。
tool_choice:必须调用时
默认情况下,模型会决定是调用工具还是以文本回答("auto")。tool_choice 可以改变这一行为:
"none"— 永不调用工具;"required"— 至少调用一个工具;{"type": "function", "function": {"name": "..."}}— 调用指定函数。
最后一种形式很适合数据提取。函数的参数 Schema 会决定答案的结构:
curl https://getvibeapi.com/v1/chat/completions \
-H "Authorization: Bearer $GETVIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "Jane Doe, [email protected], wants a demo on Thursday"}],
"tools": [{
"type": "function",
"function": {
"name": "save_lead",
"description": "Save a sales lead to the CRM",
"parameters": {
"type": "object",
"properties": {"name": {"type": "string"}, "email": {"type": "string"}, "request": {"type": "string"}},
"required": ["name", "email", "request"]
}
}
}],
"tool_choice": {"type": "function", "function": {"name": "save_lead"}}
}'
响应是一次 save_lead 调用,其中 name 为 "Jane Doe",email 为 "[email protected]",request 为 "Wants a demo on Thursday"。
需要注意的是:部分 Claude 模型(包括 Claude Sonnet 5.5 和 Claude Opus 5.5)不接受强制工具选择。GetVibe 会将此类请求转换为使用相同工具的 auto,因此你会得到答案而不是错误,但模型随后会自行决定是否调用工具。如果要让这些模型严格输出 JSON,请使用结构化输出。
Anthropic 格式:/v1/messages
使用 Anthropic SDK 时,工具的形式略有不同:参数 Schema 放在 input_schema 中,调用结果会作为 tool_use 内容块返回。
curl https://getvibeapi.com/v1/messages \
-H "x-api-key: $GETVIBE_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-haiku-5.5",
"max_tokens": 512,
"tools": [{
"name": "get_order_status",
"description": "Look up an order by its ID",
"input_schema": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}
}],
"messages": [{"role": "user", "content": "Where is order A-1042?"}]
}'
响应中的 stop_reason 为 tool_use,并包含一个调用 get_order_status 的 tool_use 块,其 order_id 为 A-1042。你需要在下一条用户消息中,将结果作为 tool_result 块发回,并使用相同的 tool_use_id。
工具调用应该使用哪个模型
- 高并发机器人和简单工具 — DeepSeek V4.1 Flash($0.0112 输入 · $0.0445 输出 每百万 token)或 Claude Haiku 5.5($0.0267 输入 · $0.1334 输出 每百万 token)。
- 拥有大量工具且执行长计划的 agent — GPT-6 Luna($0.0055 输入 · $0.0274 输出 每百万 token)或 Claude Sonnet 5.5。
Agent 循环会在每一轮重新发送相同的长前缀,因此提示词缓存在这里非常重要。有关请求字段的更多详细信息,请参阅工具调用文档。
