API FORMAT COMPARISON · REFERENCE

OpenAI vs Anthropic
HTTP 格式对比

两套 API 在端点、认证、请求参数、消息结构、响应解析、工具调用、流式传输、推理模式、多模态输入和多轮对话上的核心差异。 同一个意图,两种 JSON——理解差异,才能自由切换。

OPENAI /v1/chat/completions ANTHROPIC /v1/messages SECTIONS 10 SCOPE HTTP / JSON

总览对比

OVERVIEW
维度OpenAIAnthropic
端点POST /v1/chat/completionsPOST /v1/messages
认证方式Authorization: Bearer sk-...x-api-key: sk-ant-...
版本头—(无需版本头)anthropic-version: 2023-06-01
组织 / 项目头OpenAI-Organization / OpenAI-Project
System Prompt放在 messages 中,role: "system"独立顶层 system 字段(字符串或 block 数组)
max_tokens可选(有默认值)必填
temperature0 – 2(默认 1)0 – 1(默认 1)
top_k✗ 不支持✓ 支持
多候选(n)✓ 支持 n ≥ 1✗ 不支持
响应取值路径choices[0].message.contentcontent[0].text
内容格式content 为字符串(或工具调用时为 null)content 为 block 数组(text / thinking / tool_use)
Tool 定义 keytype:"function" 包裹 + parameters扁平结构 + input_schema
Tool 参数格式arguments(JSON 字符串)input(已解析 JSON 对象)
Tool 结果角色role: "tool"role: "user" + type: "tool_result"
tool_choice"auto" / "none" / "required" / 指定函数{type:"auto"} / {type:"any"} / {type:"tool"}
Thinking<think> 标签内嵌或 reasoning_split独立 thinking content block + budget_tokens
StreamingSSE data: {"choices":[{"delta":...}]}SSE event: content_block_delta
流结束标记data: [DONE]event: message_stop
Vision / 图片type: "image_url" + detailtype: "image" + source
错误格式{"error": {"message":..., "type":...}}{"type":"error", "error": {"type":..., "message":...}}
停止原因字段finish_reason(stop / length / tool_calls)stop_reason(end_turn / max_tokens / tool_use)
Token 计量prompt_tokens / completion_tokensinput_tokens / output_tokens

端点与认证

ENDPOINT
OpenAI
POST https://api.openai.com/v1/chat/completions

Content-Type: application/json
Authorization: Bearer sk-...
OpenAI-Organization: org-...   ← 可选
OpenAI-Project: proj_...        ← 可选
Anthropic
POST https://api.anthropic.com/v1/messages

Content-Type: application/json
x-api-key: sk-ant-api03-...
anthropic-version: 2023-06-01  ← 必填
anthropic-beta: ...             ← Beta 功能时使用

完整 curl 示例

OpenAI curl
curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4o",
    "messages": [
      {"role": "system", "content": "You are helpful."},
      {"role": "user", "content": "Hello!"}
    ]
  }'
Anthropic curl
curl https://api.anthropic.com/v1/messages \
  -H "Content-Type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-sonnet-4-20250514",
    "max_tokens": 1024,
    "system": "You are helpful.",
    "messages": [
      {"role": "user", "content": "Hello!"}
    ]
  }'
认证差异:OpenAI 使用标准 HTTP Authorization: Bearer 方案; Anthropic 使用自定义 x-api-key header,并要求 anthropic-version 版本头。

组织管理:OpenAI 通过可选的 OpenAI-OrganizationOpenAI-Project 头控制多组织/项目隔离。 Anthropic 目前无对应机制。

请求参数

PARAMETERS
参数OpenAIAnthropic
modelrequired "gpt-4o"required "claude-sonnet-4-20250514"
max_tokensoptional 有默认值required 必须显式指定
messagesrequired 含 system / user / assistant / toolrequired 仅含 user / assistant
system—(在 messages 数组中传入)optional 顶层字段,字符串或 block 数组
temperatureoptional 范围 0 – 2,默认 1optional 范围 0 – 1,默认 1
top_poptional 0 – 1optional 0 – 1
top_koptional 整数
noptional 返回多个候选(1 – 128)
stopoptional 字符串或数组(最多 4 个)optional stop_sequences 数组
streamoptional booleanoptional boolean
toolsoptional function 定义数组optional tool 定义数组
tool_choiceoptional "auto" / "none" / "required" / 指定函数optional {type:"auto"} / {type:"any"} / {type:"tool"}
presence_penaltyoptional -2.0 – 2.0
frequency_penaltyoptional -2.0 – 2.0
logprobsoptional boolean
response_formatoptional json_object / json_schema
seedoptional 整数(尽力复现)
thinking—(通过 reasoning_split 等参数控制)optional {type:"enabled", budget_tokens:N}
metadata(通过 user 字符串追踪)optional {user_id: "..."}
stream_optionsoptional {include_usage: true}—(usage 默认包含在事件中)
参数哲学差异:OpenAI 提供更多"调味"参数(penalty、logprobs、seed、response_format),适合精细控制输出分布。 Anthropic 则提供更多"结构化"控制(thinking.budget_tokens、metadata、system 独立字段),侧重请求语义的明确性。 两者的 temperature 范围也不同(0–2 vs 0–1),迁移时需注意。

请求体结构

REQUEST BODY

基础请求体

OpenAI 请求体
{
  "model": "gpt-4o",
  "messages": [
    {
      "role": "system",        ← system 在 messages 中
      "content": "You are a helpful assistant."
    },
    {
      "role": "user",
      "content": "Hi, how are you?"
    }
  ]
}
Anthropic 请求体
{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,           ← 必填
  "system": "You are a helpful assistant.",  ← 顶层字段
  "messages": [
    {
      "role": "user",
      "content": "Hi, how are you?"
    }
  ]
}
关键差异 ①:System Prompt 在 OpenAI 中是 messages 数组的一条消息(role: "system"), Anthropic 将其提升为请求体的顶层字段 "system"

