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. ✅ **容错性**:某个厂商故障不影响其他厂商
|
|||
|
|
|
|||
|
|
## 📈 未来扩展
|
|||
|
|
|
|||
|
|
- 支持厂商负载均衡
|
|||
|
|
- 支持厂商降级和熔断
|
|||
|
|
- 支持厂商价格对比和智能选择
|
|||
|
|
- 支持多厂商并行调用(取最快)
|
|||
|
|
|