如果你也想做一个能自动生成文案、拆分角色、逐句配音、最后合成为完整音频的 AI 工作流,这篇文章可以直接给你一条完整路径。

    这次我做的,不是一个普通的 TTS 小工具,而是一个更像内容产品的东西:

    输入一个主题,生成一段有共鸣、扎心、哲学感的中文对话,再由“熊大”和“熊二”分别配音,最后输出成一个完整音频。

    听起来不复杂,但真正做下来,我踩到的坑几乎把这条链路的每一层都走了一遍:模型输出格式、工作流编排、逐句 TTS、音频下载、音频合并、对象存储、签名 URL、文件返回类型……

    所以这篇文章,我不只讲“做成了什么”,更讲清楚:每一步我是怎么做的,为什么这么做,以及中间到底踩了哪些坑。

一、我最终想做成什么

    先说目标。

    我希望这个工作流能完成下面这件事:

      1. 用户输入一个主题,比如: 大家都说人生要先爱自己,可到底怎么做才算“爱”?

      2. 大模型围绕这个主题,生成一段适合短视频配音的“熊大熊二对话文案”

      3. 把整段对话结构化,拆成一句一句

      4. 根据不同角色,分别选择不同音色

      5. 每句单独调用 TTS

      6. 再按原始顺序把所有音频片段合并成一个完整的 mp3

      7. 最终输出一个可播放、可下载的音频文件

一句话概括就是:

从一个主题,自动生成一条“有情绪张力的社会学对话视频音频”。

二、为什么我一开始卡住了

   最早我其实不是从 Coze 开始的,而是先在自动化平台里做。

一开始我以为这件事很简单:

    • 熊大一段文本,生成一段音频

    • 熊二一段文本,生成一段音频

    • 最后把两段音频合并

    但很快我发现,这样做有一个根本问题:对话感会消失。

    比如原本的内容是:

      • 熊大说一句

      • 熊二回一句

      • 熊大再说一句

      • 熊二再接一句

    如果我把它先聚合成:

    • xiongda_text

    • xionger_text

    那最后输出的效果就会变成:

    • 熊大把自己的所有话一次说完

    • 熊二再把自己的所有话一次说完

    这就不再是“对话”,而变成了“两段独白”。

这一步让我意识到:

如果想保留真实对话节奏,就不能按角色聚合后再 TTS,必须逐句处理。

三、我后来为什么换到 Coze

    继续往下做的时候,我发现另一个问题:

    TTS 不只是“生成音频”这么简单,真正麻烦的是后面这些:

      • 每一句怎么走不同音色

      • 怎么拿到真实 mp3 文件

      • 怎么按顺序合并

      • 怎么把最终文件返回给前端

      • 怎么保证能播放、能下载

    如果只是搭一个通用自动化流程,会在很多细节上变得很绕。

    所以后来我切到了 Coze,原因很直接:

      1. 它更适合把“大模型 + 流程 + 代码节点”组合起来

      2. 我可以直接写节点代码,精细控制每一步

      3. 对“内容生成 + 配音”这种工作流,表达会更自然

换句话说,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

        }

然后逐条处理:

    1. 识别角色

    2. 选对应音色

    3. 调用 TTS

    4. 下载真实音频

    5. 保存成 mp3

    6. 校验文件是否有效

    为什么不能只拿 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 来做标准音频合并:

    1. 先按 order 排序

    2. 把每个有效 mp3 读出来

    3. 一个一个往后追加

    4. 最后重新导出成新的标准 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 或音频处理,先看三件事:

  1. 文件大小是不是正常

  2. 时长是不是正常

  3. 下载链接是不是可直接访问

如果前两项都正常,问题通常就在第三项。

十、这个项目真正打通后,价值不只是“能配音”

把整个链路走通之后,我越来越觉得,这个东西本质上不是一个 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
    }
  ]
}

主题:
【在这里填写主题】

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