先说清边界
本文聚焦「怎么改」,而不是「字段等于什么」。所有具体的模型名、base_url、参数取值范围、错误码含义,都以官方文档和实际接口返回为准;下文给出的适配层结构属于工程分析,可独立于任何一端的实现细节使用。
一、先判断差异属于哪一层
把迁移问题拆成三层,能避免把「协议差异」和「模型行为差异」混在一起排查:
- 协议层:请求路径、鉴权头字段名、请求体的字段名与嵌套结构、返回体的包裹方式。
- 语义层:同一字段在不同后端下的取值范围、默认值、上限,以及参数之间的互相约束。
- 行为层:流式分片的切分粒度、事件类型、错误分类、限流与重试语义。
实践中最常见的情况是:换后端就报 4xx,多半是语义层;流式输出「少半句话」或解析中断,多半是行为层;而「密钥明明对却 401」,通常只是协议层的 base_url 或鉴权头拼错。适配层的价值就是让三层差异只收敛在一个文件里。
二、请求体字段:先盘点,再映射
不要逐字段去改业务代码。做法是先把现网调用中真正用到的参数列成清单,再按四类处理:
- 两端同名同义:如消息数组的 role/content、采样类参数、输出长度上限一类。可以透传,但要留意默认值与可设区间可能不同。
- 同名不同形:如消息内容既可能是字符串也可能是分段数组、工具调用相关字段的嵌套层级、结构化输出的表达方式。
- 只有一端存在:某端特有的扩展开关。这类字段必须由适配层拦截,不能让它们原样落到另一个后端。
- 语义相同但需换算:停止符数量上限、部分惩罚系数的取值区间等。
工程上建议维护一张「能力表」而不是散落的 if 分支,并用配置开关选择 profile:
const profiles = {
openai: { baseUrl: "", authHeader: "Authorization", caps: { /* ... */ } },
deepseek: { baseUrl: "", authHeader: "Authorization", caps: { /* ... */ } },
};
type Caps = {
models: string[];
maxOutputTokensKey: string;
supportsTools: boolean;
supportsJsonSchema: boolean;
streamUsage: boolean; // 流式末帧是否带用量
stopLimit: number;
};
调用前做一次 normalize(request),把业务层的「中立请求」翻译成目标后端的请求体;调用后再做一次 denormalize(response)。业务代码只认中立结构,切换后端只改 profile。
三、流式返回:按行缓冲,别按网络分片解析
兼容接口普遍返回 SSE。解析时有两条纪律:按行或按空行分隔缓冲,不能假设「一个网络分片 = 一条完整事件」;也不要假设结束标记一定存在、用量一定出现在首帧或末帧。
常见坑有三个:一是把增量文本直接拼给 UI,却忽略了工具调用参数也是分片下发的;二是把结束原因当成只在最后出现,实际可能单独成帧;三是把多个事件粘在一个分片里时只解析了第一条。
适配层应把每个事件归一化为统一类型,例如 delta、tool_delta、usage、done、error,再由上层消费。这样上层 UI 与状态机不需要知道当前用的是哪一套后端。
四、错误处理与鉴权
错误至少分三类:
- 请求格式类(4xx):重试无用,应记录、告警并按策略降级。
- 限流与超时类:需要带退避的重试,并区分请求是否幂等。
- 鉴权类:密钥错误、头字段名不符、base_url 拼接错误都会落到这里。
鉴权头字段名、前缀写法、是否支持额外的组织级头,都应在适配层统一注入,不要在业务分支里散写。重试策略也建议放在适配层而非直接依赖 SDK 客户端,因为它与后端的错误体结构强相关;流式请求的重试必须约束在首帧到达之前。
五、可落地的改造顺序
- 抽出 client 工厂:base_url、鉴权头、超时、重试统一收口。
- 引入中立请求/响应类型,业务代码不再直接构造 SDK 请求体。
- 编写 normalize / denormalize 两个纯函数,并补单测:同一份中立请求在两个 profile 下应各自产出合法请求体。
- 用契约测试回放真实响应样本,覆盖流式、非流式与错误响应,断言归一化后结构一致。
- 灰度切换 profile,重点观察日志中被拦截的「只在一端存在」的字段——这些就是后续要补齐的能力缺口。
结论
迁移的核心不是背下两套参数表,而是让差异只出现在一个可测试的边界上:能力表描述差异,normalize/denormalize 收敛差异,契约测试锁住差异。做到这三点,同时支持 OpenAI SDK 与 DeepSeek API 就不再是维护两套调用逻辑,而是切换一个配置。