Claude 原生 Messages API 详解:与 OpenAI 格式的字段差异及迁移实战

调用 Claude 有两套常见姿势:一套是走 OpenAI 兼容的 /v1/chat/completions,另一套是 Anthropic 自家的 /v1/messages(Messages API)。前者上手快,但部分原生能力(更细的 stop_reason、content blocks 等)只有原生格式表达得完整。这篇把两套格式的差异逐项过一遍,并演示迁移要改哪些地方。

1. 准备:一个能访问的入口

无论哪套格式,都需要一个调用地址(Base URL)和 API Key。这类入口有几种来源:官方接口、自建网关(如开源的 OneAPI),或第三方统一接入服务(如 OneAPI、聚合服务 等,按需自行实测)。不少网关两套格式都支持。下文统一用环境变量占位。

2. 关键差异对照

维度 原生 Messages API OpenAI 兼容格式
端点 /v1/messages /v1/chat/completions
认证头 x-api-key + anthropic-version Authorization: Bearer
system 提示 顶层 system 参数 messages 里 role: system
max_tokens 必填 选填
回复正文 content 数组(content block) choices[0].message.content
结束原因 stop_reason(end_turn/max_tokens/tool_use) finish_reason(stop/length/tool_calls)

最容易踩的是两条:原生接口 max_tokens 不传直接报 400;messages 里塞 role: system 会被原生接口拒绝。

3. 原生调用示例

import os, requests

url = os.getenv("BASE_URL", "YOUR_BASE_URL") + "/v1/messages"
headers = {
    "x-api-key": os.getenv("API_KEY"),
    "anthropic-version": "2023-06-01",
    "content-type": "application/json",
}
payload = {
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,          # 原生接口必填
    "system": "你是一名严谨的代码审查助手",
    "messages": [
        {"role": "user", "content": "这段循环有没有越界风险:for(i=0;i<=n;i++) a[i]=0;"}
    ],
}
resp = requests.post(url, headers=headers, json=payload, timeout=60)
data = resp.json()
print(data["content"][0]["text"])   # 注意是 content 数组
print("stop_reason:", data["stop_reason"])

注意响应解析:原生格式的回复在 content[0]["text"],不是 choices[0].message.content

4. 从 OpenAI 格式迁移:一个适配函数

迁移的核心就是两件事——把 system 消息抽到顶层、补上 max_tokens

def openai_to_anthropic(messages, max_tokens=1024):
    system = "\n".join(
        m["content"] for m in messages if m["role"] == "system"
    )
    rest = [m for m in messages if m["role"] != "system"]
    body = {"messages": rest, "max_tokens": max_tokens}
    if system:
        body["system"] = system
    return body

把原有 OpenAI 风格的 messages 喂进去,返回值合并上 model 字段即可直接 POST 到 /v1/messages

5. 怎么选

  • 已有大量 OpenAI SDK 代码、只想换模型:继续用兼容格式,成本最低;
  • 需要精确区分 tool_usemax_tokens 等结束状态,或要用 content block 处理多段输出:用原生格式;
  • 两套混用也可以,用上面的适配函数收敛差异,解析层各写一份即可。

6. 排错速查

  • 400 且提示 max_tokens:原生接口忘了传 max_tokens
  • system 相关报错:检查是否把 system 塞进了 messages 数组;
  • 401:确认用的是 x-api-key 而不是 Authorization 头(原生格式);
  • KeyError: choices:拿原生响应按 OpenAI 结构解析了,改读 content 数组。

两套格式本质只是「信封」不同,理解差异之后,迁移就是十几行适配代码的事。

更多推荐