Skip to content

Troubleshooting.zh CN

Saco Song edited this page Jul 30, 2026 · 8 revisions

故障排查

English · 首页

建议从最小故障层开始检查:service → control/state → ASR → refinement → output → HUD。

Service 与 control socket

systemctl --user status voice-input.service voice-input-hud.service
systemctl --user cat voice-input.service voice-input-hud.service
journalctl --user -u voice-input.service -u voice-input-hud.service -b
voice-input status --format json --extended

Daemon 应创建:

$XDG_RUNTIME_DIR/voice-input/control.sock
$XDG_RUNTIME_DIR/voice-input/state.json
$XDG_RUNTIME_DIR/voice-input/waveform.sock

检查文件 metadata,同时避免打印 state 内容:

stat "$XDG_RUNTIME_DIR/voice-input/control.sock" \
     "$XDG_RUNTIME_DIR/voice-input/state.json" \
     "$XDG_RUNTIME_DIR/voice-input/waveform.sock"

常见原因:

  • failed to connect to daemon:daemon 已停止、在读取 config/credential 时 crash,或者 CLI 与 service 使用不同的 XDG_RUNTIME_DIR
  • Config parse error:运行 voice-input config,并根据 配置参考 检查 enum 拼写。
  • 更新后二进制文件与 asset 不匹配:重新执行 make enable-service,然后检查生成的 unit path。如果 lockfile 增加了 dependency,Cargo 会按需下载。
  • Service 无法访问 Hyprland:确保 systemd 用户管理器环境包含图形会话变量,再重启 daemon。

手动启动的 daemon 与 systemd daemon 不能竞争同一个 socket。启动其中一个之前应停止另一个。

Settings 无法打开、保存或重启 daemon

Settings 需要 Quickshell 0.3 或更高版本。先执行不涉及 secret 的可执行文件与 asset 检查:

/usr/bin/qs --version
test -x "$(command -v voice-input)"
test -d ~/.local/share/voice-input/quickshell-settings
pgrep -af 'qs.*voice-input/quickshell-settings'

再次运行 voice-input settings 时,程序应通过 Quickshell IPC 激活已有的普通窗口,而不是创建重复实例。如果没有现有实例,launcher 会使用 /usr/bin/qs --daemonize --no-duplicate --path …/quickshell-settings。如果需要在 terminal 中查看 QML 启动诊断,请先关闭 Settings 窗口,再以前台方式运行同一套配置:

VOICE_INPUT_BIN="$(readlink -f "$(command -v voice-input)")" \
  /usr/bin/qs --no-duplicate --path \
  "$HOME/.local/share/voice-input/quickshell-settings"

QML 会启动一个专用的隐藏 backend。只有在故障排查或开发时,才应直接检查其 protocol envelope。以下命令需要 jq,并且不会显示配置或 credential 数据:

printf '%s\n' \
  '{"version":1,"id":1,"method":"settings.get","params":{}}' \
  | voice-input settings-backend --stdio \
  | jq '{version, id, ok, error: (.error.code // null)}'

预期输出包含 version: 1、相同的 idok: true。不要在 shell 中发送 credential replacement request;用户输入的 secret 应从 password field 经继承的 stdin 传递,避免进入 shell history。

保存失败时,可以在不打印配置的情况下验证配置,并检查权限:

voice-input config >/dev/null
stat -c '%a %n' "$HOME/.config/voice-input" \
  "$HOME/.config/voice-input/config.toml"
systemctl --user status voice-input.service

目录和文件的预期权限分别为 700600。Revision conflict 表示 Settings 加载后,原始配置的精确内容又发生了变化;请重新加载,再重新应用需要的修改。如果 Settings 表示持久化成功但重启失败,新文件仍然已经保存。此时请分别使用 systemctljournalctl 排查 voice-input.service

HUD 不显示或停止更新

标准部署使用独立的 Quickshell service。只重启 voice-input.service 不会重启 HUD:

systemctl --user restart voice-input-hud.service
journalctl --user -u voice-input-hud.service -f

检查 /usr/bin/qs 和安装的 QML:

test -x /usr/bin/qs
ls -l ~/.local/share/voice-input/quickshell/

随后分别检查 HUD 的两个输入:

  1. state.json 提供 phase/transcript。运行 voice-input status --follow --format json --extended 进行测试。
  2. waveform.sock 提供包含 30 根 bar 的 NDJSON frame。QML client 会在断开 400 ms 后重试。

如果 status 会变化但 HUD 不显示,请检查 Quickshell/QML、focused-monitor detection 和 voice-input-hud.service。如果 HUD phase 会变化但波形保持不动,请检查 waveform.sock、麦克风 PCM 和 Qwen 语音活动。Realtime Server VAD 没有标记 voice active 时,波形会有意保持隐藏。

