跳到主要内容
AIRouter
关于我们
登录免费注册
AIRouter

统一接入全球大模型,按量计费,无最低消费。

support@airouter.hk

产品

  • 模型广场
  • 计费口径
  • 免费开始

开发者

  • 文档
  • 快速开始
  • 服务状态
  • API Key 管理

公司

  • 关于我们
  • 服务条款
  • 隐私政策

支持

  • 帮助中心
  • 登录
  • 进入控制台

© 2026 AIRouter. 保留所有权利。AIRouter 是模型聚合与路由服务,模型能力由各供应商提供。

所有系统运行正常

开发者文档

文档目录

入门

  • 平台介绍
  • 快速开始
  • 鉴权

API 参考

  • 对话补全
  • 图像生成
  • 文本向量化
  • 重排
  • 视频生成
  • 搜索与阅读
  • 模型与路由

平台机制

  • 计费口径
  • 错误码与排查

入门

  • 平台介绍
  • 快速开始
  • 鉴权

API 参考

  • 对话补全
  • 图像生成
  • 文本向量化
  • 重排
  • 视频生成
  • 搜索与阅读
  • 模型与路由

平台机制

  • 计费口径
  • 错误码与排查

对话补全

POST /v1/chat/completions —— 发一段对话过去,模型接着往下写一句。

先理解这个接口#

对话补全接口做的事只有一件:你把整段对话发过去,模型接着往下写一句,然后返回。它没有记忆——服务端不保存任何上下文,第二轮要接着聊,就得把前面所有轮次连同模型上一次的回答一起再发一遍。

对话内容放在 messages 数组里,按时间顺序排列。每条消息有 role 和 content 两个必填字段,role 表明这句话是谁说的。

`role`这句话是谁说的用来做什么
system你给模型的设定定角色、定语气、定规则。通常放在数组第一条,且只有一条
user终端用户实际的提问或指令
assistant模型上一轮的回答多轮对话时由你把模型的上一次输出原样放回来,模型据此知道自己说过什么
tool工具执行结果模型要求调用函数后,你把执行结果用这个角色回填

一个三轮对话的 messages:注意 assistant 那条是模型之前说的,由你带回来

json
[  { "role": "system", "content": "你是一名简洁的技术支持助理,只用中文回答。" },  { "role": "user", "content": "我的订单还能退款吗?" },  { "role": "assistant", "content": "可以的,支付后 7 天内都能申请全额退款。" },  { "role": "user", "content": "那退到账要多久?" }]
上下文是你自己维护的
每一轮都要把完整历史重发,所以请求会越来越长,费用也随轮次增长——token 是按每次请求的全量输入计的。长对话建议只保留最近若干轮,或先做一次摘要再继续。

第一次调用#

下面三段代码做同一件事:问一个问题,打印模型的回答。

curl:最小请求

bash
curl https://ai.oceango.hk/v1/chat/completions \  -H "Authorization: Bearer $AIROUTER_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "model": "openai/gpt-5.2",    "messages": [      { "role": "user", "content": "用一句话解释什么是向量数据库" }    ]  }'

响应:回答在 choices[0].message.content 里

json
{  "id": "chatcmpl-abc123",  "object": "chat.completion",  "created": 1754126400,  "model": "openai/gpt-5.2",  "choices": [    {      "index": 0,      "message": {        "role": "assistant",        "content": "向量数据库是一种按语义相似度而不是精确匹配来检索数据的存储系统。"      },      "finish_reason": "stop"    }  ],  "usage": {    "prompt_tokens": 21,    "completion_tokens": 28,    "total_tokens": 49  }}

finish_reason 说明模型为什么停下:stop 是正常说完,length 是撞上了 max_tokens 上限(回答被截断了,多半要调大重来),tool_calls 是模型要求你去调工具。

Python:用官方 openai 库,改 base_url 即可

