diff --git a/README_zh.md b/README_zh.md index 7e3501b37..2e553c97c 100644 --- a/README_zh.md +++ b/README_zh.md @@ -197,7 +197,7 @@ curl http://localhost:8000/v1/audio/transcriptions \ docker pull registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.12 ``` -[OpenAI API 示例 →](./examples/openai_api/) · [客户端配方 →](./examples/openai_api/CLIENTS.md) · [工作流配方 →](./examples/openai_api/WORKFLOWS.md) · [部署选型 →](./docs/deployment_matrix_zh.md) · [部署文档 →](./runtime/readme_cn.md) · [Agent 集成 →](https://modelscope.github.io/FunASR/agent.html) +[OpenAI API 示例 →](./examples/openai_api/) · [客户端配方 →](./examples/openai_api/CLIENTS.md) · [工作流配方 →](./examples/openai_api/WORKFLOWS_zh.md) · [部署选型 →](./docs/deployment_matrix_zh.md) · [部署文档 →](./runtime/readme_cn.md) · [Agent 集成 →](https://modelscope.github.io/FunASR/agent.html) --- diff --git a/docs/deployment_matrix_zh.md b/docs/deployment_matrix_zh.md index 8180dc3ab..e31243527 100644 --- a/docs/deployment_matrix_zh.md +++ b/docs/deployment_matrix_zh.md @@ -24,7 +24,7 @@ ### 我想替代云端转写服务 -使用 OpenAI 兼容 API。它提供 `/v1/audio/transcriptions`、`/v1/models`、`/health` 和 Swagger docs。先用 `sensevoice` 跑通 `examples/openai_api/smoke_test.sh`,再根据 [客户端配方](../examples/openai_api/CLIENTS.md) 接入 SDK 或 HTTP 客户端。Dify、n8n、HTTP 节点或 webhook worker 可参考 [工作流配方](../examples/openai_api/WORKFLOWS.md)。 +使用 OpenAI 兼容 API。它提供 `/v1/audio/transcriptions`、`/v1/models`、`/health` 和 Swagger docs。先用 `sensevoice` 跑通 `examples/openai_api/smoke_test.sh`,再根据 [客户端配方](../examples/openai_api/CLIENTS.md) 接入 SDK 或 HTTP 客户端。Dify、n8n、HTTP 节点或 webhook worker 可参考 [工作流配方](../examples/openai_api/WORKFLOWS_zh.md)。 ### 我想要可复现的容器 demo diff --git a/docs/use_case_showcase_zh.md b/docs/use_case_showcase_zh.md index 8fd4f94b5..de9e3965c 100644 --- a/docs/use_case_showcase_zh.md +++ b/docs/use_case_showcase_zh.md @@ -9,7 +9,7 @@ FunASR 不只是一个离线转写命令。这个页面把常见的评测、部 | 本地转写一个文件 | [README 快速开始](../README_zh.md#快速开始) | 几分钟内验证安装、模型下载和首次推理。 | | 对比准确率和速度 | [性能评测报告](https://modelscope.github.io/FunASR/zh/benchmark.html) | 选型前先查看 184 条长音频评测结果。 | | 从 Whisper/云端 ASR 迁移 | [迁移指南](./migration_from_whisper_zh.md) | 将现有流水线映射到 FunASR,用代表性音频评测并规划安全上线。 | -| 搭建私有语音 API | [OpenAI 兼容 API 示例](../examples/openai_api/)、[客户端配方](../examples/openai_api/CLIENTS.md) 和 [工作流配方](../examples/openai_api/WORKFLOWS.md) | 复用 LangChain、Dify、n8n、AutoGen 等 OpenAI 风格客户端,音频不出内网。 | +| 搭建私有语音 API | [OpenAI 兼容 API 示例](../examples/openai_api/)、[客户端配方](../examples/openai_api/CLIENTS.md) 和 [工作流配方](../examples/openai_api/WORKFLOWS_zh.md) | 复用 LangChain、Dify、n8n、AutoGen 等 OpenAI 风格客户端,音频不出内网。 | | 给 Agent 增加语音输入 | [MCP 服务](../examples/mcp_server/) 和 [语音输入示例](../examples/voice_input/) | 将本地 ASR 接入 Claude、Cursor 和桌面 Agent 工作流。 | | 选择部署路径 | [部署选型表](./deployment_matrix_zh.md) | 对比 Python API、OpenAI API、Docker Compose、WebSocket、vLLM、MCP、批处理、字幕和 Triton。 | | 部署流式 ASR | [Runtime 服务文档](../runtime/readme_cn.md) | 面向实时字幕、客服、会议等低延迟场景。 | diff --git a/examples/openai_api/CLIENTS.md b/examples/openai_api/CLIENTS.md index 85c32dc6e..adffb9ac7 100644 --- a/examples/openai_api/CLIENTS.md +++ b/examples/openai_api/CLIENTS.md @@ -1,6 +1,6 @@ # Client Recipes for the FunASR OpenAI-Compatible API -Use this page when `funasr-server` is already running and you want to connect an existing application, agent tool, or workflow engine to local speech recognition. For Dify, n8n, HTTP nodes, and webhook workers, see the [workflow recipes](WORKFLOWS.md). +Use this page when `funasr-server` is already running and you want to connect an existing application, agent tool, or workflow engine to local speech recognition. For Dify, n8n, HTTP nodes, and webhook workers, see the [workflow recipes](WORKFLOWS.md) or [Chinese workflow recipes](WORKFLOWS_zh.md). ## Preflight @@ -96,7 +96,7 @@ Use a multipart HTTP node or custom tool: | Text fields | `model=sensevoice`, `response_format=verbose_json` | | Result path | `text` for transcript, `segments` for timestamps/speakers | -When the workflow system cannot send files directly, upload audio to an internal object store first, then run a small worker that downloads the object and calls FunASR with the `requests` recipe above. See [workflow recipes](WORKFLOWS.md) for Dify, n8n, and webhook-worker patterns. +When the workflow system cannot send files directly, upload audio to an internal object store first, then run a small worker that downloads the object and calls FunASR with the `requests` recipe above. See [workflow recipes](WORKFLOWS.md) for Dify, n8n, and webhook-worker patterns, or the [Chinese workflow recipes](WORKFLOWS_zh.md). ## Response formats diff --git a/examples/openai_api/README.md b/examples/openai_api/README.md index 33f1304b4..a7844a5d1 100644 --- a/examples/openai_api/README.md +++ b/examples/openai_api/README.md @@ -11,7 +11,7 @@ python server.py --model sensevoice --device cuda --port 8000 Server starts in ~20s (model loading). Health check: `GET /health` -Need copy-paste integration snippets for Python SDK, HTTP clients, agent tools, or Dify/n8n-style workflows? See [Client recipes](CLIENTS.md) and [workflow recipes](WORKFLOWS.md). +Need copy-paste integration snippets for Python SDK, HTTP clients, agent tools, or Dify/n8n-style workflows? See [Client recipes](CLIENTS.md), [workflow recipes](WORKFLOWS.md), and the [Chinese workflow recipes](WORKFLOWS_zh.md). ### End-to-end smoke test @@ -89,7 +89,7 @@ curl http://localhost:8000/v1/audio/transcriptions \ ## Agent Framework Integration -Works with: **LangChain**, **LlamaIndex**, **AutoGen**, **CrewAI**, **Semantic Kernel**, **Dify**, **n8n**, or any framework using OpenAI audio API. See [Client recipes](CLIENTS.md) for SDK and agent-tool patterns, plus [workflow recipes](WORKFLOWS.md) for low-code HTTP nodes and webhook workers. +Works with: **LangChain**, **LlamaIndex**, **AutoGen**, **CrewAI**, **Semantic Kernel**, **Dify**, **n8n**, or any framework using OpenAI audio API. See [Client recipes](CLIENTS.md) for SDK and agent-tool patterns, plus [workflow recipes](WORKFLOWS.md) for low-code HTTP nodes and webhook workers ([中文](WORKFLOWS_zh.md)). ### LangChain Example ```python diff --git a/examples/openai_api/WORKFLOWS.md b/examples/openai_api/WORKFLOWS.md index 8bb0c03dc..4a8941ff6 100644 --- a/examples/openai_api/WORKFLOWS.md +++ b/examples/openai_api/WORKFLOWS.md @@ -1,5 +1,7 @@ # Low-code Workflow Recipes for the FunASR OpenAI-Compatible API +[中文](WORKFLOWS_zh.md) + Use this guide when you want Dify, n8n, webhook workers, or another workflow engine to call a private FunASR speech API. Start with the local smoke test in this directory, then replace `localhost` with the reachable service name inside your network. ## Server preflight diff --git a/examples/openai_api/WORKFLOWS_zh.md b/examples/openai_api/WORKFLOWS_zh.md new file mode 100644 index 000000000..edf48d8d3 --- /dev/null +++ b/examples/openai_api/WORKFLOWS_zh.md @@ -0,0 +1,162 @@ +# FunASR OpenAI 兼容 API 低代码工作流配方 + +[English](WORKFLOWS.md) + +当你希望让 Dify、n8n、HTTP 节点、webhook worker 或其他低代码工作流引擎调用私有 FunASR 语音 API 时,可以从这份配方开始。建议先在本目录跑通本地 smoke test,再把 `localhost` 换成工作流运行环境能够访问到的服务名或内网地址。 + +## 服务预检 + +```bash +cd examples/openai_api +python server.py --model sensevoice --device cuda --port 8000 +``` + +在工作流所在主机或容器中执行: + +```bash +export FUNASR_BASE_URL=http://:8000 +curl -fsS "$FUNASR_BASE_URL/health" +curl -fsS "$FUNASR_BASE_URL/v1/models" +``` + +如果工作流引擎运行在 Docker 中,`localhost` 通常指的是工作流容器自身。请改用 Docker Compose service name、Kubernetes service name 或内网主机名。 + +## Multipart HTTP 请求 + +所有工作流引擎最终都需要发出下面这种请求: + +| 字段 | 值 | +|---|---| +| Method | `POST` | +| URL | `http://: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 命令: + +```bash +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 自定义工具或 HTTP 节点 + +当 Dify 应用接收上传音频文件,或收到内部音频存储 URL 时,可以使用下面两种模式。 + +### 直接上传文件 + +在 HTTP request 节点或自定义工具中配置: + +- Method: `POST` +- URL: `http://:8000/v1/audio/transcriptions` +- Body: `multipart/form-data` +- File part: `file`,绑定到上传音频变量 +- Text parts: `model=sensevoice`、`response_format=verbose_json` +- Output variable: 把 `text` 映射为转写文本;需要时间戳或说话人信息时保留 `segments` + +### 音频 URL 转写 + +有些工作流工具只能传文件 URL,而不能直接传 multipart 二进制。此时建议加一个内网 worker: + +1. Dify 将音频 URL 和元数据发送给 worker。 +2. worker 从可信存储下载音频。 +3. worker 使用 multipart 请求调用 FunASR。 +4. worker 将 `text`、`segments` 和运行日志返回给 Dify。 + +```python +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 节点 + +一个常见 n8n 流程是:触发器 -> 二进制音频数据 -> HTTP Request -> 转写结果消费节点。 + +推荐 HTTP Request 配置: + +| n8n 设置 | 值 | +|---|---| +| Method | `POST` | +| URL | `http://: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}}` 传给字幕、说话人分析或质检节点。 + +## Webhook worker 模式 + +当工作流引擎不能稳定发送 multipart 文件,或音频需要预处理时,可以把转写封装成一个内部 webhook worker。 + +```python +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`,使用返回列表中的别名。 |