Responses API 与 Chat Completions 的选择主要取决于由谁来维护对话状态,而不是哪个端点看起来更新。Chat Completions 要求你的编排器在每一轮对话中重新发送整个 messages 数组。而 Responses 可以通过存储的响应 ID 继续对话。我们查阅了 TokenLab 在 2026-10-03 发布的 API 文档及实时模型 API,答案还取决于你的模型支持哪些格式。因此,我们从一个决策表开始。
| 智能体需求 | 选择 | 原因 |
|---|---|---|
| 现有的 OpenAI 兼容聊天客户端,可在多个模型间移植 | Chat Completions (/v1/chat/completions) |
根据 API 格式指南(观察于 2026-10-03),具有最广泛的模型兼容性 |
| 从存储的服务器端响应继续对话 | Responses (/v1/responses) |
previous_response_id 和 store 是 Responses 的字段 |
| 在轮次之间进行任意历史编辑、修剪或消息屏蔽 | Chat Completions | 你拥有完整的 messages 数组 |
| 后台响应或 Responses 事件 | Responses | 针对支持它们的模型进行了文档说明 |
| 带有延续功能的持久 WebSocket 连接 | Responses | wss://api.tokenlab.sh/v1/responses |
| 可移植的 JSON 对象输出 | Chat Completions | response_format: {"type": "json_object"} |
模型未列出 openai_responses |
Chat Completions 或模型原生格式 | Responses 仅适用于列出该格式的模型 |
| Claude 思维块 (thinking blocks)、Claude 工具调用、提示词缓存 (prompt-cache) 字段 | Anthropic Messages (/v1/messages) |
Chat Completions 不保证这些字段 |
Gemini contents、文件、缓存内容、Gemini 工具 |
Gemini (/v1beta/models/:model:generateContent) |
在 Gemini 路由上保留 Gemini 原生字段 |
关键要点
- Chat Completions 是客户端管理状态。Responses 可以通过
previous_response_id引用服务器端状态。 - 工具调用从嵌套的
tool_calls加role: "tool"消息转变为类型化的function_call和function_call_output项。 - 流式传输从
choices[0].delta数据块变为命名事件,且 Responses 客户端必须同时监控response.failed和response.incomplete。 - 格式支持因模型而异。在 2026-10-03,我们三个示例模型中仅
gpt-5.6-terra接受openai_responses。 - 切勿在同一对话中混合使用格式。在下一次请求前,请转换并验证已存储的历史记录。
Responses API 与 Chat Completions:同一任务,两种请求
以一个单轮任务为例:用两句话解释为什么天空是蓝色的。以下是使用 API 格式指南(观察于 2026-10-03)中的客户端设置进行的 Chat Completions 请求:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
)
response = client.chat.completions.create(
model="gpt-5.6-terra",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain why the sky is blue in two sentences."},
],
max_tokens=1000,
)
print(response.choices[0].message.content)
以下是作为 Responses 请求的相同任务:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
)
response = client.responses.create(
model="gpt-5.6-terra",
instructions="You are a helpful assistant.",
input="Explain why the sky is blue in two sentences.",
max_output_tokens=1000,
)
print(response.output_text)
对于这个简单的例子,字段是一一对应的。系统消息变为顶层的 instructions。messages 变为 input,它接受字符串或项数组。max_tokens 变为 max_output_tokens。
响应体则有较大差异。Chat Completions 返回 choices[0].message 和 finish_reason。Responses 返回一个类型化的 output 数组和一个 status 字段。创建响应参考文档(观察于 2026-10-03)列出了 queued、in_progress、completed、incomplete、failed 和 cancelled,并提示即使 HTTP 请求成功,也应检查 status。使用量键值也不同:一边是 prompt_tokens 和 completion_tokens,另一边是 input_tokens 和 output_tokens。参考示例分别显示了 20/9/29 和 10/12/22。这些是示例负载,而非测量值。
继续对话
想象一个支持智能体刚刚回答了一个问题,现在收到“订单 #4472 怎么样?”。使用 Chat Completions,你需要将新的用户消息附加到存储的数组中并重新发送所有内容。使用 Responses,你只需传递上一个响应的 id 并发送新的一轮:
{
"model": "gpt-5.6-terra",
"previous_response_id": "resp_abc123",
"input": "What about order #4472?"
}
store 默认为 true,因此除非你将其关闭,否则之前的响应会被保留以供后续检索。你的应用程序仍需在各次跳转之间持久化返回的 id。如果你需要自定义修剪或消息屏蔽,无状态格式是更直接的选择。
一个粗略的估算显示了为什么无状态重发在智能体规模下很重要。假设在 20 次调用中的每一次都重发 10,000 个 token 的历史记录。那就是 200,000 个输入 token。按照 gpt-5.6-terra 每 100 万个 token 0.60 美元的输入价格计算,即 200,000 × 0.60 / 1,000,000 = 0.12 美元(估算值;假设 token 计数,无缓存)。该价格来自 实时模型 API,观察于 2026-10-03,定价更新于 2026-10-02T16:53:30.068Z。
有两个注意事项。我们阅读的文档没有说明服务器持有的上下文是否计为输入 token,因此请在自己的流量上比较 usage。此外,超过 272K 输入 token 的请求,其完整请求的定价为输入 2 倍、输出 1.5 倍:每 100 万个 token 分别为 1.20 美元和 5.40 美元,而不是 0.60 美元和 3.60 美元。长无状态历史记录可能会跨越该界限。
工具调用:请求与响应形状
两种格式都遵循 结构化输出与工具调用指南(观察于 2026-10-03)中的循环。发送工具、读取调用、在自己的后端运行它、以相同格式返回结果,并重复直到收到最终答案。形状有所不同。
Chat Completions。 工具声明为 {"type": "function", "function": {...}}。模型在 choices[0].message 上以 tool_calls 回答,且 finish_reason 为 tool_calls。要继续,你需要重发整个数组,包括带有 tool_calls 的助手消息以及带有 tool_call_id 的 role: "tool" 消息。以下值仅供说明;lookup_customer 是指南中使用的工具名称:
{
"model": "gpt-5.6-terra",
"messages": [
{"role": "user", "content": "Look up ana@example.com"},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_xyz789",
"type": "function",
"function": {"name": "lookup_customer", "arguments": "{\"email\": \"ana@example.com\"}"}
}
]
},
{"role": "tool", "tool_call_id": "call_xyz789", "content": "{\"status\": \"active\"}"}
]
}
Responses。 模型在 output 内以 function_call 项回答,每一项都带有 call_id、name 和 arguments。你将结果作为 function_call_output 输入项返回,并指向之前的响应:
{
"model": "gpt-5.6-terra",
"previous_response_id": "resp_abc123",
"input": [
{
"type": "function_call_output",
"call_id": "call_xyz789",
"output": "{\"status\": \"shipped\", \"tracking_number\": \"1Z999\"}"
}
]
}
你无需重建中间的助手工具消息。parallel_tool_calls 在两种格式中都存在。在创建响应参考文档中,它默认为 true。Chat 参考文档列出它时没有默认值,并称它适用于“所选模型支持时”。
我们发现了一个缺口。我们阅读的页面仅以一般术语描述了 Responses 的 tools 和 tool_choice。在移植工具模式之前,请阅读创建响应参考文档中的工具定义形状,并在目标模型上进行测试。此外,请遵循指南中的以下规则:
- TokenLab 不会在格式之间转换内置工具。
- 在产生任何副作用之前,请验证工具参数。
- 使工具执行具有幂等性,因为客户端重试可能会重复相同的调用。
- 应用你自己的权限检查。
流式传输:客户端必须处理的事件
Chat Completions 流式传输 SSE 数据块。你的客户端读取 choices[0].delta.content,记录 finish_reason,并跳过没有 choices 的数据块。使用 stream_options={"include_usage": True} 时,使用量会出现在这样的数据块中。流式传输指南(观察于 2026-10-03)将 stop 视为完成,length 视为 token 限制,tool_calls 视为运行工具的请求。
Responses 流式传输命名事件。指南的消费者处理这些事件:
response.output_text.delta用于追加文本response.completed用于成功标记response.failed、response.incomplete和error作为终止问题
import os
from openai import OpenAI
with OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
timeout=30.0,
max_retries=0,
) as client:
completed = False
with client.responses.create(
model="gpt-5.6-terra",
input="Write a short poem.",
stream=True,
) as stream:
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
elif event.type == "response.completed":
completed = True
elif event.type in {"response.failed", "response.incomplete", "error"}:
raise RuntimeError(f"Stream ended with {event.type}")
if not completed:
raise RuntimeError("Stream closed before response.completed")
指南中得出三条客户端规则:
- 在第一个事件到达后,中断的流是不完整的,不会自动重启。如果有所帮助,请保留部分输出,但不要将其标记为完成。重复的请求可能会有所不同并再次产生费用。
- 使用 SDK 解析 SSE,因为一个网络数据块不一定是一个完整的事件。
- 当用户停止或断开连接时,取消上游流,并在你的服务器上保留该键。
Responses 还提供了一个位于 wss://api.tokenlab.sh/v1/responses 的 WebSocket。那里的响应总是流式的,不支持 background 和 response.cancel,每个连接一次运行一个活动响应,连接持续时间最长 60 分钟。事件带有响应范围内的 sequence_number。名为 error 的流事件是扁平对象,而连接和协议故障使用嵌套的 error 对象,因此请处理这两种形状。
发送前检查模型接受的格式
从模型页面或 GET /v1/models/{model} 读取 tokenlab.accepted_request_formats,如 获取模型参考文档(观察于 2026-10-03)中所述:
curl "https://api.tokenlab.sh/v1/models/gpt-5.6-terra"
响应包含一个带有 accepted_request_formats、pricing、限制和 lifecycle 的 tokenlab 对象。这些值映射到端点,如 错误指南 所述:openai_chat_completions 到 /v1/chat/completions,openai_responses 到 /v1/responses,anthropic_messages 到 /v1/messages,以及 gemini_generate_content 到 /v1beta/models/{model}:generateContent。接受的格式仅确认端点。单个工具和字段仍可能因模型而异。
我们在 2026-10-03 针对实时模型 API 检查了本文引用的三个模型:
| 模型 | 接受的格式 | 最大输入 / 输出 token | 每 100 万输入 / 输出价格 (USD) | 来源 |
|---|---|---|---|---|
gpt-5.6-terra |
openai_chat_completions, openai_responses |
1,050,000 / 128,000 | 0.60 / 3.60 (输入最高 272K) | 实时 API |
claude-sonnet-5 |
anthropic_messages, openai_chat_completions |
1,000,000 / 128,000 | 0.90 / 4.50 | 实时 API |
gemini-3.5-flash |
gemini_generate_content, openai_chat_completions |
1,048,576 / 65,536 | 0.75 / 4.50 | 实时 API |
这三个模型都处于活动生命周期阶段,没有替换模型,定价更新于 2026-10-02T16:53:30.068Z。因此,今天只有 gpt-5.6-terra 接受 Responses 请求。另外两个通过 Chat Completions 或其原生路由工作。在发布前请重新检查,因为生命周期和价格可能会发生变化。
错误处理在这里也很重要。创建调用中拼写错误的模型会返回 400 model_not_found,并可能包含 did_you_mean。向 GET /v1/models/{model} 查询未知 ID 会返回 404 model_not_found。带有 retryable: false 的 503 all_channels_failed 意味着重复请求无济于事。除非你的产品有权这样做,否则不要自动切换模型。
迁移清单:Responses API 与 Chat Completions
将智能体从 Chat Completions 迁移到 Responses 时,请使用此顺序。
- 确认模型列出了
openai_responses。 如果没有,请在此停止。留在 Chat Completions 是受支持的路径。 - 选择切换边界。 在 Responses 上开始新的对话。不要在飞行中切换实时线程,因为这两种格式表示状态和工具调用的方式不同。
- 刻意转换存储的历史记录。 如果必须迁移旧线程,请在下一次请求前在你的应用程序中转换并验证它们。
- 重命名核心字段。
messages变为input,系统或开发者消息变为instructions,max_tokens或max_completion_tokens变为max_output_tokens。 - 重写工具循环。 将
choices[0].message.tool_calls解析替换为function_call项,并将role: "tool"消息替换为以call_id为键的function_call_output项。 - 持久化响应 ID。 存储每个
id,以便你可以传递previous_response_id。预先决定store和保留策略,并检查 TokenLab 保留存储响应的时长,因为我们阅读的页面没有说明。 - 重写流消费者。 处理
response.output_text.delta、response.completed、response.failed、response.incomplete和error。将没有response.completed的关闭流视为失败。 - 检查
status。 HTTP 成功仍可能带有incomplete或failed。 - 审计可能无法携带的参数。 创建响应页面列出了
temperature、top_p、tools、parallel_tool_calls、reasoning和text。它没有列出n、logprobs、logit_bias、frequency_penalty、presence_penalty、seed、top_k、stop、user或stream_options。在依赖它们之前,请在你的模型上验证每一个。TokenLab 可能会透传未知字段,但这并不意味着模型接受它们。 - 映射 JSON 输出。 Chat 使用
response_format。Responses 使用text对象,且text.format的支持取决于模型。 - 围绕缺失的端点进行规划。 目前无法使用 Cancel、input-items 和 input-tokens 端点。删除存储的响应不会取消生成。
- 关闭重试进行重放测试。 保持
max_retries=0,并在你自己的代码中决定何时重试是安全的。超时的生成不应自动重放。
常见问题解答
我可以向 Claude Sonnet 5 或 Gemini 3.5 Flash 发送 Responses 请求吗?
根据 2026-10-03 的实时模型 API,不可以。claude-sonnet-5 列出了 anthropic_messages 和 openai_chat_completions。gemini-3.5-flash 列出了 gemini_generate_content 和 openai_chat_completions。请使用 Chat Completions 或原生路由,并在发布前再次检查 accepted_request_formats。
当我使用 previous_response_id 时,我是否停止为之前的轮次付费?
我们阅读的页面没有说明。它们记录了服务器可以从存储的响应继续,但没有说明恢复的上下文如何计费。在计划节省成本之前,请运行几次多轮调用,并将 usage 字段与你的无状态基准进行比较。
我可以在对话中间切换格式吗?
不可以。TokenLab 的指南建议在每次对话中保持一种格式,因为每种格式表示状态和工具调用的方式不同。如果必须迁移存储的历史记录,请在发送下一次请求前在你的应用程序中转换并验证它。
如何取消正在运行的 Responses 请求?
目前没有取消端点。删除存储的响应不会取消生成,且 WebSocket 响应不支持 response.cancel。对于流,当用户停止或断开连接时,请从你的服务器关闭上游流。
在更改端点之前,请打开 TokenLab 模型目录 并确认你智能体调用的确切模型的 accepted_request_formats。
来源
价格更新于 2026-10-03
- TokenLab Docs: API formats资料更新于 2026-10-03
- TokenLab Docs: Create Response资料更新于 2026-10-03
- TokenLab Docs: Create Chat Completion资料更新于 2026-10-03
- TokenLab Docs: Structured Outputs & Tool Calling资料更新于 2026-10-03
- TokenLab Docs: Streaming资料更新于 2026-10-03
- TokenLab Docs: Errors agents can act on资料更新于 2026-10-03
- TokenLab Docs: Migration Guides资料更新于 2026-10-03
- TokenLab Docs: Get a Model资料更新于 2026-10-03