python
import osfrom openai import OpenAI
client = OpenAI(    api_key=os.environ["AIROUTER_API_KEY"],    base_url="https://ai.oceango.hk/v1",)
response = client.chat.completions.create(    model="openai/gpt-5.2",    messages=[        {"role": "system", "content": "你是一名简洁的技术写作者。"},        {"role": "user", "content": "用一句话解释什么是向量数据库"},    ],)
print(response.choices[0].message.content)print(response.usage.total_tokens, "tokens")

Node.js:同样只改 baseURL

javascript
import OpenAI from 'openai';
const client = new OpenAI({  apiKey: process.env.AIROUTER_API_KEY,  baseURL: 'https://ai.oceango.hk/v1',});
const response = await client.chat.completions.create({  model: 'openai/gpt-5.2',  messages: [    { role: 'system', content: '你是一名简洁的技术写作者。' },    { role: 'user', content: '用一句话解释什么是向量数据库' },  ],});
console.log(response.choices[0].message.content);
为什么能直接用 openai 库
这个端点的请求与响应格式与业界最通用的对话补全格式一致,所以任何按该格式写的客户端都能直接指过来。你不需要为此了解 OpenAI 本身——把它当成一种数据格式约定即可。

请求字段#

下表逐条说明请求体认识的字段。控制随机性的采样参数(temperature、top_p 等)单独放在下一节。未列出的字段会原样转发给上游。

modelstring必填
模型名。支持别名、:变体 与逗号降级链,语法见 模型与路由。
messagesarray必填
整段对话,按时间顺序排列;角色与多轮写法见上面的"先理解这个接口"。content 允许为 null(工具调用回合的合法形态),会被规范化成空文本部分。
max_tokensinteger
不传时预扣按 0.6 × 模型最大输出 估算,见 计费口径。给了它能让冻结额更贴近实际。
streamboolean
true 时返回 text/event-stream,帧序列见下一节。
stream_optionsobject
传 {"include_usage": true} 才会在流的末尾多收到一帧用量统计;不传的话整条流里没有任何用量信息。
tools / tool_choicearray / string
工具调用,透传。parallel_tool_calls 也透传;Anthropic 上游用方向相反的 disable_parallel_tool_use 表达,网关会自动取反。
response_formatobject
结构化输出。是否可用取决于目标端点,可配合 provider.require_parameters 强制只选支持它的线路。
reasoningobject
跨方言统一的思考开关:enabled 打开思考,effort 取 minimal / low / medium / high,max_tokens 给思考预算,exclude 表示照样思考但不返回思考内容。出站时映射成上游各自的字段(Anthropic 的 thinking、OpenAI 的 reasoning_effort);模型不支持时以上游行为为准,要强制只走支持它的线路请配合 provider.require_parameters。
providerobject
路由偏好(本平台扩展):白名单、黑名单、排序、价格上限、区域与合规约束。见 模型与路由。
modelsarray
模型级降级链。它不是 model 里逗号写法的替代品,而是与之合并去重(顺序为 model 的逗号链 → models → provider.models);主模型加兜底最多 4 个,多出来的静默忽略。
transformsarray
为兼容 OpenRouter 保留的字段:能被收下并记录,但当前没有任何变换真的生效(middle-out 压缩尚未实现)。输入超窗请自己裁剪或换长上下文模型,别指望它兜住。

一个带路由约束的请求体

json
{  "model": "anthropic/claude-fable-5",  "messages": [{ "role": "user", "content": "总结这份会议记录" }],  "provider": {    "sort": "price",    "only": ["cl-sp", "cl-of"],    "allow_fallbacks": true,    "max_price": { "prompt": "5.00000000", "completion": "20.00000000" }  }}

采样参数#

模型每写一个词,实际是在一堆候选词上算概率再挑一个。下面这些参数控制"怎么挑",它们都是可选的,不传就用模型自己的默认值。