关键差异 ②:Anthropic 的 max_tokens 是必填字段,OpenAI 则有默认值可省略。

System Prompt 的高级用法

OpenAI — 多条 system 消息
// OpenAI 支持在 messages 中放置多条 system 消息
"messages": [
  {"role": "system",
   "content": "You are a translator."},
  {"role": "system",
   "content": "Always respond in JSON."},
  {"role": "user",
   "content": "Translate: Hello"}
]

// 也可以在对话中间插入 system 消息
Anthropic — system block 数组 + 缓存控制
// system 支持 block 数组格式(可附加 cache_control)
"system": [
  {
    "type": "text",
    "text": "You are a translator.",
    "cache_control": {"type": "ephemeral"}
  },
  {
    "type": "text",
    "text": "Always respond in JSON."
  }
]
Anthropic 的 system 字段可以是字符串或 block 数组,后者支持 cache_control 实现 prompt caching

消息内容的两种写法

OpenAI — 两种写法均可
// 简写:content 为字符串
{"role": "user", "content": "Hello"}

// 完整写法:content 为数组(多模态时必须用此格式)
{"role": "user",
 "content": [
   {"type": "text", "text": "Hello"},
   {"type": "image_url", "image_url": {"url": "..."}}
 ]}
Anthropic — 两种写法均可
// 简写:content 为字符串
{"role": "user", "content": "Hello"}

// 完整写法:content 为 block 数组(多模态时必须用此格式)
{"role": "user",
 "content": [
   {"type": "text", "text": "Hello"},
   {"type": "image", "source": {"type": "url", "url": "..."}}
 ]}

响应体

RESPONSE BODY

完整响应 JSON

OpenAI 响应
{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "created": 1700000000,
  "model": "gpt-4o-2024-08-06",
  "choices": [{                  ← choices 数组包裹
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "I'm doing well!"  ← 纯字符串
    },
    "finish_reason": "stop"        ← 结束原因在 choice 内
  }],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 8,
    "total_tokens": 20,
    "prompt_tokens_details": {
      "cached_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0
    }
  }
}
Anthropic 响应
{
  "id": "msg_abc123",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-4-20250514",
  "content": [{                  ← content block 数组
    "type": "text",
    "text": "I'm doing well!"  ← block 中的 text 字段
  }],
  "stop_reason": "end_turn",      ← 结束原因在顶层
  "stop_sequence": null,
  "usage": {
    "input_tokens": 12,
    "output_tokens": 8,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0
  }
}
结构差异:OpenAI 用 choices[] 数组包裹(支持 n > 1 多候选),文本在 message.content(字符串)。 Anthropic 直接在顶层返回 content block 数组,文本在 content[i].text

用量命名:prompt_tokens / completion_tokens vs input_tokens / output_tokens。 Anthropic 额外返回 cache_creation_input_tokenscache_read_input_tokens 用于 prompt caching 计费。

取值路径对比

OpenAI 路径
// 文本
response.choices[0].message.content  → 字符串

// 结束原因
response.choices[0].finish_reason

// Token 用量
response.usage.prompt_tokens
response.usage.completion_tokens
response.usage.total_tokens
Anthropic 路径
// 文本
response.content[0].text  → 字符串

// 结束原因
response.stop_reason

// Token 用量
response.usage.input_tokens
response.usage.output_tokens
// 无 total,需自行相加

Usage 字段全览对照

语义OpenAI 字段Anthropic 字段
输入 token 总数usage.prompt_tokensusage.input_tokens
输出 token 总数usage.completion_tokensusage.output_tokens
总 tokenusage.total_tokens(API 给出)—(需 input + output + cache_* 自行相加)
缓存命中usage.prompt_tokens_details.cached_tokensusage.cache_read_input_tokens
缓存写入—(自动,不单独计量)usage.cache_creation_input_tokens
缓存细分(多 TTL)usage.cache_creation.ephemeral_5m_input_tokens
usage.cache_creation.ephemeral_1h_input_tokens
推理 tokenusage.completion_tokens_details.reasoning_tokens计入 output_tokens(thinking block 部分)
接受的预测 tokenusage.completion_tokens_details.accepted_prediction_tokens
拒绝的预测 tokenusage.completion_tokens_details.rejected_prediction_tokens
音频输入 tokenusage.prompt_tokens_details.audio_tokens—(暂不支持音频输入)
音频输出 tokenusage.completion_tokens_details.audio_tokens
Web 搜索请求数usage.server_tool_use.web_search_requests
服务层级—(请求体 service_tier 控制)usage.service_tier"standard" | "priority" | "batch"
计费基本规则:OpenAI 的 prompt_tokens 已经包含缓存命中部分(cached_tokens 是其子集),按"原价 × (prompt − cached) + 折扣价 × cached"计费。 Anthropic 的 input_tokens 只统计未命中缓存且未触发写缓存的部分,三类 token(input / cache_creation / cache_read)互不重叠,需分别按不同费率计费。

常见踩坑:用 Anthropic 时若简单地 input + output,会漏算 cache_creation_input_tokens(缓存写入按 1.25× 或 2× 原价计费)和 cache_read_input_tokens(按 0.1× 原价计费)。

完整 usage 对象(含全部子字段)

OpenAI usage — 全字段示例
"usage": {
  "prompt_tokens": 2048,        ← 含 cached_tokens
  "completion_tokens": 512,     ← 含 reasoning_tokens
  "total_tokens": 2560,

  "prompt_tokens_details": {
    "cached_tokens": 1024,      ← 自动命中缓存
    "audio_tokens": 0            ← 音频输入(GPT-4o-audio)
  },

  "completion_tokens_details": {
    "reasoning_tokens": 256,    ← o1/o3/o4 思考
    "audio_tokens": 0,           ← 音频输出
    "accepted_prediction_tokens": 0,
    "rejected_prediction_tokens": 0
  }
}

