5.7 KiB
5.7 KiB
Technical Design: SiliconFlow Voice Provider
Context
硅基流动(SiliconFlow)是一个提供多种 AI 服务的平台,包括语音合成、语音克隆等功能。本次设计将其作为新的语音供应商集成到现有的多供应商架构中。
约束条件:
- 必须兼容现有的
VoiceCloneProvider接口 - 不能影响现有的 CosyVoice 供应商功能
- 需要适配硅基流动的 API 差异
关键 API 差异:
- 语音克隆: 硅基流动需要先上传参考音频,返回
uri作为音色 ID - TTS 合成: 使用
/v1/audio/speech端点,返回二进制音频数据 - 认证: 使用 Bearer Token 格式的 API Key
- 模型: 使用
IndexTeam/IndexTTS-2模型
Goals / Non-Goals
Goals
- 实现
SiliconFlowProvider完整支持语音克隆和 TTS - 支持硅基流动
IndexTeam/IndexTTS-2模型 - 提供完整的配置支持,可独立开关
- 处理 API 差异,提供统一的服务接口
Non-Goals
- 不实现语音转文字(STT)功能(已有其他服务)
- 不修改现有的
VoiceCloneProvider接口定义 - 不改变前端 API 契约
Decisions
1. 实现类结构
架构:
SiliconFlowProvider (implements VoiceCloneProvider)
├── SiliconFlowApi (HTTP 客户端)
├── SiliconFlowProviderConfig (配置类)
└── DTO 类 (请求/响应适配)
Why:
- 遵循现有的
CosyVoiceProvider模式 - 分离 API 调用逻辑,便于测试和维护
- 专用 DTO 处理硅基流动 API 差异
2. 语音克隆流程适配
硅基流动语音克隆 API:
- 端点:
POST /v1/uploads/audio/voice - 请求参数:
model,customName,text,audio(base64) - 响应:
{"uri": "speech:customName:xxx:xxx"}
适配策略:
- 将统一请求的
audioUrl下载并转换为 base64 - 使用
prefix作为customName - 使用
audioUrl对应的转录文本作为text参数 - 返回的
uri存储为voiceId
代码示例:
@Override
public VoiceCloneResult cloneVoice(VoiceCloneRequest request) {
// 1. 下载音频文件
byte[] audioData = downloadAudio(request.getAudioUrl());
String base64Audio = Base64.getEncoder().encodeToString(audioData);
// 2. 构建硅基流动请求
SiliconFlowVoiceUploadRequest sfRequest = new SiliconFlowVoiceUploadRequest();
sfRequest.setModel("IndexTeam/IndexTTS-2");
sfRequest.setCustomName(request.getPrefix());
sfRequest.setText(getTranscriptionText(request.getAudioUrl()));
sfRequest.setAudio("data:audio/mpeg;base64," + base64Audio);
// 3. 调用 API
SiliconFlowVoiceUploadResponse sfResponse = siliconFlowApi.uploadVoice(sfRequest);
// 4. 适配返回结果
VoiceCloneResult result = new VoiceCloneResult();
result.setVoiceId(sfResponse.getUri());
return result;
}
3. TTS 合成流程适配
硅基流动 TTS API:
- 端点:
POST /v1/audio/speech - 请求参数:
model,input,voice,speed,sample_rate,response_format - 响应: 二进制音频数据
适配策略:
- 使用
voiceId(uri 格式) 作为voice参数 - 支持语速调节 (
speed) - 将二进制响应转换为 Base64 返回
4. 配置设计
配置结构:
yudao:
voice:
providers:
siliconflow:
enabled: false
api-key: ${SILICONFLOW_API_KEY}
base-url: https://api.siliconflow.cn
default-model: IndexTeam/IndexTTS-2
audio-format: mp3
sample-rate: 24000
connect-timeout: 10s
read-timeout: 180s
配置类设计:
@Data
@EqualsAndHashCode(callSuper = true)
public class SiliconFlowProviderConfig extends VoiceProviderProperties.ProviderConfig {
private String baseUrl = "https://api.siliconflow.cn";
private String defaultModel = "IndexTeam/IndexTTS-2";
private String audioFormat = "mp3";
private Integer sampleRate = 24000;
private Duration connectTimeout = Duration.ofSeconds(10);
private Duration readTimeout = Duration.ofSeconds(180);
}
5. 错误处理策略
- API 调用失败时记录详细日志
- 统一转换为
VOICE_TTS_FAILED业务异常 - 不暴露硅基流动技术细节给上层
- 支持重试机制(网络错误)
Risks / Trade-offs
| Risk | Mitigation |
|---|---|
| 硅基流动 API 变更 | 封装在独立的 API 客户端中,便于更新 |
| 语音克隆需要转录文本 | 在创建配音时已有转录流程,复用该文本 |
| 音频下载增加延迟 | 可考虑配置是否需要下载,或使用异步处理 |
| Base64 编码增加内存占用 | 限制音频文件大小(已有 50MB 限制) |
Migration Plan
阶段一:后端实现
- 创建
SiliconFlowApiHTTP 客户端 - 创建
SiliconFlowProviderConfig配置类 - 创建硅基流动专用 DTO 类
- 实现
SiliconFlowProvider - 更新
application.yaml配置
阶段二:测试验证
- 单元测试:
SiliconFlowApi调用 - 单元测试:
SiliconFlowProvider适配逻辑 - 集成测试:语音克隆完整流程
- 集成测试:TTS 合成完整流程
阶段三:前端支持(已有基础)
- 验证
voiceConfig.js已支持siliconflow类型 - 验证 API 请求已支持
providerType参数
回滚方案
- 通过配置
enabled: false禁用硅基流动 - 删除
SiliconFlowProvider相关代码 - 恢复
application.yaml配置
Open Questions
-
Q: 语音克隆时是否必须提供转录文本? A: 硅基流动 API 需要
text参数,使用配音创建时的转录文本 -
Q: 是否需要支持硅基流动的其他模型? A: 本次仅支持
IndexTeam/IndexTTS-2,后续可扩展 -
Q: 音频下载失败如何处理? A: 抛出业务异常,提示用户检查音频 URL