Router One

工具调用:一套请求格式,调所有模型

Tool calling(函数调用)让模型返回一个结构化的函数调用请求,而不是一段文字。各大模型家族都支持它,但每家官方 API 的格式都不一样——这正是网关能省掉的集成成本。Router One 遵循 OpenAI Chat Completions 规范:同一个 tools 数组、同一套响应处理循环,在 GPT、Claude、Gemini、DeepSeek 上通用;具体支持程度因模型而异,动手前先查目标模型的目录页。

在请求里声明工具

工具就是附在普通 chat completion 上的 JSON Schema 函数声明。从 /models 目录选一个支持工具调用的模型 ID:

request.sh
curl https://api.router.one/v1/chat/completions \
  -H "Authorization: Bearer sk-your-router-one-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model-id-from-/models>",
    "messages": [{"role": "user", "content": "上海今天天气?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "查询指定城市的当前天气",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"]
        }
      }
    }]
  }'

四步调用循环

处理一次工具调用是一段四步对话,无论请求最终由哪个模型家族服务,结构完全一致:

1. 模型请求调用工具

响应里没有 content,而是 finish_reason 为 "tool_calls" 和一个 tool_calls 数组——每个元素带 id、函数名,以及 JSON 字符串形式的参数。

2. 你执行函数

解析参数字符串、按你的 schema 校验,然后执行真正的函数。模型自己不会执行任何东西——它只是提出调用。

3. 你回传结果

先把收到的 assistant 消息原样追加进消息列表,再追加一条 role 为 "tool" 的消息,带上对应的 tool_call_id 和函数结果。

4. 模型完成回答

把扩展后的消息列表发回同一个端点。模型把工具结果织进最终回复——或者继续请求下一个工具,循环重复。

tool_loop.py
from openai import OpenAI
import json

client = OpenAI(
    base_url="https://api.router.one/v1",
    api_key="sk-your-router-one-key",
)

resp = client.chat.completions.create(
    model="<model-id-from-/models>", messages=messages, tools=tools
)
msg = resp.choices[0].message

if resp.choices[0].finish_reason == "tool_calls":
    messages.append(msg)
    for call in msg.tool_calls:
        result = run_tool(call.function.name,
                          json.loads(call.function.arguments))
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result),
        })
    final = client.chat.completions.create(
        model="<model-id-from-/models>", messages=messages, tools=tools
    )

各模型的差异在哪

  • 是否支持工具调用——先看模型目录页上的能力标记,再决定在它上面构建。
  • 并行调用:有的模型一次响应返回多个 tool_calls,有的一次一个。永远遍历整个数组,不要只读第 0 个。
  • Schema 严格度:不同模型对参数遵循 JSON Schema 的程度不同。执行前一律先校验。
  • 调用意愿:有的模型逢机会就调工具,有的更倾向直接文字回答。更锋利的函数描述和 tool_choice 参数对两类都有效。

在网关上排查工具调用

两类故障占绝大多数。模型用文字回答而不调工具:收紧函数描述、写明触发条件,或用 tool_choice 强制。参数解析失败或校验不过:绝不要盲目执行——先校验,失败时把错误信息作为工具结果回传,让模型自己纠正。无论哪种,每次尝试都是 Dashboard → Logs 里的一行请求记录,带 Token、花费、延迟和状态——循环每转一圈花了多少,看得一清二楚。

常见问题

哪些模型支持工具调用?
支持程度因模型而异。Router One 遵循 OpenAI Chat Completions 规范,/models 目录标注了每个模型的能力——按你要用的具体模型查目录页,不要按家族想当然。
工具声明算 Token 吗?
算——工具声明随 prompt 一起发送,按输入 Token 计费,而且循环每一轮都会计入。Dashboard → Logs 的每请求 Trace 会显示 Token 和费用影响,过大的 schema 一眼就能看出来。
该用工具调用还是结构化输出?
工具调用适合「要执行动作」:模型决定调你的函数,你把结果喂回去。如果只是想让回复固定成某个 JSON 结构、没有真函数要跑,用 schema 约束输出是更简单的模式。很多应用两者并用。
带工具调用的响应可以流式吗?
可以。开启流式后,工具调用的片段会在 delta 分块中逐步到达——累积 id、函数名和参数片段,直到流以 finish_reason 为 tool_calls 结束,然后走正常循环。分块格式见 LLM 流式输出指南。
同一套循环真的跨模型家族通用吗?
这正是 OpenAI 兼容规范的意义:tools 数组、tool_calls 响应、tool 角色的结果消息保持同一个形状,Router One 把请求路由到你指定的模型。各模型的行为差异(并行调用、严格度)依然存在,但循环结构不用变。