主题
语言
·2 分钟阅读

使用 LLM API 进行函数调用:完整可运行的循环

介绍 OpenAI-compatible API 上的函数调用(工具使用)如何工作:我们在 GPT-6 Luna、DeepSeek V4.1 Flash 和 Claude Haiku 5.5 上运行的完整 Python 循环,使用 tool_choice 强制发起调用,以及 Anthropic Messages 格式。

使用 LLM API 进行函数调用:完整可运行的循环

函数调用(也称为工具调用或工具使用)是让模型访问你的代码的方式。你可以描述“查询订单”或“报价运费”等函数。模型决定调用哪个函数以及使用哪些参数;你的代码执行该函数并返回结果。从商店客服机器人到 Claude Code,每个 agent 都建立在这个循环之上。

在 GetVibe API 中,函数调用通过 OpenAI 格式的标准 /v1/chat/completions 端点,以及 Anthropic 格式的 /v1/messages 端点实现。下面是一个完整的循环示例,我们在来自三家不同厂商的三个模型上运行了这段代码。

循环的工作方式

  1. 你发送消息以及一个 tools 列表。每个工具都有名称、描述,以及用于定义参数的 JSON Schema。
  2. 模型返回的不是文本,而是 tool_calls:要调用哪个函数以及使用哪些参数。finish_reason 的值为 tool_calls。
  3. 你的代码执行这些函数,并为每次调用追加一条角色为 tool 的消息,其中携带相同的 tool_call_id。
  4. 你再次发送整个历史记录。模型要么请求更多调用,要么以文本形式回答用户。

模型本身不会执行任何操作,它只会请求调用。实际执行什么操作以及使用哪些权限,都由你的代码决定。

完整示例:商店客服助手

这里有两个函数:查询订单状态和获取运费报价。用户同时询问这两项内容,模型在同一轮中调用了这两个函数。

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 循环会在每一轮重新发送相同的长前缀,因此提示词缓存在这里非常重要。有关请求字段的更多详细信息,请参阅工具调用文档。

函数调用 APILLM 工具调用OpenAI 函数调用示例tool_choiceAnthropic 工具使用LLM 智能体工具

免费试用 GetVibe API

通过一个 OpenAI 兼容的 API 接入 GPT、Claude、Gemini 等模型,支持信用卡或加密货币支付。

相关文章

使用 LLM API 进行函数调用:完整可运行的循环 | GetVibe API