常驻 Quickshell HUD 会从 snapshot 读取全部几何控制值。只查看这些不包含 transcript 的字段:

jq '{hud_enabled, hud_margin_bottom, hud_height,
     hud_position, hud_offset_x, hud_offset_y}' \
  "$XDG_RUNTIME_DIR/voice-input/state.json"

hud_enabled 为 false 时,即使 voice-input-hud.service 继续常驻,surface 也会保持隐藏。Margin、height、position 和 offset 不再固定在 QML 中。

只应在 daemon 停止时删除 stale runtime socket。正常启动会自行删除并重新绑定 control 和 waveform socket:

systemctl --user stop voice-input.service
rm -f "$XDG_RUNTIME_DIR/voice-input/control.sock" \
      "$XDG_RUNTIME_DIR/voice-input/waveform.sock"
systemctl --user start voice-input.service

Dictation 静默取消

如果停止录音时,实时 session 在 350 ms 宽限期内仍未收到服务端语音事件或非空 transcript,程序会有意取消该 session。日志为:

voice-input realtime ASR: no speech detected; cancelling empty dictation

此时不会执行 final pass、LLM 请求或文本输出。

检查录音:

pw-record --raw --rate 16000 --channels 1 --format s16 /tmp/voice-input-test.raw
# 短暂说话,以 Ctrl+C 停止;确认文件非空,然后删除。
rm -f /tmp/voice-input-test.raw

同时检查 audio.device、Qwen 连接、turn_modevad_thresholdsilence_duration_ms。请根据 provider 接受的范围逐步调整 VAD 值。

当前取消判断发生在 manual-mode stop commit 之前。因此,如果 manual session 此前没有产生 transcription event,它可能会被视为静音。排查该问题时优先使用 server-vad。本地 CLI session 不走 realtime 无语音判断。

主动运行 voice-input record cancel 或捕获到空 audio buffer 时,也会按设计直接返回 idle 且不输出文本。

录音自行停止,或 realtime transcript 开始延迟

独立采集和共享 pre-roll 采集都会在达到 audio.max_duration_secs 后自动停止;该字段默认为 300 秒。Journal 会显示:

voice-input capture: reached configured 300-second limit; finishing automatically

程序会按正常停止流程继续执行 final ASR、本地 fallback、refinement 和文本输出。增大该值之前,请考虑内存中的 PCM buffer 和 final-pass upload 也会随录音时长增加。

实时音频使用容量受限的非阻塞 queue。如果网络处理速度跟不上,音频采集和波形传输会继续,HUD 会显示 Realtime transcript delayed — audio will recover when stopped,journal 则会报告 audio queue 无法跟上。停止录音后,Voice Input 会拒绝不完整的远程 transcript,并通过 final ASR 或本地 fallback 处理完整录音。如果两条恢复路径都未启用,最终处理会报告没有可用的全音频恢复方式;需要可靠处理长录音时,请启用 final_pass_enabledfallback_to_local

Qwen realtime 或 final pass 失败

录音时查看 daemon journal:

journalctl --user -u voice-input.service -f

检查:

  • provider = "alibaba-qwen-realtime",并且存在有效的 alibaba-api-key credential;
  • endpoint region、model ID,以及网络、DNS 和 TLS;
  • connect/finalize timeout;
  • 启用 fallback_to_local = true 时是否存在 /usr/bin/voxtype
  • 启用 final_pass_enabled = true 时的 final model、base URL 和 timeout。

只有 realtime host 为已知的 dashscope.aliyuncs.comdashscope-intl.aliyuncs.comdashscope-us.aliyuncs.com 时,程序才能从空的 final-pass base URL 推导地址。自定义 host 需要明确填写。

部分 worker 报错后,程序仍可能使用已经生成的 realtime partial/final text。Final pass 失败时,会先退回 realtime final text,再根据配置调用本地 CLI。Queue backpressure 属于不同情况:它可以确定远程 service 收到的音频不完整,因此程序会拒绝其 transcript,只允许使用全音频恢复路径。

/usr/bin/voxtype fallback 失败

test -x /usr/bin/voxtype
/usr/bin/voxtype --help

Voice Input 只通过以下 CLI 形式代理 backend setup:

voice-input setup gpu [backend arguments...]
voice-input setup onnx [backend arguments...]

执行识别时,程序会把 engine/model/language flag 和 transcribe <temporary-wav> 传给配置的可执行文件。请在 Voxtype 自身配置中确认选定的 engine/model 已安装。不要设置 backend_command = "voice-input",否则会递归调用本程序。

OpenCC 报错或中文字体不符合预期

本地和远程中文最终文本都会调用:

opencc -c t2s   # simplified-chinese
opencc -c s2t   # traditional-chinese

检查命令:

command -v opencc
printf '測試\n' | opencc -c t2s

