用 OpenAI SDK 在 Python 与 Node.js 中调用 MiniMax API
只需修改 Base URL、Key 和模型 ID,就能用官方 OpenAI SDK 在 Python 与 Node.js 中调用 MiniMax-M2.7、MiniMax-M2.7-highspeed 和 MiniMax-M3。本页提供对话、流式输出、工具调用和 JSON 模式的可运行示例,以及按 token 计的价格、成本控制和错误处理。
YiduoChan 通过 OpenAI 兼容接口提供 MiniMax 的文本模型。如果你的代码已经在 Python 或 Node.js 中使用官方 openai 包,需要改的只有 Base URL、API Key 和模型 ID。本页覆盖完整流程:安装、客户端初始化、基础对话、流式输出、工具调用、JSON 输出、模型与价格选择、成本控制、错误处理,以及对应的 curl 请求。
准备工作
- 一个 YiduoChan 账号。先注册,新账号注册即送 $0.10 试用额度,够发几次测试请求;正式使用需预付充值,最低充值 $5。
- 在控制台创建的 API Key。每个请求都以
Authorization: Bearer <API key>的形式携带它。 - Base URL
https://yiduochan.com/v1。对话请求发往/v1/chat/completions,/v1/models列出你的 Key 可调用的模型,语音合成接口是/v1/audio/speech。 - 三个文本模型 ID 之一:
MiniMax-M2.7、MiniMax-M2.7-highspeed或MiniMax-M3。ID 区分大小写,必须原样发送。
计费方式为按量从美元余额中扣费,无订阅,失败的请求从不计费。当前价格见定价页,平台上所有模型的概要见 MiniMax 模型总览。
安装 SDK
两个官方 SDK 都支持自定义 Base URL,不需要任何第三方封装。
pip install openai
npm install openai
示例假设使用 Python 3.9 及以上版本,以及采用 ES module 语法的 Node.js 18 及以上版本。下文内容不依赖任一 SDK 的特定小版本。
初始化客户端
Python
from openai import OpenAI
client = OpenAI(
base_url="https://yiduochan.com/v1",
api_key="YOUR_API_KEY", # created in the YiduoChan console
)
Node.js
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://yiduochan.com/v1",
apiKey: "YOUR_API_KEY", // created in the YiduoChan console
});
不要把 Key 提交到版本控制,应在启动时从密钥管理系统中加载。后面的每个示例都复用同一个 client 对象。
基础对话补全
Python
resp = client.chat.completions.create(
model="MiniMax-M2.7",
messages=[
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "Explain idempotency in one paragraph."},
],
max_tokens=300,
)
print(resp.choices[0].message.content)
print(resp.usage)
Node.js
const resp = await client.chat.completions.create({
model: "MiniMax-M2.7",
messages: [
{ role: "system", content: "You are a concise assistant." },
{ role: "user", content: "Explain idempotency in one paragraph." },
],
max_tokens: 300,
});
console.log(resp.choices[0].message.content);
console.log(resp.usage);
响应是标准的 OpenAI 格式:choices[0].message.content 是文本内容,usage 给出提示和补全的 token 数。生产环境中请记录 usage,费用就是用它乘以下表中的单价。
流式输出
把 stream 设为 true,即可在生成过程中逐步接收 tokens。流式输出不改变价格,改变的是首个 token 的返回时间。另一端有用户在等待时,建议搭配 MiniMax-M2.7-highspeed 使用。
Python
stream = client.chat.completions.create(
model="MiniMax-M2.7-highspeed",
messages=[{"role": "user", "content": "Write a haiku about retries."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()
Node.js
const stream = await client.chat.completions.create({
model: "MiniMax-M2.7-highspeed",
messages: [{ role: "user", content: "Write a haiku about retries." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
process.stdout.write("\n");
要防范 choices 数组为空、或 delta 中没有 content 的 chunk,两个示例都做了这层处理。最后一个 chunk 带有 finish_reason,可以在这里判断是否因 length 而停止。对于没有人实时查看输出的批处理任务,请关闭流式输出:单个完整响应更便于记录日志、重试和存储。
函数调用与工具调用
工具调用使用标准的 tools 和 tool_choice 参数。流程是:发送工具定义;检查模型是否返回了 tool_calls;在本地执行每个调用;把结果作为 role: "tool" 消息追加进去;再调用一次 API,让模型写出最终回答。示例完整可运行,把 get_weather 换成真实的查询即可。
Python
import json
def get_weather(city: str) -> dict:
return {"city": city, "temp_c": 21, "condition": "clear"} # stub
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "What is the weather in Berlin?"}]
first = client.chat.completions.create(
model="MiniMax-M2.7", messages=messages, tools=tools, tool_choice="auto",
)
msg = first.choices[0].message
messages.append(msg)
for call in msg.tool_calls or []:
args = json.loads(call.function.arguments)
result = get_weather(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
final = client.chat.completions.create(
model="MiniMax-M2.7", messages=messages, tools=tools,
)
print(final.choices[0].message.content)
Node.js
const tools = [{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather for a city.",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
},
}];
function getWeather({ city }) {
return { city, temp_c: 21, condition: "clear" }; // stub
}
const messages = [{ role: "user", content: "What is the weather in Berlin?" }];
const first = await client.chat.completions.create({
model: "MiniMax-M2.7", messages, tools, tool_choice: "auto",
});
const msg = first.choices[0].message;
messages.push(msg);
for (const call of msg.tool_calls ?? []) {
const result = getWeather(JSON.parse(call.function.arguments));
messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) });
}
const final = await client.chat.completions.create({
model: "MiniMax-M2.7", messages, tools,
});
console.log(final.choices[0].message.content);
function.arguments是 JSON 字符串,不是对象。执行任何操作之前,先解析并校验它。- 先追加携带
tool_calls的 assistant 消息,再追加工具结果,否则第二次请求的格式是错误的。 - 如果模型继续请求调用,就循环执行工具步骤,并限制循环次数。
- 每次请求中,工具定义都计入提示 tokens。描述尽量简短,并参考下文的 Prompt 缓存一节。
JSON 模式与结构化输出
要得到机器可读的输出,请传入 type 为 json_object 的 response_format,并在系统提示中说明期望的键,提示中要包含 JSON 这个词。在客户端解析结果,并用你自己的 schema 校验;解析失败时,按其他可重试错误一样处理。
Python
resp = client.chat.completions.create(
model="MiniMax-M2.7",
messages=[
{"role": "system", "content": "Reply with JSON only. Keys: title (string), tags (array of strings)."},
{"role": "user", "content": "Summarize: PostgreSQL 16 adds logical replication from standbys."},
],
response_format={"type": "json_object"},
max_tokens=200,
)
data = json.loads(resp.choices[0].message.content)
assert isinstance(data["tags"], list)
Node.js
const resp = await client.chat.completions.create({
model: "MiniMax-M2.7",
messages: [
{ role: "system", content: "Reply with JSON only. Keys: title (string), tags (array of strings)." },
{ role: "user", content: "Summarize: PostgreSQL 16 adds logical replication from standbys." },
],
response_format: { type: "json_object" },
max_tokens: 200,
});
const data = JSON.parse(resp.choices[0].message.content);
如需严格约束 schema,可以在 Python 中用 pydantic、在 Node.js 中用 zod 校验,失败时把校验错误作为后续消息发回给模型。这种做法与服务商无关,能保持代码的可移植性。另外,解析之前先检查 finish_reason:被 max_tokens 截断的响应几乎一定不是合法 JSON,解决办法是调大上限,而不是重试。
选择模型
三个文本模型均按 MiniMax 刊例价水平以美元计费,价格单位为每百万 tokens。
| 模型 | 上下文 | 输入 | 输出 | 缓存读取 | 缓存写入 |
|---|---|---|---|---|---|
MiniMax-M2.7 | 标准 | $0.30 | $1.20 | $0.06 | $0.375 |
MiniMax-M2.7-highspeed | 标准 | $0.60 | $2.40 | $0.06 | $0.375 |
MiniMax-M3(提示不超过 512K) | 1,048,576 tokens | $0.30 | $1.20 | $0.06 | — |
MiniMax-M3(提示 512K 到 1M) | 1,048,576 tokens | $0.60 | $2.40 | $0.12 | — |
- MiniMax-M2.7 是大多数场景的默认选择:批处理、Agent、代码生成,以及其他不受交互延迟约束的任务。详见 M2.7 页面。
- MiniMax-M2.7-highspeed 是同一个模型,质量相同、延迟更低,输入和输出价格都是两倍。适合聊天界面和流式助手。它的缓存读取和缓存写入价格与 M2.7 完全相同,所以缓存用得好的 highspeed 工作负载,实际花费比标价看上去要低。
- MiniMax-M3 拥有 1,048,576 tokens 的上下文窗口。提示不超过 512K tokens 时与 M2.7 同价;512K 到 1M 之间,输入、输出和缓存读取价格都翻倍。适合整仓库分析、长转录文本,或无法切块的多文档检索。详见 M3 页面。
切换模型只需改一行 model 参数,代码其他部分完全相同。M2.7 与 M2.7-highspeed 是同一个模型、质量相同,不需要分别评估准确率,选择时只需权衡延迟和价格。对 M3 来说,计费档位由每次请求的提示长度决定,所以即使 1M 窗口可用,只要工作负载大多数时候在 512K tokens 以内,就按较低的价格计费。
控制成本
max_tokens
这里每个模型的输出 token 价格都是输入的四倍(M2.7 为 $1.20 对 $0.30),所以 max_tokens 是最主要的调节手段。每次调用都把它设为该功能实际需要的最长回答:分类器很少需要超过 50,代码生成器可能需要几千。模型因达到上限而停止时,finish_reason 为 length,你可以检测并处理,而不是默默接受被截断的回答。
Prompt 缓存
三个模型的缓存读取均为每百万 tokens $0.06(只有 M3 的 512K 到 1M 档升至 $0.12),是 $0.30 输入价的五分之一。缓存写入为 $0.375,所以一个前缀只要复用一次,花费就已经低于不走缓存发送两次:$0.375 + $0.06 对比 2 × $0.30。要享受缓存带来的节省:
- 稳定的内容放在前面:系统提示、工具定义、few-shot 示例、参考文档。
- 变化的内容放在后面:用户消息和每次请求检索到的片段。
- 让稳定前缀在不同请求之间逐字节一致。系统提示里的时间戳或请求 ID 会让缓存失效。
- 在 Agent 循环中,往同一份消息历史后面追加,而不是换一种顺序重建。
对比重复请求返回的 usage 数据和价格表,确认缓存对你自己工作负载的实际效果。
错误处理与重试
接口返回 OpenAI 风格的错误体,其中带有 code 字段,两个 SDK 都会在抛出的错误对象上提供这个字段。需要显式处理的情况如下:
| 状态码 | 错误码 | 含义 | 处理方式 |
|---|---|---|---|
| 401 | — | API Key 缺失或无效 | 修正 Authorization 请求头,不要重试。 |
| 403 | insufficient_user_quota | 预付余额已用完 | 在控制台充值,不要重试。 |
| 429 | — | 触发速率限制 | 指数退避后重试。 |
| 不固定 | model_not_found | 模型 ID 拼写错误,或该 Key 未启用此模型 | 对照上表检查 ID。按错误码匹配,而不是按状态码。 |
| 5xx / 网络错误 | — | 临时故障 | 退避后重试。 |
两个 SDK 都会自动重试连接错误、429 和 5xx,由 Python 的 max_retries 和 Node.js 的 maxRetries 控制。model_not_found 错误可能带着 5xx 状态码返回,所以默认配置的 SDK 会把一个拼写错误重试好几次才抛出来。下面的示例关闭了内置重试,并先检查错误码。失败的请求从不计费,所以重试只花时间。在工具调用循环中,只重试 API 调用,不要重试本地工具的执行,以免一次临时错误让有副作用的函数执行两次。
Python
import time
from openai import (
OpenAI, APIConnectionError, APIStatusError,
AuthenticationError, PermissionDeniedError, RateLimitError,
)
client = OpenAI(
base_url="https://yiduochan.com/v1",
api_key="YOUR_API_KEY",
max_retries=0, # retried below so the error code is checked first
)
def ask(prompt, model="MiniMax-M2.7", attempts=4):
for attempt in range(attempts):
try:
return client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
max_tokens=400,
)
except AuthenticationError:
raise # 401: fix the key
except PermissionDeniedError as e:
if e.code == "insufficient_user_quota":
raise RuntimeError("balance exhausted, top up") from e
raise
except RateLimitError:
pass # 429: back off below
except APIStatusError as e:
if e.code == "model_not_found":
raise ValueError(f"unknown model: {model}") from e
if e.status_code < 500:
raise # other 4xx: not transient
except APIConnectionError:
pass # network: back off below
time.sleep(2 ** attempt)
raise RuntimeError("gave up after retries")
Node.js
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://yiduochan.com/v1",
apiKey: "YOUR_API_KEY",
maxRetries: 0, // retried below so the error code is checked first
});
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function ask(prompt, model = "MiniMax-M2.7", attempts = 4) {
for (let attempt = 0; attempt < attempts; attempt++) {
try {
return await client.chat.completions.create({
model,
messages: [{ role: "user", content: prompt }],
max_tokens: 400,
});
} catch (err) {
if (err instanceof OpenAI.APIError) {
if (err.code === "model_not_found") throw new Error(`unknown model: ${model}`);
if (err.status === 401) throw new Error("invalid API key");
if (err.status === 403 && err.code === "insufficient_user_quota") {
throw new Error("balance exhausted, top up");
}
if (err.status !== 429 && err.status < 500) throw err; // other 4xx
}
await sleep(1000 * 2 ** attempt); // 429, 5xx or network
}
}
throw new Error("gave up after retries");
}
对应的 curl 请求
上面的每个 SDK 调用都对应一个 HTTP 请求。可以用下面的命令验证 Key,或者在终端里调试。
curl https://yiduochan.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MiniMax-M2.7",
"messages": [{"role": "user", "content": "Say hello in one sentence."}],
"max_tokens": 50
}'
在请求体中加上 "stream": true,就会收到 server-sent events 事件流,而不是单个 JSON 对象。响应体(包括 usage 对象和任何错误)与 SDK 解析的内容逐字节一致,所以 SDK 抛出异常时,用 curl 是查看网关究竟返回了什么的最快方法。列出你的 Key 可用的模型:
curl https://yiduochan.com/v1/models \
-H "Authorization: Bearer YOUR_API_KEY"
Chat Completions 之外
同一个 Key 也可以通过 /v1/messages 以 Anthropic Messages 格式调用。设置 ANTHROPIC_BASE_URL=https://yiduochan.com、ANTHROPIC_AUTH_TOKEN=<API key>,以及 ANTHROPIC_MODEL=MiniMax-M2.7 或 MiniMax-M3;该接口不提供 count_tokens。Claude Code 的配置见 Claude Code 页面。通过 OpenAI 兼容的 /v1/audio/speech 接口做语音合成,见 speech-2.8 页面。
计费要点
- 预付美元额度,按量付费,无订阅。
- 最低充值 $5;预设档位有 $5、$10、$20、$50、$100、$200、$500,也可以自定义金额。
- 额度有效期 12 个月。
- 失败的请求从不计费。
接入或计费问题请联系 support@yiduochan.com。
常见问题
MiniMax API 能用官方的 OpenAI Python 和 Node.js SDK 调用吗?
能。把客户端指向 https://yiduochan.com/v1,传入你的 YiduoChan API Key,模型 ID 使用 MiniMax-M2.7、MiniMax-M2.7-highspeed 或 MiniMax-M3 即可,代码其他部分不需要改动。
通过 OpenAI SDK 调用 MiniMax 支持流式输出和函数调用吗?
支持。stream、tools 和 tool_choice 参数原样透传,工具调用循环与任何 OpenAI 兼容模型相同:在本地执行返回的 tool_calls,把结果作为 tool 消息追加进去,再次调用 API。
MiniMax API 每百万 tokens 多少钱?
MiniMax-M2.7 每百万 tokens 输入 $0.30、输出 $1.20;MiniMax-M2.7-highspeed 为 $0.60 和 $2.40;MiniMax-M3 在提示不超过 512K tokens 时与 M2.7 同价,超过后价格翻倍。完整价格见定价页。
用 OpenAI SDK 调用时如何降低 MiniMax API 的成本?
按功能的实际需要为每次调用设置 max_tokens,因为输出价格是输入的四倍;同时保持提示前缀稳定,让重复的 tokens 按每百万 $0.06 的缓存读取价计费,而不是 $0.30 的输入价。
model_not_found 错误是什么意思?
请求中的模型与 MiniMax-M2.7、MiniMax-M2.7-highspeed 或 MiniMax-M3 不完全一致,或者该模型没有为你的 Key 启用。请检查拼写和大小写;处理时按错误码而不是 HTTP 状态码匹配,因为状态码不一定是 404。
在 YiduoChan 使用 MiniMax API 需要订阅吗?有免费额度吗?
新账号注册即送 $0.10 试用额度,够发几次测试请求;正式使用需预付充值,最低 $5。不需要订阅:额度为预付美元,按 token 扣费,有效期 12 个月,失败的请求从不计费。在 /register 创建账号。