四个 API 一张表:Chat Completions、Responses、Messages、Embedding 差在哪

四个 API 一张表:Chat Completions、Responses、Messages、Embedding 差在哪

上两篇我们聊了 vLLM Semantic Router 和它的 Decision 3.0。但无论路由怎么选模型,请求本身总得有个形状——它是长成 OpenAI 的 Chat Completions、还是 Responses、还是 Anthropic 的 Messages,直接决定了你的代码怎么写、历史怎么存、成本怎么算。

这篇就把这几个 API 摆在一起看:它们的调用例子、上下文历史存哪、OpenAI 为什么留了两个,顺便把不说话的 Embedding 和只回答"是/否"的 System One 也一起说了。

目录

一、总览:四个 API,四种"对话形状"

先把差异摆到一张表里,后面逐一展开:

API 端点 输入 输出 服务端有无状态
Chat Completions POST /v1/chat/completions messages[](含 system 角色) choices[0].message 无状态
Responses POST /v1/responses input + 顶层 instructions output[] / output_text 可选有状态
Anthropic Messages POST /v1/messages messages[] + 顶层 system content[](内容块) 无状态
Embeddings POST /v1/embeddings input(文本数组) 向量数组 无状态

一句话记住它们的性格:

  • Chat Completions 是行业普通话——几乎所有厂商和框架都兼容它。
  • Responses 是Agent 原生接口——推理状态、内置工具、服务端历史都在这。
  • Messages 是Anthropic 的方言——把 system 拎到顶层,把内容拆成"块"。
  • Embeddings 是唯一不生成文本的——它只把文本变成向量。

二、Chat Completions:兼容生态的"普通话"

2023 年就定型的接口,也是被抄得最多的一份。请求就是一组 messages,system 是其中一种角色:

curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o",
    "messages": [
      {"role": "system", "content": "你是一个简洁的助手。"},
      {"role": "user", "content": "用一句话解释 embedding。"}
    ]
  }'

返回里读 choices[0].message.content:

{
  "id": "chatcmpl-...",
  "choices": [
    { "index": 0, "message": { "role": "assistant", "content": "Embedding 是把文本映射成..." } }
  ]
}

关键点:它是无状态的。服务端不记得你上一次问过什么,每一轮你都要把完整的历史 messages 再发一遍:

{
  "model": "gpt-4o",
  "messages": [
    {"role": "system", "content": "你是一个简洁的助手。"},
    {"role": "user", "content": "用一句话解释 embedding。"},
    {"role": "assistant", "content": "Embedding 是把文本映射成..."},
    {"role": "user", "content": "再举一个例子。"}
  ]
}

它的好处也正是"无状态":逻辑简单、跨供应商可移植、历史完全在你手里。DeepSeek、Qwen、vLLM、Ollama、甚至 Anthropic 自己都提供了兼容层——这就是它能当"普通话"的原因。

三、Responses:为 Agent 重造的那一个

2025 年推出的新接口,端点是 /v1/responses。它在形状上像 Chat Completions,但在能力上是为推理模型和 Agent 从零设计的。

最简调用,注意 instructions 是顶层字段,input 可以直接是字符串:

curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4.1",
    "instructions": "你是一个简洁的助手。",
    "input": "用一句话解释 embedding。",
    "store": true
  }'

返回是一个输出项数组 output[],而且很方便地给了一个拼好的 output_text:

{
  "id": "resp_abc123",
  "output": [
    { "type": "reasoning", "summary": [] },
    { "type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "Embedding 是把文本映射成..."}] }
  ],
  "output_text": "Embedding 是把文本映射成..."
}

它和 Chat Completions 拉开距离的地方,全在这几项:

  • 推理项(reasoning items):推理模型产生的思考状态能作为 output 里的一类项被保留和回传,这是 Chat Completions 的扁平 message 表达不了的。
  • 内置工具:Web Search、File Search、Code Interpreter、Computer Use 都是原生 tools,不用你自己搭。
  • 服务端历史:store: true 存下这次响应,下次直接用 previous_response_id 续上(下一节细说)。
  • 后台执行:长任务可以 background 方式跑,再回来取结果。
  • 更细的流式事件:按语义事件流式返回,而不是只吐 delta。

官方现在的口径也很明确:新模型、新功能优先只在 Responses 上做,Chat Completions 进入"维护兼容"状态。

四、Anthropic Messages:把 system 拎出来,把内容变成块

Claude 的接口是 /v1/messages,和 OpenAI 有两个一眼可见的差异:

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4",
    "max_tokens": 1024,
    "system": "你是一个简洁的助手。",
    "messages": [
      {"role": "user", "content": "用一句话解释 embedding。"}
    ]
  }'
  1. system 是顶层参数,不是一条 message。OpenAI 把 system 当成第一种角色塞进 messages,Anthropic 则把它拎出来单独放。这不只是美观差异——它意味着 system 的语义被独立对待。
  2. max_tokens 是必填的。OpenAI 里它是可选,Anthropic 里你必须显式给出上限。

返回的 content 是一个内容块数组,而不是一段字符串:

{
  "content": [
    { "type": "text", "text": "Embedding 是把文本映射成..." }
  ],
  "stop_reason": "end_turn"
}

这个"块"的设计在工具调用上体现得最明显。Claude 要调工具时,返回一个 tool_use 块,其中 input 是已经解析好的对象;而 OpenAI 的 function call 返回的是 JSON 字符串 arguments,你还得自己 json.loads 一次。工具结果则以一个 tool_result 块放在 user 消息里回传。

Messages 同样是无状态的——多轮就是把历史 messages 一路拼下去。

五、上下文历史到底存在哪

这是最容易被忽略、却最影响成本和架构的一处差异。历史有三种存法:

MERMAID_BLOCK_0

第一种:客户端持有(Chat Completions / Messages)

服务端不存任何东西。历史躺在你的数据库、内存或队列里,每次请求把整段 messages 重新发一遍。

  • 好:完全可控,能随意裁剪、摘要、改角色;换供应商不用迁移数据。
  • 坏:重复计费——第 10 轮的请求里,前 9 轮的内容要再算一次 token;上下文越长越贵越慢。

Anthropic 和 OpenAI 都提供了提示缓存(prompt caching)来缓解:命中的前缀部分按更低价格计费。但注意——缓存的是计算,不是历史,你仍然要把 messages 发过去。

第二种:服务端持有(Responses 的 previous_response_id)

store: true 之后,历史存在服务端。下一轮你只传一个 id:

curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4.1",
    "previous_response_id": "resp_abc123",
    "input": "再举一个例子。"
  }'
  • 好:省带宽、省心智,推理项等复杂状态自动保留。
  • 坏:状态在别人机器上——不可跨供应商移植,涉及数据驻留时要格外小心;也可以用 store: false 自己带历史,但那等于退回第一种。

需要显式管理时,还有一层 Conversation 资源:创建一次对话,把多轮 response 挂上去,适合一个会话要跨多个 Agent 或多次运行的场景。

第三种:外部向量库(Embeddings + 检索)

严格说这不是"对话历史",而是长期记忆。你把历史切片成文本、算成向量存近邻库,需要时检索出最相关的几段,拼进当前请求。它和上面两种是互补关系,不是替代。

一句话总结:Chat Completions 和 Messages 把历史放在你这边,Responses 把历史放在它那边。 你的 Harness 里那个"agent 循环 + 任务状态",其实就是第一种存储模型的具体实现——这也是为什么上一篇文章里说,路由层不负责存历史,那是 Harness 的活。

六、为什么 OpenAI 有两个 API

每次讲到这里都有人问:既然 Responses 更好,为什么不直接换掉 Chat Completions?

因为这两个 API 服务的是两类完全不同的诉求。

Chat Completions 是"标准接口"。 它 2023 年定型,被几乎所有厂商和框架兼容——它已经是生态里的"最大公约数"。无数 SDK、Agent 框架、自建推理服务只认这一个形状。动它,等于动整个生态。

Responses 是"Agent 原生接口"。 Agent 需要的东西——保留推理状态、内置工具、服务端会话、后台执行——Chat Completions 一件件往外挂(打补丁)都很别扭,不如重做。所以 OpenAI 选择新旧并存:

  • 旧项目、兼容性优先、只想换个模型 → 继续用 Chat Completions。
  • 新项目、要跑 Agent、要用推理模型的内置能力 → 用 Responses。
  • 官方策略是渐进迁移,并给了迁移指南,而不是一刀切停用。