// 计费示例(GPT-4o,价格仅示意):
// 普通输入: (2048 - 1024) × $2.50/M = ...
// 缓存输入: 1024 × $1.25/M = ...
// 输出: 512 × $10.00/M = ...
// (reasoning_tokens 已在 completion_tokens 中)
Anthropic usage — 全字段示例
"usage": {
  "input_tokens": 512,             ← 未命中也未写缓存
  "cache_creation_input_tokens": 1024,
  "cache_read_input_tokens": 2048,

  "cache_creation": {              ← 缓存写入按 TTL 拆分
    "ephemeral_5m_input_tokens": 512,
    "ephemeral_1h_input_tokens": 512
  },

  "output_tokens": 768,            ← thinking + 正文
  "server_tool_use": {              ← 服务端工具计量
    "web_search_requests": 2
  },
  "service_tier": "standard"      ← standard/priority/batch
}

// 计费示例(Claude Sonnet 4,价格仅示意):
// 普通输入: 512 × $3.00/M = ...
// 缓存写入(5m): 512 × $3.75/M (1.25×) = ...
// 缓存写入(1h): 512 × $6.00/M (2.00×) = ...
// 缓存读取: 2048 × $0.30/M (0.10×) = ...
// 输出: 768 × $15.00/M = ...
OpenAI 的 cached_tokens 是 prompt_tokens 的子集(重叠);Anthropic 三类输入字段相互独立(不重叠),合计才是实际处理的输入总量

Prompt Caching 计费机制对比

