低代码集成:Fish Speech与钉钉机器人对接实战
早上九点,公司晨会刚结束,产品经理小张就急匆匆地跑过来:“王哥,刚才的会议纪要能帮我转成语音吗?我路上开车听,下午还要跟客户开会。” 我点点头,打开电脑,不到两分钟,一份清晰的语音文件就发到了他的钉钉上。
这不是什么魔法,而是我们团队最近落地的一个小项目——把开源的Fish Speech TTS模型,通过低代码的方式集成到了钉钉机器人里。现在,无论是会议纪要语音播报、审批通知朗读,还是日报提醒,都能自动转换成语音推送给同事。上线一个月,日均调用量已经超过2万次,错误率还不到0.5%。
今天,我就来跟你聊聊,怎么用最简单的方法,把强大的语音合成能力“塞”进你们每天都在用的钉钉里。
1. 为什么是Fish Speech + 钉钉?
在决定用Fish Speech之前,我们也调研过不少方案。市面上的TTS服务很多,但要么太贵,按调用量计费,用起来肉疼;要么音质一般,机械感重,听久了容易疲劳;还有的部署复杂,对运维要求高。
Fish Speech吸引我们的地方很直接:
第一,它开源免费。这意味着没有持续的使用成本,对于内部工具来说,这一点太重要了。一次部署,长期受益。
第二,音质足够好。它的1.5版本在开源TTS圈子里评价很高,合成的声音自然度、流畅度都接近商用水平,支持中英日等多国语言,还能做简单的音色克隆。
第三,部署相对简单。虽然底层是复杂的AI模型,但社区提供了打包好的镜像和WebUI,让我们这种不是专攻AI的团队也能快速跑起来。
而选择钉钉机器人作为出口,理由就更简单了:它就在那里。几乎每个同事每天都会打开钉钉,消息推送的到达率几乎是100%。把语音能力集成到机器人里,用户不需要安装新APP,不需要记住新网址,在熟悉的聊天窗口里就能获取语音信息,学习成本为零。
我们的目标很明确:用最低的技术门槛和成本,打造一个公司内部人人可用的“语音小助手”。
2. 核心架构:三块积木搭起一座桥
整个方案的核心,就像搭积木,只需要三块:
- Fish Speech服务:负责“生产”语音。我们在内网的一台服务器上部署好,提供一个HTTP API。
- 钉钉机器人:负责“送达”消息。在钉钉开放平台创建一个自定义机器人,拿到它的Webhook地址。
- 中间“粘合剂”:负责“调度”工作。这是最关键的一块,它需要听懂业务系统的指令,去调用Fish Speech生成语音,再把语音文件通过钉钉机器人发出去。
这个“粘合剂”的选择,决定了项目的复杂度。我们追求低代码,所以直接用了钉钉官方推荐的钉钉宜搭和阿里云函数计算FC的组合。你完全不用自己买服务器、搭环境。
下面这张图展示了数据是怎么流动的:
graph LR A[业务系统<br>(如OA/会议系统)] --> B[钉钉宜搭流程] B --> C[阿里云函数计算 FC] C --> D[内网 Fish Speech API] D -- 返回音频文件 --> C C -- 上传媒体文件 --> E[钉钉服务器] C -- 发送机器人消息 --> F[钉钉群/用户] E -- 提供media_id --> C整个流程是自动化的:业务系统触发流程,函数计算调用TTS服务,拿到语音后再通过机器人推出去。全程没有人工干预。
3. 分步实战:从零到一的搭建过程
好了,理论说完,我们动手把它建起来。跟着步骤走,你也能在你们公司复现一个。
3.1 第一步:把Fish Speech跑起来
首先,我们需要一个能提供TTS服务的后端。如果你公司有GPU服务器,部署起来很方便。如果没有,用CPU也能跑,只是慢一点。
这里以使用社区提供的预打包镜像为例,这是最快的方式:
- 获取镜像并启动:我们使用一个集成了所有依赖的Fish Speech镜像。启动后,它会提供一个WebUI界面(通常在
http://你的服务器IP:6006)和一个关键的API接口。 - 验证API接口:服务启动后,最重要的一个接口是合成接口。你可以用
curl命令简单测试一下:
如果一切正常,当前目录下会生成一个curl -X POST http://你的服务器IP:6006/api/v1/tts \ -H "Content-Type: application/json" \ -d '{ "text": "你好,钉钉,这是一条测试语音。", "reference_audio": null, "language": "zh" }' \ --output test_audio.wavtest_audio.wav文件,播放它就能听到合成的声音了。
关键点:记下这个API的地址(http://你的服务器IP:6006/api/v1/tts)和端口,后面在函数计算里会用到。确保你的Fish Speech服务器能被阿里云函数计算访问到(如果是内网,可能需要配置VPC)。
3.2 第二步:创建钉钉机器人
这一步是在钉钉上操作,给我们的语音助手一个“身份”。
- 在钉钉群里,点击右上角
...->机器人->添加机器人。 - 选择
自定义机器人。 - 设置机器人名字,比如“公司语音助手”,并选择消息发送的群。
- 最重要的一步:在安全设置中,至少选择“自定义关键词”。我们填上“语音”或“TTS”。这样,只有包含这个关键词的消息,机器人才会处理,防止被刷。
- 创建完成后,你会得到一个Webhook地址,长得像
https://oapi.dingtalk.com/robot/send?access_token=XXXXXX。这个地址像密码一样,千万保管好,不要泄露。
3.3 第三步:编写“粘合剂”函数
这是最核心的编码部分,但代码量很少。我们将在阿里云函数计算(FC)中创建一个Python函数。
函数的职责很清晰:
- 接收来自宜搭流程的触发(触发信息里包含需要合成的文本)。
- 调用第一步部署好的Fish Speech API,把文本转换成音频文件。
- 将音频文件上传到钉钉的临时媒体库,获取一个
media_id。 - 使用第二步获得的Webhook,发送一条钉钉机器人消息,消息体中携带这个
media_id,告诉钉钉播放哪段语音。
以下是函数的核心代码示例:
import json import requests import logging from io import BytesIO # 初始化日志 logger = logging.getLogger() def handler(event, context): """ 主处理函数 event: 触发事件,包含需要合成的文本等信息 context: 函数运行上下文 """ # 1. 解析事件数据(从宜搭传来) try: # 这里根据宜搭实际传递的数据结构进行解析 body = json.loads(event.get('body', '{}')) text_to_speak = body.get('text', '') # 可以扩展其他参数,如语速、音色ID等 target_dingtalk_webhook = body.get('webhook') # 通常宜搭流程里可以配置 except Exception as e: logger.error(f"解析事件参数失败: {e}") return {'statusCode': 400, 'body': 'Invalid input'} if not text_to_speak: return {'statusCode': 400, 'body': 'Text is empty'} # 2. 调用Fish Speech API生成语音 fish_speech_url = "http://你的FishSpeech服务器IP:6006/api/v1/tts" tts_payload = { "text": text_to_speak, "language": "zh", # 根据文本识别语言,这里示例用中文 # "reference_audio": "base64编码的参考音频", # 如需音色克隆可启用 # "speed": 1.0 # 语速 } try: tts_response = requests.post(fish_speech_url, json=tts_payload, timeout=30) tts_response.raise_for_status() audio_data = tts_response.content logger.info(f"TTS合成成功,音频大小: {len(audio_data)} bytes") except requests.exceptions.RequestException as e: logger.error(f"调用TTS服务失败: {e}") return {'statusCode': 502, 'body': 'TTS service error'} # 3. 上传音频到钉钉服务器(获取media_id) # 注意:这里需要钉钉机器人的access_token,通常从webhook URL中提取,或单独申请 # 此处简化,假设我们有上传接口的token (实际需从企业后台获取) dingtalk_upload_token = "你的钉钉上传token" upload_url = f"https://oapi.dingtalk.com/media/upload?access_token={dingtalk_upload_token}&type=voice" files = {'media': ('message.wav', BytesIO(audio_data), 'audio/wav')} try: upload_resp = requests.post(upload_url, files=files) upload_data = upload_resp.json() if upload_data.get('errcode') != 0: logger.error(f"钉钉音频上传失败: {upload_data}") return {'statusCode': 502, 'body': 'DingTalk upload failed'} media_id = upload_data.get('media_id') logger.info(f"音频上传成功,media_id: {media_id}") except Exception as e: logger.error(f"上传音频到钉钉时异常: {e}") return {'statusCode': 502, 'body': 'Upload exception'} # 4. 通过机器人Webhook发送语音消息 dingtalk_webhook = target_dingtalk_webhook or "你的默认机器人Webhook URL" msg_payload = { "msgtype": "voice", "voice": { "media_id": media_id, "duration": 10 # 估算的语音时长,单位秒 } } try: # 钉钉机器人要求必须包含安全签名或关键词 headers = {'Content-Type': 'application/json'} dingtalk_resp = requests.post(dingtalk_webhook, json=msg_payload, headers=headers) result = dingtalk_resp.json() if result.get('errcode') == 0: logger.info("钉钉语音消息发送成功") return {'statusCode': 200, 'body': 'Success'} else: logger.error(f"钉钉消息发送失败: {result}") return {'statusCode': 502, 'body': json.dumps(result)} except Exception as e: logger.error(f"发送钉钉消息时异常: {e}") return {'statusCode': 502, 'body': 'DingTalk send exception'} # 理论上不会执行到这里 return {'statusCode': 500, 'body': 'Unknown error'}把这个函数部署到阿里云FC上,记下它的HTTP触发器地址。
3.4 第四步:用宜搭串联一切
现在,我们有TTS服务(A)、有机器人(B)、有调度函数(C)。宜搭的作用,就是定义一个流程,让A、B、C按顺序动起来。
例如,我们创建一个“会议纪要转语音”的流程:
- 触发节点:当OA系统里的会议纪要状态变为“已发布”时,触发这个宜搭流程。宜搭会抓取会议纪要的文本内容。
- HTTP请求节点:将会议纪要文本、以及接收语音的钉钉群Webhook地址,作为参数,调用我们在第三步部署好的阿里云函数(FC)的HTTP触发器地址。
- 结束:流程结束。函数会异步执行TTS合成和推送,用户稍后就会在钉钉群收到语音。
在宜搭里,你几乎不需要写代码,通过拖拽和配置就能完成这个流程的搭建。这就是“低代码”的威力。
4. 真实效果与踩坑经验
方案上线后,效果立竿见影。最受欢迎的場景是:
- 会议纪要语音版:像开头小张那样,同事们在通勤路上就能“听”完会议重点。
- 系统审批提醒:重要的报销或请假审批通过后,机器人会用语音播报“您的XXX审批已通过”,比单纯的消息框更醒目。
- 每日运营日报:每天下午,机器人自动将核心数据摘要合成语音,推送给管理团队。
目前日均2万多次的调用,稳定运行,错误率极低。当然,过程中也踩过一些坑:
坑1:钉钉语音消息限制钉钉机器人发送的语音消息,有格式和时长限制(目前是amr或wav格式,2MB以内,不超过60秒)。对于长文本,我们需要在函数里先做文本分割,生成多条语音顺序发送。
坑2:网络与超时函数计算、Fish Speech服务、钉钉服务三方网络要通畅。特别是函数调用内网TTS服务时,要配置好VPC,并将超时时间设置得长一些(比如30秒),给模型生成留出时间。
坑3:安全与权限钉钉机器人的Webhook和上传媒体的Token是最高权限凭证,必须妥善保管,最好放在函数计算的环境变量中,而不是硬编码在代码里。同时,利用好钉钉的“关键词”、“加签”等安全机制,防止接口被恶意调用。
坑4:音色与效果默认的Fish Speech音色可能比较单一。如果公司有需要,可以利用Fish Speech的“语音克隆”功能,用老板或代言人几分钟的录音,训练一个专属的企业音色,让通知听起来更亲切。
5. 总结
回过头看,这个项目技术难度不高,但带来的便利性却是实实在在的。它证明了,利用现有的优秀开源模型(Fish Speech)和成熟的云原生、低代码平台(阿里云FC、钉钉宜搭),即使是一个小型技术团队,也能快速构建出体验良好的AI应用,并无缝融入现有的工作流(钉钉)。
核心价值不在于用了多炫酷的AI,而在于用最低的成本和门槛,解决了真实的效率痛点。如果你所在的团队也有类似的内部信息语音化需求,不妨试试这条路径。从部署Fish Speech测试,到写出第一个能工作的函数,可能只需要一个下午的时间。当第一句由你们自己搭建的系统合成的语音,从钉钉里播放出来时,那种感觉还是挺棒的。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。