还有一个现实原因:Chat Completions 是"方言最多"的接口。Anthropic 的 Messages、各家国产模型的兼容层,都在往它对齐。OpenAI 保留它,等于保留了一个通用的对接面——这跟前面 vLLM SR 那个"统一 API 后面站一群模型"的思路是一回事:接口稳定,后端随便换。

七、Embedding:不生成文本的那一个

上面三个 API 都在"生成",而 Embedding 只做一件事:把文本变成向量。

curl https://api.openai.com/v1/embeddings \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "text-embedding-3-small",
    "input": ["今天天气不错", "外面阳光很好"]
  }'

返回一个向量数组,每个输入对应一个浮点数组:

{
  "data": [
    { "index": 0, "embedding": [0.021, -0.004, 0.113, ...] }
  ],
  "usage": { "prompt_tokens": 6, "total_tokens": 6 }
}

它没有 choices,没有 content,没有"对话"这个概念——因为它根本不是对话接口。它的用途是相似度:语义相近的两段文本,向量夹角就小。RAG、语义搜索、去重、聚类、分类,全靠它。

两个容易踩的坑:

  • 维度要固定:存进向量库之前先定好维度(比如 1536),换模型可能不兼容。
  • 别指望 Anthropic 有独立的 embedding 端点:Claude 那边不提供第一方 embedding API,官方建议用 Voyage AI 之类的第三方。所以"用 Claude 做对话 + 用 OpenAI 做 embedding"是很常见的组合。

八、System One:只回答是/否的第五个 API

最后说一个不在上面体系里、但和它们并列的接口。前面聊 Decision 3.0 时提过 System One——决策模型的 API,端点长这样:

curl -sS http://localhost:8801/v1/systemone \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "vllm-sr/auto",
    "state": "I cannot sign into my account.",
    "questions": {
      "task": {
        "type": "choice",
        "instructions": "Which team should handle this request?",
        "criteria": {
          "account": "Account access or authentication",
          "billing": "Payments, invoices or refunds"
        }
      }
    }
  }'

看形状就懂了:这里既没有 messages(因为不对话),也没有 input(因为它不生成)。它给的是 state(一段文本或 JSON)+ questions(Choice / Yes-No / Score),拿回来的是每个选项的概率。

放进同一张表里对比一下:

维度 语言 API(Chat/Responses/Messages) System One
输入 messages / input state + questions
输出 生成的文本 每个选项的概率
用途 说话 判断
历史 客户端或服务端持有 无对话,无历史

为什么值得单独列一个:路由、护栏、工具选择这些高频动作,本质上都是判断,不是生成。让一个 27B 模型"生成一段话"再解析,既慢又贵还容易出错;换成 System One,几十毫秒内直接给你概率,还能设阈值。这就是"System One(快思考)"和"System Two(慢思考)"的分工——日常判断走前者,真正的推理才交给后者。

而且它和语言 API 可以共用一个前端:vLLM SR 既代理 /v1/chat/completions、/v1/responses、/v1/messages 这三种语言接口,也代理 /v1/systemone 这一个决策接口。你的应用只管发请求,选哪个模型、走哪条路,交给路由层。


把这五个接口排在一起,其实是一条清晰的演化线:

Embedding 把文本变成向量(不生成)→ Chat Completions / Messages 把对话变成标准化请求(无状态)→ Responses 把 Agent 的状态收进服务端(有状态)→ System One 把判断从生成里剥离出来(只给概率)。

理解了"历史存在哪"和"是生成还是判断"这两把尺子,再选 API 就不会迷路:要兼容用 Chat Completions,要 Agent 用 Responses,要接 Claude 用 Messages,要检索用 Embeddings,要快速做判断用 System One。


作者: itech001
来源: 公众号:AI人工智能时代(the-ai-era)
网站: https://www.theaiera.top/
关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top

本文首发于 AI人工智能时代,转载请注明出处。

posted @ 2026-10-14 21:38  iTech  阅读(9)  评论(0)    收藏  举报