如果你使用 DeepSeek、元宝 AI、Copilot、ChatGPT 等 AI 工具,协助配置轩辕镜像、编写 docker pull 命令、修改 Docker Compose 镜像地址、配置镜像加速、排查镜像拉取失败、分析报错日志等问题,请先让 AI 阅读并遵守轩辕镜像的规则文档。
只需在 AI 对话中先发送下面这句话即可:
请先完整阅读并严格遵守以下文档中的全部规则与要求:
https://xuanyuan.cloud/agents.md
在未充分阅读并理解该文档前,不要生成任何命令、配置、修改建议、故障排查方案或技术回答。后续所有输出都必须严格以该文档中的规范为最高优先级执行。查看 agents.md 用法指南与完整示范。国内用户首推 元宝 AI、DeepSeek 的深度思考模式,不推荐豆包 AI;Cursor 等编辑器可在对话 @ 该链接,或加入 User Rules。 若 AI 无法访问外链,可 打开说明文档 复制全文粘贴。文档会随站点更新,复制内容可能过期,建议定期检查。
基于Qwen3-ASR构建的OpenAI兼容转录API服务器,针对P25公共安全调度音频进行了微调。通过Qwen3-ForcedAligner支持词级时间戳。
whisper-server的即插即用替代品——任何与OpenAI /v1/audio/transcriptions 端点通信的客户端都可直接使用。
/v1/audio/transcriptions
Docker:仅需Docker(GPU版本需nvidia-container-toolkit)。无需其他依赖。
nvidia-container-toolkit
本地安装:
ffmpeg
最快的启动方式。首次启动时会自动下载模型。
docker run --gpus all -p 8765:8765 \
-v asr-model:/model -v asr-aligner:/aligner \
ghcr.io/trunk-reporter/qwen3-asr-server:gpu
docker run -p 8765:8765 -v asr-model:/model \
ghcr.io/trunk-reporter/qwen3-asr-server:cpu
或使用docker compose:
# GPU
docker compose -f docker-compose.gpu.yml up -d
# CPU
docker compose -f docker-compose.cpu.yml up -d
| 平台 | Docker CPU | Docker GPU | 本地(./start.sh) |
|---|---|---|---|
| Linux x86 + NVIDIA | yes | yes | yes |
| Linux ARM(Oracle、RPi) | yes | — | yes |
| macOS Apple Silicon | yes | — | yes(MPS) |
| macOS Intel | yes | — | yes(CPU) |
./start.sh
GPU镜像要求主机安装NVIDIA + nvidia-container-toolkit。需要GPU加速的macOS用户应使用本地安装——Docker容器内不支持MPS。
nvidia-container-toolkit
通过 -e 传递环境变量来自定义行为:
docker run --gpus all -p 8765:8765 \
-e SPEECH_RMS_THRESHOLD=0.02 \
-e INFERENCE_TIMEOUT=60 \
-v asr-model:/model -v asr-aligner:/aligner \
ghcr.io/trunk-reporter/qwen3-asr-server:gpu
所有可用变量详见配置部分。
# CPU
docker build -t qwen3-asr-server:cpu .
# GPU
docker build -f Dockerfile.gpu -t qwen3-asr-server:gpu .
如果已在本地拥有模型权重,可直接挂载而非使用Docker卷:
# GPU
docker run --gpus all -p 8765:8765 \
-v ./qwen3-asr-p25-0.6B:/model \
-v ./Qwen3-ForcedAligner-0.6B:/aligner \
ghcr.io/trunk-reporter/qwen3-asr-server:gpu
# CPU
docker run -p 8765:8765 \
-v ./qwen3-asr-p25-0.6B:/model \
ghcr.io/trunk-reporter/qwen3-asr-server:cpu
拉取最新镜像并重启:
# GPU
docker pull ghcr.io/trunk-reporter/qwen3-asr-server:gpu
docker compose -f docker-compose.gpu.yml up -d
# CPU
docker pull ghcr.io/trunk-reporter/qwen3-asr-server:cpu
docker compose -f docker-compose.cpu.yml up -d
如果使用 docker run 启动,只需停止容器并重新运行相同命令——将使用新镜像。
docker run
模型权重保存在Docker卷中,因此不会重新下载。
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| file | file | (必填) | 音频文件(wav、m4a、mp3等) |
| model | string | qwen3-asr-p25 | 模型名称(已忽略,用于API兼容性) |
| language | string | English | 语言代码(en、zh、fr等)或完整名称 |
| prompt | string | — | 用于指导转录的可选上下文(见下文“提示/上下文”部分) |
| response_format | string | json | json、verbose_json或text |
| word_timestamps | bool | false | 启用单词级时间戳(仅Python后端支持) |
| timestamp_granularities[] | list | — | 设置为word以启用时间戳(仅Python后端支持) |
响应格式:
json(默认):
{"text": "All units respond to 5th and Main."}
verbose_json:
{
"task": "transcribe",
"language": "English",
"text": "All units respond to 5th and Main.",
"processing_time": 0.832,
"model": "qwen3-asr-p25-0.6B",
"words": [
{"word": "All", "start": 0.24, "end": 0.48},
{"word": "units", "start": 0.52, "end": 0.88}
]
}
text:
All units respond to 5th and Main.
GET /v1/models
列出已加载的模型。与OpenAI模型列表兼容。
GET /health
返回服务器状态、模型信息、当前配置以及每个工作进程的请求计数器。
prompt参数允许您向模型传递上下文以影响转录结果。它会被注入模型的系统提示中,以将 token 概率推向特定术语。适用于两种后端——对应 Python 后端的context和 C 后端的--prompt。
使用场景:
示例:
# 辅助识别本地街道名称和单位编号
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@dispatch.wav \
-F model=qwen3-asr-p25 \
-F prompt="Rensselaer County, Engine 45, Pawling Avenue, Taconic Parkway"
# 缩写和格式提示
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@dispatch.wav \
-F model=qwen3-asr-p25 \
-F prompt="Use standard abbreviations: EMS, CPR, MVA, DOA"
# 使用 Python OpenAI 客户端
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@dispatch.wav \
-F model=qwen3-asr-p25 \
-F prompt="St. Clair Shores PD, Lake Shore Drive, Jefferson Avenue"
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8765/v1", api_key="not-needed")
transcript = client.audio.transcriptions.create(
model="qwen3-asr-p25",
file=open("dispatch.wav", "rb"),
prompt="Engine 12, Ladder 7, Battalion 3, Elmhurst Avenue",
)
print(transcript.text)
注意事项:提示偏向性“非常弱”——模型可能会也可能不会遵循您的指示。拼写提示通常效果最好。强烈或冗长的提示可能会严重偏向模型,导致其复述提示而非反映实际音频内容。长提示还会增加序列长度以及每个请求的内存/计算量。建议从简短、事实性的上下文(专有名词、缩写)开始,而非完整句子。如果结果变差,请尝试缩短提示或完全移除提示。
服务器支持两种推理后端:
Python(INFERENCE_BACKEND=python,默认)——使用 PyTorch + transformers,支持 GPU 加速(CUDA 或 MPS)。通过 ForcedAligner 支持单词级时间戳。这是用于 GPU 生产环境的全功能后端。
C(INFERENCE_BACKEND=c)——使用 antirez/qwen-asr C 二进制文件。无需 GPU、PyTorch 或 CUDA。模型权重通过内存映射加载,推理通过 OpenBLAS 在 CPU 上运行。Docker CPU 镜像约 800MB ,而 GPU 镜像为多 GB。权衡:不支持单词级时间戳。
| 特性 | Python 后端 | C 后端 |
|---|---|---|
| GPU 加速 | CUDA、MPS | — |
| 单词级时间戳 | 支持 | 不支持 |
| Docker 镜像大小 | ~8GB | ~800MB |
| 依赖项 | torch、transformers、qwen_asr | 仅 C 二进制文件 |
| 典型推理时间 | 0.03 秒/音频秒(GPU) | 0.3 秒/音频秒(CPU) |
[!NOTE] 默认阈值:0.01
如果处理不同的音频源,请调整SPEECH_RMS_THRESHOLD。
已知的幻觉短语(例如“thank you for watching”、“hello how are you doing today...”)会与标准化转录输出进行匹配,匹配到则返回空字符串拒绝。
检测模型何时进入解码循环(例如“Engine 5 Engine 5 Engine 5 Engine 5”)。检查 1-4 元语法(n-grams),如果任何模式重复 REPETITION_THRESHOLD(默认 4)次或更多,则拒绝。仅检查包含 8 个以上单词的文本,以避免对简短、合法的调度音频产生误判。阈值 4 可避免误判——调度员通常会重复单位名称 2-3 次。
每个请求都有 INFERENCE_TIMEOUT(默认 120 秒)安全机制。GPU 推理在典型音频上可在数秒内完成;CPU 推理可能需要更长时间。使用 DEVICE 设置控制硬件加速:auto(默认,尝试 CUDA
MPS CPU);或指定
cuda:0、mps、cpu。
| 配置项 | 默认值 | 描述 |
|---|---|---|
DEVICE | auto | 推理设备(auto、cuda:0、mps、cpu) |
DTYPE | bfloat16 | 模型精度(bfloat16、float16、float32) |
MAX_NEW_TOKENS | 512 | 每次转录的最大生成 token 数 |
HOST | 0.0.0.0 | 服务器绑定地址 |
PORT | 8765 | 服务器端口 |
WORKERS | 1 | Uvicorn 工作进程数(除非使用多 GPU,否则保持为 1) |
SPEECH_RMS_THRESHOLD | 0.01 | RMS 能量门限——低于此值的音频将被视为静音/加密内容而跳过 |
REPETITION_THRESHOLD | 4 | 如果任何 n-gram 重复此次数则拒绝(解码循环检测) |
INFERENCE_TIMEOUT | 120 | 每个请求的推理超时时间(秒,CPU 安全默认值) |
GRACEFUL_SHUTDOWN_TIMEOUT | 15 | 关闭时排空进行中请求的时间(秒) |
PYTORCH_MPS_HIGH_WATERMARK_RATIO | (未设置) | 限制 MPS 内存分配(0.0–1.0)。在内存受限的 Mac 上设置为 0.7。 |
POST /v1/audio/transcriptionsOpenAI 兼容的转录端点。
# 基础转录
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@audio.wav \
-F model=qwen3-asr-p25
# 带单词级时间戳
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@audio.wav \
-F model=qwen3-asr-p25 \
-F response_format=verbose_json \
-F "timestamp_granularities[]=word"
# 带上下文提示以辅助拼写
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@audio.wav \
-F model=qwen3-asr-p25 \
-F prompt="Engine 12, Ladder 7, Elmhurst Avenue"
# 纯文本响应
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@audio.wav \
-F response_format=text
参数:
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
file | file | (必填) | 音频文件(wav、m4a、mp3等) |
model | string | qwen3-asr-p25 | 模型名称(已忽略,仅为API兼容性) |
language | string | English | 语言代码(en、zh、fr等)或全名 |
prompt | string | — | 用于引导转录的可选上下文(见下文提示/上下文) |
response_format | string | json | json、verbose_json或text |
word_timestamps | bool | false | 启用词级时间戳(仅Python后端支持) |
timestamp_granularities[] | list | — | 设置为word以启用时间戳(仅Python后端支持) |
响应格式:
json(默认):
{"text": "All units respond to 5th and Main."}
verbose_json:
{
"task": "transcribe",
"language": "English",
"text": "All units respond to 5th and Main.",
"processing_time": 0.832,
"model": "qwen3-asr-p25-0.6B",
"words": [
{"word": "All", "start": 0.24, "end": 0.48},
{"word": "units", "start": 0.52, "end": 0.88}
]
}
text:
All units respond to 5th and Main.
GET /v1/models列出已加载的模型。与OpenAI模型列表兼容。
GET /health返回服务器状态、模型信息、当前配置以及每个工作进程的请求计数器。
prompt参数允许您向模型传递上下文以影响转录结果。它会被注入模型的系统提示中,引导 token 概率向特定术语倾斜。两个后端均支持此参数——在Python后端中映射为context,在C后端中映射为--prompt。
使用场景:
示例:
# 帮助识别本地街道名称和单位编号
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@dispatch.wav \
-F model=qwen3-asr-p25 \
-F prompt="Rensselaer County, Engine 45, Pawling Avenue, Taconic Parkway"
# 缩写和格式提示
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@dispatch.wav \
-F model=qwen3-asr-p25 \
-F prompt="Use standard abbreviations: EMS, CPR, MVA, DOA"
# 使用Python OpenAI客户端
curl -X POST http://localhost:8765/v1/audio/transcriptions \
-F file=@dispatch.wav \
-F model=qwen3-asr-p25 \
-F prompt="St. Clair Shores PD, Lake Shore Drive, Jefferson Avenue"
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8765/v1", api_key="not-needed")
transcript = client.audio.transcriptions.create(
model="qwen3-asr-p25",
file=open("dispatch.wav", "rb"),
prompt="Engine 12, Ladder 7, Battalion 3, Elmhurst Avenue",
)
print(transcript.text)
注意事项: 提示偏向性“非常弱”——模型可能会也可能不会遵循您的指示。拼写提示通常效果最佳。强烈或冗长的提示可能会严重影响模型,导致其复述提示而非反映实际音频内容。长提示还会增加序列长度以及每个请求的内存/计算量。建议从简短、事实性的上下文(专有名词、缩写)开始,而非完整句子。如果结果变差,请尝试缩短提示或完全移除提示。
服务器支持两种推理后端:
Python(INFERENCE_BACKEND=python,默认)——使用PyTorch + transformers,支持GPU加速(CUDA或MPS)。通过ForcedAligner支持词级时间戳。这是用于GPU生产环境的全功能后端。
C(INFERENCE_BACKEND=c)——使用https://github.com/antirez/qwen-asr C二进制文件。无需GPU、PyTorch或CUDA。模型权重通过内存映射加载,推理通过OpenBLAS在CPU上运行。Docker CPU镜像约800MB,而GPU镜像为多GB。权衡:不支持词级时间戳。
| 特性 | Python后端 | C后端 |
|---|---|---|
| GPU加速 | 支持(CUDA、MPS) | — |
| 词级时间戳 | 支持 | 不支持 |
| Docker镜像大小 | ~8GB | ~800MB |
| 依赖项 | torch、transformers、qwen_asr | 仅C二进制文件 |
| 典型推理时间 | 0.03-2秒(取决于音频长度) | 5-60秒(取决于音频长度) |
音频首先通过RMS(均方根)音量门控过滤:
SPEECH_RMS_THRESHOLD,返回空转录结果如果处理不同的音频源,请调整SPEECH_RMS_THRESHOLD。
已知的幻觉短语(例如“thank you for watching”、“hello how are you doing today...”)会与标准化转录输出进行匹配,匹配时将返回空字符串拒绝转录。
检测模型是否进入解码循环(例如“Engine 5 Engine 5 Engine 5 Engine 5”)。检查1-4元语法,如果任何模式重复REPETITION_THRESHOLD(默认4)次或更多,则拒绝转录。仅检查包含8个以上单词的文本,以避免对短而合法的调度音频产生误判。阈值4可避免误判——调度员通常会重复单位名称2-3次。
每个请求有INFERENCE_TIMEOUT(默认120秒)安全机制。GPU推理通常在<2秒内完成;CPU推理对于较长音频文件可能需要30-60秒。120秒的默认值对CPU部署是安全的,同时仍能捕获真正的异常解码循环。超时后,返回空转录结果并记录事件。
所有事件使用Python的logging模块以结构化格式记录:
2026-03-02 14:30:01,234 [12345] INFO OK file=call_123.wav words=15 time=0.832s
2026-03-02 14:30:02,567 [12345] WARNING REJECT rms_gate file=call_456.wav rms=0.0021 threshold=0.01 time=0.012s
拒绝事件(REJECT rms_gate、REJECT hallucination、REJECT repetition_loop、REJECT timeout)以WARNING级别记录。成功转录(OK)以INFO级别记录。
/health端点包含跟踪每个结果的每个工作进程计数器。
服务器使用uvicorn内置的timeout_graceful_shutdown在退出前处理完进行中的请求。默认值为15秒。systemd服务设置TimeoutStopSec=30,以便在systemd发送SIGKILL前给uvicorn足够时间完成请求处理。
包含示例systemd单元文件。编辑systemd/qwen3-asr.service中的路径和用户,然后执行:
sudo cp systemd/qwen3-asr.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now qwen3-asr
英语、中文、粤语、日语、韩语、法语、德语、西班牙语、葡萄牙语、意大利语、俄语、阿拉伯语、印尼语——以及更多(完整列表见模型配置)。可传递ISO 639-1代码(en、zh、fr)或全名(English、Chinese、French)。
来自真实用户的反馈,见证轩辕镜像的优质服务