BUZZ AI Gateway

用 OpenAI SDK 调用 Claude(以及 Gemini、Grok)

一个 SDK,一个 base_url。OpenAI 兼容路径让你现有的 Python 代码不重写就能打到 Anthropic、Google、xAI 的模型。

OpenAI SDK Claude Gemini Grok chat.completions Python Streaming Tool Use

作者:BUZZ AI Gateway 工程团队 · 阅读约 11 分钟

OpenAI Python SDK 是 LLM 这一行最接近"普通话"的客户端。它不是表达能力最强的那一个,也不是和每家上游都能完美对齐的那一个,但它几乎一定就是你代码库、你的依赖、你的链路追踪和你的重试策略已经会说的那一种语言。真正值得讨论的不是"要不要用它",而是"模型策略不再只盯着 OpenAI 之后,你能不能继续用它"。

结论是能。用 BUZZ AI Gateway,同一个 openai 客户端可以在同一行代码里调 gpt-5claude-opus-4-8、Gemini 或 Grok。改的字符串只有两个:base_urlmodel。这篇文章讲为什么这条路能走通、它的代价是什么,以及哪些边角它不再无缝。

1. 为什么大家会用 OpenAI SDK 去调 Claude

维护过对接两个以上模型家族的服务的人都知道,真正的摩擦不在那次 API 调用本身,而在调用周围的一切。

OpenAI 那套基础设施你已经搭好了。2023 年之后写的、和 LLM 沾边的代码库,大多有一行 from openai import OpenAI。某个角落里一定有一个薄薄的封装层在管 timeout,挂着配好 TLS 的 HTTP client,注册着 tracer,带着懂得在 429 上退避的重试策略。每多一个 SDK,你要么再复刻一份这套封装,要么抽一层更高的抽象 —— 都是真实的工程成本,而且这成本不会推动产品向前。

上层框架已经认它了。LangChain 的 ChatOpenAI、LlamaIndex 的 OpenAI 集成、Vercel AI SDK 的 openai provider,以及大多数 agent 框架,都接受 base_url 覆盖。把它们指到一个 OpenAI 兼容端点,改的是配置文件里的一行;换成另一家原生 SDK,改的是一个 feature 分支。

可观测性栈是按它的形状校准的。OpenTelemetry 的 GenAI 语义约定围绕 OpenAI 的形状写。Langfuse、Helicone、Phoenix,以及大多数自研日志中间件,都在解析 chat.completions 的请求体和响应体。线协议保持不变,你的看板就保持不变。

心智模型是统一的。半年后翻代码的工程师不需要记住哪个 SDK 用 messages、哪个用 contents,也不需要记住 system 是顶层字段还是 messages 列表里的第一条。client.chat.completions.create(model="...", messages=[...]) 一句话就够。

这并不是说 Anthropic SDK 不好。这是说,对绝大多数应用层代码,"一个 SDK 通吃所有模型家族"的价值高于"为某一家拿到原生人体工学"的价值。

2. /v1 上的 OpenAI 兼容适配器

BUZZ 在同一把 key 上同时暴露两套协议:

/v1 端点接收 OpenAI 形状的请求,根据 model 字段分发到对应上游,再把响应翻译回 chat.completions 格式。整个翻译过程是机械的:messages 映射成 Anthropic 的 content blocks 或 Gemini 的 contents,toolstool_choice 映射成 Anthropic 的 tool_use 或 Gemini 的 functionDeclarations,流式 chunk 映射成 OpenAI 的 delta 事件,finish reason 映射成 stop / length / tool_calls。用户能看到的内容文本一字不动。

透明转发。适配器不修改你的 system prompt、不注入指令、不偷换模型、不缓冲流式响应。它只做协议翻译,别的什么都不做。在线模型清单见 https://buzzai.cc/models

3. 实战:用 OpenAI SDK 调 Claude

装常规的 OpenAI 客户端就行,BUZZ 没有任何自定义包要装。

