周末折腾记:本来想装个本地语音克隆,结果先把配音这件事用一条命令解决了

起因
这周末本来打算干件事:我在做一期讲本地语音克隆的视频,脚本写完了,卡在配音上。自己念太干,为三五分钟的视频单独找一次外包又不划算,网上免费 TTS 那种平直的中文机器味,做过内容的大概都熟。
刷 GitHub Trending 的时候正好看见 Python 周榜第一是个叫 VoiceStudio 的项目,一周涨了七千多颗星,累计两万一。口号很直接:fully-local ElevenLabs alternative,本地跑,不要账号,不要订阅,646 种语言。
我当时的想法很朴素:本地跑不花钱,配音这事以后肯定反复做,装一次一劳永逸。
于是先把它的 README 从头翻到尾。翻完我没装。
README 里的三行字
第一行,646 种语言是靠 16 个 TTS 引擎拼出来的,它自己写着一句 "actual coverage and quality depend on the selected engine"。也就是说中文到底哪个引擎靠谱,得我自己一个个装、一个个听。
第二行,默认引擎的权重是 CC-BY-NC,非商用。我要发的是带流量的内容,商用边界得逐个引擎核清楚。
第三行,项目自述 active beta,8GB 内存、10GB 磁盘起步,Intel Mac 连本地后端都跑不起来。GPU 是可选的,CPU 模式能跑,用 GPU 时 4GB 显存起。
这几条不是黑它。声音不出本机在很多场景下是硬需求,16 个 TTS 引擎加 11 个 ASR 引擎的组合也没几个项目比得了,它还带桌面应用、本地 API 和 MCP Server。但对我这种"就想要一段能用的中文旁白"的人来说,路径变成了先装环境、再挑引擎、再核许可,才可能拿到一段还不知道行不行的音频。
我把配音这件事拆出来单独找方案,摸到了百炼 CLI 里的 bl speech 和 bl omni。下面是流水账,包括两个我撞上的报错。

装
npm 这条路要 Node.js 18.17 以上:
npm install -g bailian-cli
bl skill init
不想预装 Node 的话,macOS 和 Linux 有脚本:
curl -fsSL https://bailian.aliyun.com/cli/install.sh | bash
Windows 在 PowerShell 里:
irm https://bailian.aliyun.com/cli/install.ps1 | iex
装完 bl --version,我这边是 1.22.0。然后去控制台建一把 API Key(在这里领,地域记得选华北2 北京,免费额度只在这个地域生效),登录:
bl auth login --api-key sk-xxxxx
bl auth status
auth status 会打印当前 profile、配置文件路径、凭证状态。安装说明和命令清单在 CLI 主页。
免费额度的规则我特意去官方文档核了一遍,网上流传的"100 万 Tokens 永久有效"是错的:每个模型各自一份(通常 100 万 token),90 天有效期,只在北京地域,过期不补不结转,一个模型用完不会去借别的模型的。已认证账号额度用尽会自动转按量付费,怕手滑就开用完即停:
bl usage freetier --all
bl usage free
开了以后超额调用返回 403、错误码 AllocationQuota.FreeTierOnly,看到这个别去翻代码,是额度问题。
先看有哪些音色
这一步只读列表,不调模型,不花额度:
bl speech synthesize --list-voices --model cosyvoice-v3-flash
末尾打一行 Total: 64 voices,四列:VOICE ID、NAME、DESCRIPTION、LANGUAGE。我按语言列数了一遍:中文/英文 36 个(童声 4 个算在里面,还有 1 个台式),粤语 3 个,方言 3 个,美式英语 10 个,英式英语 4 个,日语 5 个,韩语 2 个,印尼语 1 个。
方言那三行我看了两遍:longlaotie_v3 东北直率男、longshange_v3 原味陕北男、longanmin_v3 闽南。做地方内容的人找方言配音有多难,试过就知道,多数云端工具的音色表里根本没这一栏。
童声四个:龙呼呼(天真烂漫女童)、龙泡泡(飞天泡泡音)、龙闪闪(戏剧化童声)、龙牛牛(阳光男童声)。做儿童内容不用自己去调音高。
描述写得挺朴素,"龙橙 智慧青年男"、"龙华 元气甜美女"、"龙天 磁性理智男"、"龙婉 细腻柔声女"。那期视频的旁白我定的是龙橙,跟产品向的内容对得上。

