前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
返回 AI 情报前线
All News · 全部资讯8394
  • 固定Mistral模型快照版本实战指南
  • 用Commit Hash固定Llama检查点而非移动标签
  • 固定Grok模型快照版本指南
  • 固定Gemma检查点而非跟踪Main分支
  • 固定Cohere模型版本实战
  • 测试中固定API版本防止无声破坏
  • AI生成电话号码格式的正确处理方式
  • Ollama启动失败:端口11434占用根因与解决
  • Ollama树莓派部署:硬件要求与模型选型指南
  • Ollama并发调优:num_parallel提升吞吐的原理
  • Ollama num_ctx参数:它其实是内存分配而非限制
  • Ollama多模型并行:三个环境变量控制逻辑详解
  • 谷歌发布 Gemini 3.7 Flash:编程与 Agent 专用,半价优惠
  • Agent 记忆不应在聊天中,应在项目文件里
  • 搜索索引前必须对 RTL 文本做五步规范化
  • AI文本水印技术原理解析
  • OpenAI GPT-5.6 Sol 推出 Ultrafast 模式:吞吐提升14倍
  • 浏览器测试中用 MSW Mock OpenAI API 的正确姿势
  • AI Agent 记忆污染与工具劫持风险解析
  • 变形测试:LLM Prompt的测试方法论
  • Chat API消息数组格式深度对比:OpenAI/Anthropic/Gemini
  • 本地部署LoRA:合并与分离的工程权衡
  • LLM 长对话中 KV 缓存的显存开销实测推导
  • 日志模板提取:单遍算法从百万行日志恢复句子结构
  • ML 日志异常检测:模板频率 vs 模板顺序两种检测范式
  • Locust 压测流式接口的坑:响应时间和载荷都测不准
  • 换大模型供应商后 i18n 三处必检点
  • llamafile:下载即跑的跨平台模型可执行文件,一条命令起动推理
  • llama.cpp 服务端并发Slot机制与503处理
  • llama.cpp Slots机制与连续批处理原理
  • llama-server兼容OpenAI API实战指南
  • Lambda 容器镜像部署模型调用实战
  • Lambda 冷启动:Init 时长 vs 模型调用时长的真相
  • Lambda 调用 Bedrock 的 AccessDeniedException 排障
  • K8s GPU 节点池:Taint/Toleration 避坑指南
  • 模型加载慢?K8s 探针配置正确姿势
  • KoboldCpp 上下文切换原理深度解析
  • Provider迁移期间如何避免两套Prompt版本漂移
  • 模型术语差异:Anthropic与OpenAI同名不同义的技术词汇详解
  • ChatGPT Work:桌面自动化、记忆与治理,OpenAI进军AI工作流
  • 流式Chat接口集成测试:压缩、中间件与SSE帧边界的坑
  • Writer基于GLM-5.2开源版推出低成本AI模型
  • AI实现前先写ADR和SDD:让AI在有上下文的条件下做设计决策
  • GPT-4o Strict JSON Schema:端到端强制输出格式的原理与限制
  • Gemini 代码执行工具原理解析
  • RAG Pipeline需要出口匝道:何时该拒答或转人工
  • Qwen 3.5/3.6/3.8全系修复版Jinja对话模板
  • 2026年AI协调缺口:多Agent生产系统的工程框架
  • 提示注入是架构问题:硬件隔离才是解法
  • 用AI Agent和MCP协议调试Shopify webhook
  • React Flow + TypeScript 构建节点式流编辑器
  • 已加载 51 / 8394
8.0
热点
AI SCORE
编程提效2026-08-14 06:40

Chat API消息数组格式深度对比:OpenAI/Anthropic/Gemini

dev.to · AI#API对比#OpenAI#Anthropic
Editor brief · 编辑速览

详细对比三大平台messages数组的结构差异:OpenAI用同质数组+角色区分,Anthropic分离system,Gemini用parts数组。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

对话 transcript 是所有 Chat API 都认同的字段,也是所有 Chat API 塑造得最不一样的字段。重命名这个数组很容易;真正的问题是那些没有对应物的角色、在转换中被丢弃的字段,以及必须从自己的消息里挪出来塞进别人消息里的工具结果。

三种 transcript 形态

OpenAI 的 Chat Completions 端点接收一个扁平的数组,挂在一个 messages 下面。每个条目都有 role 和 content,其他所有东西——指令、tool_calls、tool_results——都作为同一数组里另一个条目,用不同的 role 来区分。这是一个单一同构列表,这也是它成为所有人对标对象的原因。

Anthropic 的 Messages API 也接收 messages,但数组只承载两种 role,系统指令被提取到同级参数里。OpenAI 用额外 role 表达的结构,在这里用 content 内部的类型化 block 来表达。

Google 的 Gemini generateContent 把这个数组整个重命名了。transcript 叫 contents,每个条目是 Content 对象,文本内容放在 parts 数组而不是 content 字段里。系统指令是独立的顶层 systemInstruction,系统 prompt 映射会单独覆盖到它。

Role 词汇表并不对齐

这是 naive adapter 丢失信息的第一个地方。四套词汇有重叠但不是同一套:

OpenAI Chat Completions — system、developer、user、assistant、tool,以及已废弃的 function。

Anthropic Messages — 数组里只有 user 和 assistant。更新版本的模型也接受在数组中间放 system 条目作为操作符通道,但根本不存在 tool role。

