把 Responses API 当成“输入文本、返回文本”的端点,会很快被三个问题击穿:多轮上下文放在哪里,流式传输如何从增量还原为可靠状态,模型提出工具调用后谁负责继续运行。更准确的理解是:一次 Response 是由多种 item 组成的状态快照;流是这份快照生成时的语义事件;工具调用则让一次请求变成由应用驱动的多回合协议。
本文面向已经使用 OpenAI JavaScript SDK、需要实现多轮对话或工具流程的开发者。前置知识是异步迭代器、SSE、JSON Schema 与状态机。示例依据 2026-07-16 的官方文档核对,展示的是结构而不是可直接复制的业务实现。
图 1(1600 × 900):WEB/SUN 原创 SVG。蓝色箭头是流式事件,橙色箭头是发起请求或执行工具;最终状态必须由完成事件和完整 Response 决定。
先选状态所有者
创建 Response 的 API 契约把输入、工具、存储、流式和输出配置收进同一次调用;官方会话状态指南再提供三种主要路径:应用手工回放历史、用 previous_response_id 串联响应,或创建持久的 Conversation。它们不是语法偏好,而是数据所有权选择。
手工历史适合需要完全控制留存的系统。请求设置 store: false,应用保存用户消息以及前一次 response.output 中需要继续使用的完整 item,再把它们带入下一次请求。对 reasoning 模型,不要只保存可见文本;官方示例明确把整个 output 追加回历史,以保留 reasoning item 等协议状态。代价是你要自己负责裁剪、加密、迁移与并发冲突。
previous_response_id 适合短链任务。下一次请求引用前一个 Response ID,服务端即可关联上下文。它减少了传参代码,但不是免费压缩:官方文档说明,链中先前输入 token 仍会按输入计费。Response 对象默认保存 30 天,可以通过 store: false 关闭存储,因此使用前应把保留策略写进隐私设计,而不是沿用默认值。
Conversations API 适合跨会话、设备或任务持续的状态。Conversation 持有消息、工具调用和工具输出等 item;与它关联的 item 不受 Response 默认 30 天 TTL 约束。持久并不等于永不管理:应用仍需定义用户删除、租户隔离和业务事实更新策略。
不要把三种方式混成模糊历史。可以给每类产品流程定一条规则:临时提取任务手工且不存储;一次工单处理用 response chain;确需跨端恢复的助手才使用 Conversation。业务数据库继续保存已确认事实,不把聊天上下文当作订单、权限或配置的唯一真相。
多端并发还需要应用自己的版本控制。两个标签页如果都基于同一个前序 Response 继续,会形成两条都“合法”的分支;API 能保存上下文,却不知道哪条应成为业务主线。为会话维护递增 revision 或乐观锁,提交新轮次时校验基线版本,冲突后让用户选择合并、丢弃或从最新事实重新生成。这个控制不能交给模型判断。
把流式输出还原成 reducer
设置 stream: true 后,SDK 返回异步事件流。Responses 使用带 type 的语义事件,而不是一串没有身份的 token。官方流式指南列出的常见文本生命周期包括 response.created、多次 response.output_text.delta、response.completed 与 error;完整事件集合还覆盖 item、内容片段、拒绝、函数参数、文件搜索和代码执行等状态。
因此前端或服务端应维护 reducer,而不是遇到 delta 就直接拼到一个全局字符串:
type RunState = {
phase: 'idle' | 'streaming' | 'tool' | 'completed' | 'failed';
responseId?: string;
textByItem: Map<string, string>;
terminal?: unknown;
};
function reduceEvent(state: RunState, event: Pick<ResponseStreamEvent, 'type'>) {
void event;
// 按 response / item / content_part 的身份更新;
// completed、failed、error 进入互斥终态。
return state;
}
真实 reducer 至少处理三类不变量。第一,delta 只负责预览,持久化和业务提交以终态 Response 为准。第二,同一页面可能并发出现多个 item,不能假设所有文字属于一个槽位。第三,failed、incomplete、拒绝与传输 error 是不同状态:它们对应不同的重试、提示和审计策略。
流也不自动等于安全。官方文档的流式审核风险指出,部分输出更难审核;随生成请求返回的审核分数要到完整输出可用后才到达。高风险场景可以流式展示“处理中”与结构进度,但延迟展示内容;或者将预览明确标记为未完成,终态审核通过后再允许复制、发送或写入。
工具调用是循环,不是回调
官方函数调用指南把流程写成五步:携带工具请求模型,收到函数调用,应用执行代码,把工具输出发回模型,再接收最终回答或更多调用。关键是第三步发生在你的应用中;OpenAI API 返回的是调用意图,不会替你运行自定义函数。
Responses 的 output 可能同时含有 message、reasoning 与一个或多个 function_call item。应用应保留原始输出,按名称分派工具,解析并再次验证 arguments,然后用相同的 call_id 添加 function_call_output。如果模型一次提出多个调用,要先决定是否允许并行;涉及共享状态或副作用时,顺序执行通常更容易维持事务语义。
for (const item of response.output) {
if (item.type !== 'function_call') continue;
const result = await guardedDispatch(item.name, item.arguments, authContext);
nextInput.push({
type: 'function_call_output',
call_id: item.call_id,
output: JSON.stringify(result),
});
}
guardedDispatch 不能只是对象索引。它要做工具白名单、Schema 与领域校验、当前用户授权、超时、幂等、输出裁剪和审计。响应循环还要有最大轮次和总截止时间;最终回答、需要人工确认、预算耗尽与不可恢复错误都应成为显式退出条件。
流式工具参数还有一个容易误判的点:response.function_call_arguments.delta 适合展示参数正在生成,却不适合提前执行。等参数完成事件或完整 item 到达,解析与验证成功后才能触发真实工具。否则半个 JSON、后续被修正的字段或恶意超长参数都可能越过边界。
若工具执行时间明显长于模型生成,把它转换为应用任务并返回可恢复的 job ID。页面可以订阅任务状态,完成后再用工具输出继续 Response;刷新页面也不会丢失进度。不要让一个 HTTP 连接同时承担模型流、长事务和用户界面生命周期,这会让任何一处断线都变成整轮重做。
中断、重试与恢复
用户关闭页面时可以中止本地流读取,但“客户端不再监听”不必然等于远端工作或已启动工具全部撤销。应用需要把取消信号传到自己的工具执行器,并区分可取消的读操作与已提交的写操作。写操作依赖幂等键和查询状态恢复,不能靠再次发同一句 prompt 猜测结果。
重试也要分层:建立连接失败且未获得 Response ID,可以在请求幂等策略允许时重试;流中断但已有 ID,优先查询或从已保存状态恢复;工具超时先根据幂等键查执行记录;模型明确失败则记录错误类型,再决定换模型、降级或交给人工。盲目整轮重放会重复 token 消耗,也可能重复副作用。
失败模式
恢复能力需要专门的契约测试,而不是手动断网观察一次。测试夹具应在首个 delta、参数完成、工具已提交和最终事件前分别切断连接,随后用保存的 Response ID、call ID、会话 revision 与幂等键恢复;每个切点都断言不会重复副作用,也不会把未完成预览升级为最终事实。这样才能证明状态所有者和事件 reducer 真的独立于某条浏览器连接。
常见实现错误包括:只保存 output_text 导致下一轮缺少协议 item;把每个 delta 写数据库制造大量残缺记录;收到函数名后用动态执行直接调用;忘记把 call_id 带回;工具循环没有上限;把拒绝当解析错误;多个浏览器标签同时推进同一个 Conversation,却没有乐观锁或序列号。
另一个隐蔽问题是 UI 提前宣布完成。只有 response.completed 与最终对象满足业务校验时,按钮才应从“停止”切换为“再次运行”,引用或结构化数据才可被标记为已验证。视觉上的流畅不能替代状态正确。
结论
Responses API 的稳定实现依赖三个清晰选择:由谁拥有会话状态,用什么 reducer 消费有身份的语义事件,以及应用如何闭合并限制工具循环。手工历史、response chain 与 Conversation 分别适合不同留存需求;delta 只构成预览,终态对象才构成事实1;工具调用只是候选意图,必须经过应用的验证、授权和幂等执行。
把这三部分写成一个有互斥终态、可恢复游标和明确预算的运行时,才不会在从“能流出文字”走向“能完成任务”时失去控制。
Sources
- Conversation state — OpenAI,访问于 2026-07-16。
- Streaming API responses — OpenAI,访问于 2026-07-16。
- Function calling — OpenAI,访问于 2026-07-16。
- Create a model response — OpenAI,访问于 2026-07-16。
Footnotes
-
这里的“事实”指通过应用业务校验后可提交的结果,不表示模型输出天然正确。 ↩