维度OpenAIAnthropic
触发方式自动(prefix ≥ 1024 token 时)显式,在请求体加 cache_control: {type: "ephemeral"}
缓存最小粒度1024 token(不足不缓存)取决于模型:Sonnet/Opus 1024、Haiku 2048
缓存写入费用免费(不额外计费)5m TTL: 1.25× 原价;1h TTL: 2.00× 原价
缓存读取费用0.5× 原价(约 50% 折扣)0.1× 原价(约 90% 折扣)
TTL 默认~5–10 分钟(不可调整)5 分钟(默认)或 1 小时(ttl: "1h"
断点数量—(自动 prefix)最多 4 个 cache_control 断点(system + tools + messages)
响应中的命中量prompt_tokens_details.cached_tokenscache_read_input_tokens
响应中的写入量不区分(已含在 prompt_tokens 内)cache_creation_input_tokens + cache_creation.ephemeral_{5m,1h}_input_tokens
缓存计费公式:
OpenAIcost = input × full_price − cached × 0.5 × full_price + output × out_price
Anthropiccost = input × full + cache_creation × {1.25 or 2.00} × full + cache_read × 0.10 × full + output × out_price

由于 Anthropic 的三类输入不重叠,统计实际处理的 prompt 总 token 时需 total_input = input_tokens + cache_creation_input_tokens + cache_read_input_tokens

Reasoning / Thinking Token 计费

OpenAI — reasoning_tokens(o1/o3/o4 系列)
// 思考 token 是 completion 的子集,不可读
"usage": {
  "prompt_tokens": 100,
  "completion_tokens": 1500,
  "completion_tokens_details": {
    "reasoning_tokens": 1200  ← 隐式思考
  }
}

// 关键点:
// 1. reasoning_tokens 已包含在 completion_tokens 中
// 2. 计费时按 output 价计算 (整个 completion)
// 3. 思考内容对用户不可见(仅返回 summary)
// 4. max_completion_tokens 需要预留思考预算
//    (而非旧的 max_tokens,o-系列已弃用)
// 5. 通过 reasoning_effort: "low/medium/high"
//    控制思考深度
Anthropic — thinking block(Claude 3.7+ / 4.x)
// 思考 token 计入 output_tokens,但内容可见
"usage": {
  "input_tokens": 100,
  "output_tokens": 1500  ← thinking + 正文
}

"content": [
  {"type":"thinking","thinking":"Let me ...",
   "signature":"abc..."},  ← 思考块(可见)
  {"type":"text","text":"答案是 42"}
]

// 关键点:
// 1. thinking 文本按 output 价计费
// 2. 通过 thinking.budget_tokens 控制上限
// 3. budget_tokens 必须 < max_tokens
// 4. 多轮回传时需要带回 signature 才会被采信
// 5. 工具调用循环中必须回传 thinking block

多模态 Token 计算(图片 / 音频)

类型OpenAI 计费规则Anthropic 计费规则
图片(low detail)固定 85 tokens / 张—(不区分 detail,按尺寸折算)
图片(high detail)85 + 170 × tile 数(每 tile = 512×512)tokens ≈ (width × height) / 750
图片建议尺寸上限≤ 2000×768 或 768×2000(自动缩放)≤ 1568 px 长边(超出会缩放)
图片 token 归属prompt_tokens(不单列)input_tokens(不单列)
音频输入prompt_tokens_details.audio_tokens
(约 25 tokens/秒,gpt-4o-audio)
—(暂不支持)
音频输出completion_tokens_details.audio_tokens
PDF / 文档通常先转图片,按图片计费直接传 PDF,按页转 token(约 1500–3000 / 页)
图片 token 估算示例:一张 1024×768 的图片,OpenAI(high detail)需 85 + 170 × 6 = 1105 tokens; Anthropic 约 (1024 × 768) / 750 ≈ 1049 tokens。两者数量级接近,但实际请求可能因预处理差异略有不同。

流式(Streaming)中获取 usage

OpenAI — 需显式开启
// 请求体必须加 stream_options
{
  "model": "gpt-4o",
  "messages": [...],
  "stream": true,
  "stream_options": {"include_usage": true}
}

// 中间事件 usage 字段为 null
data: {"choices":[{"delta":{...}}],
       "usage": null}

// 倒数第二个 chunk(choices 为空)携带 usage
data: {"choices": [],
       "usage": {
         "prompt_tokens": 12,
         "completion_tokens": 8,
         "total_tokens": 20
       }}

data: [DONE]

// 不加 include_usage 时整个流没有 usage 字段
Anthropic — 默认包含,分两次返回
// 无需任何额外配置

// 首个事件 message_start 含 input_tokens
event: message_start
data: {"type":"message_start","message":{
  ...,
  "usage":{
    "input_tokens": 12,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "output_tokens": 1  ← 仅占位
  }}}

// 末尾事件 message_delta 含 output_tokens
event: message_delta
data: {"type":"message_delta",
  "delta":{"stop_reason":"end_turn"},
  "usage":{"output_tokens": 8}}

// 需要合并 message_start.usage + message_delta.usage
// 才能拿到完整的 usage 对象
OpenAI 在最后一个非 [DONE] 的 chunk 一次性返回完整 usage;Anthropic 把输入和输出 usage 拆到流的两端,需要客户端做合并

服务层级(Service Tier)对 usage 的影响

层级OpenAIAnthropic
标准请求体 "service_tier": "auto"(默认)usage.service_tier: "standard"
优先 / Flex"service_tier": "flex"(折扣价、更慢)
"service_tier": "priority"(部分模型,溢价、更快)
usage.service_tier: "priority"
(请求体加 "service_tier": "auto" 或调用 Priority Tier endpoint)
批处理独立 /v1/batches 接口,约 0.5× 原价、24h 内完成独立 /v1/messages/batches,0.5× 原价;usage.service_tier: "batch"
用量字段差异响应里没有 service_tier,请求体里指定响应 usage.service_tier 明示实际处理层级(可能与请求不一致)

典型场景下的 Token 计算示例

OpenAI — 多轮 + 缓存 + 推理
// 场景:o3 模型 + 长 system + 多轮对话
"usage": {
  "prompt_tokens": 5200,        ← system + 历史
  "completion_tokens": 800,
  "total_tokens": 6000,
  "prompt_tokens_details": {
    "cached_tokens": 4800      ← 92% 命中
  },
  "completion_tokens_details": {
    "reasoning_tokens": 600    ← 75% 用于推理
  }
}

// 计算(假设 $2/M 输入, $1/M 缓存, $8/M 输出):
// 非缓存输入: (5200 - 4800) × $2/M = $0.0008
// 缓存输入:   4800 × $1/M = $0.0048
// 输出(含推理): 800 × $8/M = $0.0064
// 合计: ≈ $0.012
Anthropic — 多轮 + 缓存 + thinking + 工具
// 场景:Sonnet 4 + cache + thinking + 工具调用
"usage": {
  "input_tokens": 120,            ← 仅本轮新增
  "cache_creation_input_tokens": 800,
  "cache_read_input_tokens": 4280,  ← 83% 复用
  "cache_creation": {
    "ephemeral_5m_input_tokens": 800
  },
  "output_tokens": 900,            ← thinking + tool_use + text
  "server_tool_use": {"web_search_requests": 1},
  "service_tier": "standard"
}

// 计算(假设 $3/M 输入, $15/M 输出):
// 普通输入: 120 × $3/M = $0.00036
// 缓存写入: 800 × $3.75/M (1.25×) = $0.003
// 缓存读取: 4280 × $0.30/M (0.10×) = $0.00128
// 输出: 900 × $15/M = $0.0135
// Web 搜索: 1 × $10/1000 = $0.01
// 合计: ≈ $0.028
// 实际处理输入 = 120 + 800 + 4280 = 5200 tokens

用量追踪与会话级聚合

OpenAI — 追踪用户与会话
// 请求体加 user 字段,便于在 Dashboard / 风控里聚合
{
  "model": "gpt-4o",
  "messages": [...],
  "user": "user_8a3f..."     ← 字符串,自由格式
}

// 响应中没有 user 回显,但可在用量报告中按 user 切片
// 项目 / 组织维度通过 OpenAI-Project / OpenAI-Organization
// 请求头自动归类

// 多轮 token 聚合 = 每次响应的 total_tokens 累加
// (已含 cached_tokens,不会重复计费)
Anthropic — metadata.user_id
// 请求体加 metadata.user_id 用于滥用检测和报表
{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,
  "messages": [...],
  "metadata": {
    "user_id": "user_8a3f..."  ← 建议哈希化
  }
}

// 多轮 token 聚合需要分别累加:
//   total_input = Σ (input + cache_creation + cache_read)
//   total_output = Σ output_tokens
// 否则会漏掉缓存读写的两类输入 token

// 注意:metadata.user_id 不会出现在响应中,
// 仅用于服务端聚合和滥用检测
会话级累加易错点:
▸ OpenAI:直接累加 total_tokens 即可(缓存命中已折算在 prompt_tokens 中,但 dashboard 计费按实际付费 token)。
▸ Anthropic:必须三类输入字段都参与累加,否则统计的"实际处理 prompt 长度"会严重偏低(典型场景下可能只统计到真实值的 10–20%)。
▸ Streaming:OpenAI 不开 include_usage 时 usage 全程缺失;Anthropic 必须合并 message_start.usagemessage_delta.usage

停止原因值对照

场景OpenAI finish_reasonAnthropic stop_reason
正常结束"stop""end_turn"
达到 token 上限"length""max_tokens"
需要调用工具"tool_calls""tool_use"
命中停止序列"stop"(同正常结束)"stop_sequence"(有独立值)
内容过滤"content_filter"—(以错误响应返回)

错误响应格式

OpenAI 错误(HTTP 401)
{
  "error": {
    "message": "Incorrect API key provided: sk-...xxxx.",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_api_key"
  }
}

// 错误类型:invalid_request_error, authentication_error,
// permission_error, rate_limit_error, server_error
Anthropic 错误(HTTP 401)
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "invalid x-api-key"
  }
}

// 错误类型:invalid_request_error, authentication_error,
// permission_error, not_found_error, rate_limit_error,
// api_error, overloaded_error
OpenAI 错误体多一个 param(指向出错参数)和 code(细分错误码)字段;Anthropic 结构更简洁,外层多一个 "type": "error" 标识

