接入自定义 TTS 服务
通过接入自定义文本转语音 (TTS) 服务,你可以将自研 TTS、私有化部署 TTS 或第三方 TTS 服务作为对话式智能体的语音合成模块。本文介绍如何准备兼容 OpenAI TTS 协议的 HTTP 服务,并在创建对话式智能体时通过 TTS 配置接入该服务。
实现原理
Agents SDK 通过 GenericTTS 构造自定义 TTS 配置。启动智能体会话时,SDK 会将该配置序列化为 tts.vendor = "generic_http",并由声网对话式 AI 引擎在需要播报文本时向你的 TTS HTTP 服务发起请求。
前提条件
开始前,请确保你已经:
- 参考使用 Agents SDK 实现对话式 AI 引擎实现了与 AI 智能体对话互动的基本逻辑。
- 准备好可被声网对话式 AI 引擎访问的 TTS HTTP 服务。
- 确保 TTS 服务支持 HTTP/1.1 或更高版本,生产环境建议使用 HTTPS。
- 确保 TTS 服务能够返回 PCM 格式音频数据。
- 安装包含
GenericTTSgeneric_http支持的 Agents SDK 版本。Go 和 TypeScript SDK 需要使用v2.4.0之后的版本;Python SDK 需要使用v2.4.1之后的版本。
GenericTTS 当前只支持 HTTP 和 HTTPS endpoint。不要传入 ws:// 或 wss:// 地址,否则 SDK 会在构造 TTS vendor 时抛出错误。
实现步骤
1. 准备 TTS 服务接口
你的 TTS 服务需要提供一个兼容 OpenAI TTS 协议的 HTTP 接口。基础要求如下:
- 请求方法:
POST - 请求路径:
/audio/speech - 请求 Header:支持通过
Authorization: Bearer <api_key>鉴权 - 请求 Body:JSON 格式
- 响应 Body:PCM 音频数据流
请求体示例如下:
{
"model": "your-tts-model",
"input": "欢迎使用声网对话式 AI 服务。",
"voice": "alloy",
"speed": 1.0,
"sample_rate": 16000,
"response_format": "pcm",
"instruction": "calm"
}
请求体字段说明如下:
| 字段 | 类型 | 说明 |
|---|---|---|
input | String | 待合成的文本内容。由声网对话式 AI 引擎根据智能体回复自动传入。 |
model | String | TTS 模型名称。如果你的 TTS 服务不区分模型,可以忽略该字段。 |
voice | String | 音色名称。如果你的 TTS 服务不区分音色,可以忽略该字段。 |
speed | Number | 语速。 |
sample_rate | Integer | 输出音频采样率,单位为 Hz,默认值为 16000。 |
response_format | String | 输出音频格式,默认值为 pcm。当前仅支持 pcm。 |
instruction | String | 语音风格、情绪或其他播报指令。 |
如果你的 TTS 服务不支持多采样率,请确保实际返回音频的采样率与通过 SDK 配置的 sample_rate 一致。
2. 验证 TTS 服务响应
你可以先使用 curl 验证 TTS 服务是否能正常返回 PCM 音频流:
curl --request POST \
--url https://your-tts-service.example.com/v1/audio/speech \
--header 'Authorization: Bearer <your_tts_api_key>' \
--header 'Content-Type: application/json' \
--data '{
"model": "your-tts-model",
"input": "欢迎使用声网对话式 AI 服务。",
"voice": "alloy",
"speed": 1.0,
"sample_rate": 16000,
"response_format": "pcm"
}' \
--output test.pcm
服务成功处理请求后,应以 HTTP 分块传输编码(Chunked Transfer Encoding)的方式流式返回合成后的 PCM 音频数据。
如果请求失败,建议返回标准 HTTP 错误状态码,并在响应体中携带 JSON 格式错误信息。例如:
{
"error": {
"message": "Incorrect API key provided.",
"type": "invalid_request_error",
"param": null,
"code": "invalid_api_key"
}
}
3. 配置智能体使用自定义 TTS
以下示例中的 sttVendor/stt_vendor 和 llmVendor/llm_vendor 表示已创建好的 STT 和 LLM vendor。实际接入时,请根据你的业务选择对应的语音识别和大语言模型服务并完成配置;本文重点展示如何通过 GenericTTS 接入自定义 TTS 服务。
- Go
- Python
- TypeScript
- Restful API
你可以使用 Agents SDK 的 GenericTTS 配置接入自定义 TTS 服务。
import (
Agora "github.com/AgoraIO/agora-agents-go/v2"
agentkit "github.com/AgoraIO/agora-agents-go/v2/agentkit/cn"
vendors "github.com/AgoraIO/agora-agents-go/v2/agentkit/cn/vendors"
)
sampleRate := vendors.SampleRate16kHz
speed := 1.0
client := agentkit.NewAgoraClient(agentkit.ClientOptions{
AppID: requireEnv("AGORA_APP_ID"),
AppCertificate: requireEnv("AGORA_APP_CERTIFICATE"),
})
agent := agentkit.NewAgent(
client,
agentkit.WithTurnDetectionConfig(&agentkit.TurnDetectionConfig{
Language: Agora.AsrLanguageZhCn.Ptr(),
}),
).WithStt(
// 替换为你已配置的 STT vendor。
sttVendor,
).WithLlm(
// 替换为你已配置的 LLM vendor。
llmVendor,
).WithTts(
vendors.NewGenericTTS(vendors.GenericTTSOptions{
URL: "https://your-tts-service.example.com/v1/audio/speech",
Headers: map[string]string{
"Authorization": "Bearer " + requireEnv("CUSTOM_TTS_API_KEY"),
},
Model: "your-tts-model",
Voice: "alloy",
Speed: &speed,
SampleRate: &sampleRate,
ResponseFormat: "pcm",
Instruction: "calm",
AdditionalParams: map[string]interface{}{
"enable_request_id": false,
},
}),
)
你可以使用 Agents SDK 的 GenericTTS 配置接入自定义 TTS 服务。
from agora_agent import Agent, GenericTTS
client = Agora(
area=Area.CN,
app_id=os.environ["AGORA_APP_ID"],
app_certificate=os.environ["AGORA_APP_CERTIFICATE"],
)
agent = (
Agent(
client=client,
turn_detection={"language": "zh-CN"},
)
# 替换为你已配置的 STT vendor。
.with_stt(stt_vendor)
# 替换为你已配置的 LLM vendor。
.with_llm(llm_vendor)
.with_tts(
GenericTTS(
url="https://your-tts-service.example.com/v1/audio/speech",
headers={
"Authorization": f"Bearer {require_env('CUSTOM_TTS_API_KEY')}",
},
model="your-tts-model",
voice="alloy",
speed=1.0,
sample_rate=16000,
response_format="pcm",
instruction="calm",
additional_params={
"enable_request_id": False,
},
)
)
)
你可以使用 Agents SDK 的 GenericTTS 配置接入自定义 TTS 服务。
import { Agent, GenericTTS } from 'agora-agents';
const client = new AgoraClient({
area: Area.CN,
appId: requireEnv("AGORA_APP_ID"),
appCertificate: requireEnv("AGORA_APP_CERTIFICATE"),
});
const agent = new Agent({
client,
turnDetection: { language: 'zh-CN' },
})
// 替换为你已配置的 STT vendor。
.withStt(sttVendor)
// 替换为你已配置的 LLM vendor。
.withLlm(llmVendor)
.withTts(
new GenericTTS({
url: 'https://your-tts-service.example.com/v1/audio/speech',
headers: {
Authorization: `Bearer ${process.env.CUSTOM_TTS_API_KEY}`,
},
model: 'your-tts-model',
voice: 'alloy',
speed: 1.0,
sampleRate: 16000,
responseFormat: 'pcm',
instruction: 'calm',
additionalParams: {
enable_request_id: false,
},
})
);
你可以调用 POST 创建对话式智能体接口时,在 tts 模块中将 vendor 设置为 generic_http,并配置你的 TTS 服务地址和参数。
{
"tts": {
"vendor": "generic_http",
"url": "https://your-tts-service.example.com/v1/audio/speech",
"headers": {
"Authorization": "Bearer <your_tts_api_key>"
},
"params": {
"model": "your-tts-model",
"voice": "alloy",
"speed": 1.0,
"sample_rate": 16000,
"response_format": "pcm",
"instruction": "calm",
"enable_request_id": false
}
}
}
相关配置说明如下:
- Go
- Python
- TypeScript
- Restful API
| 参数 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
URL | string | 是 | 自定义 TTS 服务的 HTTP(S) endpoint。 |
Headers | map[string]string | 否 | 调用 TTS 服务时携带的请求头,常用于配置 Authorization。未设置时不会写入 headers。 |
APIKey | string | 否 | TTS 服务鉴权 API key。 |
Model | string | 否 | TTS 模型名称。 |
Voice | string | 否 | 音色名称。 |
Speed | *float64 | 否 | 语速。 |
SampleRate | *vendors.SampleRate | 否 | 输出音频采样率,单位为 Hz。如果你的 TTS 服务暂不支持多采样率,请确保返回音频的采样率与该配置一致。 |
ResponseFormat | string | 否 | 输出音频格式。当前 ConvoAI 支持 pcm。 |
Instruction | string | 否 | 语音风格、情绪或其他播报指令。 |
AdditionalParams | map[string]interface{} | 否 | 透传给 TTS 服务的其他参数;同名字段会被非空的显式字段覆盖。 |
SkipPatterns | []int | 否 | 控制 TTS 跳过特定文本模式。 |
| 参数 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
url | str | 是 | 自定义 TTS 服务的 HTTP(S) endpoint。 |
headers | Dict[str, str] | 否 | 调用 TTS 服务时携带的请求头,常用于配置 Authorization。未设置时不会写入 headers。 |
api_key | str | 否 | TTS 服务鉴权 API key。 |
model | str | 否 | TTS 模型名称。 |
voice | str | 否 | 音色名称。 |
speed | float | 否 | 语速。 |
sample_rate | int | 否 | 输出音频采样率,单位为 Hz。如果你的 TTS 服务暂不支持多采样率,请确保返回音频的采样率与该配置一致。 |
response_format | str | 否 | 输出音频格式。当前 ConvoAI 支持 pcm。 |
instruction | str | 否 | 语音风格、情绪或其他播报指令。 |
additional_params | Dict[str, Any] | 否 | 透传给 TTS 服务的其他参数;同名字段会被显式参数覆盖。 |
skip_patterns | List[int] | 否 | 控制 TTS 跳过特定文本模式。 |
| 参数 | 类型 | 是否必需 | 说明 |
|---|---|---|---|
url | string | 是 | 自定义 TTS 服务的 HTTP(S) endpoint。 |
headers | Record<string, string> | 否 | 调用 TTS 服务时携带的请求头,常用于配置 Authorization。未设置时不会写入 headers。 |
apiKey | string | 否 | TTS 服务鉴权 API key。 |
model | string | 否 | TTS 模型名称。 |
voice | string | 否 | 音色名称。 |
speed | number | 否 | 语速。 |
sampleRate | number | 否 | 输出音频采样率,单位为 Hz。如果你的 TTS 服务暂不支持多采样率,请确保返回音频的采样率与该配置一致。 |
responseFormat | "pcm" | 否 | 输出音频格式。当前 ConvoAI 支持 pcm。 |
instruction | string | 否 | 语音风格、情绪或其他播报指令。 |
additionalParams | Record<string, unknown> | 否 | 透传给 TTS 服务的其他参数;同名字段会被显式字段覆盖。 |
skipPatterns | number[] | 否 | 控制 TTS 跳过特定文本模式。 |
| 配置路径 | 类型 | 是否必需 | 默认值 | 说明 |
|---|---|---|---|---|
tts.vendor | String | 是 | - | 设置为 generic_http,表示使用自定义 TTS 服务。 |
tts.url | String | 是 | - | TTS 服务地址,要求兼容 OpenAI TTS 协议。 |
tts.headers | Object | 否 | - | 调用 TTS 服务时携带的请求头,常用于配置 Authorization。 |
tts.params | Object | 否 | - | TTS 请求参数。除本文列出的字段外,还支持按需配置其他字段,系统会将其透传给 TTS 服务。 |
tts.params.api_key | String | 否 | - | TTS 服务鉴权 API key。如果配置该字段,系统会在调用 TTS 服务时自动携带 Authorization: Bearer <api_key>。 |
tts.params.model | String | 否 | - | TTS 模型名称。 |
tts.params.voice | String | 否 | - | 音色名称。如果你的 TTS 服务暂不支持多音色,可以忽略该参数。 |
tts.params.speed | Number | 否 | - | 语速。 |
tts.params.sample_rate | Integer | 否 | 16000 | 输出音频采样率,单位为 Hz。如果你的 TTS 服务暂不支持多采样率,请确保返回音频的采样率与该配置一致。 |
tts.params.response_format | String | 否 | pcm | 输出音频格式。当前仅支持 pcm。 |
tts.params.instruction | String | 否 | - | 语音风格、情绪或其他播报指令。 |
tts.params.enable_request_id | Boolean | 否 | false | 是否在 TTS 请求体中附带 request_id、request_seq_id 和 request_end。 |
鉴权信息可以通过请求头中的 Authorization 传递,也可以通过 API key 字段传入。如果你的服务同时支持两种方式,建议优先使用请求头,便于和现有 HTTP 鉴权逻辑保持一致。
4. 启动会话
完成配置后,启动智能体会话。会话启动后,对话式 AI 引擎会在智能体需要播报 LLM 回复时自动调用你的 TTS 服务。
- Go
- Python
- TypeScript
session := agent.CreateSession(agentkit.CreateSessionOptions{
Channel: requireEnv("AGORA_CHANNEL"),
AgentUID: requireEnv("AGORA_AGENT_UID"),
RemoteUIDs: []string{"*"},
})
agentSessionID, err := session.Start(ctx)
if err != nil {
log.Fatal(err)
}
log.Printf("Agent started: %s", agentSessionID)
session = agent.create_session(
channel=require_env("AGORA_CHANNEL"),
agent_uid=require_env("AGORA_AGENT_UID"),
remote_uids=["*"],
)
agent_session_id = session.start()
print(f"Agent started: {agent_session_id}")
const session = agent.createSession({
channel: process.env.AGORA_CHANNEL!,
agentUid: process.env.AGORA_AGENT_UID!,
remoteUids: ['*'],
});
const agentSessionId = await session.start();
console.log('Agent started:', agentSessionId);
按需接收请求标识
如果你的 TTS 服务需要区分同一轮播报中的多次请求,可以将 enable_request_id 作为附加参数透传给 TTS 服务。
- Go
- Python
- TypeScript
AdditionalParams: map[string]interface{}{
"enable_request_id": true,
}
additional_params={
"enable_request_id": True,
}
additionalParams: {
enable_request_id: true,
}
开启后,系统会在发送给 TTS 服务的请求体中附带以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
request_id | String | 单次 TTS 请求的唯一标识。 |
request_seq_id | Integer | 同一轮播报内的请求序号,从 0 开始递增。 |
request_end | Boolean | 是否为同一轮播报的最后一次请求。 |