pip install openai

用你的 BUZZ key 和网关的 OpenAI 兼容 base URL 配置 client。

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_BUZZ_KEY",
    base_url="https://buzzai.cc/v1",
)

resp = client.chat.completions.create(
    model="claude-opus-4-8",
    messages=[
        {"role": "system", "content": "You are a precise technical writer."},
        {"role": "user", "content": "Explain content-addressable storage in three sentences."},
    ],
    temperature=0.3,
    max_tokens=400,
)

print(resp.choices[0].message.content)

这就是全部对接。模型是 Claude,SDK 是 OpenAI 的,响应对象是 ChatCompletion,choicesmessagefinish_reasonusage 都在你预期的位置。

Streaming

流式调用用的开关和迭代方式,跟真实 OpenAI 调用完全一样。token 通过每个 chunk 的 delta.content 到达。

stream = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Write a haiku about cold storage."}],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)

网关把上游的 SSE 事件按到达顺序原样下发。token 不缓冲、不重写。如果你有直接消费 iter_lines 的封装,也能继续工作 —— SSE 帧结构是保留的。

用 OpenAI tools schema 做 Tool Use

按 OpenAI 的形状定义 tools。网关在出向把它们映射到 Anthropic 的 tool_use 块,再把模型的工具选择回译成 OpenAI 的 tool_calls 进来。

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"},
                "units": {"type": "string", "enum": ["c", "f"]},
            },
            "required": ["city"],
        },
    },
}]

resp = client.chat.completions.create(
    model="claude-opus-4-8",
    messages=[{"role": "user", "content": "What is the weather in Tokyo right now?"}],
    tools=tools,
    tool_choice="auto",
)

call = resp.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments)

如果你已经有一段 OpenAI tool calling 循环 —— 处理 tool_calls、解析参数、跑函数、再把 {"role": "tool", "tool_call_id": ...} 消息塞回对话 —— 这段循环对接到 Claude 上能直接继续跑,一行不用动。

切到 Gemini 或 Grok

切换到不同家族就是改一个 model 名字。同一个 client、同一段代码、同一套重试封装。

# 同一个 client,只换 model
gemini_resp = client.chat.completions.create(
    model="gemini-2.5-pro",
    messages=[{"role": "user", "content": "Summarize this 200-page PDF transcript..."}],
)

grok_resp = client.chat.completions.create(
    model="grok-4",
    messages=[{"role": "user", "content": "What happened in markets this week?"}],
)

支持的模型标识符以及它们的实时 token 单价,看在线列表:https://buzzai.cc/modelshttps://buzzai.cc/api/pricing

4. 相对直连 OpenAI,有哪些变化

调用现场几乎什么都不变,差异是手术刀级别的。

关注点直连 OpenAIBUZZ /v1
base_urlhttps://api.openai.com/v1(默认)https://buzzai.cc/v1
api_keyOpenAI keyBUZZ key(一把通用)
model 命名空间仅 OpenAI 家族Claude、GPT、Gemini、Grok
请求形状chat.completionschat.completions
响应形状ChatCompletionChatCompletion
StreamingSSE deltaSSE delta
Tool callingtools + tool_callstools + tool_calls
重试 / 追踪 / 日志你现有的封装你现有的封装,原样

这种迁移通常一个 config diff 就能搞定。如果 client 的构造是集中管理的,那就是一个文件;如果不是,那本来就是一笔你早该做的重构。

采样参数

常用参数(temperaturetop_pmax_tokensstop、上游支持时的 seed、上游支持时的 presence_penaltyfrequency_penalty)都按最近似的语义转发到对应上游。行为以上游模型为准,不是 OpenAI。temperature=0.7 在 Claude 上的分布,和 temperature=0.7 在 GPT-5 上不一样 —— 不管你是走网关还是直连都一样。调参要按模型分别调,而不是按数字调。

