独立于任何现有业务项目的本地语音编排 CLI,面向 Apple Silicon + MLX:
- ASR:
Qwen3-ASR-1.7B(4bit/8bit) - TTS:
Qwen3-TTS(clone / custom / design,含1.7B/0.6B变体) - 下载策略:默认国内镜像
hf-mirror,失败自动回退官方 Hugging Face - 缓存策略:优先复用本地 Hugging Face 缓存,避免重复下载
- Dictation:原生 macOS 按住说话输入,前端参考
picc,后端复用本地 Qwen ASR 流式会话 - 设计文档:
docs/runtime-concurrency-redesign.md(并发治理、模型加载与后续队列化演进)
展开查看 Skills 安装方式
如果你希望通过自然语言使用 vox 全能力(安装、模型管理、ASR/TTS、pipeline),推荐安装本仓库内置 skill:
npx skills add catoncat/vox-cli --skill vox -g -y说明:
- skill 路径在仓库内:
.agents/skills/vox - 首次执行会自动做平台守卫(仅
macOS arm64)并默认安装依赖:uv、ffmpeg、portaudio - CLI 安装优先
uv(可通过环境变量覆盖安装源)
- 完全独立:不依赖某个 Web/App 服务。
- 可复现:所有动作都通过 CLI 命令完成。
- 可观测:下载、转写、克隆均记录任务状态。
- 中国网络友好:镜像优先 + 官方回退。
- 系统:
macOS(Apple Silicon,arm64) - Python:
>=3.10,<3.13 - 包管理:
uv - 推理后端:
mlx+mlx-audio
说明:当前版本只支持 Apple Silicon + MLX。
cd /path/to/vox-cli
uv sync如果你要用麦克风流式转写(asr stream --input mic),再安装可选依赖:
uv sync --extra mic如果你要用 vox dictation,还需要本机有 Rust 工具链(cargo),因为首次运行会编译原生前端 helper。
uv run vox doctorJSON 输出:
uv run vox doctor --json如果你在本地开发 vox-cli,推荐用内置更新命令把当前仓库安装到全局:
cd /path/to/vox-cli
uv run vox self update --repo .只想先看会执行什么命令:
uv run vox self update --repo . --dry-run如果全局 vox 还是旧版,无法识别 self update,用手动兜底:
cd /path/to/vox-cli
uv build
uv tool install --force --prerelease=allow dist/vox_cli-0.1.0-py3-none-any.whl推荐用
wheel安装全局命令,不建议长期依赖uv tool install .。
展开查看内置模型清单
内置模型:
- TTS:
qwen-tts-1.7b->mlx-community/Qwen3-TTS-12Hz-1.7B-Base-bf16qwen-tts-1.7b-base-8bit->mlx-community/Qwen3-TTS-12Hz-1.7B-Base-8bitqwen-tts-1.7b-customvoice-8bit->mlx-community/Qwen3-TTS-12Hz-1.7B-CustomVoice-8bitqwen-tts-1.7b-voicedesign-8bit->mlx-community/Qwen3-TTS-12Hz-1.7B-VoiceDesign-8bitqwen-tts-0.6b-base-8bit->mlx-community/Qwen3-TTS-12Hz-0.6B-Base-8bit(当前默认)qwen-tts-0.6b-customvoice-8bit->mlx-community/Qwen3-TTS-12Hz-0.6B-CustomVoice-8bitqwen-tts-0.6b-voicedesign-8bit->mlx-community/Qwen3-TTS-12Hz-0.6B-VoiceDesign-8bit
- ASR:
qwen-asr-1.7b-8bit->mlx-community/Qwen3-ASR-1.7B-8bitqwen-asr-1.7b-4bit->mlx-community/Qwen3-ASR-1.7B-4bitqwen-asr-0.6b-8bit->mlx-community/Qwen3-ASR-0.6B-8bitqwen-asr-0.6b-4bit->mlx-community/Qwen3-ASR-0.6B-4bit
当 asr 命令使用 --model auto 或配置中 asr.default_model = "auto" 时:
- 内存
>= 32GB:默认8bit - 内存
< 32GB:默认4bit
阈值可配置(见后文配置章节)。
dictation 的 --model auto 单独偏向低延迟,默认会用 qwen-asr-0.6b-4bit。如果你想让 dictation 和普通 asr 完全统一,显式设置 asr.default_model 即可。
- 运行态默认先做快速校验(
refs/main、snapshot、权重文件) model verify才会做深度校验(含.incomplete扫描)- 推理链路会复用本地 snapshot path,但每次命令仍会在当前进程重新加载模型
- 下载端点默认顺序:
https://hf-mirror.comhttps://huggingface.co
model pull、asr、tts、pipeline等重命令默认会等待资源锁qwen-tts-0.6b-base-8bit默认允许最多 2 条并行推理;其余 TTS 模型仍保持单并行clone/custom/design/pipeline在未显式传--model时,都会按当前配置选择各自默认模型- 等待日志输出到 stderr,不会污染
--json的 stdout - 可用
--no-wait改成立即失败,或用--wait-timeout调整等待上限 - 当前
task仍是任务记录/审计,不是后台 worker 队列 - 详细设计见
docs/runtime-concurrency-redesign.md
默认配置文件:~/.vox/config.toml
参考模板:config.example.toml
[runtime]
home_dir = "~/.vox"
wait_for_lock = true
lock_wait_timeout_sec = 1800
tts_small_base_max_parallel = 2
[hf]
endpoints = ["https://hf-mirror.com", "https://huggingface.co"]
# cache_dir = "~/.cache/huggingface/hub"
[asr]
default_model = "auto" # auto | qwen-asr-1.7b-8bit | qwen-asr-1.7b-4bit | qwen-asr-0.6b-8bit | qwen-asr-0.6b-4bit
memory_threshold_gb = 32
[tts]
default_model = "qwen-tts-0.6b-base-8bit"
default_custom_model = "qwen-tts-0.6b-customvoice-8bit"
default_design_model = "qwen-tts-0.6b-voicedesign-8bit"展开查看环境变量与优先级
从高到低:
- 环境变量
~/.vox/config.toml- 内置默认值
关键环境变量:
VOX_HOME:修改运行目录(默认~/.vox)VOX_HF_ENDPOINTS:覆盖下载端点列表,逗号分隔HF_ENDPOINT:强制优先某个单端点(会放到端点列表首位)HF_HUB_CACHE:覆盖 Hugging Face 缓存目录VOX_ASR_DEFAULT_MODEL:覆盖 ASR 默认模型VOX_ASR_MEMORY_THRESHOLD_GB:覆盖自动选型阈值VOX_TTS_DEFAULT_MODEL:覆盖clone/pipeline默认 TTS 模型VOX_TTS_DEFAULT_CUSTOM_MODEL:覆盖custom默认 TTS 模型VOX_TTS_DEFAULT_DESIGN_MODEL:覆盖design默认 TTS 模型
锁等待行为默认走配置文件。
查看当前生效配置:
uv run vox config show
uv run vox config show --prettyvox version
vox doctor
vox model ...
vox profile ...
vox asr ...
vox tts ...
vox pipeline ...
vox task ...
vox config ...
vox dictation ...
vox self update ...uv run vox model listuv run vox model status
uv run vox model status --jsonuv run vox model verify --model qwen-tts-0.6b-base-8bituv run vox model pull --model qwen-asr-1.7b-4bituv run vox model path --model qwen-tts-0.6b-base-8bituv run vox profile create --name narrator --lang zhuv run vox profile list
uv run vox profile list --jsonuv run vox profile add-sample \
--profile narrator \
--audio ./ref.wav \
--text "这是参考文本"样本硬约束:
- 时长
2~30秒 - RMS >=
0.005 - 会统一转存为 WAV 到
~/.vox/profiles/<profile_id>/
uv run vox asr transcribe \
--audio ./speech.wav \
--lang zh \
--model auto--model 可选:
autoqwen-asr-1.7b-8bitqwen-asr-1.7b-4bitqwen-asr-0.6b-8bitqwen-asr-0.6b-4bit
重命令同样支持:--wait/--no-wait、--wait-timeout <sec>。
uv run vox asr stream \
--input file \
--source ./speech.wav \
--lang zh \
--model auto \
--format ndjson--format 可选:
text:纯文本连续输出ndjson:每行一个 JSON chunk,便于程序消费
uv run vox asr stream \
--input mic \
--source mic \
--mic-seconds 12 \
--lang zh需要
uv sync --extra mic。
uv run vox asr session-server --lang zh --model auto --port 8765 --wait协议说明:
- 二进制帧:
PCM16LE单声道音频块 - 控制消息:
partial、flush、reset、close、ping - 结果消息:
text+is_partial,用于原生 dictation 前端消费
uv run vox dictation --lang zh --model auto
uv run vox dictation start --lang zh --model auto特点:
- 原生 macOS 前端,参考
picc的 push-to-talk 体验 - 默认监听右侧
Command:按下开始录音,松开提交当前语音段 - 最终识别结果支持规则后处理与可选 LLM 润色
- 默认只输出关键状态与错误;加
--verbose会切到实时诊断视图,终端更易读,日志文件更完整 - 首次运行会自动用
cargo build --release编译native/vox-dictation - 启动时会打印 helper 版本指纹,例如:
v0.1.0 (<git-hash>, build <timestamp>) - 长时间空闲时 helper 会做周期 keep-warm;再次按键时也会立刻补一次 warmup,尽量把冷启动成本移出松键后的等待
dictation --model auto默认偏向低延迟,优先使用qwen-asr-0.6b-4bit
常用参数:
--host/--port:自定义本地会话服务地址--rebuild-native:强制重编原生 helper--partial-interval-ms:录音期间周期性请求 partial 结果--llm-timeout-sec:本次运行临时覆盖 dictation LLM 超时--verbose:显示实时诊断视图;详细原始日志写入dictation-session.log,紧凑结构化事件写入dictation-session.agent.jsonl
推荐用法:
# 开发态验证当前仓库代码
cd /path/to/vox-cli
uv run vox dictation --lang zh --rebuild-native --verbose
uv run vox dictation start --lang zh --rebuild-native --verbose
# 更新全局命令后日常使用
vox dictation --lang zh
vox dictation start --lang zh
vox dictation digest --json补充说明:
- 语音段过短或过安静时会直接丢弃,不触发识别
- 前置静音和长停顿会尽量在前端门控,减少无意义音频送入后端
- 规则后处理和 LLM 润色只作用于最终结果,不改 partial
- LLM 润色默认会请求流式响应;当前先用于更早拿到首 token 和更细的阶段日志,最终输入仍然在整段改写完成后一次性提交
--verbose现在优先服务于“看懂这次发生了什么”:是否真流式、首字多久出来、慢在什么阶段,而不是把所有原始细节都刷在终端- 更啰嗦、可取证的细节继续留在
~/.vox/logs/dictation-session.log,适合事后排查 - 给 Agent 分析的低 token 结构化事件写入
~/.vox/logs/dictation-session.agent.jsonl vox dictation digest --json会直接聚合最近窗口的 metrics、瓶颈分布、趋势、最慢样本和自动 diagnosis,适合先给 Agent 看- 启动时会打印 helper 版本指纹,方便确认不是旧二进制
- 会话服务日志写入
~/.vox/logs/dictation-session.log;启动失败时会自动附在报错里
后处理配置示例:
[dictation.transforms]
fullwidth_to_halfwidth = true
space_around_punct = true
space_between_cjk = true
strip_trailing_punctuation = false
[dictation]
llm_active_profile = "local-mlx"
[dictation.llm_profiles."local-mlx"]
enabled = true
provider = "local-mlx"
base_url = "http://127.0.0.1:18080/v1"
model = "mlx-community/Qwen2.5-1.5B-Instruct-4bit"
api_key_env = ""
timeout_sec = 4.0
stream = true
temperature = 0.0
max_tokens = 96
prompt_preset = "spoken_clean"
[dictation.llm_profiles."aliyun"]
enabled = true
provider = "dashscope"
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
model = "qwen-turbo-latest"
api_key_env = "OPENAI_API_KEY"
timeout_sec = 4.0
stream = true
temperature = 0.0
max_tokens = 96
prompt_preset = "deep_clean"
[dictation.context]
# 焦点上下文会在开始录音时异步采集,优先用于 LLM 润色
enabled = true
max_chars = 1200
# 从按下录音开始算的总预算;超过后不会继续阻塞松键后的最终输出
capture_budget_ms = 1200
[dictation.hotwords]
enabled = true
rewrite_aliases = true
case_sensitive = false
[[dictation.hotwords.entries]]
value = "潮汕"
aliases = ["潮上"]
[[dictation.hotwords.entries]]
value = "Codex CLI"
aliases = ["ColdX CLI", "CodeX CLI", "ColdX"]
[dictation.hints]
enabled = true
items = [
"说话人前后鼻音不分,优先纠正 an/ang、en/eng、in/ing 等常见混淆。",
]说明:
dictation的 LLM 现在支持多配置集,dictation.llm_active_profile决定当前激活哪一套provider只是标识名,真正决定接入的是base_url + model- 只要兼容 OpenAI Chat Completions,都可以自定义:
mlx_lm.server、OpenAI、OpenRouter、DashScope、Moonshot、SiliconFlow,或自建LiteLLM / vLLM / OneAPI api_key和api_key_env二选一;前者直接写配置,后者从环境变量读取stream = true时会按 OpenAI-compatible SSE 流式读取改写结果;如果你的网关只支持普通 JSON 返回,可以关掉- LLM 失败时会自动回退到规则后处理结果,不会中断 dictation
dictation.context当前优先支持Ghostty和 Chromium 系浏览器;会在按下开始录音时先抓一次焦点上下文,再把结果注入 promptdictation.context.capture_budget_ms用来限制上下文采集总预算;录音期间会尽量做完,松键后只会在剩余预算内再等一下,避免上下文拖慢最终出字dictation.hotwords适合维护“标准写法 <- 常见误识别”的词表,可选做精确别名改写,也会作为 prompt 提示注入 LLMdictation.hints适合放“前后鼻音不分”这类说话人层面的纠错提示;这类内容不建议写死在大段系统提示词里
本地 MLX 流式接入示例:
uv run mlx_lm.server \
--model mlx-community/Qwen2.5-1.5B-Instruct-4bit \
--host 127.0.0.1 \
--port 18080[dictation]
llm_active_profile = "local-mlx"
[dictation.llm_profiles."local-mlx"]
enabled = true
provider = "local-mlx"
base_url = "http://127.0.0.1:18080/v1"
model = "mlx-community/Qwen2.5-1.5B-Instruct-4bit"
api_key_env = ""
stream = true
temperature = 0.0
max_tokens = 96
timeout_sec = 4.0
prompt_preset = "spoken_clean"本地 MLX 说明:
- 现有
vox dictation直接兼容mlx_lm.server的 OpenAI-compatiblechat/completions,包括stream = true的 SSE 流式返回 - 当前在 Apple Silicon 上更推荐
Qwen2.5-1.5B-Instruct-4bit:流式稳定、资源更轻,也没有Qwen3 thinking那类额外输出问题 spoken_clean更适合中文口述清理;重点会删语气词、重复词和改口,通常比default/deep_clean更贴近 dictation 场景Qwen3这类支持 thinking 的模型仍可接,但要额外处理 server 侧模板参数,默认不建议拿来做低延迟 dictation- 对
127.0.0.1 / localhost这类 loopback 地址,或显式配置provider = "local-mlx"时,vox不再强制要求api_key_env - 如果你还想兼容别的客户端,也可以继续给本地服务显式配
api_key = "dummy"
日常启停脚本:
./scripts/start-local-llm.sh
./scripts/stop-local-llm.sh说明:
- 默认后台启动,日志写到
~/.vox/logs/mlx-local-18080.log - 想前台盯输出时,用
./scripts/start-local-llm.sh --foreground - 需要换模型或端口时,可临时覆盖环境变量:
VOX_LOCAL_LLM_MODEL=... VOX_LOCAL_LLM_PORT=...
快速查看当前焦点上下文:
uv run vox dictation context --json如果你想直接改这些配置,而不是手写 config.toml,可以启动本地 GUI:
uv run vox dictation ui说明:
- 这是一个零安装、本地浏览器打开的配置页,不额外引入桌面壳子
- 默认会自动打开浏览器;如果你只想起服务,用
uv run vox dictation ui --no-open --port 8769 - GUI 现在支持管理多套 LLM 配置集,并切换当前激活项
- 它会直接写回
~/.vox/config.toml的dictation相关 section,其他配置保持原样 - 页面右侧可以直接预览当前焦点上下文和最近的
dictation-session.log - 保存完后,建议马上用
uv run vox dictation start --lang zh --verbose跑一轮;终端会显示实时诊断视图,文件日志里会保留更完整的事件链和 summary
展开查看 dictation 排障日志关键字
如果需要展开排查,建议先用:
uv run vox dictation --lang zh --rebuild-native --verbose重点看这几类日志:
[vox-dictation] recording started.../recording stopped[vox-dictation] engine_start_ms=...[vox-dictation] backend ready[vox-dictation] backend_warmup status=... elapsed_ms=... reason=...[vox-dictation] partial: ...:仅在--partial-interval-ms > 0时可能出现[vox-dictation] final: ...[vox-dictation] timings utterance_id=... capture_ms=... flush_roundtrip_ms=... audio_ms=... warmup_ms=... infer_ms=... context_capture_ms=... context_available=... context_source=... backend_total_ms=... type_ms=...[vox-dictation] discarded short/quiet utterance[session-server] warmup completed reason=... elapsed_ms=...[session-server] transcribe utterance_id=... partial=... audio_ms=... warmup_ms=... infer_ms=... total_ms=...[session-server] dictation_context | utterance_id=... | state=ready | source="ghostty" | ...[session-server] dictation_context_selected | utterance_id=... | text="..."[session-server] dictation_context_excerpt | utterance_id=... | text="..."[session-server] dictation_context_budget | utterance_id=... | budget_ms=... | waited_ms=... | state="ready|timeout|expired"[session-server] dictation_config | llm_enabled=... | context_enabled=... | hotwords_enabled=... | ...[session-server] dictation_config_hotwords | text="..."[session-server] dictation_config_hints | text="..."[session-server] dictation_stage | utterance_id=... | stage=hotwords_done | ...[session-server] dictation_stage | utterance_id=... | stage=llm_start | ...[session-server] dictation_stage | utterance_id=... | stage=llm_stream | stream_used=true | stream_chunks=... | first_token_ms=...[session-server] dictation_postprocess | changed=... | llm_used=... | postprocess_ms=... | provider="..."[session-server] dictation_postprocess_error | llm_error="..." | llm_ms=... | provider="..." | model="..."[dictation] {"event":"utterance_summary", ...}:整次 utterance 的 waterfall 摘要,适合快速 grepdictation-session.agent.jsonl:紧凑 JSONL,短 key 稳定 schema,适合喂给 Agent 做时序和瓶颈分析
当前版本默认不再打印每块音频 / 每次转写的后端调试日志。
当前 vox dictation 的原生 macOS 前端实现,明确参考并部分移植自 andelf/picc 仓库中的 dictation 工具实现:
- 仓库:
https://github.com/andelf/picc - 作者:
Andelf <andelf@gmail.com>
感谢原作者在 objc2、AppKit、AVFAudio、全局热键与 push-to-talk dictation 这条链路上的探索与开源实现。
未显式传 --model 时,默认使用 tts.default_model。
uv run vox tts clone \
--profile narrator \
--text "你好,这是克隆后的语音" \
--out ./out.wav \
--model qwen-tts-0.6b-base-8bit可选参数:
--seed--instruct(仅当底层模型接口支持时生效)--wait/--no-wait--wait-timeout <sec>
未显式传 --model 时,默认使用 tts.default_custom_model。
uv run vox tts custom \
--text "你好,这是 Vivian 的示例语音" \
--speaker Vivian \
--language auto \
--instruct "开心,语速自然" \
--out ./custom.wav \
--model qwen-tts-0.6b-customvoice-8bit可选参数:
--seed(仅当底层模型接口支持时生效)--wait/--no-wait--wait-timeout <sec>
未显式传 --model 时,默认使用 tts.default_design_model。
uv run vox tts design \
--text "你好,这是按描述设计出来的声音" \
--instruct "低沉男声,播音腔,语气稳重" \
--language auto \
--out ./design.wav \
--model qwen-tts-0.6b-voicedesign-8bit可选参数:
--seed(仅当底层模型接口支持时生效)--wait/--no-wait--wait-timeout <sec>
未显式传 --tts-model 时,默认使用 tts.default_model。
uv run vox pipeline run \
--profile narrator \
--audio ./input.wav \
--clone-text "把这段内容读出来" \
--lang zh可选:
--asr-model auto|qwen-asr-1.7b-8bit|qwen-asr-1.7b-4bit|qwen-asr-0.6b-8bit|qwen-asr-0.6b-4bit--tts-model qwen-tts-0.6b-base-8bit--out ./result.wav--wait/--no-wait--wait-timeout <sec>
所有关键操作会写入 SQLite 任务表。当前阶段它用于任务记录与审计,不是后台执行队列。
uv run vox task list
uv run vox task list --jsonuv run vox task show --id <task_id>当进程被手动杀掉、机器重启,或早期版本遗留了很多 running 记录时,可以用:
uv run vox task cleanup --stale-running --delete-finished --older-than-hours 0 --json常见用途:
- 把已经不存在的
running任务标成stale - 批量删除已完成 / 已失败 / 已 stale 的历史任务
- 在重新压测或重新观察 dictation 前,先把任务表清到干净状态
默认 VOX_HOME=~/.vox,目录结构:
~/.vox/
├── config.toml
├── vox.db
├── cache/
│ └── voice_prompt/
├── logs/
│ ├── dictation-session.log
│ ├── dictation-session.log.1
│ ├── dictation-session.log.2
│ ├── dictation-session.log.3
│ ├── dictation-session.agent.jsonl
│ ├── dictation-session.agent.jsonl.1
│ ├── dictation-session.agent.jsonl.2
│ ├── dictation-session.agent.jsonl.3
│ ├── dictation.err.log
│ └── dictation.log
├── locks/
├── profiles/
│ └── <profile_id>/
└── outputs/
Hugging Face 模型缓存默认不在 ~/.vox,而在:
~/.cache/huggingface/hub
dictation 日志默认会做大小轮转:
dictation-session.log默认单文件上限5MBdictation-session.agent.jsonl共用同一套轮转上限- 默认保留
3份备份 - 可在
runtime.dictation_log_max_bytes/runtime.dictation_log_backups调整
可通过 HF_HUB_CACHE 覆盖。
cd /path/to/vox-cli
uv sync
uv run vox doctor --json
uv run vox model status --json如果 ASR 未下载:
uv run vox model pull --model qwen-asr-1.7b-4bituv run vox model verify --model qwen-tts-0.6b-base-8bit
uv run vox model status --json当 verified=true 时,说明本地 snapshot 可直接复用;但每次推理仍会在当前进程重新加载模型。
uv run vox profile create --name narrator --lang zh
uv run vox profile add-sample --profile narrator --audio ./ref.wav --text "这是参考文本"
uv run vox tts clone --profile narrator --text "这是目标文本" --out ./out.wavcd /path/to/vox-cli
uv run vox self update --repo .展开查看故障排查
优先看:
uv run vox doctor --json常见问题:
- 不是 Apple Silicon:当前版本不支持。
mlx/mlx-audio未安装:先uv sync。- 缓存路径不可写:检查
HF_HUB_CACHE或目录权限。
如果命令长时间停在等待日志,先用 vox task list --json 看是否已有重任务占用资源。
- 检查端点顺序:
uv run vox config show --pretty - 临时强制镜像:
HF_ENDPOINT=https://hf-mirror.com uv run vox model pull --model qwen-asr-1.7b-4bit- 临时强制官方:
HF_ENDPOINT=https://huggingface.co uv run vox model pull --model qwen-asr-1.7b-4bit- 确认安装了
micextra:uv sync --extra mic - macOS 麦克风权限授权终端
- 先用文件流式排除模型问题:
uv run vox asr stream --input file --source ./speech.wav
cargo不存在:先安装 Rust toolchainnative/vox-dictation/Cargo.toml缺失:确认仓库完整- 端口占用:换一个
--port - 没有输入回填:检查 macOS 的 Accessibility 权限
- 如果全局
vox看起来还是旧版,先执行:uv run vox self update --repo .
默认重点观察当前终端里的这些信号:
[vox-dictation] discarded short/quiet utterance[vox-dictation] backend not ready yet[vox-dictation] backend error: ...[vox-dictation] backend connect/read/write error: ...
如果需要更细的终端诊断视图,重试:
uv run vox dictation --lang zh --rebuild-native --verbose此时再看:
[vox-dictation] recording started.../recording stopped[vox-dictation] backend ready[vox-dictation] partial: ...[vox-dictation] final: ...
如果看起来是会话服务启动问题,查看 ~/.vox/logs/dictation-session.log。
- 时长超限(<2s 或 >30s)
- 音量过低(RMS < 0.005)
- 音频文件路径错误