适配器模式实现多模型接入方法详解(AI 网关设计模式实战)

适配器模式把一个类的接口转换成客户端期望的另一个接口,让原本不兼容的类能协作,它不改变现有实现,只在中间加一层”转接头”。在我们自研的企业级 AI 网关里,OpenAI、通义千问、DeepSeek、智谱的 API 在鉴权方式、参数命名、流式返回格式上差异很大,我们靠一套统一的 ModelAdapter 接口加每厂商一个适配器实现来屏蔽差异。业务侧只面向统一接口编程,新增一家模型厂商只需新增一个适配器类,路由、限流、日志等横切逻辑全部复用,业务代码一行不用改。该网关同时服务着内部 AI 爆款文章创作器的多阶段生成。

一、适配器模式在网关里解决什么问题

多模型接入的第一痛点不是”能不能调通”,而是”切换成本”。OpenAI 用 temperature 控制随机性,Google Gemini 用 candidateCount,通义千问的流式返回结构又和 Anthropic 不同。如果业务代码直接调用各家原生 SDK,每接一个厂商就要写一套调用逻辑、一套超时重试、一套错误码映射,改模型等于改业务代码。

适配器模式的核心价值是建立契约。我们抽出四个方法作为统一契约:

契约方法 职责 说明
chat(request) 同步对话 返回完整文本结果
chatStream(request) 流式对话 返回 Flux<String>,逐 token 推送
embed(text) 向量化 供 RAG 检索链路使用
providerName() 厂商标识 用于路由与成本统计

四个方法覆盖了网关 90% 的调用场景。同步调用给管理后台做测试,流式调用给文章创作器和对话网关用,向量化给知识库检索用。每家厂商只需把自家原生 SDK 塞进这四根管子里,对外表现一致。

二、适配层怎么落地:接口 + 适配器 + 工厂装配

网关的适配层分三层:顶层是 ModelAdapter 接口,中间是各厂商适配器,底层是 Spring 容器按配置装配。业务代码从不用关心具体实现类。

public interface ModelAdapter {
    String providerName();

    String chat(ChatRequest request);

    Flux<String> chatStream(ChatRequest request);

    float[] embed(String text);
}

DeepSeek 的适配器把 Spring AI 的 ChatModel 包一层即可:

@Service
public class DeepSeekAdapter implements ModelAdapter {
    private final ChatModel chatModel;

    public DeepSeekAdapter(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @Override
    public String providerName() {
        return "deepseek";
    }

    @Override
    public String chat(ChatRequest request) {
        Prompt prompt = new Prompt(request.messages());
        ChatResponse resp = chatModel.call(prompt);
        return resp.getResult().getOutput().getText();
    }

    @Override
    public Flux<String> chatStream(ChatRequest request) {
        return chatModel.stream(new Prompt(request.messages()))
            .map(r -> r.getResult().getOutput().getText());
    }
}

接入新厂商时照这个模板复制一份,改改参数和内部实现,适配器就完成了。网关侧再维护一个 Map<String, ModelAdapter>,启动时把容器里所有适配器按 providerName() 注册进去,路由模块按模型名取出对应适配器调用。整个过程对业务透明。

2.1 流式输出的格式差异怎么收敛

不同厂商的 SSE 流式格式不同,OpenAI 返回的是 data: {...} 逐段 JSON,Anthropic 用事件前缀标记消息边界。适配器层把这种差异消化在内部:适配器只向网关层返回统一格式的文本块,网关再把文本块按统一 SSE 协议推给前端。创作器的 Vue 前端订阅固定地址,底层是哪个模型对它不可见,多阶段生成(选题 → 大纲 → 成文 → 润色)可以分别路由到不同模型。

2.2 错误码映射也属于适配工作

各家厂商的异常体系完全不一致,OpenAI 用带 status 的 HTTP 错误,通义千问返回业务码。适配器统一转换,把厂商错误映射成网关自己的错误枚举(限流、超时、内容审核、服务不可用)。路由与降级模块只认这套枚举,判断逻辑保持简单干净。

三、接入一家新模型的五步标准动作

新模型接入被固定成可复制的流程,新同学照着走一遍就能上线:

  1. 在 application.yml 里配置 base-url、api-key、默认模型名,走 Spring 配置绑定;
  2. 新建适配器类实现 ModelAdapter,把厂商原生调用翻译成四个契约方法;
  3. 在 @Bean 工厂方法里把适配器注册进适配器注册表,启动时自动装配;
  4. 用网关内置的联调接口跑一遍同步和流式两条链路,核对返回文本;
  5. 配置一条测试路由,压测流式首 token 耗时与超时重试表现,通过后开放给生产。

这套流程跑熟后,单模型接入耗时从数天压缩到几小时,且不碰任何业务代码。

四、多模型接入三种方案的对比

方案 维护成本 扩展成本 统一能力(限流/监控) 适用阶段
业务代码直连各家 SDK 高,散落各处 高,每处都改 无,各写各的 原型验证
统一接口 + 适配器(本项目) 中,集中在适配层 低,新增适配器即可 有,网关层统一收口 生产多模型接入
引入成熟 AI 网关中间件 低 低 有 团队人手不足时

直连方案只接一家模型时很省事,接两家以上就失控。自研适配器适合已用 Spring AI、需要定制路由与合规逻辑的团队。引入成熟中间件省人力,但定制能力受限。

五、落地时容易踩的坑

最容易被忽视的是消息格式转换,尤其是工具调用(Function Calling)的格式,各家厂商差异最大,占了适配工作量的绝大部分。其次是超时与重试要放在适配器内部统一处理,用 RestClient 拦截器收口,而不是每个适配器各自写一遍。流式链路的背压也要控制好,Flux 慢消费会把前端 SSE 连接拖垮。

常见问题(FAQ)

Q1:适配器模式和策略模式怎么区分?

适配器解决接口不兼容,策略解决算法可替换。网关里二者叠加用:适配器翻译接口,策略决定路由选谁。

Q2:必须为每个模型单独写适配器吗?

同一厂商同协议可以共用一个适配器,用配置区分模型名。跨厂商、跨协议则必须单独实现。

Q3:适配器写好后业务代码还感知模型差异吗?

不感知。业务只面向统一接口编程,换模型只改配置与路由,不碰业务代码。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
其AI的头像其AI普通用户

相关推荐

返回顶部