字段类型作用
temperaturenumber随机性。0 附近近乎确定(同样输入基本给同样输出),越大越发散。事实问答、代码生成用低值;创意写作用高值。这是最常调的一个
top_pnumber另一种收敛方式:只在累计概率达到 p 的那批候选词里挑。0.1 表示只考虑最可能的那一小撮。与 temperature 调一个就够,两个一起调很难推理出效果
top_kinteger只在概率最高的 k 个候选词里挑。并非所有上游都支持
max_tokensinteger这次最多生成多少 token,是硬上限。撞上了就截断,finish_reason 会是 length
stopstring 或 array遇到这些字符串就停止生成,且停止串本身不会出现在结果里。用来切断模型自问自答的续写
seedinteger尽力而为的可复现:同样的输入加同样的 seed 倾向于给同样的输出。上游不保证严格一致,别拿它当幂等键
frequency_penaltynumber按某个词已出现的次数递增地压低它再次出现的概率。用来治"车轱辘话"
presence_penaltynumber只要某个词出现过就压低它,与出现几次无关。用来推动模型换话题
repetition_penaltynumber开源模型常用的另一种复读抑制参数,作用与上面两个重叠
logit_biasobjecttoken 到偏置值的映射,直接抬高或压低特定 token 的概率。用来强制避开或倾向某些词
logprobsboolean返回每个输出 token 的对数概率,用于做置信度判断
并非每个参数在每条线路上都生效
同一个模型名在不同上游的实现里对参数的支持程度不同,不认识的参数通常被上游静默忽略而不是报错。要强制只走支持某参数的线路,用 provider.require_parameters,见模型与路由。

流式输出#

每一帧的格式是 data: <JSON>,JSON 结构为 chat.completion.chunk,流以 data: [DONE] 结束。[DONE] 不是合法 JSON,客户端必须先判断这个哨兵值再解析。

帧说明
角色帧choices[0].delta = {"role":"assistant"},只发一次。
内容增量delta.content 是文本片段;开启思考时会先发若干 delta.reasoning_content。
工具调用增量delta.tool_calls[].index 从 0 递增,同一个工具调用的 index 在整个流里稳定不变;function.arguments 是分片的 JSON 字符串,需按 index 拼接后再解析。
结束帧finish_reason 非空,delta 为空对象。
用量帧仅当 stream_options.include_usage 为 true:choices 为空数组且带 usage。
[DONE]结束哨兵。
流中错误必须逐帧检查
已经开始输出后才发生的错误无法再改 HTTP 状态码(它已经是 200)。此时网关会发一帧带 error 字段的数据、紧接着 [DONE]。客户端如果不检查每帧是否含 error,会把失败请求当成正常结束。

典型的一段流(: ping 是 SSE 注释行,用于防止中间层超时,标准解析器会忽略)