有个细节容易踩:longanyang(龙安洋)是这批里唯一不带 _v3 后缀的,照着别的 ID 的规律给它补一个,就不是列表里的任何一个了。
第一个报错:411
稿子六十来字,直接写在命令里:
bl speech synthesize --text "很多人以为做中文配音得先租录音棚。这一期我想说的是,本地克隆解决的是声音归谁的问题,中文念得像不像人,是另一个问题。" --voice longcheng_v3 --out narration.mp3
第一次跑我没写 --model,返回是这个:
[Model: qwen-audio-3.0-tts-plus] [Voice: longcheng_v3]
Error: [cosyvoice:]Engine error [411]: TTS speak operation failed
Status: HTTP 400 (InvalidParameter)
Exit code: 1
我以为是音色名抄错了,换了四个音色名重试,一样 411。这才去翻 bl config show,profile 里写着 default_speech_model: qwen-audio-3.0-tts-plus,它把 --model 的默认值接管了,而 longcheng_v3 是 cosyvoice 系的音色。--help 里那句 "System voices vary by model" 说的就是这件事。
修法两个。要么每次显式带上 --model:
bl speech synthesize --model cosyvoice-v3-flash --text "测试" --voice longcheng_v3 --dry-run
要么改一次配置,之后就不用管了:
bl config set --key default_speech_model --value cosyvoice-v3-flash
顺便说下 --dry-run,它是全局 flag,打印实际请求体但不发起调用。我后面改参数都先干跑一遍,model 那行是不是你以为的那个,一眼就看出来,不用等报错。
正式那条命令补上 --model 就是:
bl speech synthesize --model cosyvoice-v3-flash --text-file script.txt --voice longcheng_v3 --rate 0.9 --instruction "用讲给朋友听的语气,别像念稿" --out narration.mp3
--rate 范围 0.5 到 2.0,默认 1.0。口播稿信息密度高,默认语速容易赶,我习惯压到 0.9 给句尾留点余地。--instruction 收自然语言,不用去记风格参数名,不过同一段描述换个音色表现就不太一样,只能靠听。
挑音色不想满地留文件的话用流式:
bl speech synthesize --model cosyvoice-v3-flash --text "Hello, this is a streaming test." --voice loongabby_v3 --language en --stream | afplay -
--stream 出来的是裸 PCM,必须管道接播放器,直接重定向成 mp3 是打不开的。Linux 上换 ffplay -nodisp -autoexit -f s16le -ar 24000 -ac 1 -,--help 的示例里有完整写法。
第二个报错:说话人分离被顶回来了
视频里想放一段选题会的录音转写,双人对话,我希望结果自带"谁说的"。第一次这么写:
bl speech recognize --url meeting.wav --diarization --speaker-count 2 --out result.json
连请求都没发出去,零点三秒就被顶回来了:
Error: Model "qwen-audio-3.0-asr-flash" uses sync Flash ASR and does not support: --diarization, --speaker-count.
Hint: Use an async filetrans model (e.g. fun-asr, qwen3-asr-flash-filetrans) for those flags.
Exit code: 2
说话人分离必须走异步 filetrans 模型,同步的 Flash ASR 不支持。这次是 CLI 的本地前置校验拦的,好处是没白跑一趟请求,Hint 里直接把该用哪个模型写出来了:
bl speech recognize --model fun-asr --url meeting.wav --diarization --speaker-count 2 --out result.json
--url 这名字有点误导,它同时收音频 URL 和本地路径,可以重复传,最多 100 个。--speaker-count 依赖 --diarization,单写不生效。有专有名词的场合挂热词表 --vocabulary-id,产品名人名会稳一些。长音频加 --async 拿任务 ID 走人,--poll-interval 默认 2 秒。
计价每秒 0.00022 元,一小时七毛九。要注意的是它是任务制的,实时字幕这类需求不对口。
让我改了看法的是第三条命令
前面两条还是"文本进音频出"和"音频进文本出"。bl omni 是另一种东西,图片、视频、音频都能当输入,输出可以同时是文字和语音。
我拿它做的事是短视频封面自查。封面在手机上只有指甲盖那么大,你想知道第一眼跳出来的是什么:
bl omni --message "user:这张封面图上最抢眼的是什么?一句话回答,别夸我。" --image cover.jpg --voice Tina --audio-out reply.wav
默认模型 qwen3.5-omni-plus。它的音色是另一套,和 cosyvoice 那 64 个不通用:
bl omni --list-voices
Total: 13 voices。默认 Tina(甜妹),还有北京胡同少年 Dylan、粤语阿清 Kiki、南京老李 Li、四川晴儿 Sunny、陕西秦川 Marcus、成都大哥 Eric、天津捧哏 Peter。想让模型直接用方言回答,把要求写进 message 再配对应音色,--help 自带这个例子:
bl omni --message "Answer in Sichuan dialect: How's the weather today?" --voice Sunny
--text-only 这个开关值得单独说,看计费就明白了:文本和图片视频输入每百万 token 七块,音频输入五十三块,纯文本输出四十块,文本加音频的输出两百一十三块(输出里那部分文本不再单独计费)。语音输出比文本输出贵五倍多。所以我调 prompt 的时候一律带 --text-only,逻辑对了再摘掉它去要音频。
模型自述能吃超过十小时的音频、400 秒的 720P 音视频,支持 60 多种语言音频输入、30 多种语言语音输出。那是能力上限,纯转写还是 fun-asr 划算。
把三步写成一个 YAML

