Claude API 和 OpenAI API 有什么区别

Claude 的 messages 和 OpenAI 的 chat/completions 看着像,实际有 6 处硬性差异:system 位置、max_tokens 必填、content 块数组、工具调用结构、多模态格式、流式事件名。这篇逐项对照并给出同一需求的两套写法,附迁移检查表。

很多团队先用 OpenAI SDK 跑通了业务,后来想加 Claude 做二线模型或代码任务,第一反应是「把 model 改一下就行」。多数情况下能跑,但只要用到多轮工具调用、thinking、多模态或流式解析,就会撞上一连串结构差异。原因是两家协议长得像,但设计思路不同:OpenAI 的 chat/completions 是消息数组驱动,Anthropic 的 messages 是块(block)数组驱动。

这篇把差异逐项列出来,给同一需求的两套写法,最后给一张迁移检查表。

先说结论

  • 只做纯文本单轮对话:两家几乎可以互换,改 model 即可
  • 一旦用到 system、max_tokens、工具调用、多模态、流式,就必须按协议分别写,不能混用字段
  • Anthropic 协议里 max_tokens 必填,system 是顶层参数不在 messages 里,这两点最容易漏
  • 响应体的 content 在 Anthropic 是块数组(text / thinking / tool_use),不是字符串
  • 想一个 Key 同时调两类模型,走兼容网关最省事:OpenAI 协议端点和 Claude 原生端点并存,凭证只维护一份

差异一:system 提示词的位置

OpenAI 把 system 当成一种消息角色塞进数组;Anthropic 把它提成顶层参数,messages 里只允许 user 和 assistant。

# OpenAI 协议
resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "你是严谨的技术助手。"},
        {"role": "user", "content": "解释一下幂等性"},
    ],
)
# Anthropic 协议
message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    system="你是严谨的技术助手。",
    messages=[
        {"role": "user", "content": "解释一下幂等性"},
    ],
)

如果把 {"role": "system"} 写进 Anthropic 的 messages,会直接报 400。反过来,把 Anthropic 的 system= 参数塞给 OpenAI SDK,SDK 会报参数校验错误。

差异二:max_tokens 必填

OpenAI 不传 max_tokens 时会用模型默认上限;Anthropic 要求显式声明,否则 400。

这一条看着是小事,实际影响成本控制——不传就是赌默认值,而 claude-opus-5-thinking 这类模型的默认输出上限很高,一个失控的长回复能吃掉不少额度。建议两边都养成显式设置的习惯。

差异三:响应的 content 是块数组

# OpenAI:字符串或 null
text = resp.choices[0].message.content

# Anthropic:块数组,要按 type 挑
text = ""
for block in resp.content:
    if block.type == "text":
        text += block.text

为什么 Anthropic 要设计成数组?因为它要同时承载三种东西:正常文本(text)、推理过程(thinking)、工具调用请求(tool_use)。一个回复里可能三者并存。开启了 thinking 的模型,resp.content[0] 很可能不是文本块——直接取 [0].text 会抛异常,这是迁移时最常见的崩溃点。

差异四:工具调用的往返结构

OpenAI 用 tool_calls 字段 + role: "tool" 消息回传结果;Anthropic 用 tool_use 块 + tool_result 块。

# OpenAI:工具定义是 function 包一层
tools = [{
    "type": "function",
    "function": {
        "name": "get_order",
        "description": "按订单号查询订单状态",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    },
}]
# Anthropic:input_schema 直接放定义,没有 function 包裹
tools = [{
    "name": "get_order",
    "description": "按订单号查询订单状态",
    "input_schema": {
        "type": "object",
        "properties": {"order_id": {"type": "string"}},
        "required": ["order_id"],
    },
}]

回传结果的方式也不同:

环节OpenAI 协议Anthropic 协议
工具定义字段tools[].function.parameterstools[].input_schema
模型发起调用message.tool_callscontent 里的 tool_use 块
结果回传role: "tool" 消息tool_result 内容块
结果关联方式tool_call_idtool_use_id
是否允许并行调用支持,多个 tool_calls支持,多个 tool_use 块

如果业务已经在用 OpenAI 的 function calling 跑通了,迁到 Anthropic 时建议单独写一层适配器,把「工具定义」「调用解析」「结果回传」三个函数抽象出来,上层业务只认自己的内部结构。Function calling 的基础概念可以看 工具调用入门指南。

差异五:多模态输入的格式

OpenAI 用 image_url,Anthropic 用 source 里的 base64 或 URL:

# OpenAI
{"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}

# Anthropic
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "..."}}

Anthropic 要求显式声明 media_type,OpenAI 从 data URI 里自动推断。少写这一个字段会 400。

差异六:流式事件名

OpenAI 流式返回 data: {...} 的 chunk,以 data: [DONE] 结束;Anthropic 返回带 event: 行的结构化事件流(message_start、content_block_delta、message_stop 等)。

