一文打通 OpenAI 与 Anthropic 双接口:ZBStream API 实战指南
同一个 ZBStream API Key,既可以说 OpenAI 方言(/v1/chat/completions),也可以说 Anthropic 方言(/v1/messages)。本文用 Python、Node.js、Go 三种语言把两种协议各跑一遍,并给出选型建议。
With one ZBStream API key you can speak both dialects — OpenAI's /v1/chat/completions and Anthropic's /v1/messages. This guide runs both protocols in Python, Node.js, and Go, then helps you pick the right one.
1为什么网关要同时支持两种协议 Why a gateway should speak both protocols
过去几年,OpenAI 的 Chat Completions 成了事实标准,绝大多数 SDK、框架和 Agent 工具默认讲这套协议。但 Claude 系列模型带火了另一套风格:system 是顶层字段、max_tokens 必填、响应是 content 块数组、流式事件更结构化。
如果你的技术栈里同时有两类组件——比如 Agent 框架走 Anthropic SDK,数据分析脚本走 OpenAI SDK——传统做法是分别向两家供应商申请 Key、分别管理额度。而通过 ZBStream,两套 SDK 指向同一个地址、共用一个积分账户:
https://api.zbstream.com/v1openai-python / openai-node / go-openai 直接可用
https://api.zbstream.com官方 anthropic SDK 只改 Base URL 即可用
2准备工作 Prerequisites
登录 ZBStream 控制台,在 API Keys 页面创建密钥,并确认账户有足够积分。模型名称通过 GET /v1/models 获取,注意大小写敏感。下文示例统一使用 deepseek-v4-pro 与环境变量 ZBSTREAM_API_KEY。
export ZBSTREAM_API_KEY="sk-你的密钥" curl https://api.zbstream.com/v1/models \ -H "Authorization: Bearer $ZBSTREAM_API_KEY"
3OpenAI 兼容接口:三语言实战 The OpenAI-compatible API in three languages
所有请求发往 https://api.zbstream.com/v1/chat/completions,使用 Bearer 鉴权。响应结构与 OpenAI 完全一致,读取 choices[0].message.content 即可。
Python
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["ZBSTREAM_API_KEY"],
base_url="https://api.zbstream.com/v1",
)
resp = client.chat.completions.create(
model="deepseek-v4-pro",
messages=[{"role": "user", "content": "用一句话解释什么是 LLM 网关。"}],
)
print(resp.choices[0].message.content)
Node.js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.ZBSTREAM_API_KEY,
baseURL: "https://api.zbstream.com/v1",
});
const resp = await client.chat.completions.create({
model: "deepseek-v4-pro",
messages: [{ role: "user", content: "用一句话解释什么是 LLM 网关。" }],
});
console.log(resp.choices[0].message.content);
Go
config := openai.DefaultConfig(os.Getenv("ZBSTREAM_API_KEY"))
config.BaseURL = "https://api.zbstream.com/v1"
client := openai.NewClientWithConfig(config)
resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
Model: "deepseek-v4-pro",
Messages: []openai.ChatCompletionMessage{
{Role: openai.ChatMessageRoleUser, Content: "用一句话解释什么是 LLM 网关。"},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Choices[0].Message.Content)
4Anthropic 兼容接口:三语言实战 The Anthropic-compatible API in three languages
基础地址是 https://api.zbstream.com(不带 /v1,SDK 会自动拼接)。注意三个差异:max_tokens 必填、system 是顶层字段、回答在 content 数组里。
Python
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["ZBSTREAM_API_KEY"],
base_url="https://api.zbstream.com",
)
msg = client.messages.create(
model="deepseek-v4-pro",
max_tokens=1024,
system="你是一个简洁的技术助手。",
messages=[{"role": "user", "content": "用一句话解释什么是 LLM 网关。"}],
)
print(msg.content[0].text)
Node.js
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: process.env.ZBSTREAM_API_KEY,
baseURL: "https://api.zbstream.com",
});
const msg = await client.messages.create({
model: "deepseek-v4-pro",
max_tokens: 1024,
system: "你是一个简洁的技术助手。",
messages: [{ role: "user", content: "用一句话解释什么是 LLM 网关。" }],
});
console.log(msg.content[0].text);
Go
client := anthropic.NewClient(
os.Getenv("ZBSTREAM_API_KEY"),
option.WithBaseURL("https://api.zbstream.com"),
)
msg, err := client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.F("deepseek-v4-pro"),
MaxTokens: anthropic.F(int64(1024)),
Messages: anthropic.F([]anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("用一句话解释什么是 LLM 网关。")),
}),
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Content[0].Text)
5流式输出:一个参数的区别 Streaming: the one-parameter difference
两种协议都在请求体中传 "stream": true(或 SDK 的 stream=True),区别在事件格式:OpenAI 风格按 choices[].delta 增量推送并以 [DONE] 结束;Anthropic 风格按 message_start / content_block_delta / message_stop 事件推送。
stream = client.chat.completions.create(
model="deepseek-v4-pro",
messages=[{"role": "user", "content": "请逐步回答"}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="", flush=True)
with client.messages.stream(
model="deepseek-v4-pro",
max_tokens=1024,
messages=[{"role": "user", "content": "请逐步回答"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
6怎么选 Which one should you choose?
- 现有代码零改造:已经用 OpenAI SDK 的项目直接改 base_url,一分钟迁移完成。
- Agent / 工具调用密集型:Anthropic Messages 的工具调用与 content 块结构更适合多轮工具编排,且 Claude 生态的框架(如 Claude Code 类工具)默认讲这套协议。
- 混用完全没问题:两种协议共用同一个 Key 和积分账户,按各自模型费率计费,控制台的用量统计会把它们合并展示。
7常见坑 Common pitfalls
- 404:Anthropic 基础地址多写了
/v1,SDK 又拼了一次。 - 400:Messages 接口漏了必填的
max_tokens。 - 模型不存在:model 名称与
/v1/models返回值大小写不一致。 - 流式读取超时:反向代理的 proxy_read_timeout 小于模型生成时长,建议 ≥ 300 秒。