Tool Use

FUNCTION CALLING

工具定义(请求体中的 tools 字段)

OpenAI — type + function 包裹
"tools": [{
  "type": "function",       ← 额外包裹层
  "function": {
    "name": "get_weather",
    "description": "Get weather info",
    "parameters": {         ← "parameters"
      "type": "object",
      "properties": {
        "location": {
          "type": "string",
          "description": "City name"
        }
      },
      "required": ["location"]
    },
    "strict": false          ← 可选:强制结构化输出
  }
}]
Anthropic — 扁平结构 + input_schema
"tools": [{
  "name": "get_weather",       ← 直接平铺
  "description": "Get weather info",
  "input_schema": {           ← "input_schema"
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City name"
      }
    },
    "required": ["location"]
  }
}]
差异 ①:OpenAI 有 "type":"function" + "function":{} 包裹,Anthropic 直接平铺。
差异 ②:Schema key:"parameters" vs "input_schema"
差异 ③:OpenAI 支持 "strict": true 强制结构化输出(Structured Outputs)。

tool_choice 对比

OpenAI tool_choice
// 模型自行决定(默认)
"tool_choice": "auto"

// 禁止使用工具
"tool_choice": "none"

// 强制使用工具(不限定哪个)
"tool_choice": "required"

// 指定使用某个工具
"tool_choice": {
  "type": "function",
  "function": {"name": "get_weather"}
}

// 控制并行工具调用
"parallel_tool_calls": false
Anthropic tool_choice
// 模型自行决定(默认)
"tool_choice": {"type": "auto"}

// 无 "none" — 不传 tools 即可

// 强制使用工具(不限定哪个)
"tool_choice": {"type": "any"}

// 指定使用某个工具
"tool_choice": {
  "type": "tool",
  "name": "get_weather"
}

// 控制并行工具调用(嵌套在 tool_choice 中)
"tool_choice": {
  "type": "auto",
  "disable_parallel_tool_use": true
}
OpenAI 用字符串 "auto"/"none"/"required",Anthropic 用对象 {type: "auto"/"any"/"tool"}。并行控制位置也不同

响应中的工具调用

OpenAI — message.tool_calls 数组
{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": null,         ← 有工具调用时 content 为 null
      "tool_calls": [{        ← 独立的 tool_calls 字段
        "id": "call_abc123",
        "type": "function",
        "function": {
          "name": "get_weather",
          "arguments": "{\"location\":\"San Francisco\"}"
        }               ↑ arguments 是 JSON 字符串
      }]
    },
    "finish_reason": "tool_calls"
  }]
}
Anthropic — content block 中的 tool_use
{
  "role": "assistant",
  "content": [
    {
      "type": "text",           ← 可以有文本说明
      "text": "Let me check the weather."
    },
    {
      "type": "tool_use",       ← 工具调用也在 content 数组中
      "id": "toolu_abc123",
      "name": "get_weather",
      "input": {"location": "San Francisco"}
    }                    ↑ input 是已解析的 JSON 对象
  ],
  "stop_reason": "tool_use"
}
核心差异:OpenAI 将工具调用放在 message.tool_calls 独立字段中,content 为 null。 Anthropic 将工具调用作为 content block 数组中的一个元素(可与 text block 共存)。

参数格式:OpenAI 的 argumentsJSON 字符串(需 JSON.parse()), Anthropic 的 input已解析的 JSON 对象(直接使用)。

并行工具调用

OpenAI — 多个 tool_calls 元素
"tool_calls": [
  {
    "id": "call_1",
    "type": "function",
    "function": {
      "name": "get_weather",
      "arguments": "{\"location\":\"SF\"}"
    }
  },
  {
    "id": "call_2",
    "type": "function",
    "function": {
      "name": "get_time",
      "arguments": "{\"timezone\":\"PST\"}"
    }
  }
]
Anthropic — 多个 tool_use blocks
"content": [
  {"type": "text",
   "text": "I'll check both for you."},
  {
    "type": "tool_use",
    "id": "toolu_1",
    "name": "get_weather",
    "input": {"location": "SF"}
  },
  {
    "type": "tool_use",
    "id": "toolu_2",
    "name": "get_time",
    "input": {"timezone": "PST"}
  }
]

回传工具结果

OpenAI — 每个结果一条 role: "tool" 消息
// 每个 tool_call 对应一条 tool 消息
{
  "role": "tool",
  "tool_call_id": "call_1",
  "content": "24°C, sunny"
},
{
  "role": "tool",
  "tool_call_id": "call_2",
  "content": "2:30 PM PST"
}
Anthropic — 一条 user 消息含多个 tool_result
// 所有结果放在一条 user 消息的 content 数组中
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_1",
      "content": "24°C, sunny"
    },
    {
      "type": "tool_result",
      "tool_use_id": "toolu_2",
      "content": "2:30 PM PST"
    }
  ]
}
角色差异:OpenAI 用专用 role: "tool",每个结果一条独立消息。 Anthropic 用 role: "user" + tool_result block,多个结果可合并为一条消息。

ID 字段:tool_call_id(OpenAI)vs tool_use_id(Anthropic)。

错误回传:Anthropic 支持在 tool_result 中添加 "is_error": true 标记工具执行失败,OpenAI 无此机制(需在 content 中描述)。

Streaming

SSE

启用流式(请求体加 "stream": true)

OpenAI SSE 事件流
// 首个 chunk — 携带 role
data: {"id":"chatcmpl-...","choices":[{
  "delta":{"role":"assistant","content":""},
  "index":0}]}

// 文本增量(逐 token)
data: {"choices":[{"delta":{
  "content":"I'm"},"index":0}]}

data: {"choices":[{"delta":{
  "content":" doing"},"index":0}]}

data: {"choices":[{"delta":{
  "content":" well!"},"index":0}]}

// 结束标记
data: {"choices":[{"delta":{},
  "finish_reason":"stop"}]}

