FunASRfunasr命令行接口实战指南:本地音频转写、结构化结果与 SRT 字幕生成
【免费下载链接】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 仓库中的中文 CLI 文档 docs/cli_zh.md 及其实现 funasr/cli.py 编写,覆盖funasr命令的完整参数、四种输出格式、字幕合并算法、说话人与热词转发逻辑等细节。读完本文后,你可以直接用命令行转写本地音频、批量产出 JSON/SRT/TSV 结果,并理解每个参数在源码中的真实行为与限制。
一、命令定位:它是什么、不是什么
funasr是一个本地文件的 SDK 包装命令:它转写已存在的本地音频文件、保存结构化结果或生成字幕。需要明确三个边界:
- 它不是 HTTP 服务,也不是流式推理、网络服务或原生 vLLM 服务。需要服务化部署时,应查看仓库的部署矩阵及其他服务路径;
- 它不是 Hydra CLI。仓库中同时存在一个旧版 Hydra 入口
funasr-hydra(见下文“旧版 CLI”一节),两套语法不能混用; moss-transcribe-diarize不是 CLI 模型选项,它走独立的AutoModel适配器或funasr-server路径,参见 MOSS 指南,不要把它传给--model。
命令入口由 setup.py 的console_scripts声明:funasr = funasr.cli:main,即 funasr/cli.py 中的main()函数(argparse 解析器,prog="funasr")。同一文件中还声明了funasr-server、funasr-realtime-server、funasr-hydra等服务/训练入口,可见funasr只是多个命令入口中最轻量的本地转写入口。
二、安装与前提
在选定的 Python 环境中安装 FunASR 并准备前提条件:
python -m pip install funasr funasr --help funasr --version完整的依赖准备(含先装 PyTorch 再装 FunASR 的顺序)见安装指南。几个关键前提:
- 首次使用可能触发下载:所选 ASR 模型,以及 VAD、标点、说话人组件都会按需在首次加载时从模型源下载,需要可用的模型源访问权限、足够内存,以及对应检查点要求的依赖;
- 模型源切换:默认
--hub ms使用 ModelScope,--hub hf选择 Hugging Face; - 设备自动选择:PyTorch 报告 CUDA 可用时自动选择
cuda:0,否则选择cpu,可用--device覆盖。这个可用性检查不保证显存充足或模型环境兼容; - 上述命令是使用命令,不代表全新环境安装验证。
从源码看,main()在解析参数后先import torch再实例化模型,设备解析逻辑即这一行:
device = args.device or ("cuda:0" if torch.cuda.is_available() else "cpu")并且模型实例化时固定携带disable_update=True,跳过启动时的版本更新检查。
三、基本用法
用默认的sensevoice模型转写一个文件,或选择 CLI 支持的模型别名:
funasr audio.wav funasr audio.wav --model paraformer funasr audio.wav --device cpu按任务继续:结构化 JSON(见下文 JSON 小节)、多个文件、字幕、说话人与热词。
输入必须是已存在的本地文件:audio是位置参数(nargs="+"),CLI 对每个文件执行os.path.isfile检查,不接受音频 URL,远程音频需先自行下载。
四、输出格式
通过--output-format/-f选择,默认text,可选text、json、srt、tsv。
4.1 纯文本(默认)
每个输入文件产生一份纯转写文本。模型的<|...|>富文本标签会被移除,因此它不是情感或声音事件标签输出接口。
funasr audio.wav -f text -o ./transcripts未指定-o时,CLI 将结果打印到标准输出。--verbose将 CLI 的加载与计时信息写到标准错误;模型或依赖仍可能向标准输出打印日志。需要独立结果载荷的自动化应使用输出文件。
标签清理由 funasr/cli.py 中的clean_text()完成,正则是<\|[^|]*\|>,对整段文本和每个句子文本都生效。
4.2 结构化 JSON
funasr audio.wav --timestamps -f json -o ./results jq '.text' ./results/audio.json以下是格式化器的示意测试数据,不是实测识别结果、性能数据,也不保证每个模型都返回这些可选字段:
{ "text": "Example.", "segments": [ {"start": 0, "end": 1200, "text": "Example.", "timestamp": [[0, 1200]]} ], "timestamps": [[0, 1200]], "file": "audio.wav", "model": "sensevoice", "language": "auto", "audio_duration_s": 1.2, "processing_s": 0.01 }| 字段 | 含义 |
|---|---|
text | 清理标签后的识别文本。 |
segments | 仅当非空sentence_info生成分段时包含。start/end直接复制 SDK 值(句子契约使用毫秒),文本会清理标签。分段内timestamp可以为 null。 |
timestamps | --timestamps保留的可选顶层模型时间戳。CLI 不将其统一为通用词级结构。示例中的数值对数组使用毫秒;部分模型的字典时间戳表示可能使用秒。 |
file | 输入文件名,不含完整路径。 |
model | 所选 CLI 别名,不是不可变的检查点修订号。 |
language | 用户传入的提示,省略时为auto;不是检测出的语言。 |
audio_duration_s | 音频元数据时长,单位为秒,保留三位小数;soundfile.info无法读取时为 null。 |
processing_s | 每个文件生成调用周围的耗时,单位为秒,保留三位小数。不含初次模型加载和输出格式化、写盘时间,不是端到端延迟。 |
--timestamps只保留模型已经返回的时间戳,不会请求对齐,也不保证词级时间戳。JSON 未指定该参数时省略顶层时间戳,但分段内部的时间戳仍可能存在。纯文本格式不显示时间戳。模型相关的结果差异见 SDK 输出契约。
对应源码:_format_output()中,audio_duration_s通过soundfile.info(audio_path).duration获取(读取失败置None),processing_s是围绕model.generate()的单次计时;顶层timestamps仅在非空时写入。
4.3 字幕(SRT)
funasr audio.wav -f srt -o ./subs funasr audio.wav -f srt --subtitle-segment-mode sentence -o ./raw-subsSRT 使用HH:MM:SS,mmm时间格式。SRT 和 TSV 都向 SDK 请求sentence_timestamp、output_timestamp和return_time_stamps(见下文源码印证)。默认的 SenseVoice 字幕路径还会增加ct-punc;结果仍取决于模型是否返回可用的sentence_info和时间信息。
默认readable模式会合并符合条件的相邻字幕:间隔不超过 500 毫秒,合并后时长不超过 8 秒,文字不超过 42 个字符,且不跨越已知的说话人变化。长字幕只有在已有时间戳能够支持文本对齐时才会拆分。这些是分组目标,不能保证每条字幕都满足:无法对齐或不可再拆分的文本可能超限。CLI 不会编造均匀分布的词级时间戳。sentence模式保留模型原始句子边界。JSON 和 TSV 不执行这种分组。
没有句子分段时,SRT 回退为单条字幕,先使用可用的时间戳范围,再尝试音频时长;两者都不可用时,回退字幕可能是零时长;发布字幕前应检查时间信息。
4.4 表格(TSV)
funasr audio.wav -f tsv -o ./tablesTSV 包含start、end、text三列,将句子起止时间从毫秒换算成秒,保留三位小数。没有分段时仅输出一行文本,起止时间均为0.000,不代表推导出的对齐时间。
对应源码format_tsv():
def format_tsv(segments): lines = ["start\tend\ttext"] for seg in segments: lines.append(f"{seg.get('start',0)/1000:.3f}\t{seg.get('end',0)/1000:.3f}\t{seg.get('text','')}") return "\n".join(lines)无分段时的回退分支输出start\tend\ttext\n0.000\t0.000\t{text}。
五、SRT 字幕合并算法:readable模式的源码级解析
readable模式的核心是 funasr/cli.py 中的merge_subtitle_segments(segments, max_gap_ms=500, max_duration_ms=8000, max_chars=42),它做两类操作:长段拆分与短段合并。
5.1 长段拆分:只在时间戳可对齐时进行
_split_subtitle_segment()处理单条超长分段,前置校验非常严格:
- 分段的
timestamp/timestamps必须全部是合法且单调有序的毫秒数对(end > start、非负、无乱序); - 文本必须能切分为 token span,且token 数量与时间戳数量一致(若 SDK 返回了
words,则用_subtitle_word_spans()校验词面在文本中的严格出现位置); - 任一 token 自身时长超过 8 秒或字符数超过 42,则放弃拆分(例如单个超长的英文长单词)。
满足条件后,_balanced_subtitle_ranges()用动态规划求解“条数最少 + 边界可读”的切分:边界权重综合了标点强度(句末标点 4.0、其他标点 2.0、空格 1.5、中英切换 1.0)和可选的jieba分词强度(导入失败时静默降级为纯标点规则)。这解释了“无法对齐或不可再拆分的文本可能超限”这一行为——比如阿拉伯文等未纳入_is_supported_subtitle_character()支持范围的字符,会直接返回空 span,整段原样保留。
5.2 短段合并:can_follow()的三条硬条件
pack()逐段回溯贪心分组,两个相邻分段只有同时满足以下条件才会合入同一条字幕:
- 说话人一致:
speaker/spk字段相等,不跨越说话人变化; - 间隔在 (0, 500ms] 之间;
- 衔接语义:左侧文本以续接标点(
,,、::;;)结尾,或左侧主体长度 ≤ 2 字符,或间隔 ≤ 100ms 且右侧以续接标点结尾且右侧主体长度 > 2。
文本拼接由_join_subtitle_text()完成:两边都是 ASCII 字母数字时在中间补一个空格,否则直接连接(因此中文句子合并后不会出现多余空格)。
5.3 测试用例中的可验证行为
tests/test_cli.py 用一系列单测固化了上述规则,可作为行为契约阅读:
test_cli_srt_requests_sentence_timestamps_and_writes_segmented_output:断言 SRT 路径向AutoModel传入punc_model="ct-punc"(SenseVoice 默认),generate()收到sentence_timestamp/output_timestamp/return_time_stamps均为True,且输出的sample.srt为两条编号字幕;test_cli_srt_supports_readable_and_sentence_segment_modes:对“甲,/乙。”两句,readable合并为单条00:00:00,000 --> 00:00:01,200 甲,乙。,sentence模式保留两条原始边界;test_merge_subtitle_segments_groups_continuation_cues/_keeps_continuation_chain_with_its_ending:以续接标点结尾的句子链被合并,链头以句号结尾时不并入;test_merge_subtitle_segments_preserves_hard_boundaries:speaker从 0 变为 1 的分段不被合并;test_merge_subtitle_segments_splits_overlong_source_with_token_timestamps:60 字、逐字时间戳的长段被拆成多条,且每条 ≤ 8000ms、≤ 42 字符、时间戳无丢失无重叠;test_merge_subtitle_segments_rejects_out_of_order_timestamps、_rejects_negative_timestamps、_does_not_infer_unsupported_script_surfaces:乱序/负值时间戳、未支持文字系统一律原样保留,不做猜测。
六、多文件批量转写
funasr first.wav second.wav -f json -o ./results funasr ./*.wav -f srt -o ./subs模型只实例化一次,多个文件依次处理,每次生成使用batch_size=1。通配符由 shell 展开,这不是并行批推理。输出文件名使用输入文件去掉扩展名后的名称,再加.txt、.json、.srt或.tsv;输出目录不存在时会创建(os.makedirs(args.output_dir, exist_ok=True))。同名文件可能互相覆盖,也可能覆盖上一次运行的结果,即使输入来自不同目录。
不指定-o时,多份 JSON 会作为独立的多行对象逐个打印,不是 JSON 数组或 JSONL。遇到不存在的文件会以退出码 1 停止(向 stderr 打印Error: file not found: ...),之前写出的文件保留。CLI 没有断点续跑或事务式批处理选项。
注意顺序细节:模型加载先于逐文件存在性检查——main()先AutoModel(...)再循环检查os.path.isfile,因此错误输入也可能触发模型加载或下载。推理或依赖异常不会被转成稳定的 JSON 错误对象。
七、说话人与热词
funasr meeting.wav --model paraformer --spk --timestamps -f json -o ./meetings funasr audio.wav --model paraformer --language zh --hotwords "FunASR,达摩院" funasr audio.wav --hub hf --model fun-asr-nano7.1 说话人分离
--spk在MODEL_CONFIGS基础上追加spk_model="cam++",由 AutoModel 构建独立的说话人模型。仅当 SDK 的sentence_info中含spk时,JSON 分段才包含speaker字段:
if args.spk and "spk" in seg: s["speaker"] = seg["spk"]这不是实名身份识别,也不保证所有模型组合都能进行说话人分离(spk_model依赖vad_model做分段,四个 CLI 别名均包含fsmn-vad,满足该前提)。SRT 分组会遵守已有的说话人边界(can_follow()的第一条硬条件),但 CLI 的纯文本、SRT、TSV 格式化器不输出说话人标签。
7.2 热词与语言提示
热词使用逗号分隔,去除前后空白并丢弃空项,然后按模型别名分流:
if args.model == "paraformer": gen_kw["hotword"] = " ".join(hotwords) # 空格连接的字符串 else: gen_kw["hotwords"] = hotwords # 列表paraformer别名向 SDK 传入以空格连接的hotword字符串;其他别名传入hotwords列表。tests/test_cli.py 中test_cli_routes_multiple_hotwords_to_paraformer_hotword固化了该行为:--hotwords "FunASR, ModelScope"最终变为generate(hotword="FunASR ModelScope"),且不携带hotwords键。是否生效取决于模型,而不只是解析器接受了该参数。--language也属于模型相关提示:解析器接受任意字符串,不校验模型语言覆盖(--language的短前缀--lang同样可用,测试中即通过--lang zh传入)。
八、参数参考
audio为一个或多个本地文件路径。下表None是解析器的真实默认值,不是应在命令行输入的字符串。
| 参数 | 短参数 | 解析器默认值 | 含义 / 可选值 |
|---|---|---|---|
--model | -m | sensevoice | sensevoice、paraformer、paraformer-en、fun-asr-nano。 |
--hub | -H | ms | ms(ModelScope)或hf(Hugging Face)。 |
--language | -l | None | 省略时不向模型传语言参数。zh、en、ja、ko、yue、auto等显式提示是否支持取决于模型。 |
--device | None | CUDA 可用时自动选择cuda:0,否则cpu;显式设备字符串覆盖自动选择。 | |
--output-format | -f | text | text、json、srt、tsv。 |
--subtitle-segment-mode | readable | readable或sentence,仅影响 SRT。 | |
--output-dir | -o | None | 省略时输出到 stdout,否则按输入文件分别写到指定目录。 |
--timestamps | False | 保留已有顶层时间戳,不请求对齐。 | |
--spk | False | 增加说话人模型,JSON 说话人字段取决于 SDK 返回结果。 | |
--hotwords | None | 逗号分隔的提示词,按模型别名转发。 | |
--verbose | -v | False | 将 CLI 加载和计时信息写到 stderr。 |
--version | 不适用 | 打印已安装的 FunASR 包版本并退出,不是模型修订号。 | |
--help | -h | 不适用 | 打印解析器帮助并退出。 |
--model的取值由choices=list(MODEL_CONFIGS)强制约束,下表四个别名就是全部选项;不能在这里传任意模型源 ID、本地模型目录或后端选择参数。
九、模型别名与组件装配逻辑
| CLI 别名 | ASR 模型映射 | 范围 |
|---|---|---|
sensevoice | iic/SenseVoiceSmall | 中文、英语、日语、韩语、粤语;CLI 文本移除富文本标签。 |
paraformer | paraformer-zh | 中文识别,带 VAD 和标点。 |
paraformer-en | paraformer-en | 英语识别,CLI 还会增加标点模型。 |
fun-asr-nano | FunAudioLLM/Fun-ASR-Nano-2512 | 中文、英语、日语及中文方言/口音;需要额外模型依赖。 |
四种配置均包含fsmn-vad;说话人和标点组件按上文选项逻辑添加。这些映射没有固定模型源修订号。fun-asr-nano别名不选择独立的 Fun-ASR-MLT-Nano 检查点。其他检查点请使用 Python SDK并参考模型选择指南。
funasr/cli.py 顶部的MODEL_CONFIGS是这些映射的事实来源,其中sensevoice额外携带vad_kwargs={"max_single_segment_time": 30000}(VAD 单段上限 30 秒)。标点组件的装配规则比“别名表”更精细,源码逻辑为:
if "punc_model" not in config and args.model != "fun-asr-nano": if args.model != "sensevoice" or args.output_format in ("srt", "tsv"): config["punc_model"] = "ct-punc"即:paraformer/paraformer-en恒带ct-punc;fun-asr-nano从不追加(其输出自带标点);sensevoice仅在输出格式为srt或tsv时追加——这解释了“默认的 SenseVoice 字幕路径还会增加ct-punc”这一行为的来源。
十、使用边界汇总
- 这是本地文件 SDK 包装命令,不是流式推理、网络服务或原生 vLLM 服务,其他路径见部署矩阵;
- 四个别名就是
--model的全部选项,不能传任意模型源 ID、本地模型目录或后端选择参数; moss-transcribe-diarize不是 CLI 模型选项,请按 MOSS 指南 使用独立路径;- 模型加载先于逐文件存在性检查,错误输入也可能触发加载或下载;推理或依赖异常不会被转成稳定的 JSON 错误对象;
- 速度、内存、语言覆盖、对齐和说话人质量依赖检查点、硬件、环境和音频,本文不作速度或生产容量承诺。
十一、旧版 CLI:funasr-hydra
原有的 Hydra 入口仍为funasr-hydra(setup.py 中映射到funasr.bin.inference:main_hydra):
funasr-hydra ++model=paraformer-zh ++input=audio.wav从 funasr/bin/inference.py 源码看,它用@hydra.main(config_name=None)接收全部++key=value覆写,转成普通 dict 后直接AutoModel(**kwargs)并以input=kwargs["input"]调用model.generate(),最后直接print(res)打印原始返回——没有本文 CLI 的格式器、字幕合并与多文件循环。其++key=value配置与本文的 argparse 参数分属两套语法,不要混用。
【免费下载链接】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),仅供参考