plain
data: {"choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}data: {"choices":[{"index":0,"delta":{"content":"维多利亚港的风"},"finish_reason":null}]}: pingdata: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}data: {"choices":[],"usage":{"prompt_tokens":26,"completion_tokens":48,"total_tokens":74}}data: [DONE]
上游没返回用量时,网关会用本地分词器估算补齐,usage 帧照样发——你的用量统计逻辑不必按上游分家。但注意估算标记不在响应体里:非流式看响应头 X-AiRouter-Usage-Estimated,流式只能在 调用日志 里看到(响应头此时已经发出去了)。

Python:逐字打印,帧里没有 content 的时候要跳过

python
import osfrom openai import OpenAI
client = OpenAI(api_key=os.environ["AIROUTER_API_KEY"], base_url="https://ai.oceango.hk/v1")
stream = client.chat.completions.create(    model="openai/gpt-5.2",    messages=[{"role": "user", "content": "写一首关于香港雨季的短诗"}],    stream=True,    stream_options={"include_usage": True},  # 不传这个就收不到用量帧)
for chunk in stream:    # 用量帧的 choices 是空数组,直接访问 [0] 会抛 IndexError    if not chunk.choices:        if chunk.usage:            print(f"\n\n共 {chunk.usage.total_tokens} tokens")        continue    delta = chunk.choices[0].delta.content    if delta:        print(delta, end="", flush=True)

Node.js:同样的循环

javascript
import OpenAI from 'openai';
const client = new OpenAI({  apiKey: process.env.AIROUTER_API_KEY,  baseURL: 'https://ai.oceango.hk/v1',});
const stream = await client.chat.completions.create({  model: 'openai/gpt-5.2',  messages: [{ role: 'user', content: '写一首关于香港雨季的短诗' }],  stream: true,  stream_options: { include_usage: true },});
for await (const chunk of stream) {  const delta = chunk.choices[0]?.delta?.content;  if (delta) process.stdout.write(delta);  if (chunk.usage) console.log(`\n\n共 ${chunk.usage.total_tokens} tokens`);}

用量口径#

  • 非流式响应总是带 usage;流式默认不带,需要显式传 stream_options.include_usage。
  • prompt_tokens 是全部输入 token,包含缓存读与缓存写部分;缓存读在 prompt_tokens_details.cached_tokens,缓存写在 cache_creation_input_tokens。按标准输入价计费的是三者相减后的余量。
  • completion_tokens 只含可见输出,不含思考 token;思考量在 completion_tokens_details.reasoning_tokens 里单独给出、单独计费。
  • 用量帧只报 token,不含金额。非流式的费用在响应头,流式的费用在 调用日志。

与 OpenAI 的已知差异#

字段差异
n原样透传给上游,网关不拦。是否真的返回多个候选取决于目标端点;计费按上游报回来的总用量算,所以 n 越大越贵。
logit_bias / logprobs取决于目标端点是否支持;不支持时按 provider.require_parameters 决定是忽略还是换一条线路。
service_tier / store按白名单透传给 OpenAI 兼容上游,我方不做任何处理。
metadata会记录到我方请求日志(app_id / app_title / project_id 会被识别出来),但不透传给上游。
/v1/completions提供,但只为存量 SDK 保留:prompt 会被包成一条 user 消息,响应按 legacy 形状返回(object 为 text_completion,正文在 choices[].text)。echo / suffix / best_of 与批量 prompt 一律报 invalid_request 而不是静默忽略——它们改变生成语义,忽略掉你会拿到一个不一样的结果,而且这次请求照样计费。新项目请直接用 /v1/chat/completions。
响应体多一个字段非流式响应带 provider,值是命中的分组 code(与 X-AiRouter-Group 一致)。OpenAI 没有这个字段,SDK 会忽略它。

Anthropic 方言#

除了上面那套格式,网关还提供 POST /v1/messages,它遵循 Anthropic 的 Messages 格式——那是另一套写法不同、能力相当的对话协议:system 不放在 messages 里而是独立的顶层字段,返回的正文是 content 数组而不是单个字符串。如果你现有代码用的是 Anthropic 官方 SDK,指过来就能跑;否则用上面的 /v1/chat/completions 即可,两条端点背后是同一批模型。

POST /v1/messages 与 Anthropic Messages API 兼容:model 一样支持别名、:变体 与逗号降级链,流式天然带用量(message_start / message_delta 里就有),不需要 include_usage。

curl:Anthropic 格式的同一个问题

bash
curl https://ai.oceango.hk/v1/messages \  -H "X-Api-Key: $AIROUTER_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "model": "anthropic/claude-fable-5",    "max_tokens": 256,    "system": "你是一名简洁的技术写作者。",    "messages": [      { "role": "user", "content": "用一句话解释什么是向量数据库" }    ]  }'
路由扩展字段在这条端点上不生效
provider / models / transforms 这些扩展只在 OpenAI 方言的请求体里被识别。写在 /v1/messages 上会被当成未知字段原样转发给上游,多半直接被上游 400。要用路由偏好就走 /v1/chat/completions,或者把降级链写进 model 的逗号形式。
  • anthropic-version 不用传:网关出站时自己填 2023-06-01。
  • anthropic-beta 目前不转发——出站请求头由网关重新构造,客户端的自定义头不会带过去。
  • 错误信封是 Anthropic 形状,没有 code 字段,分支要靠 HTTP 状态与 error.type,见 错误码。
上一篇鉴权下一篇图像生成

本页目录

  • 先理解这个接口
  • 第一次调用
  • 请求字段
  • 采样参数
  • 流式输出
  • 用量口径
  • 与 OpenAI 的已知差异
  • Anthropic 方言