Gemini — user 和 model。注意是 model 不是 assistant:这一个重命名是最常见的"翻译后请求在第一次调用就被拒绝"的原因。

所以 assistant turn 的映射是一个方向的 rename 加另一个方向的 rename,而 tool turn 的映射根本不是 rename——它无处可去,这个下文再说。

还有第二个更安静的差异:数组可以长成什么样子。OpenAI 接受连续相同 role 的消息并把它们当作一个 turn 处理。Anthropic 也接受并会合并它们,但要求第一个条目必须是 user turn,所以如果一个 transcript 以 assistant 打招呼开头——这是很多产品打开对话时的常见形态——那个打招呼必须被丢弃或重新安置。这两种行为单独来看都不值得记日志,但都会改变模型看到的内容。一个在发送前对数组做 normalize 的 adapter 应该显式地做这个 normalize,而不是依赖它恰好在对话的 provider 的宽容。

Content:字符串,或类型化 parts 列表

三者都接受纯字符串表示纯文本 turn,也都接受列表当 turn 是多模态或结构化的时候。列表元素在这里分叉了。OpenAI 用带 type discriminator 的 content parts。Anthropic 用 content blocks,概念相同但词汇不同,而且 block 类型宽得多,因为 blocks 同时也是它表达 tool calls、tool results 和 thinking 的方式。Gemini 用 parts,每个 part 是一个恰好只有一个键被填充的对象。

OpenAI message 对象上有两个字段在其他任何地方都没有对应,是通常的牺牲品。name,user 或 assistant message 上可选的参与者标签,会被直接丢弃:如果你的 prompt 依赖它来区分群组对话中的发言者,你必须在翻译前把它折叠进文本。另外,带 populated tool_calls 数组的 assistant message 在 Anthropic 侧会变成一个 assistant message,其 content 列表里每个调用对应一个 tool_use block——参数从 function.arguments 里的 JSON 字符串变成 input 里的解析后对象。

工具结果是难点

这里是一轮 OpenAI 形态的往返。两个工具被并行调用,所以 assistant turn 后面跟着两条 tool 消息:

{
  "messages": [
    { "role": "user", "content": "Weather in Paris and Berlin?" },
    { "role": "assistant", "content": null,
      "tool_calls": [
        { "id": "call_a1", "type": "function",
          "function": { "name": "get_weather",
                        "arguments": "{\"city\":\"Paris\"}" } },
        { "id": "call_b2", "type": "function",
          "function": { "name": "get_weather",
                        "arguments": "{\"city\":\"Berlin\"}" } }
      ] },
    { "role": "tool", "tool_call_id": "call_a1", "content": "18C, rain" },
    { "role": "tool", "tool_call_id": "call_b2", "content": "22C, clear" }
  ]
}

同一交换在 Anthropic 形态下。四条消息变成三条,tool role 消失,两个结果落进同一条 user turn 里:

{
  "messages": [
    { "role": "user", "content": "Weather in Paris and Berlin?" },
    { "role": "assistant", "content": [
        { "type": "tool_use", "id": "toolu_a1", "name": "get_weather",
          "input": { "city": "Paris" } },
        { "type": "tool_use", "id": "toolu_b2", "name": "get_weather",
          "input": { "city": "Berlin" } }
      ] },
    { "role": "user", "content": [
        { "type": "tool_result", "tool_use_id": "toolu_a1",
          "content": "18C, rain" },
        { "type": "tool_result", "tool_use_id": "toolu_b2",
          "content": "22C, clear" }
      ] }
  ]
}

三个 rename 可见:tool_call_id 变成 tool_use_id,arguments 字符串变成解析后对象,相关标识符改变前缀。结构性变化才是咬人的地方——n 条 tool 消息塌陷成一条带 n 个 block 的 user message。如果一个 adapter 每条 tool result 发一条 user message,产生的 transcript 会被接受,但会阻止模型再次发起并行调用,因为它看到的形状是顺序调用的形状。Gemini 再次用不同方式表达同一件事:model turn 上的 functionCall parts 和 user turn 上的 functionResponse parts,通过函数名而非标识符关联。

哪些信息没能幸存

name 字段。没有对应物。要么折叠进文本,要么丢失。

末尾的 assistant turn。OpenAI 接受末尾的 assistant 消息作为 prefill 让模型继续。Anthropic 当前的模型会返回 400 拒绝它,所以 prefill 必须重新表达为 structured-output 约束或系统指令。末尾 assistant turn 是唯一一个会大声失败而非静默失败的形态。

顺序自由。OpenAI 允许 system message 在任意索引位置。Anthropic 只有一个系统参数作用于整个请求,所以对话中段的指令必须变成别的东西——参见系统 prompt 映射。

缓存断点。Anthropic 可以把单个 content block 标记为缓存边界。没有每条消息的字段可以翻译过去,所以一轮往返经过标准化中间表示后会静默丢弃它,缓存也就停止被读取了。

Role 集合和消息级字段是这些 API 厂商扩展最频繁的部分——developer role 和数组中段的 system message 都是在上述形态稳定之后才加进来的。把这里的词汇表当作撰写本文时的已文档化集合,在依赖一个你近期没有发送过的 role 之前重新读一下请求参考。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
变形测试:LLM Prompt的测试方法论
下一篇
本地部署LoRA:合并与分离的工程权衡