Appearance
功能规格说明:Responses API 传输
创建日期:2026-11-04
字段改名(2026-11-07):capabilities.responsesApi 布尔改为 capabilities.apiType 枚举("chat" 默认 / "responses" / "messages"),不保留兼容别名;新增的 "messages" 取值见 core/anthropic-messages-api.md,改名带来的发版顺序约束见该规格的边界情况 2。
用户场景与测试 (必填)
用户故事:按声明为模型选择 API 端点(优先级:P1)
作为要在新代际 OpenAI 模型(gpt-6.1-sol、GPT-6 Astra、gpt-5.x-pro 等)上使用 Wave 的用户,我希望在 settings.json 里为某个模型声明「它必须走 Responses API」,Wave 就对这个模型的 agent 请求改用 Responses 端点——否则这类模型只要带 tools 就必然 400,不论 reasoning_effort 传什么。
为什么是这个优先级:这是本功能的全部前提。GPT-5.4 之后 Chat Completions 只有 reasoning_effort: "none" 才允许 function tools,而这类 6 代模型根本没有 none 档(官方模型页写明 "Tool calling requires the Responses API."),两条限制交集为空 → 对 Wave 而言这些模型结构上不可用,任何参数调整都绕不过去。
独立测试:用声明了 capabilities.apiType: "responses" 的模型配置发起一次带工具的 agent 调用,断言请求打到 Responses 端点且请求体是 Responses 形状;把声明去掉或改成 "chat",断言请求仍打到 Chat Completions 且请求体与今天逐字段一致。
验收场景:
- 假设
models["<name>"].capabilities.apiType为"responses",当 agent 主循环发起一轮带工具的请求,则该请求走 Responses 端点(client.responses.create),请求体为 Responses 形状(input+ 扁平tools) - 假设模型的
apiType未声明或为"chat"(默认),当 agent 发起请求时,则行为与今天完全一致:走 Chat Completions,请求体不新增任何字段 - 假设本轮请求实际使用的模型是覆盖后的
fastModel/visionModel/ 显式指定模型,当它与主模型的apiType不同时,则按该请求实际使用的模型的apiType选择端点(主模型走 Responses 不影响 fastModel 的子代理请求走 Chat Completions,反之亦然) - 假设仓库里不存在任何模型名清单、正则或内置默认表,当用户把一个新模型写进
models并声明apiType: "responses"时,则无需升级 Wave 即可使用 - 假设声明了
apiType: "responses"的模型在 Responses 端点返回错误,当该错误上抛给用户时,则错误文案保留服务端原文,且不会静默改用 Chat Completions 重试(一次请求只打一个端点)
用户故事:请求参数与工具声明的映射(优先级:P1)
作为走 Responses 端点的用户,我希望 Wave 把现有配置里的生成参数、系统提示、工具声明和已发生的工具调用历史翻译成 Responses 的形状,这样 models.<name>.options 里现有的写法继续可用、工具调用能连续多轮;同时我写在 options 里的其它字段仍然原样发出,不被 Wave 过滤或改写。
为什么是这个优先级:端点换了而参数照发就是 400。options 是用户手写、按 Chat Completions 校验的字段,必须由 Wave 翻译——不能让用户为同一个模型维护两套配置。反过来,options 是懂行的人写的:Wave 只负责键名映射(reasoning_effort → reasoning.effort 这类),不判断某个键在 Responses 上是否合法。静默丢弃用户写完的配置比让它 400 更糟——400 至少说得清是哪一行。
独立测试:构造含 system 提示、图片输入、工具声明、一轮已完成的「assistant tool_call + 工具结果」历史,发起一次 Responses 请求,逐字段断言请求体符合下表;并断言 options 里一个未被映射的自定义键(如 seed)在请求体里原样存在,没有被丢弃或改名。
| 现有配置(Chat Completions 形状) | Responses 形状 |
|---|---|
messages | input(items 数组) |
system 消息 / SystemPromptBlock[] | instructions(拼接成字符串) |
max_completion_tokens | max_output_tokens(数值不变) |
response_format | text.format(name/strict/schema 上移一层) |
reasoning_effort | reasoning.effort |
verbosity | text.verbosity |
tools[].function.{name,description,parameters,strict} | tools[].{name,description,parameters,strict}(扁平) |
tool_choice 具名工具 | {type:"function", name} |
assistant tool_calls[] | function_call item(call_id/name/arguments) |
role:"tool" 消息 | function_call_output item(call_id/output) |
| 图片 content part | input_image(image_url)/ input_text |
其它所有 options 键 | 原样透传(Wave 不增删、不改名、不判合法性) |
验收场景:
- 假设
models.<name>.options里有reasoning_effort: "high",当走 Responses 时,则发出reasoning: { effort: "high" }(不是顶层的reasoning_effort) - 假设
options里有verbosity,当走 Responses 时,则发到text.verbosity - 假设
options里有response_format: {type:"json_schema", json_schema:{name, schema, strict}},当走 Responses 时,则发成text.format: {type:"json_schema", name, schema, strict}(json_schema这层被去掉) - 假设生效的输出上限来自
max_completion_tokens或会话级上限,当走 Responses 时,则发到max_output_tokens,数值不变(该额度含推理 token) - 假设
options里有上表未列出的键(例如seed、stop、presence_penalty,或用户自己写的 Responses 形状字段如reasoning: {effort:"high"}),当走 Responses 时,则该键连同其值原样出现在请求体里,Wave 既不丢弃也不改名——配错了由服务端 400 直接反馈 - 假设用户同时写了
reasoning_effort与 Responses 形状的reasoning,当发生映射时,则用户显式提供的reasoning优先,映射结果不覆盖它 - 假设系统提示是分块
SystemPromptBlock[],当走 Responses 时,则所有块按顺序拼成instructions字符串;假设同一模型还声明了promptCaching: true,则不注入cache_control标记(Responses 侧由服务端自动缓存,该标记不适用,也不算错误配置) - 假设工具声明是
{type:"function", function:{name, description, parameters, strict?}},当走 Responses 时,则发成{type:"function", name, description, parameters, strict?},工具名与参数 schema 一个字节都不变 - 假设
tool_choice为"auto"/"none"/"required",则同名透传;假设是强制具名工具,则发成{type:"function", name};延伸思考生效时把强制tool_choice降为auto的既有规则照旧生效 - 假设历史里有 assistant 的
tool_calls,当翻译成input时,则每个调用成为一条function_callitem(带call_id、name、arguments),同一条 assistant 消息的正文成为它前面的messageitem;假设历史里有配对的role:"tool"消息,则成为一条function_call_outputitem,call_id与上者一致 - 假设用户消息含图片,当走 Responses 时,则content part 映射为
input_image(URL 原样)与input_text;非视觉模型剥离图片的既有行为不变 - 假设历史来自多个轮次(既有 Responses 轮次也有此前走 Chat Completions 的轮次),当组装
input时,则整段历史按同一套规则统一翻译,不因来源不同丢内容
用户故事:响应解析与用量口径(优先级:P1)
作为用户,我希望 Responses 返回的结果被解析成与 Chat Completions 等价的东西——正文、推理内容、工具调用、结束原因、token 用量——这样上层的消息渲染、工具执行、上下文占用、错误处理都不需要为新端点分叉。
为什么是这个优先级:端点换了但下游语义不变是这个设计成立的关键。finish_reason、usage 口径一旦不一致,自动续写、上下文百分比、缓存 token 统计都会静默错乱。
独立测试:用固定的 Responses 响应体(正文 + 推理 + 一个 function_call,status/incomplete_details 可切换)调用解析路径,断言正文、推理内容、工具调用、结束原因、usage 与等价 Chat Completions 响应解析结果完全一致。
验收场景:
- 假设
output里有一条type:"message"的 item 且其content含若干output_text,则 assistant 正文等于这些output_text的拼接 - 假设
output里有type:"reasoning"的 item,则其文本进入推理内容通道(与今天 Chat Completions 的reasoning_content等价),UI 思考块照常渲染 - 假设
output里有type:"function_call"的 item,则变成工具调用条目(工具名 + 参数 JSON 可直接执行),其call_id作为后续工具结果回填的 id - 假设一次响应里有多个
function_callitem,则全部按顺序转成工具调用,并行执行能力不变 - 假设
status: "incomplete"且incomplete_details.reason为"max_output_tokens",则映射成finish_reason: "length",core/ai-error-handling.md的截断自动续写照常触发 - 假设
status: "completed"且没有工具调用,则finish_reason: "stop";假设有工具调用,则finish_reason: "tool_calls" - 假设 usage 给的是
input_tokens/output_tokens/total_tokens,则分别填成prompt_tokens/completion_tokens/total_tokens,上下文占用展示仍取total_tokens - 假设 usage 里
input_tokens_details.cached_tokens有值,则填成缓存读 token,但仍不计入上下文占用(对齐core/prompt-cache-control.md的 token 口径) - 假设响应只有推理内容、既无正文也无工具调用,则按今天「只有 reasoning 也算有响应」的口径处理,不报空响应
用户故事:流式输出与断连降级(优先级:P1)
作为用户,我希望走 Responses 时正文、推理、工具参数都照常边收边渲染,流式正文中途断连时的降级重发也照旧生效——体验不能因为换了端点而退化。
为什么是这个优先级:Wave 的所有主循环请求都是流式的(工具参数靠流式增量渲染,长思考模型靠首字节占住网关空闲超时)。Responses 的事件流与 Chat Completions 的 chunk 结构完全不同,不映射就等于在 Responses 模型上失去全部流式体验与既有容错。
独立测试:投喂一段录制的 Responses 事件流,断言正文/推理/工具参数三类增量按顺序到达对应回调,且最终组装结果与非流式解析等价;再让流在正文中途断掉,断言以非流式重发并应用其结果。
验收场景:
- 假设流里收到正文增量事件,则它等同 Chat Completions 的正文增量回调(逐字上屏)
- 假设流里收到推理增量事件,则它等同推理增量回调(思考块边想边显示)
- 假设流里先收到工具调用开始(含
call_id与工具名)、随后收到若干参数增量,则工具块先出现、参数逐步补全,与今天工具调用的流式表现一致 - 假设流式响应在正文中途断开,则沿用既有降级:丢弃这次半截的 assistant 消息,用同一份请求参数以非流式重发(仍在 Responses 端点),并把结果作为一次非流式响应应用
- 假设同一响应分别以流式与非流式请求,则解析结果在正文、工具调用、
finish_reason、usage 上等价 - 假设用户在流式中途中止,则中止信号同样作用于 Responses 请求,不残留未关闭的连接
- 假设流里出现表示失败的结束事件,则按
core/ai-error-handling.md的分类处理(429/5xx 重试、其他错误上报),不吞掉错误
用户故事:非 agent 调用路径同样跟随声明(优先级:P2)
作为用户,我希望内联补全、next-edit、网页内容处理这三条路径也跟随同一个 apiType 声明,这样声明过的模型在 Wave 里处处能用——不会出现「主对话能跑、网页内容处理报错」这种按入口分裂的行为。
为什么是这个优先级:这三条不跑 agent 主循环,用户最常感知的是主对话框,所以排在 P1 之后。但它们都是同一个模型的其它入口:主模型声明了 apiType: "responses" 而网页内容处理仍打 Chat Completions 时,一旦遇到官方标为 "Chat Completions not supported" 的模型,这条路就直接失败——同一个模型一半能用一半不能用,比整块不支持更难解释。
独立测试:把当前模型声明为 apiType: "responses" 后分别触发内联补全、next-edit 与网页内容处理,断言三者请求都打到 Responses 端点且结果与 Chat Completions 下等价;把声明去掉再跑一遍,断言三者回到 Chat Completions。
验收场景:
- 假设网页内容处理使用的是声明了
apiType: "responses"的模型(未指定 fastModel 时即主模型),当发起请求时,则走 Responses 端点,纯文本结果与 Chat Completions 下等价 - 假设补全所用的模型(
completionModel)声明了apiType: "responses",当触发内联补全时,则走 Responses 端点,插入的补全文本与 Chat Completions 下等价;模型返回空文本时不渲染任何幽灵文本的规则不变 - 假设 next-edit 所用的模型声明了
apiType: "responses",当触发建议时,则走 Responses 端点,解析出的行区间建议与 Chat Completions 下等价 - 假设这三条路径所用的模型
apiType为"chat"或未声明,当发起请求时,则仍走 Chat Completions,行为与今天完全一致 - 假设这三条路径的请求本身不带工具、不是流式(都是单次纯文本请求),当走 Responses 时,则只发送它们需要的字段(模型、
instructions、input、输出上限等),不因「支持 Responses」而引入工具或流式语义 - 假设补全 / next-edit 的开关被关闭,当用户触发时,则先按开关判断直接返回、不发起任何请求(开关判断先于端点选择)
- 假设会话压缩 /
/compactfork 用主模型发起,当它走 agent 主循环时,则按主模型的声明走 Responses(属于主循环路径)
边界情况
- 边界情况 1:端点选择只有一个来源——逐模型的
capabilities.apiType(默认"chat"),不存在全局开关、模型名清单、正则或内置默认表。用户换新模型时改一行settings.json即可,不必等 Wave 发版 - 边界情况 2:Responses 独有的其它能力(file search、code interpreter、computer use、MCP 托管、image generation、
reasoning.mode: "pro"、hosted shell、apply patch、tool search)不在本规格范围。Wave 只把已有请求翻译过去,不主动索取推理摘要等 Responses-only 内容:主循环发它本来就会发的工具与消息,三条纯文本路径发它们本来就发的两段文本 - 边界情况 3:
apiType: "responses"与promptCaching: true同时声明不冲突:Responses 下不发cache_control,由服务端自动缓存;不得因此报配置错误 - 边界情况 4:
options是懂行的人写的配置,Wave 只做上表列出的键名映射,其余键一律原样透传——不丢弃、不改名、不判断端点是接受还是拒绝。用户配错由服务端 400 直接反馈,静默丢弃用户的配置比报错更糟 - 边界情况 5:工具结果回填必须用
call_id(不是 item 的id),两者用错服务端会拒绝该轮请求 - 边界情况 6:
finish_reason的映射必须让「输出被上限截断」这一语义成立,否则core/ai-error-handling.md的自动续写在 Responses 模型上静默失效 - 边界情况 7:开关是逐模型的,父子代理互不影响:同一个会话里主代理走 Responses、用 fastModel 的子代理走 Chat Completions 是正常组合
- 边界情况 8:四条调用路径(agent 主循环 / 网页内容处理 / 内联补全 / next-edit)对同一个模型必须给出同一个端点结论——不能出现按入口分裂(主循环走 Responses、网页处理仍走 Chat Completions),否则「Chat Completions not supported」的模型会一半能用一半失败
- 边界情况 9:模型的
capabilities是设置项,新增字段必须同步登记到设置文档与内置settings技能,否则用户问「怎么让这个模型走 Responses」时得不到正确答案