从零部署 FunASR 语音识别服务
本文是一篇通用教程,教你在一台无 GPU 的低配云服务器 上,从零部署一套可对外提供 HTTP 接口的中文语音识别(ASR)服务。
技术栈:FunASR + SenseVoiceSmall 模型 + Python 服务端 + systemd 守护进程。
复制本文全部内容给Agent后可自动部署。
目录
- 这是什么、能做什么
- 前置准备
- 服务器环境准备
- [安装 FunASR 与依赖](#安装 FunASR 与依赖)
- 编写识别服务端代码
- [配置 API 鉴权(多密钥)](#配置 API 鉴权(多密钥))
- [配置 systemd 守护进程](#配置 systemd 守护进程)
- 放行防火墙/安全组端口
- 测试与验证
- [常见问题 FAQ](#常见问题 FAQ)
1. 这是什么、能做什么
FunASR 是阿里巴巴达摩院开源的工业级语音识别工具包,支持:
- 离线/流式语音识别(ASR)
- 语音活动检测(VAD)
- 标点恢复、说话人分离等
本教程选用 SenseVoiceSmall 模型:它专为 CPU 也能流畅跑 设计,中文识别准确率高,天然带标点,非常适合在没有显卡的低配服务器上部署。
部署完成后,你会得到一个类似这样的 HTTP 接口,供你的应用(桌面宠物、App、网页等)通过网络调用:
POST http://<你的服务器IP>:<端口>/api/asr
Header: Authorization: Bearer <你的密钥>
Body: { "audio": "<音频的base64或url>" }
→ 返回识别出的文字
2. 前置准备
| 项目 | 说明 |
|---|---|
| 一台云服务器 | Ubuntu 20.04/22.04 均可,无需 GPU |
| 最低配置建议 | 2 核 + 4G 内存(本文示例为 4 核 3.6G) |
| 磁盘 | 至少预留 5G(Python 依赖 + 模型约 1G+) |
| 网络 | 公网 IP 或内网可达 |
| 一个 SSH 客户端 | 用于登录服务器 |
提示:SenseVoiceSmall 依赖 torch CPU,安装体积较大,请确保磁盘与带宽充足。
3. 服务器环境准备
登录服务器后,先更新系统并安装 Python 与相关工具:
bash
sudo apt update && sudo apt upgrade -y
# 安装 Python3、pip、venv 等
sudo apt install -y python3 python3-pip python3-venv git curl
# 确认版本(建议 Python >= 3.8)
python3 --version
创建独立工作目录,并建立 Python 虚拟环境(强烈建议用 venv 隔离依赖,避免污染系统环境):
bash
mkdir -p ~/funasr-asr && cd ~/funasr-asr
python3 -m venv venv
source venv/bin/activate
# 升级 pip
pip install --upgrade pip
4. 安装 FunASR 与依赖
在虚拟环境里安装(CPU 版 torch):
bash
# CPU 版 PyTorch(体积大,耐心等待)
pip install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
# 安装 FunASR 本体
pip install funasr
# 服务端 Web 框架(用于提供 HTTP 接口)
pip install flask
说明:
- 如果服务器有 GPU,可改为安装对应 CUDA 版本的 torch,并在代码里把
device改成"cuda"。torch安装较慢/较大,若网络慢可考虑换 pip 镜像源(如清华源)。
验证一下模块能正常 import:
bash
python -c "import funasr; print('funasr ok')"
python -c "import torch; print(torch.__version__)"
5. 编写识别服务端代码
创建服务端主文件 server.py(路径首行注释已标注):
python
# ~/funasr-asr/server.py
import os
import base64
import tempfile
from flask import Flask, request, jsonify
from funasr import AutoModel
from funasr.utils.postprocess_utils import rich_transcription_postprocess
app = Flask(__name__)
# 模型只加载一次,常驻内存,避免每次请求重复加载
print("[init] loading SenseVoiceSmall model ...")
model = AutoModel(
model="iic/SenseVoiceSmall",
vad_model="fsmn-vad",
device="cpu", # 有 GPU 改成 "cuda"
disable_update=True, # 禁止自动更新模型,避免启动时联网
)
print("[init] model loaded.")
def check_auth():
"""校验 API 密钥(支持逗号分隔的多密钥)"""
keys = os.environ.get("ASR_API_KEYS", "") or os.environ.get("ASR_API_KEY", "")
valid = [k.strip() for k in keys.split(",") if k.strip()]
# 若未配置任何密钥,则允许本地调试(生产环境务必配置)
if not valid:
return True
auth = request.headers.get("Authorization", "")
token = auth.replace("Bearer ", "").strip()
return token in valid
@app.route("/health", methods=["GET"])
def health():
return jsonify({"status": "ok"})
@app.route("/api/asr", methods=["POST"])
def asr():
if not check_auth():
return jsonify({"error": "unauthorized"}), 401
data = request.get_json(silent=True) or {}
audio_b64 = data.get("audio")
audio_url = data.get("url")
if not audio_b64 and not audio_url:
return jsonify({"error": "missing audio or url"}), 400
# 将输入统一转成临时音频文件
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
tmp_path = tmp.name
tmp.close()
try:
if audio_url:
import urllib.request
urllib.request.urlretrieve(audio_url, tmp_path)
else:
with open(tmp_path, "wb") as f:
f.write(base64.b64decode(audio_b64))
result = model.generate(input=tmp_path)
text = ""
if result and result[0].get("text"):
text = result[0]["text"]
return jsonify({"text": text})
except Exception as e:
return jsonify({"error": str(e)}), 500
finally:
if os.path.exists(tmp_path):
os.remove(tmp_path)
if __name__ == "__main__":
port = int(os.environ.get("PORT", "10095"))
app.run(host="0.0.0.0", port=port, threaded=True)
关键点:
model = AutoModel(...)放在模块顶层只加载一次,避免每次请求都重新加载(那可是秒级甚至几十秒的开销)。vad_model="fsmn-vad"会自动做语音活动检测,只切出有人声的片段。- 正式环境务必通过环境变量配置密钥,见下一节。
6. 配置 API 鉴权(多密钥)
为避免陌生人滥用你的识别服务,务必开启密钥鉴权。本方案支持多个密钥(逗号分隔)。
生成随机密钥(示例):
bash
python -c "import secrets; print(secrets.token_urlsafe(32))"
记录生成的密钥,例如 'AbCdEf123...'(下面用占位符表示)。
方式一:临时运行(调试用)
bash
export ASR_API_KEYS="<你的密钥1>,<你的密钥2>"
export PORT=10095
python server.py
方式二:写入 systemd(推荐,见下一节)
在 systemd 服务文件里通过 Environment= 注入密钥,重启不丢失。
7. 配置 systemd 守护进程
用 systemd 让服务开机自启、崩溃自动拉起。
创建服务文件:
bash
sudo tee /etc/systemd/system/funasr-asr.service > /dev/null <<'EOF'
[Unit]
Description=FunASR ASR Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/root/funasr-asr
Environment="PORT=10095"
Environment="ASR_API_KEYS=<你的密钥1>,<你的密钥2>"
ExecStart=/root/funasr-asr/venv/bin/python /root/funasr-asr/server.py
Restart=always
RestartSec=5
# 低配机器可限制内存,避免 OOM(按需调整)
MemoryMax=2.5G
[Install]
WantedBy=multi-user.target
EOF
⚠️ 请把
<你的密钥1>,<你的密钥2>替换成你上一步生成的真实密钥。
启用并启动:
bash
sudo systemctl daemon-reload
sudo systemctl enable funasr-asr
sudo systemctl start funasr-asr
# 查看运行状态
sudo systemctl status funasr-asr
# 查看实时日志(首次启动会看到模型加载过程)
sudo journalctl -u funasr-asr -f
看到日志里出现模型加载完成、服务监听端口,即表示成功。
8. 放行防火墙/安全组端口
光启动服务还不够,还需要开放端口。
本机防火墙(若启用了 ufw)
bash
sudo ufw allow 10095/tcp
sudo ufw status
云服务器安全组
在云服务商控制台(如腾讯云/阿里云)的安全组中,添加一条入站规则:
- 协议:TCP
- 端口:10095
- 来源:
0.0.0.0/0(或仅限你自己的 IP,更安全)
安全提示:建议来源限制为你自己的固定 IP,或至少配合密钥鉴权使用。
9. 测试与验证
本机测试
bash
curl http://127.0.0.1:10095/health
# 期望返回 {"status":"ok"}
带密钥的识别测试
用一段音频(本地 wav 文件)base64 编码后调用:
bash
# 将本地音频转 base64
B64=$(base64 -w 0 /path/to/your_audio.wav)
curl -X POST http://127.0.0.1:10095/api/asr \
-H "Authorization: Bearer <你的密钥>" \
-H "Content-Type: application/json" \
-d "{\"audio\": \"$B64\"}"
期望返回类似:
json
{"text": "欢迎大家来体验语音识别模型"}
客户端调用示例(Python)
python
# 客户端示例 client_example.py
import base64
import requests
def asr(audio_path: str, server: str, key: str) -> str:
with open(audio_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
resp = requests.post(
f"{server}/api/asr",
headers={"Authorization": f"Bearer {key}"},
json={"audio": b64},
timeout=60,
)
if resp.status_code != 200:
raise RuntimeError(f"ASR failed: {resp.status_code} {resp.text}")
return resp.json()["text"]
if __name__ == "__main__":
SERVER = "http://<你的服务器IP>:10095"
KEY = "<你的密钥>"
print(asr("/path/to/audio.wav", SERVER, KEY))
10. 常见问题 FAQ
Q1:启动很慢,日志卡在加载模型?
首次启动需要下载 iic/SenseVoiceSmall 模型(约几百 MB)。若网络慢,可设置 HuggingFace/ModelScope 镜像,或离线下载后指定本地路径。耐心等模型下载缓存完成即可,之后启动会快很多。
Q2:返回 401 Unauthorized?
检查客户端 Authorization 里的密钥,是否与服务器 ASR_API_KEYS 中配置的某一项完全一致(注意前后空格)。
Q3:内存不够被 OOM 杀掉?
低配机器建议:① 在 systemd 里设置 MemoryMax;② 关闭其他占用内存的服务;③ 只保留必要的进程。
Q4:CPU 识别慢怎么办?
- 确认使用了
device="cpu"且模型只加载一次; - 短音频通常 1~3 秒内返回,可接受;
- 如需更快,换 GPU 服务器,或改用官方
funasr-server并配合 GPU。
Q5:想支持英文/多语言?
SenseVoiceSmall 本身支持中英日韩粤等,model.generate 可加 language="auto" 自动检测。需要更多语种可参考 FunASR 官方文档选择其他模型。
Q6:如何更省心的一键部署?
FunASR 官方也提供 funasr-server(OpenAI 兼容接口)。本教程的裸机方案胜在体积小、可定制(自定义鉴权、管理页),适合需要深度控制的场景。
附:本教程的关键安全清单
- 已通过
ASR_API_KEYS配置至少一个强随机密钥 - 未在代码/日志中硬编码真实密钥
- 安全组端口来源已尽量收窄
- 服务由 systemd 托管,崩溃自动拉起
- 定期查看
journalctl -u funasr-asr日志
本文基于 FunASR(modelscope/FunASR)官方文档整理。模型与工具版本迭代较快,若遇到版本差异,请以官方仓库最新 README 为准:https://github.com/modelscope/FunASR