当你希望让 Dify、n8n、HTTP 节点、webhook worker 或其他低代码工作流引擎调用私有 FunASR 语音 API 时,可以从这份配方开始。建议先在本目录跑通本地 smoke test,再把 localhost 换成工作流运行环境能够访问到的服务名或内网地址。
cd examples/openai_api
python server.py --model sensevoice --device cuda --port 8000在工作流所在主机或容器中执行:
export FUNASR_BASE_URL=http://<funasr-host>:8000
curl -fsS "$FUNASR_BASE_URL/health"
curl -fsS "$FUNASR_BASE_URL/v1/models"如果工作流引擎运行在 Docker 中,localhost 通常指的是工作流容器自身。请改用 Docker Compose service name、Kubernetes service name 或内网主机名。
在配置低代码工具前,可以先导入 Postman collection,从图形界面跑通 health、模型列表和转写请求;需要按 schema 导入时可使用 OpenAPI spec。设置 FUNASR_BASE_URL,在 multipart file 字段选择本地音频文件,第一次测试建议保持 MODEL_ALIAS=sensevoice。
所有工作流引擎最终都需要发出下面这种请求:
| 字段 | 值 |
|---|---|
| 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 "$FUNASR_BASE_URL/v1/audio/transcriptions" \
-F file=@meeting.wav \
-F model=sensevoice \
-F response_format=verbose_json常用响应字段映射:
| 路径 | 用途 |
|---|---|
text |
纯文本转写结果,可传给聊天机器人、工单、知识库或后续摘要节点。 |
segments |
请求 verbose_json 时返回的时间戳、说话人等分段信息。 |
duration |
API 返回的音频处理时长,适合写入日志和监控。 |
model |
本次请求使用的模型别名。 |
当 Dify 应用接收上传音频文件,或收到内部音频存储 URL 时,可以使用下面两种模式。
在 HTTP request 节点或自定义工具中配置:
- Method:
POST - URL:
http://<funasr-host>:8000/v1/audio/transcriptions - Body:
multipart/form-data - File part:
file,绑定到上传音频变量 - Text parts:
model=sensevoice、response_format=verbose_json - Output variable: 把
text映射为转写文本;需要时间戳或说话人信息时保留segments
有些工作流工具只能传文件 URL,而不能直接传 multipart 二进制。此时建议加一个内网 worker:
- Dify 将音频 URL 和元数据发送给 worker。
- worker 从可信存储下载音频。
- worker 使用 multipart 请求调用 FunASR。
- worker 将
text、segments和运行日志返回给 Dify。
import requests
FUNASR_URL = "http://funasr-api: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()请把这个 worker 放在可信内网中,并在下载用户提供的链接前校验允许访问的域名或存储桶。
一个常见 n8n 流程是:触发器 -> 二进制音频数据 -> HTTP Request -> 转写结果消费节点。
推荐 HTTP Request 配置:
| n8n 设置 | 值 |
|---|---|
| Method | POST |
| URL | http://<funasr-host>:8000/v1/audio/transcriptions |
| Send Body | enabled |
| Body Content Type | Form-Data / multipart |
| Binary file field | file |
| Additional form fields | model=sensevoice、response_format=verbose_json |
| Response Format | JSON |
| Timeout | 长录音场景需要调大。 |
请求之后,使用 {{$json.text}} 作为转写文本。如果启用了 verbose_json,可以把 {{$json.segments}} 传给字幕、说话人分析或质检节点。
当工作流引擎不能稳定发送 multipart 文件,或音频需要预处理时,可以把转写封装成一个内部 webhook worker。
from pathlib import Path
import tempfile
import requests
FUNASR_URL = "http://localhost: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()这个 worker 也适合集中做音频转码、文件大小限制、请求 ID、鉴权、重试和审计日志。
- 在跨团队共享 FunASR 服务前,先加好鉴权、TLS、上传大小限制和限流;代理和网关模式见 安全与网关指南。
- 使用
/health做工作流 readiness check,使用/v1/models校验模型别名。 - 记录 request id、音频时长、模型别名、响应格式、设备、延迟和错误类型。
- 按最长音频时长设置工作流超时;超长录音建议先切分,再交给低代码工具处理。
- 私有音频放在可信存储中,避免把签名 URL、凭据或转写文本写入公开日志。
- 上生产前,至少用一个公开 smoke 样例和一个真实业务样例完整跑通同一条工作流。
| 现象 | 处理方式 |
|---|---|
工作流能访问 /health,但转写失败 |
确认请求是 multipart/form-data,且二进制字段名是 file。 |
Dify 或 n8n 访问 localhost 失败 |
换成工作流运行时可访问的主机名、Compose service name 或 Kubernetes service name。 |
响应中没有 segments |
设置 response_format=verbose_json。 |
| 请求超时 | 调大 HTTP timeout,或先切分长录音。 |
| 第一次请求很慢 | 使用 --model sensevoice 预加载模型,并用 /health 做 readiness check。 |
| 模型别名未知 | 调用 /v1/models,使用返回列表中的别名。 |