Dify作为一款开源的LLM应用开发平台,支持通过工作流编排实现各种复杂功能。虽然Dify本身没有内置语音合成能力,但借助它的自定义工具和HTTP请求节点,我们可以很方便地接入第三方的语音合成服务。百度智能云提供的语音合成接口稳定性好、中文发音自然,而且每天有一定额度的免费调用量,非常适合个人开发者和小型项目使用。本文将以完整的实操流程,讲解如何把百度语音合成接入Dify,让应用的回复内容直接变成语音播报。

一、准备百度智能云的语音合成应用
接入的第一步是去百度智能云控制台创建应用并开通语音合成服务。登录百度智能云后,进入产品服务中的语音技术板块,点击创建应用。这里需要注意,语音合成需要在应用列表里单独勾选对应的接口权限,默认创建的应用可能只包含部分能力,建议把语音合成的相关接口全部勾上,避免后面调用时提示无权限。
创建完成后会得到三个关键信息:API Key、Secret Key和应用ID。前两个用于获取access_token,这是调用百度所有语音接口的通行证。百度语音合成的免费额度为每秒并发限制2次,个人测试完全够用。如果后续需要商用,可以再考虑购买付费套餐提升并发和音色数量。
获取token的接口地址是https://aip.baidubce.com/oauth/2.0/token,把API Key和Secret Key拼接进去请求一次即可。token默认有效期30天,建议在代码里做缓存处理,不要每次合成语音都重新请求一次,否则容易被限流。
二、用Python封装语音合成接口
百度语音合成提供REST API,最基础的调用方式是向https://tsn.baidu.com/text2audio发送POST请求。为了让Dify的工作流调用起来更方便,我们先用Python写一个简单的封装函数,直接返回音频文件的Base64字符串,这样便于在Dify的后续节点中传输和使用。
import requests
def get_access_token(api_key, secret_key):
url = "https://aip.baidubce.com/oauth/2.0/token"
params = {
"grant_type": "client_credentials",
"client_id": api_key,
"client_secret": secret_key
}
res = requests.post(url, params=params)
return res.json()["access_token"]
def text_to_speech(text, token):
url = "https://tsn.baidu.com/text2audio"
data = {
"tex": text, # 待合成的文本,需要进行UTF-8编码
"tok": token, # 访问令牌
"cuid": "dify-app", # 用户唯一标识,自定义字符串
"ctp": "1", # 客户端类型,固定为1
"lan": "zh", # 中文
"spd": "5", # 语速,0-15,5为正常
"pit": "5", # 音调,0-15
"vol": "5", # 音量,0-15
"per": "4118", # 音色,4118为度小贤,情感女声
"aue": "6" # 输出格式,6为wav
}
res = requests.post(url, data=data)
if res.headers.get("Content-Type") == "audio/wav":
return res.content # 返回音频二进制内容
else:
raise Exception("合成失败:" + res.text)参数里的per是控制音色的关键,普通音色0是度小美、1是度小宇、3是度逍遥、4是度丫丫,而5000系列之后的精品音色发音更自然。语速参数spd建议控制在4到6之间,太快会影响播报的清晰度。文本长度单次不能超过2048字节,长文本需要分段合成后再拼接。
三、在Dify中创建自定义工具
Dify提供了自定义工具功能,可以通过导入OpenAPI规范(Swagger)的方式把外部API注册成工具。在Dify控制台的工具页面点击创建自定义工具,填写工具名称和描述,然后在Schema中定义接口规范。下面是一个可直接使用的YAML配置示例。
openapi: 3.0.0
info:
title: 百度语音合成
description: 将文本转换为语音文件
version: v1
servers:
- url: https://tsn.baidu.com
paths:
/text2audio:
post:
operationId: baiduTts
summary: 文本转语音
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
tex:
type: string
description: 要合成的文本
tok:
type: string
description: 访问令牌
cuid:
type: string
description: 用户标识
ctp:
type: string
description: 客户端类型,固定为1
lan:
type: string
description: 语言,固定为zh
spd:
type: string
description: 语速0-15
pit:
type: string
description: 音调0-15
vol:
type: string
description: 音量0-15
per:
type: string
description: 音色编号
aue:
type: string
description: 输出格式,6为wav
responses:
"200":
description: 音频文件流保存工具后需要先进行鉴权测试,确认接口能正常返回。由于百度接口用的是表单提交而不是JSON,Schema里要写application/x-www-form-urlencoded,这是最容易出错的地方,很多人按照默认的application/json写法会导致参数根本传不到百度那边。
四、把语音合成节点接入工作流
工具创建好之后,在Dify的聊天流或工作流编排页面里,把自定义工具节点拖到LLM回复节点之后。工具节点的tex参数可以直接引用上游LLM节点的输出变量,这样就实现了模型说什么、语音就播什么。其他参数如语速、音色可以在节点里设置默认值,也可以把它们暴露成用户输入变量,让使用者在对话时自行选择喜欢的声音。
这里有一个实际体验上的问题需要注意:百度接口返回的是音频二进制流,如果Dify前端直接展示这个输出,用户看到的可能是乱码。比较稳妥的做法是在工作流里加一个代码执行节点,把音频内容转成Base64并包装成HTML的audio标签返回,浏览器就能直接渲染出播放器。代码执行节点里的转换逻辑可以参考下面的写法。
import base64
def main(audio_bytes: bytes) -> dict:
# 将音频二进制转成Base64,内嵌到audio标签中
audio_b64 = base64.b64encode(audio_bytes).decode("utf-8")
html = '<audio controls src="data:audio/wav;base64,' + audio_b64 + '"></audio>'
return {"result": html}最后把代码节点的输出变量配置到回复内容中,一个能说话的Dify应用就完成了。调试阶段建议先用短文本验证链路是否通畅,确认音频能正常播放后,再逐步调整音色和语速等参数。常见的报错有三种:3300表示参数缺失,通常是tex没有做UTF-8编码;3302表示token无效,重新获取即可;调用频繁被限流时,可以考虑在本地部署一个token缓存服务,或者升级百度的付费套餐来提高并发额度。