好消息是官方 SDK 都封装好了:OpenAI 用 for await 迭代 chunk,Anthropic 用 client.messages.stream() 上下文管理器取 text_stream。手写 SSE 解析时才需要关心这些事件名,原理可以看 SSE 流式输出从 curl 到前端。

差异七:thinking 与 reasoning 参数

thinking 类模型两家的开关方式完全不同:

# Anthropic:thinking 是独立对象,带 token 预算
resp = client.messages.create(
    model="claude-sonnet-5-thinking",
    max_tokens=16000,
    thinking={"type": "enabled", "budget_tokens": 8000},
    messages=[{"role": "user", "content": "9.11 和 9.9 哪个大?"}],
)

OpenAI 系的推理模型一般不暴露显式预算参数,靠模型名区分思考深度(如带 -high / -low 后缀的变体)。思考 token 是计费的,预算越大账单越高,这一点在两家都一样。

一个 Key 同时调两类模型

如果两种模型都要用,最笨的办法是维护两套 Key、两套额度和两套充值;比较务实的做法是走统一网关:OpenAI 兼容端点 https://api.zaimod.com/v1 覆盖绝大多数模型,Claude 原生端点 https://api.zaimod.com/v1/messages 保留 thinking、原生工具等全特性,凭证是同一把 Key。

import anthropic
from openai import OpenAI

# 同一个 Key,两套客户端
openai_client = OpenAI(api_key="sk-xxx", base_url="https://api.zaimod.com/v1")
claude_client = anthropic.Anthropic(api_key="sk-xxx", base_url="https://api.zaimod.com")

单价按模型分别计价,default 分组倍率 ×1.0。claude-sonnet-5 站内挂单价为 $4 / $20 每百万 tokens(输入/输出),gpt-4o-mini 为 $0.156 / $0.624,都在 价格速查 可查。计费公式与分组倍率写在 计费规则文档 里:官转分组 ×1.5,官方直连渠道稳定性更高、价格也更高。

迁移检查表

从 OpenAI 协议迁到 Anthropic 协议时,按这个顺序过一遍,基本能一次跑通:

检查项常见错误写法正确写法
system 位置messages 里放 role: system顶层 system 参数
max_tokens省略不传显式声明
响应取值resp.choices[0].message.content遍历 resp.content 找 text 块
工具定义function 包一层input_schema 直挂
工具结果回传role: tool 消息tool_result 内容块
图片image_urlimage + source + media_type
流式手动解析 [DONE]client.messages.stream

反过来从 Anthropic 迁到 OpenAI 协议,把表左右两列对调即可,另外注意 OpenAI 的 content 可能是 null(只返回工具调用时),必须做空值处理。

常见问题

Q:只改 model 名能跑通吗?

纯文本单轮对话多数能跑通,但 max_tokens 和 system 这两项会立刻报错。如果你用的是 OpenAI SDK 调 Claude 模型(走兼容端点),那实际上用的是 OpenAI 协议,不是 Anthropic 协议,两者不要混为一谈。

Q:哪家的输出更便宜?

不能一概而论,同名系列的档位差异很大。站内挂单价 claude-sonnet-5 是 $4 / $20,gpt-5.6-luna 是 $1 / $8,gemini-2.5-flash 是 $0.3 / $2.5,deepseek-v4-flash 是 $3 / $9(每百万 tokens,输入/输出)。按任务质量需求选型比单纯比价更划算,参考 2026 大模型 API 价格对比。

Q:thinking 会导致输出为空吗?

不会,但会出现 content[0] 是 thinking 块的情况。如果你的代码写死取 [0].text,就会读到错误内容或直接报错。正确做法是遍历块数组按 type 过滤。

Q:两种协议的错误码一致吗?

HTTP 状态码语义一致(401 Key 无效、404 模型名不存在、429 限流、5xx 上游异常),但错误体的 JSON 结构不同。别写死解析某一家字段,统一按状态码分流最稳,详见 报错速查。

延伸阅读

#claude api 和 openai 区别#anthropic messages 接口#claude api 调用方式#openai 兼容协议差异
免费创建 API Key

相关阅读

Function Calling 工具调用入门指南

让大模型查天气、下单、读数据库,靠的是 Function Calling 而不是更长的提示词。这篇讲清工具调用的完整循环:定义 tools、模型输出 tool_calls、本地执行、结果回传,附可直接运行的 Python 代码与常见坑。

大模型 API 429 报错:原因、重试与并发调优

429 不一定是「你请求太多了」。这篇拆开 RPM、TPM、并发数和免费档限流四种成因,给出带抖动的退避重试实现、并发池写法与降级链路,附监控指标清单。

流式输出 SSE 原理与调试:从 curl 到前端

大模型 API 的流式输出本质是一条 SSE 长连接。本文拆解 text/event-stream 报文格式、stream:true 的分片结构、curl 与前端 ReadableStream 的调试方法,以及代理缓冲、[DONE]、usage 丢失等高频坑位。