big-AGI AIX 的 OpenAI 协议同步方法论:从 Wire Types、Adapter 与 Parser 到上游 API 差异排查
【免费下载链接】big-AGIAI suite powered by state-of-the-art models and providing advanced AI/AGI functions. Includes AI personas, AGI functions, world-class Beam multi-model chats, text-to-image, voice, response streaming, code highlighting and execution, PDF import, presets for developers, much more. Deploy on-prem or in the cloud.项目地址: https://gitcode.com/GitHub_Trending/bi/big-AGI
本文围绕 big-AGI 仓库中的一份内部同步指令.claude/commands/aix/sync-openai-apis.md展开,完整讲解该项目如何把自己的 OpenAI 兼容层(AIX 模块)与上游 OpenAI API 保持同步:哪些文件构成协议实现的骨架(wire types / adapter / parser)、Responses API 与 Chat Completions 的优先级关系、流式解析的关键假设,以及“查文档 → 打真实 SSE 请求 → 逐字段对比”的完整差异排查工作流。读完后你可以掌握在这类多模型网关项目中定位协议实现、复现上游同步检查、并自行判断破坏性变更与新增能力的方法。
一、这份指令解决什么问题
sync-openai-apis.md 是一个面向 AI 编码助手的 Claude Code 命令(.claude/commands/目录下与sync-anthropic-api.md、sync-gemini-api.md、sync-openrouter-api.md等并列),它的用途是:当上游 OpenAI API 发生变化(新字段、新事件类型、新模型、新价格)时,驱动一次系统化的“实现 vs 上游文档”比对,并把所有差异——尤其是破坏性变更和能改善用户体验的新能力——列出来。
指令明确了本次同步的作用范围与优先级,这几点直接决定了 big-AGI 的 OpenAI 兼容层长什么样:
- 优先 Responses API:
/v1/responses是主推协议,Chat Completions(/v1/chat/completions)仍然支持但按 legacy 对待; - 明确不做:不支持 Realtime(含 WebSocket 等)API,也不支持 Agentic 类 API(Agent SDK、AgentKit、ChatKit、Assistants API 等)——因为 AIX 自己在服务端或客户端实现了等价能力;
- 检查深度要求:必须深入到请求/响应字段的细节,重点核对“必填字段、流式事件类型、新的响应形态”,而不仅是顶层结构。
下面按指令指定的三个核心文件逐一展开,这些文件就是 OpenAI 协议实现的完整骨架。
二、协议骨架:三个文件各自负责什么
| 环节 | 文件 | 职责 |
|---|---|---|
| 线格式定义 | openai.wiretypes.ts | 用 Zod 定义请求/响应/内容片段/工具等全部线类型(wire types) |
| 请求组装(Adapter) | openai.chatCompletions.ts、openai.responsesCreate.ts | 把 AIX 内部统一的 ChatGenerate 请求翻译成 OpenAI 两种 API 的请求体 |
| 响应解析(Parser) | openai.parser.ts、openai.responses.parser.ts | 把流式(SSE)或非流式响应解析回 AIX 内部粒子(particle)流 |
同目录下还有 openai.responses.create.spec.md 这类上游规范快照和 sync.sh 同步脚本,说明“把上游规范拉下来对照”本身是工程化的常规动作。
2.1 Wire Types:Zod Schema + 上游 Changelog 注释
openai.wiretypes.ts(约 2200 行)文件头部维护了一张上游变更记录表,这是同步工作的直接产物——每次同步都会把命中的上游 Changelog 条目落到注释里:
// Implementation notes (see OpenAI changelog for upstream changes): // - 2024-12-17: "Reasoning Effort" - added reasoning_effort and the 'developer' message role // - 2024-11-05: "Predicted Outputs" // - 2024-09-12: "o1" - max_tokens is deprecated in favor of max_completion_tokens, // added completion_tokens_details // - 2024-08-06: "Structured Outputs" - added JSON Schema and strict schema adherence // - 2024-07-09: skipping Functions as they're deprecated / ignoring logprobs这份注释同时记录了主动放弃的字段(如logprobs、已废弃的 Functions),让“为什么这里没有 X”可追溯。
核心内容片段(Content Parts)定义在OpenAIWire_ContentParts命名空间内,用z.discriminatedUnion('type', ...)组织输入侧的四种 part(见 openai.wiretypes.ts#L27-L92):
text:文本,另带一个 OpenRouter 方言专属的cache_control(Anthropic 风格缓存断点,OR 负责翻译);image_url:url+detail: 'auto' | 'low' | 'high'控制视觉理解精度;input_audio:Base64 音频 +format: 'wav' | 'mp3'(上游 2024-10-17 加入的音频输入);video_url:OpenRouter 扩展,非标准 OpenAI 字段。
输出侧则定义了ToolCall(function调用,arguments是字符串,源码注释提醒模型未必生成合法 JSON,调用前必须自行校验)以及url_citation引用注解等。
2.2 Chat Completions 请求 Schema:完整参数面
OpenAIWire_API_Chat_Completions.Request_schema(见 openai.wiretypes.ts#L342-L461)覆盖了同步时需要逐项核对的请求面,摘选如下:
| 参数 | 类型/取值 | 说明 |
|---|---|---|
model/messages | string / Message[] | 基本输入,messages 为四角色消息数组 |
tools/tool_choice/parallel_tool_calls | ToolDefinition[] / ToolChoice / bool | 工具定义与调用策略,parallel_tool_calls默认 true |
max_completion_tokens | int(正数) | 现行标准字段 |
max_tokens | int | 已废弃,仅为兼容保留 |
temperature/top_p | 0–2 / 0–1 | 通用采样参数 |
modalities/audio | ['text','audio','image']/ voice+format | 多模态输出;audio 的 voice 枚举含 ash/ballad/coral/sage/verse/alloy/echo/shimmer/marin,format 支持 wav/mp3/flac/opus/pcm16 |
stream/stream_options | bool /{include_usage} | 流式开关与用量回传 |
reasoning_effort | none/minimal/low/medium/high/xhigh/max | 2024-12-17 加入;max为 DeepSeek V4 扩展 |
response_format | text / json_object / json_schema | 2024-08-06 Structured Outputs;schema 名须匹配^[a-zA-Z0-9_-]{1,64}$ |
web_search_options | search_context_size + user_location | 网络搜索上下文与近似定位 |
prediction | {type:'content', content} | 2024-11-05 Predicted Outputs |
Schema 之后还有一长串厂商方言扩展(OpenRouter 的session_id/provider/max_tool_calls、Perplexity 的search_mode、Moonshot/DeepSeek 的thinking等),这正体现了 wire types 文件的定位:一份“OpenAI 兼容超集”,以注释中的[厂商, 日期]标签标记每个扩展的来源。这种注释规范本身就是同步工作的落点——新增一个上游字段时,必须能追溯到厂商与日期。
三、Chat Completions Adapter:请求组装与方言热修
aixToOpenAIChatCompletions 是 Chat Completions 方向的翻译器,文件头部的实现笔记先声明了能力边界(见 openai.chatCompletions.ts#L11-L21):只支持 N=1;top_p、parallel_tool_calls、stop等未实现;doc part 以 markdown 文本内嵌、image part 以 base64 data URL 内嵌、所有 tool call 统一转为 function call。
组装流程里最有工程含量的是按方言(dialect)打热修,例如(见 openai.chatCompletions.ts#L40-L100):
- OpenAI/Azure + o 系推理模型(gpt-6/gpt-5/o4/o3/o1 前缀匹配):去掉不支持的
temperature/top_p,system 消息改用 2024-12-17 引入的developer角色; max_tokens→max_completion_tokens:对原生 OpenAI 与 Azure 全部改用新字段,与 wire types 里的“DEPRECATED”注释呼应;- DeepSeek/Perplexity:强制 user/assistant 角色交替、移除空消息;DeepSeek V4 在有工具且未关思考时要求历史中每条 assistant 消息都带
reasoning_content,缺失时注入空串占位; - OpenRouter:把客户端生成的
session_id发给上游做粘性路由(缓存不能跨 provider),并把尾部的cache_control断点收敛到 4 个以内(Anthropic 上游限制);对拒收强制工具调用的模型,把tool_choice: 'required'降级为auto并插入 system 提示语补救; - 函数调用不可用则快速失败:Perplexity 等方言下若携带 tools 直接抛错,而不是让上游报一个难读的错。
随后构造请求体:stream_options: { include_usage: true }(流式时)、response_format按model.strictJsonOutput决定、工具与tool_choice按模型的strictToolInvocations转换。最后还有一道_fixPairInteriorToolCalls保证“每个中间 tool call 都有配对的 tool 消息,否则整个请求会被上游拒绝”。这些细节正是同步指令中“look deep in the fields of the requests”要求的具体形态:一个字段的上游语义变化(比如角色改名、字段废弃)会同时波及 wire types、adapter 热修和 parser 三处。
四、Chat Completions Parser:块级流式协议的解析假设
openai.parser.ts 顶部(openai.parser.ts#L16-L36)用注释完整陈述了它对块级流式协议(chunk-based streaming)的理解,这也是同步时要重点验证的部分:
- 每个 chunk 含
choices数组(通常单项),delta承载增量更新; - 文本以
delta.content字符串片段增量到达; - 工具调用经
delta.tool_calls增量到达,且有固定方案:首个 delta 携带完整 id 与函数名(参数通常为空),后续 delta 只追加参数文本——没有显式的 begin/end 标记,靠时间顺序隐含工具调用的开始与结束; - 流结束靠
data: [DONE]信号,不依赖finish_reason。
解析入口 createOpenAIChatCompletionsChunkParser 在真正 Zod 解析前做了一组“防御性分流”,每一条都对应同步时观察到的真实上游行为:
- Keepalive 事件(2025-01-13 上游加入):
{"type":"keepalive",...}静默跳过; - 混淆占位 chunk:无
choices但带obfuscation的消息直接跳过,否则会打断解析器; error字段:按 openai.error-severity.ts 的严重度分级转成方言终止事件,而不是粗暴抛错;- Azure 特例:空 id/model 且带
prompt_annotations/prompt_filter_results的 chunk 忽略; - OpenRouter:在 Zod 剥掉未知字段之前,先从原始 JSON 里摘出
provider路由信息用于展示; - 尾部成本事件(如
x-opencode-type: inference-cost):先收割 token/费用指标再跳过。
之后才是ChunkResponse_schema.parse严格解析,并记录timeToFirstEvent等性能指标。可以看到 parser 的演进史(每条注释都带上游日期/厂商)与 wire types 头部注释是同一套同步流程的两个出口。
五、Responses API 侧:方言差异表与事件流解析
因为指令明确“Responses API 优先”,openai.responsesCreate.ts 和 openai.responses.parser.ts 承载了主协议。
Adapter 侧用一张方言差异表(RspDialectQuirks,见 openai.responsesCreate.ts#L26-L67)取代零散的 if-else,把“共享信封、各自校验器”的 OpenAI 兼容生态结构化:
| 差异项 | 含义 | 典型方言值 |
|---|---|---|
vndNamespace | 连续性状态(加密推理项、消息阶段)的_vnd命名空间 | openai / sakanaai / xai / metaai |
emitMessagePhase | 回放 assistant 消息时带phase字段 | Azure 为 false(能力滞后) |
webSearchTool | 托管 web_search 工具的形态:full / bare / none | Azure 为 none(“Hosted tool 'web_search' is not supported”) |
toolChoiceOnlyAuto | tool_choice 只接受 'auto' | metaai 严格校验器 |
minOutputTokens | 校验器接受的最小 max_output_tokens | metaai 为 16 |
每张表行都带日期标注(如 Azure 的滞后截至 2025-11-18 仍成立),这正是同步指令“点出所有协议差异”的输出形式。
Parser 侧则按 Responses API 的事件类型状态机推进(response.output_text.delta、response.output_text.done、response.output_text_annotation.added等,见 openai.responses.parser.ts#L603-L660),并对新旧两种 annotation 事件名做向后兼容。一个值得注意的上游怪癖处理是 “failed 响应打捞”(openai.responses.parser.ts#L74-L89):OpenAI 可能把一个实际已完整生成并流式发完的响应整体标记为failed(TPM 限流检查发生在生成中途而非准入时),解析器以 output 中每个 item 的status: 'completed'作为“生成已完成”的权威信号,把这类响应按成功处理。注释里标注了 2026-07-03 的 5/5 复现实测——这种“文档滞后、靠真实 SSE 才能发现”的行为,恰好是同步指令里“live endpoint 是 ground-truth”一节的注脚。
六、同步工作流:信息源、活体探测与差异清单
指令的后半部分给出了完整、可复用的同步操作手册,这部分对任何维护 OpenAI 兼容层的团队都通用:
1)信息源优先级(逐级降级)
- 主源:Responses API 参考(优先)、Chat Completions 参考、Changelog、模型列表、定价页(用官方页面的 Copy Page 按钮下载 markdown,便于 diff);
- 主源被挡时改用:OpenAI 官方 Node.js SDK、Python SDK、OpenAPI 规范仓库,或搜索“openai api changelog / new models / new prices”这类关键词获取近期公告;
- 全部被挡时:如实说明尝试过什么,并请人工提供文档——严禁在拿不到上游信息时编造。
2)活体端点探测(额外信号)
如果本地.env.api-keys中存在OPENAI_API_KEY,直接对真实端点打一发流式请求并检查原始 SSE:POST https://api.openai.com/v1/responses(legacy 则用/v1/chat/completions),请求体带"stream": true。文档可能滞后,而原始 SSE 是新字段、事件类型、响应形态与错误格式的最终事实来源。指令同时强调绝不提交或回显密钥。
3)差异核对与输出要求
- 深查请求与响应的字段级差异:必填字段、流式事件类型、新的响应形态、协议本身的变更(包括流式消息的最终解析与重组方式);
- 优先报告破坏性变更和能改善用户体验的新能力——前者必须尽快修,后者进入实现待办;
- 指令的 frontmatter 支持
$ARGUMENTS占位(argument-hint 为 “specific feature to check”),可以把检查范围收窄到某个具体特性。
对照仓库现状可以验证这套流程的落地效果:wire types 文件头的 Changelog 日期注释、wiretypes 内每个方言扩展的[厂商, 日期]标签、parser 里带日期注释的防御分支、以及_upstream/目录下的上游规范快照与sync.sh,都是这个循环反复运转留下的痕迹。
七、小结:一套可迁移的协议同步方法
big-AGI 的 OpenAI 同步指令把“跟上上游 API”从一次性的手工活变成了可重复的工程流程,其要点可以概括为:
- 实现分三层且各司其职——Zod wire types 定义线格式(含方言超集与日期注释)、adapter 组装请求(按方言打热修并快速失败)、parser 消费响应(先防御分流、再严格解析、以 item 级状态为权威);
- 协议优先级明确——Responses API 为主、Chat Completions 为 legacy,Realtime 与 Agentic API 明确不接,由 AIX 自研能力覆盖;
- 信息源分级 + 活体 SSE 兜底——文档、SDK、OpenAPI 规范逐级降级,真实流式请求是文档滞后时的 ground-truth;
- 输出以“破坏性变更优先”排序——必填字段、流式事件类型、新响应形态是必查项,所有上游观察都必须带厂商与日期标注沉淀回源码注释。
维护同类项目时,可直接复用这套结构:先定位本项目的 wire types / adapter / parser 三件套,再按“文档 → 活体探测 → 字段级 diff → 注释落回源码”的闭环执行同步。
【免费下载链接】big-AGIAI suite powered by state-of-the-art models and providing advanced AI/AGI functions. Includes AI personas, AGI functions, world-class Beam multi-model chats, text-to-image, voice, response streaming, code highlighting and execution, PDF import, presets for developers, much more. Deploy on-prem or in the cloud.项目地址: https://gitcode.com/GitHub_Trending/bi/big-AGI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考