如果无法启动 opencc,识别会返回错误。如果程序可以启动,但退出码非零或输出为空,Voice Input 会保留未转换的 transcript。English、Japanese 和 Korean 不调用 OpenCC。

LLM refinement 未改变文本或发生 timeout

voice-input llm test
journalctl --user -u voice-input.service -f

确认 enabled = truemodel 非空、api_base_url 正确,并且存在 openrouter-api-key。带上下文的请求和符合条件的无上下文重试共用一个 deadline。timeout_ms 默认为 15 秒,并限制在 1–30 秒。预算至少为 10 秒时,带上下文的请求会留下最后五秒,以便执行纯 transcript 恢复。

Refinement 在所有失败情况下都会保留 ASR 文本。如果至少还剩一秒,transport error(包括 timeout)、可识别的 payload/context error、无效 response structure、truncation 或带上下文请求的预算耗尽都可以触发纯 transcript 重试。Authentication、rate limit 等不可重试 HTTP response 以及 provider 明确返回的 error 会立即保留原始 ASR 文本。

长文本未出现,或 clipboard 发生变化

文本超过 120 个字符后,即使 mode = "type" 也会自动使用 paste。XWayland 默认同样优先 paste。

检查实际路径和工具:

command -v wtype wl-copy wl-paste
command -v xclip xdotool          # 仅 XWayland
journalctl --user -u voice-input.service -b | grep 'voice-input output:'

日志会显示 target、mode 和 driver,但不会打印文本。请检查:

  • mode = "clipboard" 只执行复制,不发送粘贴按键。
  • 目标应用可能不接受 shift+Insert;可把 paste_keysxwayland_paste_keys 改为应用支持的组合,例如 ctrl+v
  • Clipboard manager 或应用可能在 220 ms 恢复等待期间改变 owner。
  • 不支持的 clipboard MIME type 或备份失败可能导致无法完整恢复。
  • 如果 modifier 尚未释放,可以增加 pre_type_delay_ms;toggle+modifier 快捷键已经强制至少等待 500 ms。
  • 可以暂时设置 prefer_paste_for_xwayland = false,用来测试 XWayland 的直接输入;部分 client 可能无法正确处理 wtype 输入。

运行 voice-input status --format json --extended,检查最近一次解析得到的 output_targetoutput_modeoutput_driver;目标对应的 JSON field 名为 output_target_resolved

Credential 文件存在但未加载

只列出 metadata,不要在共享 terminal 或 report 中解密 key:

ls -l ~/.config/credstore.encrypted/
systemctl --user cat voice-input.service
journalctl --user -u voice-input.service -b

Blob 名必须恰好为 alibaba-api-keyopenrouter-api-key。Settings 只提供保留或替换:password field 留空会保留现有 blob,输入非空值则替换 blob,并在提交后清空该字段。Test LLM 会优先使用刚输入的 LLM key;没有输入时则使用加密 credential store 中的值。修改 credential 后执行:

systemctl --user restart voice-input.service

只有 systemd 启动的 service 会收到加密 credential。手动运行的 voice-input daemon 没有 $CREDENTIALS_DIRECTORY,除非它运行在适当的 credential context 中;它可以使用文档说明的环境变量 fallback,同时需要承担相应的暴露风险。

如果 Settings 无法加密或解密,请确认系统存在 systemd-creds,并且用户级 systemd credential 机制可以工作。不要把 key 写入公开 TOML 示例或 issue log。

安装或更新后 Pi 上下文未生效

重启 Pi 或 reload extension。已安装的 TypeScript file 不会自动加载到现有 Pi process:

ls -l ~/.pi/agent/extensions/voice-input-session-registry.ts
ls -l "$XDG_RUNTIME_DIR/voice-input/agent-sessions/"

重启 Pi 后仍没有 registry 时,请检查 Pi extension loading。存在 registry 时,请确认 Pi 是 Kitty window 中当前 focused foreground process,并且 Kitty endpoint 恰好为 /tmp/kitty-<kitty-pid>。完整验证流程见 Agent 上下文

Codex 不使用该 extension;它必须在 CODEX_HOME/sessions 下恰好打开一个符合条件的 CLI session JSONL。

不暴露隐私的 timing log

使用结构化 timing line,不要直接导出 state 或 session file:

journalctl --user -u voice-input.service -b \
  | grep -E 'first partial|session finished|voice-input refinement:|voice-input output:'

这些 line 通常只包含 elapsed time、计数、outcome 和 driver,不包含 transcript/context。Provider/backend error 仍可能带有外部诊断内容。分享前请阅读并脱敏;不要附加 state.json、Pi/Codex JSONL、原始音频、credential 或未经检查的完整 journal。

另请参阅:安全与隐私 · 开发指南

Clone this wiki locally