243 lines
7.8 KiB
Markdown
243 lines
7.8 KiB
Markdown
# 多厂商AI API适配器设计方案
|
||
|
||
## 📋 目标
|
||
|
||
支持接入多个AI服务提供商,包括:
|
||
1. **OpenAI格式API**(当前使用的api.apiyi.com)
|
||
2. **RunningHub API**
|
||
3. 未来的其他厂商
|
||
|
||
## 🏗️ 架构设计
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ AiTaskService │
|
||
│ (业务逻辑层,不变) │
|
||
└────────────────────┬────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ AIProviderService (新增) │
|
||
│ 根据模型配置选择对应的Provider │
|
||
└────────────────────┬────────────────────────────────────┘
|
||
│
|
||
┌───────────┴───────────┐
|
||
▼ ▼
|
||
┌──────────────────┐ ┌──────────────────┐
|
||
│ OpenAIProvider │ │ RunningHubProvider│
|
||
│ (适配器1) │ │ (适配器2) │
|
||
└──────────────────┘ └──────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────────┐ ┌──────────────────┐
|
||
│ api.apiyi.com │ │ www.runninghub.cn│
|
||
│ (第三方API) │ │ (第三方API) │
|
||
└──────────────────┘ └──────────────────┘
|
||
```
|
||
|
||
## 📝 实现步骤
|
||
|
||
### 1. 定义统一的Provider接口
|
||
|
||
```java
|
||
public interface AIProvider {
|
||
/**
|
||
* 提交任务到AI服务商
|
||
* @return 统一的任务响应对象
|
||
*/
|
||
ProviderTaskResponse submitTask(ProviderTaskRequest request);
|
||
|
||
/**
|
||
* 查询任务状态
|
||
*/
|
||
ProviderTaskStatus queryTaskStatus(String providerTaskId);
|
||
|
||
/**
|
||
* 获取任务结果
|
||
*/
|
||
ProviderTaskResult getTaskResult(String providerTaskId);
|
||
|
||
/**
|
||
* 获取服务商名称
|
||
*/
|
||
String getProviderName();
|
||
}
|
||
```
|
||
|
||
### 2. 扩展数据库表
|
||
|
||
#### points_config表新增字段
|
||
```sql
|
||
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表新增字段
|
||
```sql
|
||
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. 配置文件扩展
|
||
|
||
```yaml
|
||
# 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模型:
|
||
|
||
```sql
|
||
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(统一请求)
|
||
```java
|
||
@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(统一响应)
|
||
```java
|
||
@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请求节点)
|
||
```java
|
||
@Data
|
||
public class RunningHubNodeInfo {
|
||
private String nodeId;
|
||
private String fieldName;
|
||
private String fieldValue;
|
||
private String fieldData; // 可选
|
||
private String description;
|
||
}
|
||
```
|
||
|
||
## 🔄 任务轮询设计
|
||
|
||
### 新增定时任务
|
||
```java
|
||
@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'
|
||
```
|
||
|
||
## ⚠️ 注意事项
|
||
|
||
1. **配置隔离**:不同厂商的配置独立管理
|
||
2. **错误处理**:统一异常类型,便于业务层处理
|
||
3. **日志记录**:记录每次API调用的原始请求和响应
|
||
4. **超时控制**:异步任务需要设置最大轮询次数
|
||
5. **并发控制**:轮询任务需要考虑并发和限流
|
||
6. **配置热更新**:支持动态切换服务商
|
||
|
||
## 🎯 优势
|
||
|
||
1. ✅ **扩展性**:新增厂商只需实现AIProvider接口
|
||
2. ✅ **解耦**:业务层无需关心底层API差异
|
||
3. ✅ **灵活性**:同一个模型类型可以配置多个厂商
|
||
4. ✅ **可维护性**:每个厂商的逻辑独立封装
|
||
5. ✅ **容错性**:某个厂商故障不影响其他厂商
|
||
|
||
## 📈 未来扩展
|
||
|
||
- 支持厂商负载均衡
|
||
- 支持厂商降级和熔断
|
||
- 支持厂商价格对比和智能选择
|
||
- 支持多厂商并行调用(取最快)
|
||
|