FunASR OpenAI 兼容 API 低代码工作流接入实战:Dify、n8n 与 Webhook Worker 的 Multipart 转写配方
2026/9/13 21:06:55 网站建设 项目流程

FunASR OpenAI 兼容 API 低代码工作流接入实战:Dify、n8n 与 Webhook Worker 的 Multipart 转写配方

【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR

本指南以 FunASR 仓库examples/openai_api中的 OpenAI 兼容语音服务为核心,系统讲解如何让 Dify、n8n、HTTP 节点、webhook worker 等低代码工作流引擎调用私有 FunASR 语音转写 API。你将掌握服务预检、multipart 请求构造、响应字段语义辨析、Dify/n8n 节点配置、Webhook worker 函数实现,以及上线前必须落实的安全护栏与故障排查方法。

这些内容是面向特定工作流引擎的 multipart HTTP 接入配方,不是对所有工作流产品或版本的兼容保证;接入前请始终以实际部署服务的/openapi.json/v1/models为准。

服务预检:先跑通本地 loopback 服务

启动示例服务

在配置任何低代码工具之前,先按示例 README 的准备步骤完成代码检出与环境搭建。README 中固定的源码 revision 只锁定源码,不是依赖/模型锁定,也不代表已验证的全新安装或声学正确性。以下命令假定已在该 checkout 根目录准备好.venv

cd examples/openai_api source ../../.venv/bin/activate python server.py --host 127.0.0.1 --model sensevoice --device cpu --port 8000

准备好 CUDA 依赖后,将 CPU 命令替换为:

python server.py --host 127.0.0.1 --model sensevoice --device cuda --port 8000

注意:--host 127.0.0.1必须显式传入的,因为示例服务的默认监听地址是0.0.0.0(见 server.py)。如果服务已经在运行,请跳过启动步骤直接做健康检查,不要在同一端口再启动第二个进程。

另一个实现是打包的funasr-server路线,见 Agent 集成指南,其启动默认值、别名与响应字段与本示例不同;本文聚焦示例server.py

检查本地服务

在同一主机的第二个终端进入同一 checkout 的examples/openai_api目录,激活同一环境,等待模型加载完成后检查:

source ../../.venv/bin/activate export FUNASR_BASE_URL="http://127.0.0.1:8000" curl -fsS "$FUNASR_BASE_URL/health" curl -fsS "$FUNASR_BASE_URL/v1/models" curl -fsS "$FUNASR_BASE_URL/openapi.json"

这三个端点分别对应 server.py 中的健康检查(返回设备、已加载模型与可用别名)、OpenAI 风格模型列表(带ready标志),以及由 FastAPI 动态生成的 OpenAPI schema。健康与 schema 检查只能确认服务可达,不能验证声学正确性;真正的转写验证请使用下方 multipart 请求。转写命令中的meeting.wav应替换为你本地的真实音频文件;README 中的公开中文 smoke 样例不是多语言准确率基准。

Docker 工作流引擎的连通性

如果工作流引擎运行在 Docker 容器中,localhost通常指向工作流容器自身,主机上的 loopback 服务不会自动对容器可达。正确做法是:

  1. 明确配置一个私有的网关/容器网络;
  2. FUNASR_BASE_URL(以及下方 worker 中的FUNASR_URL)替换为工作流运行时实际可达的地址;
  3. 需要网关鉴权时使用真实凭据;本地 curl/Python 示例不添加鉴权 header;
  4. 不要通过把未鉴权端口暴露到公网来解决连通性问题。

Postman smoke test:低代码接入前的图形化验证

在配置低代码工具之前,可以先导入 Postman collection 从图形界面跑通 health、模型列表和转写请求;偏好 schema 驱动导入时使用 OpenAPI spec。