data: [DONE]
Anthropic SSE 事件流
// 消息开始(含 message 元信息和 usage)
event: message_start
data: {"type":"message_start","message":{
  "id":"msg_...","role":"assistant",
  "usage":{"input_tokens":12,...}}}

// 内容块开始(声明类型 → text)
event: content_block_start
data: {"type":"content_block_start",
  "index":0,
  "content_block":{"type":"text","text":""}}

// 文本增量(逐 token)
event: content_block_delta
data: {"type":"content_block_delta","index":0,
  "delta":{"type":"text_delta",
    "text":"I'm doing well!"}}

// 内容块结束
event: content_block_stop
data: {"type":"content_block_stop","index":0}

// 消息增量(含 stop_reason 和 output usage)
event: message_delta
data: {"type":"message_delta",
  "delta":{"stop_reason":"end_turn"},
  "usage":{"output_tokens":8}}

// 消息结束
event: message_stop
data: {"type":"message_stop"}

SSE 事件生命周期对比

阶段OpenAIAnthropic
流开始首个 data:(含 delta.roleevent: message_start
内容块开始—(无显式事件)event: content_block_start(声明 block 类型)
文本增量delta.contentdelta.type: "text_delta" + delta.text
内容块结束event: content_block_stop
用量信息需要 "stream_options": {"include_usage": true},最后一个 chunk 携带默认包含:message_start 携带 input,message_delta 携带 output
流结束data: [DONE]event: message_stop

工具调用的流式事件

OpenAI — tool_calls 增量
// 工具调用开始(首个 chunk 含完整元信息)
data: {"choices":[{"delta":{
  "tool_calls":[{"index":0,
    "id":"call_abc",
    "type":"function",
    "function":{"name":"get_weather",
      "arguments":""}}]}}]}

// arguments 分段传输(JSON 字符串片段)
data: {"choices":[{"delta":{
  "tool_calls":[{"index":0,
    "function":{"arguments":"{\"lo"}}]}}]}

data: {"choices":[{"delta":{
  "tool_calls":[{"index":0,
    "function":{"arguments":"cation\":\"SF\"}"}}]}}]}

// 结束
data: {"choices":[{"delta":{},
  "finish_reason":"tool_calls"}]}
data: [DONE]
Anthropic — tool_use block 事件
// 工具调用块开始(声明 block 类型 + 元信息)
event: content_block_start
data: {"type":"content_block_start",
  "index":1,
  "content_block":{"type":"tool_use",
    "id":"toolu_abc",
    "name":"get_weather",
    "input":{}}}

// input 分段传输(JSON 片段)
event: content_block_delta
data: {"type":"content_block_delta",
  "index":1,
  "delta":{"type":"input_json_delta",
    "partial_json":"{\"location"}}

event: content_block_delta
data: {"delta":{"type":"input_json_delta",
    "partial_json":"\":\"SF\"}"}}

// 工具调用块结束
event: content_block_stop
data: {"type":"content_block_stop","index":1}
Anthropic 的结构化优势:每个 content block 有明确的 start → delta → stop 生命周期,且 content_block_start 事件中声明了 block 类型。 这使得客户端可以在流式中即时知道即将到来的内容是 text、tool_use 还是 thinking,无需回溯解析。 OpenAI 的 tool_calls 增量通过 delta.tool_calls[i].index 定位,相对扁平。

Thinking / Reasoning

EXTENDED THINKING

请求格式

OpenAI — 默认开启(部分模型)
// 思考默认内嵌在 content 中
// 通过 reasoning_split 分离思考和回答
{
  "model": "gpt-4o",
  "messages": [...],
  "reasoning_split": true  ← 可选:分离思考内容
}

// 不使用 reasoning_split 时,思考过程
// 以 <think>...</think> 标签嵌入 content 字符串
Anthropic — 显式启用 + 预算控制
// 通过 thinking 参数显式启用
{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 16000,
  "thinking": {
    "type": "enabled",
    "budget_tokens": 10000   ← 思考 token 预算
  },
  "messages": [...]
}

// budget_tokens 控制模型可用于思考的最大 token 数
// 必须小于 max_tokens

响应格式

OpenAI — <think> 标签 / reasoning_details
// 默认:思考嵌入 content 字符串
{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "<think>\nLet me analyze this step by step.\n1. First...\n2. Then...\n</think>\n\nThe answer is 42."
    }
  }]
}

// 使用 reasoning_split=true 时:
{
  "choices": [{
    "message": {
      "content": "The answer is 42.",
      "reasoning_details": [{
        "type": "reasoning.text",
        "text": "Let me analyze this step by step.\n1. First...\n2. Then..."
      }]
    }
  }]
}
Anthropic — 独立 thinking block
// 思考作为独立的 content block
{
  "content": [
    {
      "type": "thinking",
      "thinking": "Let me analyze this step by step.\n1. First...\n2. Then..."
    },
    {
      "type": "text",
      "text": "The answer is 42."
    }
  ]
}

// 多步推理可能产生多个 thinking + text 交错:
// [thinking, text, thinking, tool_use, ...]

Thinking 的流式事件

OpenAI — <think> 标签嵌入 delta.content
// 默认:思考和回答混在 delta.content 中
data: {"choices":[{"delta":{
  "content":"<think>\nLet me"}}]}
data: {"choices":[{"delta":{
  "content":" analyze..."}}]}
data: {"choices":[{"delta":{
  "content":"</think>\n\nThe answer"}}]}

// 使用 reasoning_split=true 时:
// 思考增量通过 delta.reasoning_details 传输
// 回答增量通过 delta.content 传输
data: {"choices":[{"delta":{
  "reasoning_details":[{
    "type":"reasoning.text",
    "text":"Let me analyze..."}]}}]}
Anthropic — 独立的 thinking block 事件
// thinking block 开始 — 声明类型
event: content_block_start
data: {"index":0,"content_block":{
  "type":"thinking","thinking":""}}

