Provider Extension
provider extension 解决的是:把一个新的模型平台正式纳入 AI4J 的平台分发体系。
它不是“再写一个 service 类”这么简单,因为 AI4J 当前对 provider 的建模是显式的、枚举驱动的。
1. 先看真实入口
这一类改动至少会经过下面几组源码:
service/PlatformType.javaservice/Configuration.javaservice/factory/AiService.javaservice/factory/DefaultAiServiceRegistry.javaai4j-spring-boot-starter/.../AiConfigAutoConfiguration.java
其中最关键的是 AiService。当前 provider 能不能被 SDK 正式识别,不取决于“classpath 上有没有某个实现类”,而取决于它是否进入了这些 switch 分发:
createChatService(...)createResponsesService(...)createEmbeddingService(...)createImageService(...)createAudioService(...)createRealtimeService(...)createRerankService(...)
这意味着 provider 支持矩阵是显式维护的,而不是自动发现的。
2. 当前 provider 扩展的真实形态
AI4J 目前并没有“注册一个 provider 插件即可接入”的通用 provider SPI。
真实流程是:
- 给平台命名:增加
PlatformType - 给平台配配置:增加对应
*Config - 给统一配置对象开槽:在
Configuration中增加字段 - 给能力面落实现:增加 chat / responses / embedding 等具体 service
- 给工厂补分支:在
AiService中把该 provider 接进支持矩阵 - 给多实例补配置复制逻辑:在
DefaultAiServiceRegistry.applyPlatformConfig(...)增加分支 - 给 Spring Boot starter 补属性绑定和初始化逻辑
只做其中一两步通常都不够。比如你只写了 FooChatService,但没进入 AiService.createChatService(...),外部用户仍然拿不到它。
3. 什么时候它不该叫 provider extension
下面这些情况,通常不该升级为 provider extension:
- 同一个 provider 下增加一个新模型名
- 现有请求对象多一个字段映射
- 某个 provider 的
Chat已有能力里增加一个可选选项 - 同一协议族下只是切换
apiHost
这些更像 Model Extension。
只有当你需要:
- 新的
PlatformType - 新的 provider 配置类型
- 新的工厂分发分支
- 新的 starter 配置装配
它才算真正的 provider extension。
4. 现有代码链会要求你改什么
4.1 PlatformType
这是平台名的正式枚举入口。新增 provider 必须进入这里,否则工厂分发没有合法枚举值可用。
要注意一个细节:
DefaultAiServiceRegistry.resolvePlatformType(...)对未知平台会直接抛错PlatformType.getPlatform(...)对未知值会回退到OPENAI
前者适合正式配置校验,后者则可能掩盖拼写错误。扩展代码里不要把 getPlatform(...) 当成严格校验入口。
4.2 Configuration
所有 provider 配置最终都挂在统一的 Configuration 上。新增 provider 时,不仅要有自己的配置类,还要让 Configuration 能承载它。
否则即使你写出了 service,也没有统一配置对象可供下游实现读取。
4.3 AiService
这是最重要的一层。AiService 目前是“平台到能力实现”的最终路由器。
例如当前 Chat 的 provider 覆盖远多于 Responses。这不是文档策略,而是 AiService 里的实际分支矩阵。
所以新增 provider 时,你必须明确回答:
- 它支持哪些顶层 service
- 不支持哪些顶层 service
- 不支持时是否保留显式
IllegalArgumentException
这里最好显式保留“不支持”的事实,而不是做含糊兜底。
4.4 DefaultAiServiceRegistry
多实例模式不是简单复用同一份 Configuration,而是先复制 base Configuration,再把当前 AiPlatform 的字段拷贝到对应 provider config 中。
如果你忘了给 applyPlatformConfig(...) 增加新 provider 分支,那么:
- 单实例手工 new
AiService也许还能跑 - 多实例注册表场景会直接失效
这类问题很常见,因为扩展作者往往先在最短路径 demo 上成功,再漏掉 registry 路径。
4.5 Spring Boot starter
starter 侧还要补至少两类东西:
*ConfigPropertiesAiConfigAutoConfiguration中的初始化逻辑
否则仓库会出现一种非常典型的不一致:
- core SDK 手工组装可用
- Spring Boot 场景无法通过配置完整落地
5. 现有实现的边界和代价
没有 provider 自动发现
当前 provider 扩展是显式代码接线,不是自动注册式扩展。这么做的好处是能力矩阵清晰,坏处是每次新增 provider 都要修改多个中心点。
service 对象默认不是缓存的
AiService.getChatService(...) 当前直接调用 createChatService(...),注释里还能看到曾经考虑过缓存,但现在没有启用。
这意味着:
- 具体 provider service 要尽量无状态
- 重资源应复用
Configuration中的共享OkHttpClient - 不要把一次性初始化代价埋在每次
get*Service()调用里
多实例只是 provider 配置分片,不是完全独立容器
DefaultAiServiceRegistry 复制 Configuration 时,会继承 base OkHttpClient 等共享对象,再覆盖当前 provider 的 scoped 配置。
所以多实例更像“多套平台配置 + 共享底层客户端”,而不是每个 id 都从零创建整套网络栈。
6. 调试时先看哪条链
能手工 new,不能在配置里声明
先看:
DefaultAiServiceRegistry.applyPlatformConfig(...)AiConfigAutoConfiguration
这是最常见的“demo 通,starter 不通”故障。
getChatService(...) 能用,getResponsesService(...) 报错
先看 AiService 里对应 create*Service(...) 是否真的给该 provider 加了分支。
AI4J 当前本来就允许不同 service 面支持矩阵不对称,所以这类报错往往不是运行时偶发,而是工厂矩阵没有定义。
provider 名配置了却走错平台
检查是不是错误使用了 PlatformType.getPlatform(...)。它对未知值回退 OPENAI,会让拼写错误变成“看起来还能跑,但平台不对”。
7. 一条实用判断线
如果这次改动必须同时触碰:
PlatformTypeConfigurationAiServiceDefaultAiServiceRegistry
那你做的基本就是正式 provider extension,而不是局部修补。
8. 这一页的结论
AI4J 当前的 provider extension 是显式工厂分发扩展,不是插件式自动接入。新增 provider 的本质是把一个新平台接进
PlatformType + Configuration + AiService + Registry + Starter这条主链;只写实现类而不补这条链,扩展就还没有真正完成。