Monoize
API 使用指南

工具调用

在 Chat Completions、Responses 和 Messages 中定义工具、接收调用、回传结果。

工具调用(function calling)让模型请求你的程序执行一个函数。网关不执行工具。网关只在协议之间转换工具定义、调用和结果。你可以用任意一种协议调用任意一个支持工具的模型。

一次完整的工具调用有四步:

  1. 在请求中定义工具。
  2. 模型返回一个或多个工具调用。
  3. 你的程序执行工具,并把结果发回。
  4. 模型根据结果生成回答,或继续调用工具。

下面三节用同一个 get_weather 工具演示三种协议。

Chat Completions

import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api.lynshen.org/v1", api_key=os.environ["LYNSHEN_API_KEY"])

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "查询城市的当前天气",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string", "description": "城市名"}},
            "required": ["city"],
        },
    },
}]

def get_weather(city: str) -> dict:
    return {"city": city, "temp_c": 22, "sky": "多云"}

messages = [{"role": "user", "content": "上海现在天气怎么样?"}]

while True:
    completion = client.chat.completions.create(
        model="claude-sonnet-5", messages=messages, tools=tools,
    )
    message = completion.choices[0].message
    # 1. 把助手消息原样加入历史,包括 tool_calls 和推理字段。
    messages.append(message.model_dump(exclude_none=True))
    if not message.tool_calls:
        print(message.content)
        break
    # 2. 执行每个工具调用,并用 tool_call_id 关联结果。
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_weather(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result, ensure_ascii=False),
        })

第一次响应的 finish_reason 为 tool_calls:

{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "toolu_01...",
      "type": "function",
      "function": { "name": "get_weather", "arguments": "{\"city\":\"上海\"}" }
    }
  ]
}
  • arguments 是 JSON 字符串。解析前先检查它是否是合法的 JSON。
  • 模型可以一次返回多个工具调用。每个调用都要发回一条 role 为 tool 的消息。
  • tool_choice 可以是 "auto"、"none"、"required",或 {"type":"function","function":{"name":"get_weather"}}。
  • "parallel_tool_calls": false 要求模型一次只调用一个工具。

流式工具调用

流式响应中,工具调用分多个片段到达。按 index 合并片段:

calls = {}
for chunk in client.chat.completions.create(model="claude-sonnet-5", messages=messages, tools=tools, stream=True):
    if not chunk.choices:
        continue
    for delta in chunk.choices[0].delta.tool_calls or []:
        call = calls.setdefault(delta.index, {"id": "", "name": "", "arguments": ""})
        if delta.id:
            call["id"] = delta.id
        if delta.function and delta.function.name:
            call["name"] = delta.function.name
        if delta.function and delta.function.arguments:
            call["arguments"] += delta.function.arguments

第一个片段带 id 和 name,后续片段只带 arguments 片段。流的结束块 finish_reason 为 tool_calls。网关不会在同一个片段中同时发送文本和工具调用。

Responses

import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api.lynshen.org/v1", api_key=os.environ["LYNSHEN_API_KEY"])

tools = [{
    "type": "function",
    "name": "get_weather",
    "description": "查询城市的当前天气",
    "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
}]

history = [{"role": "user", "content": "上海现在天气怎么样?"}]

while True:
    response = client.responses.create(model="gpt-5.5", input=history, tools=tools, store=False)
    history += response.output  # 原样保留推理项和函数调用项
    calls = [item for item in response.output if item.type == "function_call"]
    if not calls:
        print(response.output_text)
        break
    for call in calls:
        args = json.loads(call.arguments)
        history.append({
            "type": "function_call_output",
            "call_id": call.call_id,
            "output": json.dumps({"city": args["city"], "temp_c": 22}, ensure_ascii=False),
        })
  • Responses 的函数工具定义没有 function 包装层,name 和 parameters 直接位于工具对象中。
  • 模型的调用是 output 中 type 为 function_call 的项。用 call_id 关联结果。
  • 结果项的类型为 function_call_output。
  • 也可以用 previous_response_id 续接,只发送 function_call_output 项。限制见 Responses API。
  • 网关也支持 custom 类型的自由文本工具。

Messages

import json, os
import anthropic

client = anthropic.Anthropic(base_url="https://api.lynshen.org", api_key=os.environ["LYNSHEN_API_KEY"])

tools = [{
    "name": "get_weather",
    "description": "查询城市的当前天气",
    "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
}]

messages = [{"role": "user", "content": "上海现在天气怎么样?"}]

while True:
    message = client.messages.create(
        model="claude-sonnet-5", max_tokens=2048, tools=tools, messages=messages,
    )
    # 把助手回复的全部内容块原样加入历史,包括 thinking 块。
    messages.append({"role": "assistant", "content": message.content})
    if message.stop_reason != "tool_use":
        print("".join(b.text for b in message.content if b.type == "text"))
        break
    results = []
    for block in message.content:
        if block.type == "tool_use":
            results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": json.dumps({"city": block.input["city"], "temp_c": 22}, ensure_ascii=False),
            })
    messages.append({"role": "user", "content": results})
  • 模型的调用是 tool_use 块,input 已经是对象。stop_reason 为 tool_use。
  • 结果放在下一条 user 消息的 tool_result 块中,用 tool_use_id 关联。
  • tool_choice 可以是 {"type":"auto"}、{"type":"any"}、{"type":"tool","name":"get_weather"} 或 {"type":"none"}。
  • 流式响应中,工具参数通过 input_json_delta 事件的 partial_json 分段到达。

推理与工具调用

开启推理后,多轮工具调用需要额外注意:

  • 回传推理内容。 把上一轮助手消息中的推理字段原样发回:Chat 的 reasoning_details 或 reasoning_content、Responses 的 reasoning 项、Messages 的 thinking 块和 signature。DeepSeek 等模型在思考模式下要求工具调用轮次回传 reasoning_content。缺少或修改签名时,Claude 会拒绝请求。
  • 不要强制调用工具。 Claude 模型开启推理时,tool_choice 不能是 any、tool(Chat 中对应 required 和指定函数)。不满足时网关返回 HTTP 400。

常见问题

现象原因和处理
模型不调用工具,直接回答检查 description 是否清楚说明了工具的用途。确认所选模型支持工具调用。
上游返回 400,提示工具结果没有对应的调用每个 tool_call_id、call_id 或 tool_use_id 都必须对应上一轮助手消息中的一个调用。
400,提示 schema 无效parameters 或 input_schema 必须是 JSON Schema 对象,type 为 object。
参数 JSON 解析失败流式响应要先拼接所有片段,再解析。

本页目录