// thinking 增量(thinking_delta 类型)
event: content_block_delta
data: {"index":0,"delta":{
  "type":"thinking_delta",
  "thinking":"Let me analyze..."}}

event: content_block_stop
data: {"index":0}

// text block 开始 — 声明类型
event: content_block_start
data: {"index":1,"content_block":{
  "type":"text","text":""}}

// text 增量(text_delta 类型)
event: content_block_delta
data: {"index":1,"delta":{
  "type":"text_delta",
  "text":"The answer is 42."}}
流式中的类型区分:Anthropic 在 content_block_start 中声明 block 类型("thinking" / "text"), 后续 delta 分别携带 "thinking_delta""text_delta",无需解析标签边界。 OpenAI 默认情况下需检测 <think> 标签来区分,使用 reasoning_split 可将二者分离到不同字段。

多轮对话中的 Thinking 回传

OpenAI — 保留完整 content / reasoning_details
// 默认格式:原样回传包含 <think> 的 content
{"role": "assistant",
 "content": "<think>...</think>\n\nThe answer is 42."}

// reasoning_split 格式:回传 reasoning_details
{"role": "assistant",
 "content": "The answer is 42.",
 "reasoning_details": [{
   "type": "reasoning.text",
   "text": "Let me analyze..."
 }]}
Anthropic — 必须回传 thinking blocks + signature
// 必须完整回传 content 数组(含 signature)
{"role": "assistant",
 "content": [
   {
     "type": "thinking",
     "thinking": "Let me analyze...",
     "signature": "WyIxNjk3..."  ← 必须保留
   },
   {
     "type": "text",
     "text": "The answer is 42."
   }
 ]}
Anthropic 的 thinking block 在回传时必须包含 signature 字段(响应中自动返回),用于验证思考内容的完整性。丢弃 thinking blocks 会导致后续推理质量下降

Vision / 多模态输入

MULTIMODAL

通过 URL 传入图片

OpenAI — image_url block
{
  "role": "user",
  "content": [
    {
      "type": "text",
      "text": "What's in this image?"
    },
    {
      "type": "image_url",
      "image_url": {
        "url": "https://example.com/photo.jpg",
        "detail": "auto"  ← "low" / "high" / "auto"
      }
    }
  ]
}
Anthropic — image block + source
{
  "role": "user",
  "content": [
    {
      "type": "text",
      "text": "What's in this image?"
    },
    {
      "type": "image",
      "source": {
        "type": "url",
        "url": "https://example.com/photo.jpg"
      }
    }
  ]
}

通过 Base64 传入图片

OpenAI — data URI 嵌入 url 字段
{
  "type": "image_url",
  "image_url": {
    "url": "data:image/png;base64,iVBORw0KGgo..."
  }
}

// base64 数据嵌入 data URI scheme 中
// 格式:data:{media_type};base64,{data}
// media_type 从 URI 中推断
Anthropic — 独立的 source 字段
{
  "type": "image",
  "source": {
    "type": "base64",
    "media_type": "image/png",
    "data": "iVBORw0KGgo..."
  }
}

// media_type 作为独立字段显式声明
// data 为纯 base64 字符串(不含 data URI 前缀)
类型名差异:OpenAI 用 "image_url"(即使传 base64 也用此类型名), Anthropic 用 "image" + source.type 区分 "url""base64"

Base64 传入方式:OpenAI 将 base64 编码为 data: URI 放入 url 字段(单字段)。 Anthropic 将 media_typedata 分为独立字段(结构化)。

精度控制:OpenAI 支持 detail 参数("low" / "high" / "auto")控制图片分析精度和 token 消耗。 Anthropic 无对应参数。

支持的图片格式

特性OpenAIAnthropic
支持格式PNG, JPEG, GIF, WebPPNG, JPEG, GIF, WebP
传入方式URL 或 data URI(base64)URL 或 base64(独立字段)
精度控制detail: "low"/"high"/"auto"
PDF 支持部分模型支持type: "document" + source
多图输入✓ 多个 image_url block✓ 多个 image block

多轮对话

CONVERSATION

基础多轮对话

OpenAI 第二轮请求体
{
  "model": "gpt-4o",
  "messages": [
    {"role": "system",
     "content": "You are a helpful assistant."},
    {"role": "user",
     "content": "What's 2+2?"},
    {"role": "assistant",
     "content": "2 + 2 = 4."},
    {"role": "user",
     "content": "Multiply that by 3"}
  ]
}
Anthropic 第二轮请求体
{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,
  "system": "You are a helpful assistant.",
  "messages": [
    {"role": "user",
     "content": "What's 2+2?"},
    {"role": "assistant",
     "content": [{"type":"text",
       "text":"2 + 2 = 4."}]},
    {"role": "user",
     "content": "Multiply that by 3"}
  ]
}

含 Tool Use 的多轮对话(完整流程)

OpenAI — 4 条消息完成一次 Tool Use 循环
"messages": [
  // ① 用户提问
  {"role": "user",
   "content": "SF weather?"},

  // ② 模型决定调用工具(原样回传)
  {"role": "assistant",
   "content": null,
   "tool_calls": [{
     "id": "call_1",
     "type": "function",
     "function": {
       "name": "get_weather",
       "arguments": "{\"location\":\"SF\"}"
     }
   }]},

  // ③ 工具执行结果
  {"role": "tool",
   "tool_call_id": "call_1",
   "content": "24°C, sunny"},

  // ④ 用户继续对话
  {"role": "user",
   "content": "Will it rain tomorrow?"}
]
Anthropic — 3 条消息完成一次 Tool Use 循环
"messages": [
  // ① 用户提问
  {"role": "user",
   "content": "SF weather?"},

  // ② 模型决定调用工具(完整 content 回传)
  {"role": "assistant",
   "content": [
     {"type": "text",
      "text": "Let me check."},
     {"type": "tool_use",
      "id": "toolu_1",
      "name": "get_weather",
      "input": {"location": "SF"}}
   ]},

  // ③ 工具结果 + 用户续问(合并为一条 user 消息)
  {"role": "user",
   "content": [
     {"type": "tool_result",
      "tool_use_id": "toolu_1",
      "content": "24°C, sunny"},
     {"type": "text",
      "text": "Will it rain tomorrow?"}
   ]}
]
assistant 内容回传:OpenAI 回传 assistant 消息时,contenttool_calls 各为独立字段。 Anthropic 的 assistant content 需回传完整的 block 数组(包括 thinking / text / tool_use 所有类型)。

