7.8 KiB
7.8 KiB
多厂商AI API适配器设计方案
📋 目标
支持接入多个AI服务提供商,包括:
- OpenAI格式API(当前使用的api.apiyi.com)
- RunningHub API
- 未来的其他厂商
🏗️ 架构设计
┌─────────────────────────────────────────────────────────┐
│ AiTaskService │
│ (业务逻辑层,不变) │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ AIProviderService (新增) │
│ 根据模型配置选择对应的Provider │
└────────────────────┬────────────────────────────────────┘
│
┌───────────┴───────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ OpenAIProvider │ │ RunningHubProvider│
│ (适配器1) │ │ (适配器2) │
└──────────────────┘ └──────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ api.apiyi.com │ │ www.runninghub.cn│
│ (第三方API) │ │ (第三方API) │
└──────────────────┘ └──────────────────┘
📝 实现步骤
1. 定义统一的Provider接口
public interface AIProvider {
/**
* 提交任务到AI服务商
* @return 统一的任务响应对象
*/
ProviderTaskResponse submitTask(ProviderTaskRequest request);
/**
* 查询任务状态
*/
ProviderTaskStatus queryTaskStatus(String providerTaskId);
/**
* 获取任务结果
*/
ProviderTaskResult getTaskResult(String providerTaskId);
/**
* 获取服务商名称
*/
String getProviderName();
}
2. 扩展数据库表
points_config表新增字段
ALTER TABLE `points_config`
ADD COLUMN `provider_type` VARCHAR(50) NOT NULL DEFAULT 'openai'
COMMENT 'AI服务提供商类型:openai, runninghub',
ADD COLUMN `provider_config` TEXT NULL
COMMENT '服务商特定配置(JSON格式)';
ai_task表新增字段
ALTER TABLE `ai_task`
ADD COLUMN `provider_type` VARCHAR(50) NULL
COMMENT 'AI服务提供商类型',
ADD COLUMN `provider_task_id` VARCHAR(100) NULL
COMMENT '服务商返回的任务ID',
ADD COLUMN `provider_response` TEXT NULL
COMMENT '服务商原始响应(JSON)';
3. 配置文件扩展
# application.yml
ai:
providers:
openai:
enabled: true
base-url: https://api.apiyi.com/v1/chat/completions
api-key: sk-xxx
timeout: 300000
runninghub:
enabled: true
base-url: https://www.runninghub.cn
submit-url: /task/openapi/ai-app/run
status-url: /task/openapi/status
output-url: /task/openapi/outputs
default-webapp-id: "1973555977595301890"
api-key: 5c44cef12da3470e9f24da70c63787dc
polling-interval: 5000 # 轮询间隔(毫秒)
max-polling-times: 120 # 最大轮询次数(10分钟)
🔧 RunningHub特殊处理
模型映射配置
在points_config表中配置RunningHub模型:
INSERT INTO `points_config`
(model_name, points_cost, description, is_enabled, provider_type, provider_config)
VALUES
('rh_sora2_portrait', 160, 'RunningHub Sora2 竖屏视频', 1, 'runninghub',
'{"webappId":"1973555977595301890","duration":10,"model":"portrait"}'),
('rh_sora2_landscape', 160, 'RunningHub Sora2 横屏视频', 1, 'runninghub',
'{"webappId":"1973555977595301890","duration":10,"model":"landscape"}'),
('rh_sora2_portrait_hd', 420, 'RunningHub Sora2 高清竖屏', 1, 'runninghub',
'{"webappId":"1973555977595301890","duration":10,"model":"portrait-hd"}');
异步任务处理流程
RunningHub是异步API,需要特殊处理:
1. 提交任务 → 获得taskId
2. 更新数据库:provider_task_id = taskId, status = 'processing'
3. 启动轮询任务:
- 每5秒查询一次状态
- RUNNING → 继续轮询
- SUCCESS → 获取结果 → 更新status='completed'
- FAILED → 更新status='failed'
- 超时 → 更新status='failed'
📦 核心类设计
ProviderTaskRequest(统一请求)
@Data
public class ProviderTaskRequest {
private String modelName;
private String prompt;
private String imageUrl;
private String imageBase64;
private String aspectRatio;
private Integer duration; // 视频时长
private Map<String, Object> extraParams; // 扩展参数
}
ProviderTaskResponse(统一响应)
@Data
public class ProviderTaskResponse {
private String providerTaskId; // 服务商任务ID
private TaskSubmitStatus status; // SUBMITTED, PROCESSING, COMPLETED, FAILED
private String resultUrl; // 如果是同步返回,直接有结果
private String errorMessage;
private Map<String, Object> rawResponse; // 原始响应
}
RunningHubNodeInfo(RunningHub请求节点)
@Data
public class RunningHubNodeInfo {
private String nodeId;
private String fieldName;
private String fieldValue;
private String fieldData; // 可选
private String description;
}
🔄 任务轮询设计
新增定时任务
@Scheduled(fixedDelay = 5000) // 每5秒执行一次
public void pollAsyncTasks() {
// 1. 查询所有 status='processing' 且 provider_type='runninghub' 的任务
// 2. 对每个任务调用 queryTaskStatus
// 3. 根据状态更新数据库
// 4. 如果完成,调用 getTaskResult 获取结果
}
📊 数据流转
OpenAI格式(同步)
用户提交 → OpenAIProvider.submitTask()
→ 直接返回结果URL
→ 更新status='completed'
RunningHub格式(异步)
用户提交 → RunningHubProvider.submitTask()
→ 返回taskId,status='RUNNING'
→ 定时任务轮询
→ 检测到SUCCESS
→ getTaskResult()获取结果URL
→ 更新status='completed'
⚠️ 注意事项
- 配置隔离:不同厂商的配置独立管理
- 错误处理:统一异常类型,便于业务层处理
- 日志记录:记录每次API调用的原始请求和响应
- 超时控制:异步任务需要设置最大轮询次数
- 并发控制:轮询任务需要考虑并发和限流
- 配置热更新:支持动态切换服务商
🎯 优势
- ✅ 扩展性:新增厂商只需实现AIProvider接口
- ✅ 解耦:业务层无需关心底层API差异
- ✅ 灵活性:同一个模型类型可以配置多个厂商
- ✅ 可维护性:每个厂商的逻辑独立封装
- ✅ 容错性:某个厂商故障不影响其他厂商
📈 未来扩展
- 支持厂商负载均衡
- 支持厂商降级和熔断
- 支持厂商价格对比和智能选择
- 支持多厂商并行调用(取最快)