操作要点:

  • 将 collection 变量FUNASR_BASE_URL设置为可达的服务地址(如http://localhost:8000);
  • 在转写请求的Body标签页为 multipart 的file字段选择本地音频文件;
  • 第一次测试保持MODEL_ALIAS=sensevoice,或切换为/v1/models返回的其他别名。

处理离线多人会议时,先按 MOSS 部署指南 准备独立的第三方 MOSS 服务(注意其 GPU 与文件时长边界),再将MODEL_ALIAS改为moss-transcribe-diarize,并保持response_format=verbose_json以保留原生匿名说话人 segments。不要额外添加外部 VAD 或spk=true——MOSS 自带原生说话人标签,录音内标签不是已验证身份,也不是跨录音稳定编号。仅修改客户端别名并不会自动准备好该服务环境。

Multipart HTTP 请求:所有工作流引擎的统一请求形态

无论使用何种低代码工具,最终都需要发出下面这种请求:

  • Method:POST
  • URL:http://<funasr-host>:8000/v1/audio/transcriptions
  • Body type:multipart/form-data
  • File field:file
  • Text field:model=sensevoice
  • Text field:response_format=verbose_json
  • Timeout:根据最长音频时长设置,例如长录音可先设为 300 秒

等价 curl 命令:

curl -fsS "$FUNASR_BASE_URL/v1/audio/transcriptions" \ -F file=@meeting.wav \ -F model=sensevoice \ -F response_format=verbose_json

text映射为转写文本。response_format=verbose_json只选择响应格式,不会开启说话人分离,也不会强制生成时间戳。在消费其他字段前,先阅读 客户端响应契约。

两种服务的响应语义差异

从源码看,示例server.py与打包的funasr-server共享 multipart 转写请求子集,但默认值、别名与响应 schema 并不相同:

维度示例server.py打包funasr-server
durationgenerate()调用周围的耗时秒数,不含初次模型加载,不是录音时长音频时长秒数,无法读取音频元数据时 fallback 可为 0
segments仅来自模型返回的sentence_info,否则为segments=[]使用可用片段,fallback 可合成按文本划分的粗粒度时间区间
start/end秒为单位秒为单位,不保证是词级强制对齐
speaker模型提供或 null有标签才包含;非原生说话人模型需spk=true,MOSS 为原生标签
其他字段包含modellanguage回显请求提示或auto,非检测语言包含task和片段id/words,无顶层model;语言可用后端检测

具体到示例服务的实现,server.py 中segments由模型返回的sentence_info转换而来(start/end除以 1000 从毫秒转为秒),否则返回空数组;language只是回显你提交的提示或auto

其他需要牢记的语义:

  • 片段start/end单位是,不是 SDK 的毫秒坐标;
  • speaker字段可能缺失、为 null、数字或字符串,标签不标识真实身份;
  • 非空 segments 或verbose_json都不保证准确的字幕对齐;
  • HTTP 展示文本会剥离 SenseVoice 富标签(clean_text正则去除<|...|>),不等于 SDK 原始标签结果,也不是独立的情绪/事件响应;
  • SDK 字段如timestamptimestampsctc_timestampsuse_itn、hotwords 与原始数组不是额外的表单字段,原始 SDK 时间戳不会自动转换为 HTTP segments。

响应示例

sentence_info时示例服务的示意响应(0.42是处理耗时,不是音频时长):

{"text": "recognized speech", "segments": [], "language": "auto", "duration": 0.42, "model": "sensevoice"}

3.2 秒文件对应的打包服务响应示意(粗粒度 fallback 片段,未开启说话人选项)。这些示例只描述 schema,不是新模型测量结果:

{ "task": "transcribe", "language": "en", "duration": 3.2, "text": "recognized speech", "segments": [ {"id": 0, "start": 0.0, "end": 3.2, "text": "recognized speech", "words": []} ] }

Dify 自定义工具或 HTTP 节点

当 Dify 应用接收到上传的音频文件,或收到内部音频存储 URL 时,可以使用下面两种模式。

直接文件上传路径

在 HTTP request 节点或自定义工具中配置:

  • Method:POST
  • URL:http://<funasr-host>:8000/v1/audio/transcriptions
  • Body:multipart/form-data
  • File part:file,绑定到上传的音频变量
  • Text parts:model=sensevoiceresponse_format=verbose_json
  • Output variable:text映射为转写文本;在使用时间戳或说话人标签前,先检查segments是否可用及其来源

音频 URL 路径

有些工作流工具只能传文件 URL 而不是原始 multipart 二进制。multipartfile字段中的 URL 字符串不是音频上传。优先直接上传二进制,或传受控存储对象 ID 并由经过审查的存储客户端解析。

下面的 sketch仅用于运维人员批准的可信存储 URL。目标 allowlist、私网访问策略、重定向校验、下载字节上限与鉴权均未实现requests.get会跟随重定向并将整个响应缓存在内存中,其 timeout 不是字节限制,也不是完整的端到端截止时限。不要将用户提供的 URL 传给这个 helper。接受此类 URL 前必须使用经过审查的下载边界执行全部控制,包括阻止非预期私网/元数据目标并检查每次重定向——位于可信内网并不能防止 SSRF。

对可信输入的工作流:

  1. 运维人员向 worker 提供已批准的音频 URL 和元数据;
  2. worker 从可信存储下载音频;
  3. worker 使用 multipart 请求调用 FunASR;
  4. worker 返回服务 JSON,由下游节点检查可选字段。日志不得暴露签名 URL、凭据或私有转写。

在同一已激活的客户端环境安装独立 HTTP 依赖:

python -m pip install requests

按已准备的服务设置FUNASR_URL(默认值适用于同主机 worker)。以下函数定义可被你的 worker 导入,但它们不创建 HTTP 监听器,也不实现入站鉴权或上传限制:

import requests FUNASR_URL = "http://127.0.0.1:8000/v1/audio/transcriptions" def transcribe_from_url(audio_url: str) -> dict: audio_response = requests.get(audio_url, timeout=120) audio_response.raise_for_status() files = {"file": ("audio.wav", audio_response.content, "audio/wav")} data = {"model": "sensevoice", "response_format": "verbose_json"} response = requests.post(FUNASR_URL, files=files, data=data, timeout=300) response.raise_for_status() return response.json()

请把此示意限制在已批准的输入内;仅校验主机名不是完整的安全下载策略。

n8n HTTP Request 节点

一个常见的 n8n 流程是:触发器 → 二进制音频数据 → HTTP Request → 转写结果消费节点。

推荐配置:

  • Method:POST
  • URL:http://<funasr-host>:8000/v1/audio/transcriptions
  • Send Body:开启
  • Body Content Type:Form-Data/ multipart
  • Binary file field:file
  • Additional form fields:model=sensevoiceresponse_format=verbose_json
  • Response Format:JSON
  • Timeout:长录音场景需要调大

请求之后,使用{{$json.text}}作为转写文本。只有先确认{{$json.segments}}存在且适合当前任务,才将其传给后续节点;空片段或粗粒度片段不能当作已验证的字幕时刻或说话人分离结果。节点标签与二进制属性配置随所安装的 n8n 版本变化;file发出的multipart 字段名,不一定是传入二进制属性的名字。

n8n OpenAI Audio 节点

对于发送model=whisper-1的 OpenAI Audio > Transcribe 节点版本,FunASR 会把该兼容别名映射到服务启动时选择的模型,而不是选择 Whisper checkpoint。这一映射在源码中有明确实现:N8N_OPENAI_MODEL_ALIAS = "whisper-1",resolve_openai_transcription_model 将其解析为DEFAULT_MODEL(即--model启动参数);仓库测试 test_openai_api_n8n_compat.py 也验证了该映射行为。

配置要点:

  • Base URL使用可达的服务地址并带/v1
  • 非空占位 key 仅适用于未保护的本地端点,访问鉴权网关时应提供真实凭据;
  • 请核验所安装节点版本的请求行为,不要假定所有版本一致;
  • 此配方适用于纯文本转写;需要显式response_format或服务支持的说话人选项时,改用 HTTP Request 节点,并仍受上述响应边界限制。

Webhook worker 模式

当工作流引擎不能稳定发送 multipart 文件,或音频需要预处理时,可以使用下面的函数。该 POSIX 临时文件示例使用同一个requests依赖,并在上传后关闭句柄。它接受已驻留内存的 bytes——应在缓冲之前应用上传大小限制。它是普通函数,不是受保护的 webhook 服务。

from pathlib import Path import tempfile import requests FUNASR_URL = "http://127.0.0.1:8000/v1/audio/transcriptions" def transcribe_bytes(filename: str, payload: bytes, content_type: str = "audio/wav") -> dict: with tempfile.NamedTemporaryFile(suffix=Path(filename).suffix or ".wav") as tmp: tmp.write(payload) tmp.flush() with open(tmp.name, "rb") as audio: response = requests.post( FUNASR_URL, files={"file": (filename, audio, content_type)}, data={"model": "sensevoice", "response_format": "verbose_json"}, timeout=300, ) response.raise_for_status() return response.json()

注意:此处未实现音频转码、文件大小检查、请求 ID、入站/上游鉴权和重试策略,共享使用前必须在相应边界落实这些控制——重试可能重复执行昂贵的转写工作。示例服务本身会把上传内容写入临时文件并在处理结束后删除(server.py),但这不构成"无磁盘"或"安全擦除"保证。

生产环境护栏

在跨团队共享 FunASR 服务前,务必落实以下护栏:

  • 在 FunASR 前置鉴权、TLS、上传大小限制和限流;代理与网关模式见 安全与网关指南(含 NGINX Basic-auth 与 Caddy 反向代理 sketch、Kubernetes NetworkPolicy 要点与逐步上线检查清单)。示例服务没有内置鉴权或上传大小限制,占位api_key不会让服务具备鉴权;
  • 使用/health做工作流 readiness check,使用/v1/models校验模型别名;
  • 记录 request id、音频时长、模型别名、响应格式、设备、延迟和错误类型;
  • 按最长音频时长设置工作流超时;超长录音先切分再交给低代码工具处理;
  • 私有音频放在可信存储中,避免把签名 URL、凭据或转写文本写入公开日志;
  • 上生产前,至少用一个公开 smoke 样例和一个真实业务样例完整跑通同一条工作流。

仓库提供了可复用的 smoke 工具(smoke_test.sh 与 smoke_test.py),它们会依次检查/health/v1/models并执行转写;Python 版本在缺少音频文件时会下载公开中文样例,其 multipart 构造逻辑可作为低代码工具配置的参考。

故障排查

  • 工作流能访问/health,但转写失败:确认请求是multipart/form-data,且二进制字段名是file
  • Dify 或 n8n 访问localhost失败:换成工作流运行时可访问的主机名、Compose service name 或 Kubernetes service name;
  • 响应中没有可用segments:检查格式与已部署的 schema,再检查模型的sentence_info与说话人配置;仅设置verbose_json不能创建时间戳或标签;
  • 请求超时:调大 HTTP timeout,或先切分长录音;
  • 第一次请求很慢:使用--model sensevoice预加载模型,并用/health做 readiness check;
  • 模型别名未知:调用/v1/models,使用返回列表中的别名。

相关文档

  • 示例服务 README:快速开始、API 契约、模型别名、Docker 与 Kubernetes 部署
  • 客户端接入配方:OpenAI SDK、requests、Agent 工具与响应格式详解
  • OpenAPI 规范:schema 驱动导入与客户端生成
  • 安全与网关指南:共享服务前的 TLS、鉴权、限流与数据治理
  • MOSS 部署指南:离线多人会议转写与说话人分离
  • Python SDK 指南:进程内AutoModel.generate()用法

【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询