1. 背景
早期 LLM API 的核心任务是"给一段上下文,生成一段回复"。因此 Chat Completions 采用了非常直接的抽象:客户端传入 messages[],模型返回一条 assistant message。这一设计适合聊天、问答、摘要、改写、简单结构化输出等场景,也成为大量 OpenAI-compatible API 的事实标准。
但模型能力已经从"文本生成器"演进为"可调用工具的执行单元"。一次模型请求可能包含多轮推理、工具调用、文件检索、网页搜索、代码执行、图片生成、后台任务、结构化输出和流式事件。此时,把所有能力继续塞进 assistant message 会使接口语义变得混乱:message 既是文本回复,又承载工具调用、工具结果、运行状态和执行轨迹。
OpenAI Responses API 的出现,代表抽象从 chat completion 转向 model run / response。Claude Messages API 则走了另一条路线:仍保留 messages[] -> assistant message 的顶层模型,但通过 typed content blocks 表达工具调用、多模态和结构化内容。
2. Chat Completions 的 message-centric 抽象
Chat Completions 的核心模型是:
text
messages[] -> assistant message
典型请求:
json
{
"model": "gpt-4.1",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "解释一下 TCP 三次握手"}
]
}
典型响应:
json
{
"id": "chatcmpl_xxx",
"choices": [
{
"message": {
"role": "assistant",
"content": "TCP 三次握手是..."
}
}
],
"usage": {
"prompt_tokens": 32,
"completion_tokens": 300,
"total_tokens": 332
}
}
这个模型的优点是简单、稳定、容易代理。后端只需要维护一个 messages[] 数组,每轮把历史消息拼进去,再把 assistant message 追加到历史中。对兼容层、简单业务和多模型路由来说,这仍然是最小公分母。
但 Chat Completions 的主要限制也来自这个抽象本身:它默认一次调用的目标是生成一条 assistant message。后续的 function calling、tool calling、structured output、多模态输入,都是在这个模型上逐步扩展出来的。
工具调用时,assistant message 可能不包含最终文本,而是包含 tool_calls:
json
{
"role": "assistant",
"tool_calls": [
{
"id": "call_1",
"type": "function",
"function": {
"name": "get_order",
"arguments": "{\"order_id\":\"A001\"}"
}
}
]
}
后端执行工具后,再把结果作为 tool role message 追加回去:
json
{
"role": "tool",
"tool_call_id": "call_1",
"content": "{\"status\":\"shipped\"}"
}
这套机制可用,但本质上是用 chat message 模拟执行流。模型的动作、工具结果、最终回复都被编码进同一个 messages timeline。随着工具和多模态能力增加,这种编码方式会越来越重。
3. Chat 抽象的工程边界
从后端角度看,Chat Completions 有四个典型边界。
第一,状态完全由客户端维护。多轮对话需要客户端保存历史,并在下一轮请求中重放:
json
{
"messages": [
{"role": "user", "content": "解释 TCP 三次握手"},
{"role": "assistant", "content": "..."},
{"role": "user", "content": "那 TIME_WAIT 呢?"}
]
}
这使 Gateway 必须负责上下文裁剪、摘要、token budget、历史重建和审计持久化。
第二,streaming 主要是 token delta。很多系统把接口抽象成 onToken(token),这对纯文本输出足够,但无法自然表达工具调用开始、工具参数增量、后台状态变化、reasoning summary 等事件。
第三,工具调用是 message 的扩展字段,而不是一等执行事件。后端需要从 assistant message 中解析 tool_calls,再构造 tool message 继续对话。
第四,多模态和结构化输出会增加 message schema 的复杂度。content 从 string 变成 array,工具调用、JSON schema、图片、文件等能力不断扩展,message-centric 模型会变得越来越像一个通用运行时协议。
这就是 OpenAI 需要 Responses API 的主要原因:不是 Chat Completions 不可用,而是它已经不是表达 agentic workflow 的最佳抽象。
4. Responses 的 model-run 抽象
Responses API 的核心模型是:
text
input items -> response/model run -> output items
它不再把"assistant message"作为唯一中心,而是把一次模型调用视为一次 response/run。这个 run 可以产生文本、工具调用、reasoning、文件检索结果、图片结果、后台状态等多种 output item。
简化请求:
json
{
"model": "gpt-4.1",
"input": "查询订单 A001 的状态",
"tools": [
{
"type": "function",
"name": "get_order",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"}
},
"required": ["order_id"]
}
}
]
}
简化响应:
json
{
"id": "resp_xxx",
"status": "completed",
"output": [
{
"type": "function_call",
"call_id": "call_1",
"name": "get_order",
"arguments": "{\"order_id\":\"A001\"}"
}
]
}
后端执行工具后,将结果作为 input item 传回:
json
{
"model": "gpt-4.1",
"previous_response_id": "resp_xxx",
"input": [
{
"type": "function_call_output",
"call_id": "call_1",
"output": "{\"status\":\"shipped\"}"
}
]
}
这套模型的关键变化是:工具调用不再是 message 的附属字段,而是 response 运行过程中产生的结构化 output item。后端可以把模型输出当作事件或动作列表来处理,而不是解析一条 assistant message。
5. Responses 的状态性
Responses API 是"可有状态"的。它不是让模型自动记住所有用户历史,而是提供显式的 provider-side continuation。
核心字段是 previous_response_id:
json
{
"model": "gpt-4.1",
"previous_response_id": "resp_abc123",
"input": "继续上一个问题"
}
它表示本次请求接在上一个 response 后面。OpenAI 可以根据 resp_abc123 恢复上一次 response 的输入、输出、工具调用等上下文。
对比两种状态模型:
text
Chat Completions:
client stores messages[]
client sends full history each turn
provider processes one stateless request
Responses:
provider stores response object
client sends previous_response_id + new input
provider continues from prior response chain
状态性主要体现在五点:
| 维度 | 体现 |
|---|---|
| Response object | 每次调用返回稳定 response.id |
| Continuation | 下一轮可通过 previous_response_id 续接 |
| Tool state | function_call 与 function_call_output 通过 call_id 关联 |
| Background task | response 可处于 in_progress、completed、failed 等状态 |
| Execution trace | output[] 保存结构化执行轨迹 |
需要注意的是,previous_response_id 不是业务会话 ID,也不是用户记忆 ID。它更接近 provider-side execution state pointer。企业系统仍应保存自己的 conversation event log,用于审计、回放、跨模型迁移和故障恢复。
6. previous_response_id 与 prompt cache
previous_response_id 和 cache 有关系,但不是同一个概念。
text
previous_response_id: 解决上下文从哪里续接
prompt cache: 解决相同或稳定上下文能否复用以降低成本/延迟
使用 previous_response_id 后,客户端不必重传完整历史。但服务端恢复出的历史上下文仍可能进入本轮模型上下文,因此仍可能计入 input tokens。区别在于,历史上下文已经在 provider 侧出现过,更容易命中 prompt cache 或内部上下文复用机制。
错误理解:
text
previous_response_id = 历史上下文免费
正确理解:
text
previous_response_id = 少传 HTTP payload
+ provider-side continuation
+ 更稳定的缓存复用机会
+ usage 中仍可能包含历史 input tokens
因此成本统计应以 provider 返回的 usage 为准,而不是以请求体大小估算。
Gateway 里也不要把该字段命名为 cacheKey。更合适的命名是:
ts
type ProviderState = {
provider: "openai"
previousResponseId?: string
}
它是状态续接 token,不是缓存 key。
7. Claude Messages 的 typed-block 抽象
Claude Messages API 与 OpenAI Responses 有相似点,但不是同一个抽象。
Claude 的核心模型仍然是:
text
messages[] -> assistant message with content blocks
请求:
json
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "解释一下 TCP 三次握手"
}
]
}
响应:
json
{
"id": "msg_xxx",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "TCP 三次握手是..."
}
],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 20,
"output_tokens": 300
}
}
Claude 的关键设计是 typed content blocks。工具调用不是纯文本,而是 tool_use block:
json
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "我需要查询订单状态。"
},
{
"type": "tool_use",
"id": "toolu_123",
"name": "get_order",
"input": {
"order_id": "A001"
}
}
],
"stop_reason": "tool_use"
}
工具结果通过下一轮 user message 的 tool_result block 传回:
json
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": "{\"status\":\"shipped\"}"
}
]
}
因此 Claude Messages 不是老式 role + string 的简陋 chat API。它是 message-centric,但 message 内部使用 typed blocks 表达多模态、工具调用和结构化内容。
8. Claude Messages 与 OpenAI Responses 的关键差异
二者相似之处在于都支持结构化内容单元。OpenAI 称为 output/input items,Claude 称为 content blocks。二者都能表达文本、工具调用、多模态内容,都适合映射到内部统一事件模型。
但顶层语义不同。
| 维度 | OpenAI Responses | Claude Messages |
|---|---|---|
| 顶层资源 | response / model run |
message |
| 输入模型 | input / input items |
messages[] |
| 输出模型 | output[] items |
content[] blocks |
| 状态续接 | previous_response_id |
默认重传 messages[] |
| 工具调用 | function_call output item |
tool_use content block |
| 工具结果 | function_call_output input item |
tool_result content block in user message |
| message id / response id | resp_xxx 可用于 continuation |
msg_xxx 不等价于 continuation token |
不要把 Claude 的 msg_xxx 当成 OpenAI 的 resp_xxx 使用。Claude message id 是返回对象标识,不是"下次只传 id 就能续接上下文"的状态指针。
Claude 也支持 prompt caching,但它与 OpenAI previous_response_id 的语义不同。Claude 的典型模式仍然是客户端维护 messages,并通过稳定前缀获得缓存收益;OpenAI Responses 可以通过 response id 显式引用 provider-side response chain。
9. 关键结论
Chat Completions 是 message-centric 的文本/聊天 RPC。它简单、兼容性强,但表达复杂 agent workflow 时会逐渐吃力。
OpenAI Responses 是 run-centric 的模型执行 API。它把文本、工具调用、reasoning、后台任务和多模态结果统一为 typed input/output items,并通过 previous_response_id 支持 provider-side continuation。
Claude Messages 是 message-centric with typed blocks 的 API。它保留 messages 顶层结构,但用 content blocks 表达工具调用和多模态。它与 Responses 在结构化内容上相似,但默认状态模型仍是 client-managed context。
Gateway 的核心设计不应绑定任一厂商的表层 API。更稳的主抽象是:
text
conversation events -> model run -> typed output events/items
Chat Completions、OpenAI Responses 和 Claude Messages 都应作为该核心抽象的 adapter。
Sources
- OpenAI: New tools for building agents --- openai.com/index/new-t...
- OpenAI: Migrate to the Responses API --- platform.openai.com/docs/guides...
- OpenAI: Responses API reference --- platform.openai.com/docs/api-re...
- OpenAI: Chat Completions API reference --- platform.openai.com/docs/api-re...
- OpenAI: New tools and features in the Responses API --- openai.com/index/new-t...
- OpenAI Help Center: Assistants API v2 FAQ --- help.openai.com/en/articles...
- OpenAI: Deprecations --- platform.openai.com/docs/deprec...