Workbuddy 的适配器架构是怎么兼容多种模型接口的,接一个新供应商到底要改哪些地方?

Workbuddy 的适配器架构把供应商协议差异收敛在适配层内部,上层只认统一的请求对象与流式事件,因此新增一个模型接口通常只需实现适配器并登记能力表,不必改动会话、工具与计费逻辑;但供应商私有字段仍需通过透传通道单独处理。

Workbuddy 兼容多种模型接口靠的不是给每家供应商写一套业务逻辑,而是一层可插拔的适配器:上层只认内部的统一请求对象和统一流式事件,协议差异全部在适配器边界内被翻译掉。也就是说,会话管理、工具编排、用量计费等模块不需要知道背后是哪种协议风格。这个结论只在下游确实走统一抽象时成立,如果某个功能直接调用了供应商原生 SDK,它仍然会绕过适配层。 适配层通常分三段:接口层负责暴露统一入口,适配层按供应商实现请求构造与响应解析,能力层登记每个模型支持哪些特性。这样分的好处是新增供应商只动适配层和能力表,接口层与上层业务保持不动。如果三者混在一起写,兼容成本会随着接入数量线性上升,最终退化成一堆按供应商名字判断的条件分支。 请求归一化最先要解决的是系统提示与消息结构的位置差异。有的协议把 system 作为独立顶层字段,有的把它当成 messages 里的一条角色消息,还有的把对话内容拆成内容块数组。适配器要做的就是把统一格式翻译成各家要求的结构,并在返回时逆向还原,保证上层看到的永远是同一种消息模型。 流式响应是兼容难度最高的部分,因为各家的流事件语义并不对齐。有的按增量文本块推送,有的按事件类型区分内容块开始、增量、结束,还有的整段候选结果返回后再切分。适配器要把这些统一成同一套内部事件:开始、文本增量、工具参数增量、结束、用量上报,上层只需监听一种事件序列,不必为每家写一套解析器。 工具调用必须做双向映射,而不是简单转发。统一格式里工具调用通常带名字、参数 JSON 和一个标识符,但有的协议把它放在消息的独立字段里,有的作为内容块返回,还有的通过独立事件流式下发参数。适配器要负责生成或复用标识符,并在流式拼装结束后把参数校验成合法 JSON,否则模型侧的一次截断就会让上层直接解析失败。 参数映射与能力矩阵决定适配器能否优雅降级。温度、采样阈值、最大输出长度、停止词在不同协议里的取值范围和默认值都不一样,适配器要统一成内部语义再做换算,并夹取到合法区间。能力矩阵则记录某个模型是否支持工具调用、是否支持图片输入、是否支持流式,不支持时是抛错还是降级为提示词模拟,必须显式声明而不是隐式假设。 错误处理要先把各家错误码翻译成内部分类,再决定是否重试。限流、超时、服务端故障属于可重试类别,参数非法、内容被拒则不应重试,否则只会放大成本和时间消耗。重试策略还要和幂等设计配合:流式请求一旦已经开始吐字,中途重试会产生重复内容,通常做法是只在首字节之前重试,并在适配层统一做退避。 用量统计必须在适配层统一口径,否则计费与对账会出现对不上的情况。不同协议返回的用量字段名和粒度不同,有的把输入输出分开计,有的只给总量,有的在流式模式下只在最后一个事件里带上用量。适配器的职责是把它折算成同一组内部字段,并标记缺失值的来源,让上层能区分“用量为零”和“供应商没返回”这两种完全不同的状态。 接入一个新供应商的落地顺序通常是:先实现适配器接口并跑通一次非流式调用,再补齐流式事件映射,接着做工具调用与多模态,最后接入能力表和错误分类。每一步都应有契约测试,用固定的请求样本和录制的响应样本做回归,避免某家协议在某次版本更新中悄悄改了字段名,导致线上解析在半夜崩溃。 适配器并非要把所有能力彻底拉平,供应商私有字段需要留出透传通道。某些模型独有的推理参数、缓存标记、思考过程返回,在统一模型里没有对应位置,硬塞进公共字段会污染其它供应商的实现。更稳妥的做法是在请求和响应中保留一个厂商命名空间,上层要用就显式读取,不用则忽略,这样既不阻塞新能力上线,也不破坏既有抽象。 什么时候不该强上适配器抽象也要说清楚:如果只接一家供应商,或者业务深度依赖某家的独有编排能力,过早抽象只会增加一层无谓的翻译和调试成本。判断标准很简单,当接入第二家、第三家模型的边际成本开始成为真正的问题时,适配层带来的收益才会超过它自身的复杂度。