API 使用指南
工具调用
在 Chat Completions、Responses 和 Messages 中定义工具、接收调用、回传结果。
工具调用(function calling)让模型请求你的程序执行一个函数。网关不执行工具。网关只在协议之间转换工具定义、调用和结果。你可以用任意一种协议调用任意一个支持工具的模型。
一次完整的工具调用有四步:
- 在请求中定义工具。
- 模型返回一个或多个工具调用。
- 你的程序执行工具,并把结果发回。
- 模型根据结果生成回答,或继续调用工具。
下面三节用同一个 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和指定函数)。不满足时网关返回 HTTP400。
常见问题
| 现象 | 原因和处理 |
|---|---|
| 模型不调用工具,直接回答 | 检查 description 是否清楚说明了工具的用途。确认所选模型支持工具调用。 |
上游返回 400,提示工具结果没有对应的调用 | 每个 tool_call_id、call_id 或 tool_use_id 都必须对应上一轮助手消息中的一个调用。 |
400,提示 schema 无效 | parameters 或 input_schema 必须是 JSON Schema 对象,type 为 object。 |
| 参数 JSON 解析失败 | 流式响应要先拼接所有片段,再解析。 |