5. 哪些功能没法干净映射

chat.completions 这套 schema 是公约数 —— 大多数应用代码只需要这一层表面。但有几个特性不在这个交集里。

Anthropic extended thinking。Claude 可以在答案旁边返回一个 thinking 块。OpenAI 响应里没有规范位置放这个块,所以 /v1 适配器不会暴露它。如果你的应用真的要消费 thinking 轨迹,直接用 Anthropic SDK 走 https://buzzai.cc,读结构化响应。

Anthropic Prompt Cache。具体 content block 上的 cache_control 是 Anthropic 原生概念。OpenAI 请求形状里没有放它的字段。要在 Claude 上用 Prompt Cache,用 Anthropic SDK 打到同一个网关即可。

Gemini 专属的安全 / grounding 控制。分类粒度的安全阈值、grounding-with-search 之类的 Gemini-only 旋钮,在 chat.completions 里没有等价物,只能走原生 Google 协议。

上游原生流式事件。Anthropic 会发出有类型的事件,例如 content_block_startcontent_block_deltamessage_delta。OpenAI 适配器把它们压平成 chat.completion.chunk 的 delta。token 文本被保留,有类型的结构没有。如果你确实需要 Anthropic 的事件分类(比如把 thinking 块和最终答案分开渲染),用 Anthropic SDK。

务实的做法是:应用代码默认用 OpenAI SDK,只在少数确实需要 Claude 原生特性的地方,加一个 Anthropic SDK 作为第二个 client —— 用同一把 key、同一个网关。pip install anthropic,base_url="https://buzzai.cc",完事。

from anthropic import Anthropic

ant = Anthropic(
    api_key="YOUR_BUZZ_KEY",
    base_url="https://buzzai.cc",
)

msg = ant.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Plan a refactor of this module..."}],
    extra_body={"thinking": {"type": "enabled", "budget_tokens": 8000}},
)

同一把 key,同一个网关,只在需要的时候换协议。

6. 关于成本

这套配置有一个不太一样的属性:OpenAI 兼容适配器并不是套在 OpenAI 上的壳。它是一个能分发到任何受支持家族的统一壳,而且按 BUZZ 的费率计费,不是一手官方费率。昨天还指着 https://api.openai.com/v1gpt-5 的脚本,把 URL 改成 https://buzzai.cc/v1 之后,跑同一个 prompt 的成本会明显更低。代码里除了 URL 之外不用动。

BUZZ 不公布一个固定折扣百分比,因为不同模型、不同 token 配比下省下来的额度不一样。最实在的答案是看实时数字 —— 所有受支持家族(Claude、GPT、Gemini、Grok)的全部变体的实时单价都在 https://buzzai.cc/api/pricing。把它和上游官方价目页对一遍,乘以你每月 token 体量,Excel 就会给你一个真实数字。

零数据留存。prompt 和 completion 不写盘、不写库、不进日志。计费只保留 token 计数和模型标识。协议桥跑在内存里,响应一结束 payload 就消失。

同一把 key 在 Claude Code 上

如果你也用 Claude Code 当终端编程助手,同一把 BUZZ key 可以在那边直接用。一行命令把 CLI 指到网关:

curl -fsSL https://buzzai.cc/sh/claudecode.sh | bash

应用代码通过 OpenAI SDK 打 https://buzzai.cc/v1,Claude Code 通过 Anthropic 协议打 https://buzzai.cc,账单都落到同一把 key、同一个看板。

7. FAQ

真的能用官方 OpenAI Python SDK 直接调 Claude 吗?

能。设 base_url="https://buzzai.cc/v1",把 api_key 填成 BUZZ key,给 client.chat.completions.createmodel="claude-opus-4-8"(或任何其他支持的模型标识)。SDK 不需要知道上游是 Anthropic。网关替你在 chat.completions 和 Anthropic Messages 之间双向翻译。

Streaming 还能正常用吗?

