使用 OpenAI 兼容 API 在 Python 中进行函数调用
发送 tools,读取 tool_calls,并用 role tool 返回结果:使用 MiniMax-M2.7 在 Python 中实现两步函数调用循环。

函数调用(工具调用)让模型请求你的代码运行一个函数。你在请求中发送一个 tools 列表,模型返回 tool_calls 而不是文本,你自己运行该函数,然后通过一条带有 role: tool 的消息把结果发回去。模型随后写出最终答案。本指南展示了在 Python 中针对 OpenAI 兼容端点 https:/ 使用 MiniMax-M2.7(id 为 MiniMaxAI/,180K 上下文,每次回答最多 8,192 个输出 token)的完整循环。
目录中的每个模型都列出了支持工具调用(CheapTokens 文档,于 2026 年 10 月 22 日检查)。你需要一个 API key;如果你还没有发起过请求,请先从你的第一个 API 请求开始。
函数调用如何工作#
模型从不运行任何东西。它只生成一个结构化请求:“用 {\"city\": \"Paris\"} 调用 get_weather”。你的程序完成实际工作并回报结果。这就形成了一个包含两次请求的循环:
- 请求 1。 你发送用户消息以及
tools列表。模型要么用文本回答,要么返回一个或多个tool_calls。 - 你的代码。 你解析每个调用,运行匹配的函数并收集结果。
- 请求 2。 你再次发送整个对话:原始消息、带有
tool_calls的 assistant 消息,以及每个调用对应的一条role: tool消息。模型使用这些结果写出最终答案。
每次请求都会重新发送整个历史记录,因此工具循环比单个问题使用更多输入 token。如果你对此不熟悉,请阅读什么是 token。
设置客户端#
安装 OpenAI SDK(pip install openai),并把你的 key 放在环境变量中,而不是源代码里:
export CHEAPTOKENS_API_KEY="your-key-here"base URL 是 https:/。从 CheapTokens 文档中获取模型 id:不同提供商对同一模型的命名不同。本指南使用 MiniMaxAI/。
声明函数并发送第一个请求#
函数在 tools 数组中描述。每个条目都有一个 type: function 和一个 function 对象,其中包含 name、description 以及以 JSON Schema 编写的 parameters。描述是模型用来决定何时调用函数的内容,因此请把它写成一句简短、具体的句子。
下面是一个完整脚本。天气函数是一个带有假数据的桩,因此该示例不需要额外服务:
import json
import os
from openai import OpenAI
client = OpenAI(
base_url='https://api.cheaptokens.dev/v1',
api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'MiniMaxAI/MiniMax-M2.7'
def get_weather(city: str) -> dict:
# Stub with fake data: replace with a real lookup
fake = {'Paris': 14, 'Tokyo': 19}
return {'city': city, 'temp_c': fake.get(city, 10)}
FUNCTIONS = {'get_weather': get_weather}
tools = [
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': 'Get the current temperature in Celsius for a city.',
'parameters': {
'type': 'object',
'properties': {
'city': {'type': 'string', 'description': 'City name, for example Paris'},
},
'required': ['city'],
},
},
}
]
messages = [{'role': 'user', 'content': 'What is the weather in Paris right now?'}]
# Step 1: the model decides whether to call a function
first = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = first.choices[0].message
if not message.tool_calls:
print(message.content)
raise SystemExit
# Step 2: run every requested call and send the results back
messages.append(message)
for call in message.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),
})
final = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
print(final.choices[0].message.content)有三个细节决定循环能否工作:
call.是一个 JSON 字符串,不是字典。用function. arguments json.loads解析它。- 每条
role: tool消息都必须携带它所回答调用的tool_call_id,即call.id。 - 工具消息的
content是字符串。结构化结果请使用json.dumps。
你应该看到什么#
如果你在第一次请求后打印这些字段,它们看起来像这样(调用 id 每次运行都不同):
finish_reason: tool_calls
call.id: call_abc123
call.function.name: get_weather
call.function.arguments: {"city": "Paris"}然后脚本会打印最终答案,一句类似“It is currently 14 °C in Paris.”的普通句子。措辞每次运行都会变化;数字来自你的函数。
多个调用和更长的循环#
模型可以在一次响应中返回多个调用,例如当你询问两个城市的天气时。这就是示例遍历 message. 并按各自的 id 回答每个调用的原因。OpenAI 的函数调用指南描述了相同的行为:在默认的 tool_choice 为 auto 时,模型可以调用零个、一个或多个函数(OpenAI 文档,于 2026 年 10 月 22 日检查;该页面没有最后更新日期,描述的是较新的 Responses API 形式,而本指南使用的是 / 端点的 Chat Completions 形式)。
Agent 会重复这两个步骤,直到模型不再请求工具。把请求包在循环中,并始终设置步数上限:
for _ in range(5): # safety limit against endless loops
response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = response.choices[0].message
if not message.tool_calls:
print(message.content)
break
messages.append(message)
for call in message.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)})如果你希望答案逐 token 出现,请将此与 Python 中的流式传输结合使用。
为什么 MiniMax-M2.7 适合工具循环#
工具循环会发送许多请求,且历史记录不断增长。针对这种模式,CheapTokens 提供 MiniMax-M2.7:180K 上下文,每次回答最多 8,192 个输出 token,全天任意时段统一价格、没有高峰时段,所有模型共用一个余额。将输入和输出合并计算,它比 OpenRouter 的参考价格更便宜(CheapTokens 价格比较,于 2026 年 10 月 31 日检查)。
当前每 1M token 的价格:输入 $0.05,输出 $0.15。作为一个示例工作负载(并非实测),上面那次两次请求的往返总共约 2,700 个输入 token 和 350 个输出 token,成本为 $0.00019。完整价格列表在价格页面上。
要尝试另一个模型,只需更改 id;id 列表在模型页面上。
如果它不工作#
message.是tool_ calls None。 模型用文本回答了。让函数description更具体,并确保问题确实需要该函数。json.出现在JSONDecodeError arguments上。 模型生成了无效 JSON。将json.loads包在try/except中,并把错误文本作为工具结果发回,以便模型用修正后的参数重试。- 第二次请求失败或模型忽略结果。 检查每个调用是否恰好有一条
role: tool消息,且其tool_call_id匹配,并检查带有tool_calls的 assistant 消息是否在它们之前的历史记录中。 KeyError出现在FUNCTIONS[...]中。 模型命名了一个你未注册的函数。改为把简短的错误字符串作为工具结果返回,而不是让程序崩溃。- 答案被截断。 检查
finish_reason:值为length表示达到了输出 token 上限。参见为什么 AI 回答会在句子中间停止。 - 401 或身份验证错误。 检查 key 是否在
CHEAPTOKENS_中,以及 base URL 是否以API_ KEY /v1结尾。
后续步骤#
用真实函数替换桩:数据库查询、搜索调用或计算器。向 tools 和 FUNCTIONS 添加更多条目,并让每个函数结果保持简短,因为它会在后续每次请求中回到历史记录里。要获取 key 并发起第一次调用,请打开 CheapTokens 文档。



