我把一个「熊大熊二情感对话配音 Bot」从 0 做出来的全过程
如果你也想做一个能自动生成文案、拆分角色、逐句配音、最后合成为完整音频的 AI 工作流,这篇文章可以直接给你一条完整路径。
这次我做的,不是一个普通的 TTS 小工具,而是一个更像内容产品的东西:
输入一个主题,生成一段有共鸣、扎心、哲学感的中文对话,再由“熊大”和“熊二”分别配音,最后输出成一个完整音频。
听起来不复杂,但真正做下来,我踩到的坑几乎把这条链路的每一层都走了一遍:模型输出格式、工作流编排、逐句 TTS、音频下载、音频合并、对象存储、签名 URL、文件返回类型……
所以这篇文章,我不只讲“做成了什么”,更讲清楚:每一步我是怎么做的,为什么这么做,以及中间到底踩了哪些坑。
一、我最终想做成什么
先说目标。
我希望这个工作流能完成下面这件事:
-
-
-
用户输入一个主题,比如: 大家都说人生要先爱自己,可到底怎么做才算“爱”?
-
大模型围绕这个主题,生成一段适合短视频配音的“熊大熊二对话文案”
-
把整段对话结构化,拆成一句一句
-
根据不同角色,分别选择不同音色
-
每句单独调用 TTS
-
再按原始顺序把所有音频片段合并成一个完整的 mp3
-
最终输出一个可播放、可下载的音频文件
-
-
一句话概括就是:
从一个主题,自动生成一条“有情绪张力的社会学对话视频音频”。
二、为什么我一开始卡住了
最早我其实不是从 Coze 开始的,而是先在自动化平台里做。
一开始我以为这件事很简单:
-
-
熊大一段文本,生成一段音频
-
熊二一段文本,生成一段音频
-
最后把两段音频合并
-
但很快我发现,这样做有一个根本问题:对话感会消失。
比如原本的内容是:
-
-
-
熊大说一句
-
熊二回一句
-
熊大再说一句
-
熊二再接一句
-
-
如果我把它先聚合成:
-
-
xiongda_text
-
xionger_text
-
那最后输出的效果就会变成:
-
-
熊大把自己的所有话一次说完
-
熊二再把自己的所有话一次说完
-
这就不再是“对话”,而变成了“两段独白”。
这一步让我意识到:
如果想保留真实对话节奏,就不能按角色聚合后再 TTS,必须逐句处理。
三、我后来为什么换到 Coze
继续往下做的时候,我发现另一个问题:
TTS 不只是“生成音频”这么简单,真正麻烦的是后面这些:
-
-
-
每一句怎么走不同音色
-
怎么拿到真实 mp3 文件
-
怎么按顺序合并
-
怎么把最终文件返回给前端
-
怎么保证能播放、能下载
-
-
如果只是搭一个通用自动化流程,会在很多细节上变得很绕。
所以后来我切到了 Coze,原因很直接:
-
-
-
它更适合把“大模型 + 流程 + 代码节点”组合起来
-
我可以直接写节点代码,精细控制每一步
-
对“内容生成 + 配音”这种工作流,表达会更自然
-
-
换句话说,Coze 更像是做一个完整 AI 内容产品,而不是在拼积木。
四、我最终把整个流程拆成了 3 个核心节点
整个工作流最后其实就三层:
脚本解析
-> 逐句处理所有对话
-> 多对话合并
-> 输出结果
下面我按顺序讲每一步。
五、第一步:脚本解析
我的目标
先把用户输入的主题,变成结构化对白。
这里我不是让模型输出普通文案,而是要求它输出严格 JSON,比如:
{
"dialogues": [
{
"speaker": "熊大",
"text": "......",
"order": 1
},
{
"speaker": "熊二",
"text": "......",
"order": 2
}
]
}
为什么一定要 JSON
因为如果模型只输出这种普通文本:
熊大:……
熊二:……
熊大:……
熊二:……
人是看得懂的,但工作流后面的节点不好接。
后面的 TTS 节点要的是:
-
-
-
谁说的
-
说了什么
-
第几句
-
-
也就是:
-
-
-
speaker
-
text
-
order
-
-
所以我必须先把文案变成结构化数据。
这一步我是怎么做的
我写了一个“脚本解析”节点,用大模型生成严格 JSON,然后在代码里做解析:
-
-
-
读取 LLM 配置
-
渲染用户主题
-
调用模型
-
提取 dialogues
-
自动补全 order
-
-
如果模型格式不稳定,就返回一个空结构兜底,避免整个流程直接崩掉。
这一层的核心逻辑
它做的不是“情绪表达”,而是“结构化”。
也就是说,第一步最重要的不是文采,而是:
把内容变成后续工作流能消费的数据。
六、第二步:逐句处理所有对话
这是整个流程里最关键的一步。
这一步要做什么
我拿到的是 dialogues 数组,每一项像这样:
{
"speaker": "熊大",
"text": "很多人嘴上说爱自己,其实说的是别再让我受伤了。",
"order": 12
}
然后逐条处理:
-
-
识别角色
-
选对应音色
-
调用 TTS
-
下载真实音频
-
保存成 mp3
-
校验文件是否有效
-
为什么不能只拿 URL 就往下走
这是我前面踩过的一个大坑。
最开始我以为 TTS 返回了 audio_url 就够了,后面直接用这个 URL 合并就行。
结果不行。
因为很多 TTS 返回的是临时链接,不是真正已经落地的本地文件。
如果后面直接拿 URL 去拼,很容易出现:
-
-
读到的是跳转页
-
读到的是失效链接
-
读到的是权限页
-
或者根本不是标准 mp3
-
所以后来我改成:
每一句 TTS 生成后,立刻下载成真实本地 mp3 文件,再做校验。
我是怎么区分熊大和熊二音色的
在代码里,我按 speaker 做了映射:
-
-
熊大 -> 更沉稳的男声音色
-
熊二 -> 更活泼、更年轻的男声音色
-
逻辑很简单:
-
-
熊大负责更清醒、看透现实的表达
-
熊二负责更真诚、直接、带情绪的表达
-
这样一来,内容本身的角色分工,和声音听感也能一致。
这一层还做了什么保护
我在代码里加了很多校验,避免后面合并时出问题:
-
-
文本为空就跳过
-
文件大小必须大于 0
-
pydub 能正常读取
-
音频时长必须大于 0
-
最后每条对话都会变成这种“可用状态”:
-
-
audio_url
-
local_file_path
-
file_name
-
file_size
-
is_valid
-
这样到下一步的时候,我就不需要再猜哪个片段能用,哪个不能用。
七、第三步:多对话合并
为什么这里不能直接拼二进制
这也是一个非常典型的坑。
一开始很容易有个错误直觉:
“两个 mp3,不就是两个二进制文件吗?直接拼起来不就好了?”
实际不行。
因为 mp3 不是纯文本,不是你把两个 Buffer 直接 concat 一下就能得到一个标准音频文件。
如果硬拼,通常会出现:
-
-
播放异常
-
时长异常
-
前半段能播,后半段坏掉
-
直接 0:00
-
我后来怎么做的
我改成用 pydub 来做标准音频合并:
-
-
先按 order 排序
-
把每个有效 mp3 读出来
-
一个一个往后追加
-
最后重新导出成新的标准 mp3
-
这样得到的,就不再是“拼接过的字节流”,而是一个真正重新封装过的音频文件。
这一层的关键意义
它保留的是:
-
对话顺序
-
角色交替
-
节奏感
所以合并出来的结果不再是:
-
熊大整段
-
熊二整段
而是:
-
熊大一句
-
熊二一句
-
熊大一句
-
熊二一句
这才是真正的视频对白感。
八、我以为合并成功了,结果还是播不了
当时我在调试里看到这些数据都正常:
-
文件大小正常
-
时长正常
-
file_is_valid = true
-
upload_status = success
我本来以为万事大吉了。
结果前端播放器还是显示:
-
0:00 / 0:00
-
下载也打不开
这说明问题已经不在“音频生成”,而是在“文件怎么返回”。
后来我发现根因有两个
1. 我返回的不是平台真正识别的音频文件对象
我最开始返回的是一个普通结构:
{
"url": "...",
"file_type": "audio"
}
这在人眼里像“音频文件”,但对平台来说,它可能只是一个普通 JSON 对象。
所以前端拿到的其实还是一个 URL 字符串,而不是平台原生音频对象。
2. 我手动拼出来的对象存储链接没有签名
这一步是最关键的坑。
我当时是这么生成 URL 的:
combined_audio_url = f"{endpoint_url}/{object_key}"
看起来像一个正常下载地址,但实际点开后直接报:
-
AccessDenied
-
missing token
也就是说:
文件上传成功了,但我返回的是私有对象地址,而不是带签名的可访问链接。
所以播放器请求时自然失败。
九、真正的问题,不在音频,而在文件访问权限
这是整个流程里最容易被误判的一点。
因为表面上看,像是:
-
音频坏了
-
合并有问题
-
播放器有 bug
但实际上根因是:
音频文件是好的,只是返回的链接没有访问权限。
也就是说,后端已经把文件合好了,只是前端拿到的是一个不能直接访问的私有 URL。
这一步我最终得出的经验
以后只要遇到这种情况,先别急着怀疑 TTS 或音频处理,先看三件事:
-
文件大小是不是正常
-
时长是不是正常
-
下载链接是不是可直接访问
如果前两项都正常,问题通常就在第三项。
十、这个项目真正打通后,价值不只是“能配音”
把整个链路走通之后,我越来越觉得,这个东西本质上不是一个 TTS Demo,而是一个内容生产引擎。
因为它能串起来三件很有价值的事:
1. 情绪表达
它不是在机械读稿,而是在做“角色化表达”。
2. 内容结构化
它把抽象主题,变成了可执行的对白数据。
3. 音频产品化
它不只是生成片段,而是输出一个完整可用的成品文件。
如果继续往下做,它完全可以升级成:
-
情感类短视频脚本生成器
-
哲学共鸣对话 Bot
-
社会议题口播生成器
-
多角色内容自动配音工具
十一、这套流程里,我最重要的几个认知变化
做完整个流程以后,我有几个特别深的感受。
1. 先结构化,再生成,不然工作流会越来越乱
如果一开始就只让模型“自由发挥”,后面每个节点都会很难接。
2. 做对话内容,不能只按角色聚合
一聚合,对话感就没了。
3. TTS 的重点不只是“生成”,而是“落地为可用文件”
很多问题都不是出在生成,而是出在下载、存储、返回。
4. 音频合并必须用标准方式
不能拿字符串或二进制硬拼。
5. 文件可播放,不等于链接可访问
很多时候真正坏掉的,不是文件,而是访问权限。
十二、如果你也想复刻这条流程,可以按这个顺序做
我建议你不要一上来就追求“全自动成品”,而是分四步:
第一步:先跑通文案生成
先让模型稳定输出一段像样的熊大熊二对话。
第二步:再跑通结构化 JSON
确保每句都能拆成:
-
speaker
-
text
-
order
第三步:再做逐句 TTS
先验证每个片段能单独生成、单独播放。
第四步:最后再做音频合并和文件返回
不要在前面还不稳定时,就急着做最后一层。
因为这个项目里,越往后,调试成本越高。
十三、我最后想说的一句话
很多人以为,做一个 AI 配音工作流,难点在模型够不够聪明。
但真正做下来我发现,模型聪明只是起点,真正决定成败的,是后面这些看起来“不性感”的细节:
-
数据结构对不对
-
节点怎么衔接
-
文件怎么落地
-
链接有没有权限
-
输出是不是平台认得的类型
内容创作这件事也是一样。
真正打动人的,从来不只是“灵感来了”,而是你有没有能力,把一个模糊的想法,一步一步变成一个真的能跑起来的东西。
结尾
如果你也在做:
-
情感类短视频
-
AI 对话内容
-
多角色配音
-
Coze / 工作流自动化
-
社会议题型内容生产
那这条链路值得你认真走一遍。
因为它最后带给你的,可能不只是一个 Bot,而是一套真正能把内容变成产品的方法。
提示词:
你是一名短视频情感对话文案创作者。请围绕我提供的主题,生成一段适合“熊大”和“熊二”配音的中文对话文案。
你的输出必须是严格 JSON,只能输出一个 JSON 对象,不能输出任何解释、说明、标题、markdown 或代码块。
要求:
1. 只包含两个角色:熊大、熊二
2. 熊大负责更沉稳、清醒、看透现实、带思考的表达
3. 熊二负责更真诚、直接、带情绪、带疑问的表达
4. 内容要有共鸣、扎心、哲学、启发、动人,贴近当代社会现实
5. 聚焦关系、成长、孤独、焦虑、婚恋、内卷、自我价值、委屈、懂事、比较、沉默等现实议题
6. 对话必须自然,像两个人真的在聊天
7. 不能空泛,不能鸡汤,不能假装深刻
8. 适合短视频配音
9. 总时长控制在 5 到 6 分钟内
10. 每一句都必须单独拆分成一条
11. 每条文案只允许一个 speaker 和一条 text
12. order 必须从 1 开始连续递增,不能重复,不能跳号
输出格式必须严格如下:
{
"dialogues": [
{
"speaker": "熊大",
"text": "......",
"order": 1
},
{
"speaker": "熊二",
"text": "......",
"order": 2
}
]
}
主题:
【在这里填写主题】
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)