能。传 stream=True。网关按 OpenAI 的 chunk 格式下发 SSE delta。遍历响应拿到的 chunk.choices[0].delta.content 和真实 OpenAI 流一模一样,网关侧零缓冲。

Tool Calling / Function Calling 怎么办?

走标准 OpenAI 的 toolstool_choice 参数。网关出向把它们映射到 Anthropic 的 tool_use 块或 Gemini 的 functionDeclarations,入向把上游的工具选择回译成 OpenAI 的 tool_calls。已有的 tool calling 循环原样工作。

重试、日志、链路追踪要改吗?

不用。包在 OpenAI 客户端外面的所有东西 —— LangChain、LlamaIndex、OpenTelemetry instrumentation、retry 装饰器、请求日志中间件 —— 全部原样工作。只有 base_url 和 model 名字变了。

temperaturemax_tokens 这些采样参数会被尊重吗?

标准 chat.completions 参数(temperaturetop_pmax_tokensstoppresence_penaltyfrequency_penalty、上游支持的 seed)按最近似语义转发。行为以上游模型为准,不是 OpenAI 语义,所以同一个数值在不同家族下分布可能不同。按模型调参,不要按数字调。

OpenAI 兼容路径下哪些功能没法干净映射?

chat.completions 里没有规范位置的上游原生特性:Anthropic 的 extended_thinking 块、Anthropic 的 prompt cache 控制、Gemini 专属的安全和 grounding 控制,以及上游有类型的流式事件。需要这些时,用 Anthropic SDK 打到 https://buzzai.cc。同一把 key 在两套协议上都有效。

跨家族的模型名怎么写?

model 字段就是路由 key。claude-opus-4-8claude-sonnet-4-6claude-haiku-4-5gpt-5gpt-5.5gpt-5.4gpt-5.4-mini,以及当下的 Gemini 和 Grok 名字都能正确路由。完整在线列表见 https://buzzai.cc/models

价格相对直连 OpenAI 怎么样?

BUZZ 在所有支持的家族上都明显低于一手官方价。因为只有 base_url 改了,代码路径不变,同一份代码就更便宜。各模型实时单价见 https://buzzai.cc/api/pricing

同一把 key 能在 Claude Code 上用吗?

能。curl -fsSL https://buzzai.cc/sh/claudecode.sh | bash 装好预配置过的 Claude Code。CLI 走 Anthropic 协议打到 https://buzzai.cc,和 OpenAI SDK 用 https://buzzai.cc/v1 共享同一把 key。

这是真透明转发,还是网关偷偷改了 prompt?

BUZZ 透明转发请求体和响应体。不修改 system prompt、不注入指令、不偷换模型。唯一的转换是 chat.completions 与上游原生协议之间的协议桥,只动结构,不动内容。

8. 结论

OpenAI Python SDK 已经赢下了应用层 LLM 代码的"普通话"之争 —— 这没什么不好。生产代码大多数事情只要发消息、流 token、调工具、暴露用量,它够用了。错误的姿势是把它当成一份把你和某一个模型家族绑死的合约。一旦有一个网关在入向说同一种协议、在出向按你写的 model 名字分发,SDK 就退化成传输细节,模型名字才是选择本身。

把 OpenAI SDK 指到 https://buzzai.cc/v1,你换来的是:一行配置打通所有主流模型家族;成本明显更低;零数据留存;Streaming 和 Tool Use 全部保真;现有重试、日志、追踪封装一个不动。哪天某个 Claude 原生特性(比如 extended thinking)真的需要时,Anthropic SDK 就在同一把 key、同一个网关上,多一个依赖而已。无聊的部分继续无聊 —— 这才是基础设施工作唯一诚实的目标。

把 URL 丢进 client 构造器,改一下 model 名字,跑一遍现有测试。这种迁移要的是一杯咖啡,不是一个 sprint。


发布时间:2026-05-22 · 最近修订:2026-05-22