消息合并:Anthropic 的 tool_result 和后续 user 文本可以合并在同一条 role: "user" 消息中(因为 block 数组支持混合类型)。 OpenAI 的 role: "tool" 必须是独立消息。

消息排列规则

规则OpenAIAnthropic
首条消息角色通常 systemuser(灵活)必须是 user(system 在顶层字段)
末条消息角色通常 user(灵活)必须是 user
角色交替不强制——可连续多条同角色消息强制交替——user 和 assistant 必须严格交替
连续 user 消息✓ 允许✗ 不允许(需合并为一条或插入 assistant)
system 位置messages 中任意位置(通常首条)仅在顶层 system 字段
Anthropic 要求 messages 中 user 和 assistant 严格交替,这也是为什么 tool_result 用 role: "user" 而非独立角色——保持交替规则

总结速查

SUMMARY
OPENAI 设计哲学

choices[] 包裹 · 字符串优先 · 灵活宽松

Bearer token 认证,无需版本头。 响应套在 choices 数组中(支持 n > 1 多候选)。 content 通常是纯字符串,tool_calls 作为 message 的独立字段。 参数丰富(penalty / logprobs / seed / response_format),允许精细控制输出分布。 消息排列灵活,支持连续同角色消息。 temperature 范围 0–2。

ANTHROPIC 设计哲学

content blocks · 结构化优先 · 严格有序

自定义 header 认证 + 版本控制。 响应顶层 content block 数组,每个 block 有明确 type。 工具参数是已解析对象,工具结果以 user 角色回传。 System Prompt 为独立字段,max_tokens 必填。 Thinking 有独立 block 和 budget 控制。 消息必须 user / assistant 严格交替。 temperature 范围 0–1。

常见问题

FAQ
Q1 两套 API 的请求体可以直接互转吗? +
A

不可以直接互转,需要处理多个关键差异: ① system 消息需从 messages 中提取为顶层字段(或反向回填); ② max_tokens 在 Anthropic 中必填; ③ Tool 定义的 "parameters" 需改为 "input_schema"(反之亦然),并去除/添加 "type":"function" 包裹; ④ Tool 结果的 role 不同("tool" vs "user" + "tool_result"); ⑤ tool_choice 格式不同(字符串 vs 对象); ⑥ 认证 header 完全不同; ⑦ Anthropic 要求消息严格交替,可能需要合并连续同角色消息。 建议编写一个请求适配层做映射。

Q2 Anthropic 为什么要把 system 提为独立字段? +
A

Anthropic 认为 System Prompt 在语义上与对话消息不同—— 它是对模型行为的全局指令,而非对话的一部分。 独立字段可以避免 system 消息被意外修改或在多轮对话中丢失, 同时也让请求体结构更清晰:messages 只包含用户和助手的交互。 此外,独立字段支持 cache_control 做精细的 prompt caching 控制。

Q3 content blocks 相比纯字符串有什么优势? +
A

content block 数组使一次响应可以包含多种类型的内容: text(文本)、thinking(推理过程)、tool_use(工具调用)等, 每种类型都有明确的 "type" 标识和对应的数据结构。 这让客户端可以用标准的遍历逻辑处理混合内容, 而不需要用正则表达式从字符串中解析特殊标记(如 <think> 标签)。 在 Agentic 场景(交错思考 + 多次工具调用)中,结构化的优势尤其明显。

Q4 流式 SSE 中如何区分 thinking 和 text 的增量? +
A

OpenAI:默认情况下,thinking 和 text 混在 delta.content 中(以 <think> 标签区分), 需客户端自行检测标签边界。 如启用 "reasoning_split": true,thinking 增量通过 delta.reasoning_details 字段发送,text 通过 delta.content

Anthropic:每个 content block 有独立的 content_block_startcontent_block_deltacontent_block_stop 生命周期。 content_block_start 事件中声明 block 类型("thinking" / "text"), 后续 delta 分别携带 "thinking_delta""text_delta",无需额外解析。

Q5 为什么 Anthropic 的 tool 参数 (input) 已经是 JSON 对象,而 OpenAI 是字符串? +
A

这反映了两家 API 的不同设计选择。OpenAI 的 argumentsJSON 字符串, 历史上沿袭了早期 function calling 的设计——在流式传输中,字符串片段更容易拼接。 但这意味着客户端在非流式响应中需要多一步 JSON.parse()

Anthropic 的 input已解析的 JSON 对象, 与 content block 的结构化哲学一致——所有数据都已结构化,客户端无需二次解析。 在流式模式下,Anthropic 使用 input_json_delta(partial JSON 字符串),客户端在 content_block_stop 后拿到完整的 parsed 对象。

Q6 Anthropic 的消息交替要求会带来什么影响? +
A

Anthropic 要求 messages 中 user 和 assistant 严格交替,首尾都必须是 user。 这带来几个实际影响:

① 不能连续发送多条 user 消息——需合并为一条(用 content block 数组组合文本和 tool_result);
② 工具结果必须用 role: "user"——因为 tool_result 必须跟在 assistant 之后,且下一条必须是 user;
③ 如果要模拟"系统消息插入对话中间",需要放在 user 消息的 content 数组里;
④ 合并/拆分消息时需要注意 block 类型的兼容性。

OpenAI 没有此限制,消息排列更灵活,但也更容易出现"忘记传 system 消息"等问题。

"Good APIs are not about being clever;
they are about being clear."
2026 · API FORMAT REFERENCE · AN CHENG