当前位置:首页 > 技术 > 正文内容

在AI Agent中集成语音合成:从文本到自然语音的实践

访客 技术 2026年8月15日 1

1 语音交互架构设计

一个具备语音能力的AI Agent,其交互流程通常包含以下层次:

1.1 基础交互模式

用户 → 智能体 → 文本响应

这是最简化的流程:用户输入触发智能体,智能体返回纯文本。

1.2 引入语音输出

用户 → 智能体 → 文本响应
               ↓
            TTS引擎 → 语音输出

在基础流程上增加文本转语音(TTS)模块。智能体生成的文本被送入TTS服务,最终转换为语音。

1.3 异步并行处理

用户 → 智能体 → 文本响应
               ↓(异步)
            TTS引擎 → 语音输出

文本响应立即返回,语音合成在后台异步执行,提升用户体验。

1.4 设计要点

  • 模块解耦:文本处理与语音合成分离,便于独立升级和维护。
  • 异步非阻塞:语音合成不阻塞主流程,响应速度更快。
  • 灵活切换:可根据场景仅返回文本或同时提供语音。
  • 接入成熟方案:如Google Cloud TTS、Azure TTS等,确保合成质量。

2 TTS技术选型与核心概念

以Google Cloud Text-to-Speech为例,其API能将文本或SSML(语音合成标记语言)转换为MP3、LINEAR16等格式的音频数据。

2.1 基本请求示例

通过curl调用合成接口:

curl -H "Authorization: Bearer $(gcloud auth print-access-token)" \
     -H "x-goog-user-project: YOUR_PROJECT_ID" \
     -H "Content-Type: application/json; charset=utf-8" \
     --data '{
  "input": { "text": "I have added the event to your calendar." },
  "voice": {
    "languageCode": "en-GB",
    "name": "en-GB-Standard-A",
    "ssmlGender": "FEMALE"
  },
  "audioConfig": { "audioEncoding": "MP3" }
}' "https://texttospeech.googleapis.com/v1/text:synthesize"

2.2 合成过程

将文本输入转换为音频数据的过程称为"合成"。API返回base64编码的音频数据,需解码后保存为文件或直接播放。

2.3 语音选择

不同的语音参数(语言、性别、口音)可模拟不同说话者。例如使用英式口音女性,或澳式口音男性。

2.4 WaveNet模型

Google WaveNet是一种深度神经网络,基于真人语音样本训练,生成的语音更自然、温暖。相比传统拼接合成,音质明显提升。

2.5 可调参数

除语音外,还可调节语速、音高、音量、采样率(Hz)等。

2.6 SSML支持

通过SSML可在文本中嵌入停顿、强调、数字发音规则等细节,提升合成表现力。

3 后端语音功能实现

以下使用FastAPI框架实现一个带异步语音合成的端点。

3.1 聊天接口

@app.post("/chat")
async def chat(query: str, background_tasks: BackgroundTasks):
    agent = Master()
    reply = agent.run(query)
    task_id = str(uuid.uuid4())
    background_tasks.add_task(agent.voice_synthesis_background, reply, task_id)
    return {"reply": reply, "task_id": task_id}

后台任务触发语音合成,不阻塞接口响应。

3.2 后台合成任务

def voice_synthesis_background(self, text: str, uid: str):
    asyncio.run(self._synthesize_text(text, uid))

3.3 核心合成方法

async def _synthesize_text(self, text: str, uid: str):
    print("TTS input:", text)
    print("UID:", uid)
    print("Current emotion:", self.emotion)

    # 使用REST传输避免gRPC的503错误
    client = texttospeech.TextToSpeechClient(transport="rest")

    input_text = texttospeech.SynthesisInput(text="Hello world")  # 示例文本
    voice = texttospeech.VoiceSelectionParams(
        language_code="en-US",
        name="en-US-Studio-O",
    )
    audio_config = texttospeech.AudioConfig(
        audio_encoding=texttospeech.AudioEncoding.LINEAR16,
        speaking_rate=1.0
    )

    response = client.synthesize_speech(
        request={"input": input_text, "voice": voice, "audio_config": audio_config}
    )

    # 保存音频文件
    with open(f"output_{uid}.mp3", "wb") as f:
        f.write(response.audio_content)
    print("Audio saved to output.mp3")

4 语音克隆与增强替代方案

4.1 Bark (语音克隆)

Bark是第二代声音克隆模型,支持中文及英文等多种语言,可生成高度拟人化的语音,甚至模拟笑声、叹息等非语言声音。

4.2 阿里云Sambert语音合成

基于达摩院自研的SAMBERT+NSFGAN模型,结合深度神经网络与领域知识,具有高准确度、自然韵律和强表现力。支持实时流式合成。

4.2.1 保存音频到文件

from dashscope.audio.tts import SpeechSynthesizer

result = SpeechSynthesizer.call(
    model='sambert-zhichu-v1',
    text='今天天气怎么样',
    sample_rate=48000
)
if result.get_audio_data():
    with open('output.wav', 'wb') as f:
        f.write(result.get_audio_data())
    print('音频保存成功,大小:', len(result.get_audio_data()), 'bytes')
else:
    print('请求失败:', result.get_response())

4.2.2 实时播放音频

import pyaudio
from dashscope.audio.tts import ResultCallback, SpeechSynthesizer, SpeechSynthesisResult

class AudioCallback(ResultCallback):
    def __init__(self):
        self.player = None
        self.stream = None

    def on_open(self):
        self.player = pyaudio.PyAudio()
        self.stream = self.player.open(
            format=pyaudio.paInt16,
            channels=1,
            rate=48000,
            output=True
        )

    def on_event(self, result: SpeechSynthesisResult):
        if result.get_audio_frame():
            self.stream.write(result.get_audio_frame())
        if result.get_timestamp():
            print('时间戳:', result.get_timestamp())

    def on_close(self):
        self.stream.stop_stream()
        self.stream.close()
        self.player.terminate()

callback = AudioCallback()
SpeechSynthesizer.call(
    model='sambert-zhichu-v1',
    text='你是睿智的JavaEdge',
    sample_rate=48000,
    format='pcm',
    callback=callback
)

运行后即可通过扬声器听到合成语音。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。