
1. 音频转文本工作流到底解决什么问题先说说我为什么会在 Dify 上折腾音频转文本这件事。手头经常有一堆录音文件——会议记录、访谈素材、课程回放甚至自己随手录的灵感片段。传统做法要么是丢给在线转写服务按分钟付费要么是本地跑 Whisper 但每次都要敲命令行、调参数、处理各种格式重复劳动太多。Dify 的工作流编排能力刚好能把“上传音频→调用语音识别→整理输出”这条链路固化下来做成一个可复用、可分享、甚至能对外提供 API 的自动化流程。这个工作流的核心价值在于三点把零散的语音识别能力封装成标准节点、用可视化编排替代手写胶水代码、通过 API 对外暴露让其他系统直接调用。适合谁参考如果你手头有批量音频需要转写、想给内部系统加一个语音转文字入口、或者单纯想摸清 Dify 工作流和外部 API 怎么配合这篇内容都能直接抄作业。我实测下来从零搭好一条可用的音频转文本流水线熟悉 Dify 的话半小时内能跑通不熟悉的话跟着步骤走一小时也够了。需要提前说明的是Dify 本身并不内置语音识别模型它的角色是编排调度——把音频文件接进来调用外部的 Speech To Text 服务比如各家云厂商的语音识别 API或者自部署的识别服务再把返回的文本做后续处理。所以整条工作流的关键在于文件怎么传、API 怎么调、返回结果怎么解析和落库。下面我按实际搭建顺序把每个环节拆开讲。2. 整体架构设计与方案选型思路2.1 为什么选 Dify 工作流而不是写脚本一开始我也想过直接写个 Python 脚本用 requests 调识别 API循环处理文件夹里的音频。但很快就遇到几个问题第一脚本要处理文件上传、格式校验、错误重试、结果存储代码越写越长第二每次换一个识别服务商就要改代码第三非技术同事想用的时候还得教他们配环境。Dify 工作流把这些都变成了可视化节点改服务商只需要换一个 HTTP 请求节点的配置分享给别人只需要给一个链接或 API 地址。从架构上看这条工作流分成四层输入层负责接收音频文件识别层调用 Speech To Text 服务处理层对返回文本做清洗和结构化输出层把结果返回或存储。Dify 的工作流节点刚好能对应这四层而且每一层都可以独立替换。比如识别层今天用 A 服务商明天想换 B只要 HTTP 节点的请求格式改一下就行前后层完全不用动。2.2 音频输入的几种方式与取舍音频怎么进到工作流里这是第一个要决策的点。Dify 工作流的起始节点支持多种输入类型常见的有文件上传、文本输入、API 参数传入。对于音频转文本场景我试过三种方式文件上传变量在开始节点定义一个 file 类型的变量用户在工作流调试界面或通过 API 上传音频文件。这种方式最直观适合手动触发场景。URL 传入通过 API 传一个音频文件的公网可访问 URL工作流内部再去下载。适合其他系统已经把文件存在对象存储里的情况。Base64 编码把音频内容编码后作为文本参数传入。适合文件很小、不想走文件存储的场景但大文件会撑爆请求体不推荐。我最终选的是文件上传为主、URL 传入为辅的方案。开始节点定义两个变量一个 file 类型的audio_file一个 text 类型的audio_url在工作流里用条件判断决定走哪条路。这样既方便手动测试又能对接外部系统。注意Dify 的文件上传有大小限制默认单文件不超过 15MB具体取决于部署配置。如果音频超过这个限制建议先在外部压缩或切片再传入工作流。2.3 识别服务选型的几个考量维度Speech To Text 服务的选择直接决定转写质量和成本。我在选型时主要看四个维度识别准确率、支持的音频格式、响应延迟、计费方式。准确率方面中文场景下各家服务商对普通话的识别都不差但遇到方言、专业术语、多人对话时差距就出来了。格式支持上大部分服务商支持 wav、mp3、m4a但有些对采样率和码率有要求。延迟方面短音频1 分钟内基本都在秒级返回长音频要么走异步接口要么切片处理。计费通常是按音频时长计费也有按调用次数计费的。我的建议是先用一家服务商跑通流程再根据实际转写效果决定是否更换。因为 Dify 工作流里识别层是一个独立的 HTTP 请求节点换服务商只需要改这个节点的配置迁移成本很低。如果你有自部署的识别服务比如本地跑的模型也可以把它的接口地址填进 HTTP 节点这样数据不出内网适合对隐私要求高的场景。3. 核心节点配置与实操要点3.1 开始节点的变量定义开始节点是整个工作流的入口变量定义得合不合理直接影响后续节点的取值方不方便。我定义的变量清单如下变量名类型是否必填说明audio_filefile否上传的音频文件支持 mp3/wav/m4aaudio_urltext否音频文件的公网 URLlanguageselect是识别语言默认 zhoutput_formatselect是输出格式默认 plain_text这里有个细节audio_file和audio_url都设为非必填因为用户可能只提供其中一种。在工作流内部用条件分支判断哪个有值再走对应的处理路径。language和output_format设成下拉选择避免用户手输错误的值。Dify 的 select 类型变量支持预设选项配置起来很方便。实操心得变量命名尽量用英文小写下划线避免中文和特殊字符。虽然后续节点引用时中文也能识别但在 API 调用和日志排查时英文名更省事。3.2 文件处理节点的格式校验与转换音频文件进来后不能直接丢给识别 API得先做一轮校验和预处理。我加了一个代码节点做这件事主要干三件事检查文件扩展名是否在白名单内、检查文件大小是否超限、必要时做格式转换。格式转换这块要说明一下Dify 的代码节点运行在沙箱环境里不能直接调用 ffmpeg 这类系统命令。所以我的做法是如果识别服务商支持多种格式就跳过转换如果只支持特定格式就在代码节点里做校验不支持的格式直接返回错误提示让用户先自行转换。这样虽然多了一步人工操作但避免了在沙箱里折腾音频处理的复杂度。import os ALLOWED_EXTENSIONS [.mp3, .wav, .m4a, .flac] MAX_SIZE_MB 15 def main(audio_file): if not audio_file: return {valid: False, message: 未提供音频文件} file_name audio_file.get(name, ) file_size audio_file.get(size, 0) ext os.path.splitext(file_name)[1].lower() if ext not in ALLOWED_EXTENSIONS: return {valid: False, message: f不支持的格式: {ext}} if file_size MAX_SIZE_MB * 1024 * 1024: return {valid: False, message: f文件超过 {MAX_SIZE_MB}MB 限制} return {valid: True, message: 校验通过, ext: ext}这段代码返回的valid字段会在后续的条件分支节点里用到决定是继续走识别流程还是直接返回错误。3.3 HTTP 请求节点调用识别 API 的关键配置这是整条工作流的核心节点。以常见的语音识别 API 为例请求方式通常是 POST请求体里带音频数据或音频 URL请求头里带认证信息。在 Dify 的 HTTP 请求节点里配置时有几个地方容易踩坑认证信息怎么放。不要把 API Key 硬编码在节点里而是配置成环境变量或使用 Dify 的凭据管理功能。这样换 Key 的时候不用改工作流也避免分享工作流时泄露密钥。请求体格式怎么选。如果识别 API 接受音频 URL请求体用 JSON 最方便如果只接受音频文件流就要用 multipart/form-data 格式。Dify 的 HTTP 节点支持这两种格式在 Body 配置里切换即可。超时时间怎么设。音频识别通常比普通 API 慢默认超时可能不够。我在节点配置里把超时设成 60 秒长音频场景可以更长。但要注意Dify 工作流整体也有超时限制如果音频特别长建议走异步识别接口先提交任务再轮询结果。返回结果怎么解析。不同服务商的返回结构不一样有的把文本放在result.text有的放在data.transcription。我一般先用一个代码节点把返回的 JSON 打印出来看结构确认字段路径后再在后续节点里取值。注意如果识别 API 返回的是异步任务 ID需要再加一个轮询节点每隔几秒查一次任务状态直到完成或超时。轮询节点可以用循环节点实现但要注意设置最大轮询次数避免死循环。3.4 文本后处理节点的清洗与结构化识别 API 返回的原始文本往往带有一些噪音多余的空格、换行、识别置信度标记、时间戳等。我在识别节点后面加了一个代码节点做清洗主要处理这几类问题去除首尾空白和多余换行用 strip 和正则替换连续空行。合并断句语音识别有时会把一句话拆成多行根据标点符号做合并。提取纯文本如果返回结构里包含时间戳和置信度只保留文本内容。敏感内容过滤根据业务需要对识别结果做关键词过滤或脱敏。import re def main(raw_text): if not raw_text: return {clean_text: } # 去除多余空白 text raw_text.strip() # 合并连续换行 text re.sub(r\n{2,}, \n, text) # 去除行首行尾空格 lines [line.strip() for line in text.split(\n)] text \n.join([line for line in lines if line]) return {clean_text: text}清洗后的文本再根据output_format变量决定输出形式纯文本直接返回带时间戳的格式则把识别结果里的时间信息重新组织成结构化数据。4. 完整实操流程与关键环节实现4.1 从零搭建工作流的步骤拆解我把整个搭建过程拆成七步按顺序操作即可创建工作流应用。在 Dify 里新建一个工作流类型的应用命名比如“音频转文本流水线”填写描述方便后续识别。配置开始节点变量。按 3.1 节的表格定义四个变量注意 file 类型变量要设置允许的文件类型。添加文件校验代码节点。把 3.2 节的代码粘贴进去输入变量选audio_file输出变量定义valid、message、ext。添加条件分支节点。判断valid是否为 truetrue 走识别流程false 走错误返回。配置 HTTP 请求节点。填入识别 API 的地址、请求头、请求体认证信息用环境变量引用。添加文本清洗代码节点。把 3.4 节的代码粘贴进去输入变量选 HTTP 节点返回的文本字段。配置结束节点。把清洗后的文本作为输出变量同时输出识别状态和耗时信息。每一步配置完都可以点“运行”做单节点测试不用等整条流程搭完再调。Dify 的单节点测试功能很实用能快速定位是哪个节点配置有问题。4.2 音频文件上传与传递的实操细节文件上传这块我踩过几个坑这里展开说说。Dify 工作流的文件变量在 API 调用时传参方式和普通文本不一样。如果你是通过 Dify 的 API 触发工作流文件需要先上传到 Dify 的文件接口拿到文件 ID再把文件 ID 作为参数传入。具体流程是调用/files/upload接口上传音频拿到返回的id然后在调用工作流的请求体里把audio_file设成这个 ID。如果是手动在 Dify 界面测试直接点上传按钮选文件就行。但要注意界面上传的文件默认存在 Dify 的存储里如果工作流里要把文件转发给外部识别 API需要先拿到文件的访问 URL。Dify 的文件变量在代码节点里可以通过audio_file.get(url)拿到临时访问地址但这个地址有时效性最好在拿到后尽快使用。实操心得如果识别 API 支持直接传音频 URL优先走 URL 传入路径省去文件转发的麻烦。如果只支持文件流在 HTTP 节点里用audio_file变量作为文件参数Dify 会自动处理文件流的传递。4.3 识别 API 调用的参数计算与选择调用识别 API 时有几个参数需要根据实际情况计算或选择。采样率方面大部分服务商要求 16kHz如果原始音频是 44.1kHz有些服务商会自动重采样有些会报错。我一般在上传前用工具统一转成 16kHz 单声道减少兼容性问题。音频时长方面如果超过服务商的单次限制常见是 60 秒或 5 分钟需要切片处理。切片可以用代码节点做但更推荐在外部预处理因为沙箱里处理音频效率不高。语言参数的选择也有讲究。如果音频里是中英文混杂选zh可能英文部分识别不准选en则中文部分受影响。这种情况我建议选自动检测语言或者分两次识别再合并。标点符号参数如果服务商支持建议开启否则识别出来的文本没有断句后续处理很麻烦。4.4 结果输出与存储的几种方案识别完的文本往哪存取决于你的使用场景。我试过三种方案直接返回结束节点输出文本调用方拿到后自行处理。适合一次性转写、不需要留存的场景。写入数据库在结束节点前加一个 HTTP 节点调用数据库的写入接口把音频文件名、识别文本、识别时间存进去。适合需要积累语料、做后续检索的场景。存为文件把文本写入 Dify 的知识库或对象存储生成一个可访问的链接返回。适合需要分享转写结果的场景。我目前用的是直接返回加数据库记录的组合结束节点返回文本给调用方同时异步写一条记录到数据库方便后续统计和排查。数据库写入用 HTTP 节点调 REST 接口实现不需要在 Dify 里装数据库驱动。5. 常见问题与排查技巧实录5.1 API 调用报错的典型原因与解决400 错误模型名称不支持。这个错误通常出现在 HTTP 节点调用的识别 API 地址或模型名写错了。排查方法是先用 curl 或 Postman 单独调一次识别 API确认地址、请求头、请求体都正确再把同样的配置搬到 Dify 节点里。注意 Dify 的环境变量引用格式是{{env.变量名}}如果写成别的格式会解析失败。401 错误认证失败。检查 API Key 是否过期、是否有多余空格、是否放在了正确的请求头字段里。有些服务商要求Authorization: Bearer xxx有些要求X-API-Key: xxx看文档确认清楚。超时错误。音频识别比普通 API 慢如果节点超时时间设得太短会报错。把 HTTP 节点的超时调到 60 秒以上同时确认 Dify 工作流整体的超时配置也够长。文件格式不支持。识别 API 返回格式错误时先确认音频的实际编码格式有些文件扩展名是 mp3 但内部编码是别的格式用工具查一下真实编码再处理。5.2 识别结果不准确的排查思路识别准确率不理想时按这个顺序排查音频质量是否太差背景噪音、多人同时说话、采样率是否符合服务商要求、语言参数是否选对、音频时长是否超过限制导致截断。我遇到过一段访谈录音识别出来全是乱码最后发现是录音设备采样率是 8kHz而识别服务商最低要求 16kHz重采样后问题解决。还有一个容易被忽略的点专业术语。通用识别模型对行业术语的识别率有限如果音频里大量出现特定领域的词汇可以考虑用支持自定义词表或热词的服务商把术语提前录入能明显提升准确率。5.3 工作流调试的实用技巧Dify 工作流的调试界面能看每个节点的输入输出这是排查问题最直接的工具。我的习惯是每加一个节点就先单独测一次确认输入输出符合预期再往下接。如果整条流程跑不通从报错节点往前逐个检查看是哪个节点的输出不符合下一个节点的输入要求。另外日志要打全。在代码节点里把关键变量打印出来HTTP 节点开启详细日志这样出问题时能快速定位。Dify 的运行日志会保留每次执行的记录可以对比成功和失败的执行记录找出差异点。常见问题速查表问题现象可能原因解决方法文件上传失败超过大小限制或格式不支持压缩文件或转换格式后重试识别 API 返回 400请求体格式或参数错误用 curl 单独测试 API 确认配置识别结果为空音频无有效语音或格式问题检查音频内容确认采样率工作流执行超时音频过长或 API 响应慢切片处理或改用异步接口文本乱码编码格式不匹配统一用 UTF-8 编码处理5.4 性能优化的几个入手点如果工作流要处理大量音频性能优化就很重要。我总结的几个入手点并发处理方面Dify 工作流默认是串行执行如果有多条音频要转写可以在外部用脚本并发调用工作流 API而不是在一个工作流里循环处理。缓存方面如果同一段音频可能被重复转写把识别结果缓存起来下次直接返回缓存省去 API 调用。切片策略方面长音频切片后可以并发调用识别 API再把结果按顺序拼接比整段识别快很多。还有一个容易忽略的点识别 API 的限流。大部分服务商对调用频率有限制如果并发太高会被限流。在外部调用脚本里加一个简单的令牌桶或信号量控制并发数能避免大量请求被拒。6. 我踩过的坑和几条实在建议先说一个最坑的Dify 文件变量的 URL 时效性。我一开始把文件 URL 存下来想着后续节点慢慢用结果过了几分钟 URL 失效识别 API 拿不到文件。后来改成在 HTTP 节点里直接用文件变量让 Dify 在请求时实时生成访问地址问题才解决。所以文件相关的操作尽量放在一个节点里完成不要跨节点传递 URL。第二个坑是环境变量引用格式。Dify 里引用环境变量要用{{env.KEY}}这种格式我一开始写成了${KEY}节点一直报解析错误排查了半天才发现是格式问题。不同版本的 Dify 对环境变量的支持方式可能略有差异升级后最好重新检查一遍引用格式。第三个坑是识别 API 的返回结构变化。有些服务商会不定期调整返回字段名如果工作流里硬编码了字段路径服务商一改就报错。我的做法是在代码节点里做兼容处理用get方法取字段并设默认值同时把原始返回打印到日志里方便对比。最后分享一个实用技巧把工作流拆成两个。一个负责文件校验和识别一个负责文本清洗和存储。这样识别部分可以独立测试和替换清洗部分也能复用到其他场景。两个工作流之间用 API 调用串联虽然多了一次网络请求但灵活性和可维护性提升很多。这套音频转文本工作流我目前跑了几个月处理了几百个音频文件整体稳定性不错。后续打算加一个自动切片节点把超过 5 分钟的音频自动切成小段再识别这样就不用每次手动预处理了。如果你也在搭类似的工作流建议先把最短路径跑通——一个文件、一次识别、一个文本输出然后再逐步加校验、清洗、存储这些环节。