那期视频的流程是三步:给个选题,出口播稿,再配音。要每天更一条,这三步就得串起来。
version: workflow/v1
steps:
- id: script
type: text/chat
input:
message: "{{topic}}"
system: "你是短视频口播文案作者。只输出口播稿正文,120字以内,不要标题,不要解释。"
- id: voiceover
type: speech/synthesize
input:
text: "{{steps.script.output}}"
model: cosyvoice-v3-flash
voice: longcheng_v3
rate: 0.95
out: voiceover.mp3
{{topic}} 是运行时传进去的,{{steps.script.output}} 是上一步的产出。步骤类型有 11 种,语音相关的是 speech/synthesize 和 speech/recognize。
校验的时候我犯了个小错,一开始写的是 bl pipeline validate voice-workflow.yaml,被顶回来一句 Unexpected argument。validate 和 run 一样,文件必须走 --file:
bl pipeline validate --file voice-workflow.yaml
通过会回一句 Pipeline definition is valid.。再看执行计划,这一步不花额度:
bl pipeline run --file voice-workflow.yaml --input '{"topic":"本地语音克隆工具的中文短板"}' --dry-run
Pipeline planned [~]
[~] script (text/chat) — planned
[~] voiceover (speech/synthesize) — planned

确认步骤和依赖没问题,去掉 --dry-run 真跑。想并行加 --concurrency,想把每步的生命周期事件接到日志里加 --events jsonl。
有个设计细节我觉得挺讲究:script/js 这一步的 code 必须是字面字符串,校验器会明确拒绝从上游步骤取代码,理由是"会把不可信文本当宿主机代码执行"。要在上一步产出上做判断,用 logic/assert(字段是 condition,支持 {{steps.x.output}} 插值)或者 logic/switch。
算笔账
价格是 bl model list --model <模型> --output json 直接打出来的,不是抄的第三方汇总:
| 模型 | 单价 |
|---|---|
| cosyvoice-v3.5-flash | ¥0.8/万字符(不吃系统音色,要传 clone 或 design 音色 ID) |
| cosyvoice-v3-flash | ¥1/万字符(64 个系统音色在这个模型上) |
| cosyvoice-v3.5-plus | ¥1.5/万字符 |
| cosyvoice-v3-plus | ¥2/万字符 |
| cosyvoice-clone-v1 | ¥2/万字符(声音复刻) |
| fun-asr | ¥0.00022/秒,约 ¥0.792/小时 |
| qwen3.5-omni-plus | 输入 ¥7(文本/图/视频)、¥53(音频);输出 ¥40(文本)、¥213(文本+音频)每百万 token,Batch File 约五折 |
十二万字的中文配音,按量付费是十二块钱。六十来字的稿子试十个版本不到一毛钱,这是我愿意把配音收回自己手里做的全部理由。
海外订阅制那边,ElevenLabs 官方 pricing 页现在是 Free $0、Starter $6、Creator $22(首月 $11)、Pro $99、Scale $299、Business $990,按 credit 计,TTS 大约 1 credit 一个字符,Speech to Text 是 330 credits 一分钟。Creator 那档一个月 121,000 credits,也就是十二万字符左右,跟上面那个十二块钱是同一个量级的活。转写一小时 19,800 credits,这边七毛九。汇率按 1 美元 ≈ 7.1 人民币估的。
订阅制的问题不在贵,在于它和用量脱钩。一个月配三条视频的人和每天更的人付一样的钱。
VoiceStudio 那边的钱确实花不出去,代价换了个形态待在那儿:从 16 个引擎里挑一个中文能听的,把 CC-BY-NC 的许可读一遍,active beta 期间遇到的问题自己扛。
最后
三条路不互斥,我这期视频最后就是混着用的。
要克隆某个特定人的声音、素材又不能出本机,走 VoiceStudio 这类本地方案,它带 MCP Server,能被 Agent 直接调用,这个形态云端给不了。
要中文成品配音、要方言和童声、要转写带说话人分离、要按量付费不背订阅,走 bl。
要模型看着素材直接开口说话,bl omni 这条我暂时没找到同形态的替代。
想上手的话先跑那条 --list-voices,不花钱,64 行看完对中文音色市场有什么就有数了。挑音色没有捷径,ID 摆在那儿,中文名和描述能帮你缩到三五个,剩下的拿自己那段稿子挨个试。模型和价格随时可以查:
bl model list --capability TTS
bl model list --capability ASR
bl model list --model cosyvoice-v3-flash
控制台侧的模型大全和体验入口在百炼首页。

浙公网安备 33010602011771号