Owner 语音桌宠

这是一个 Python 语音桌宠运行程序。当前第一版先提供无 GUI 的真实实时语音循环:

小杰小杰 本地唤醒 -> 本地应答“我在” -> 本地降噪 -> 主说话人端点采集问题 -> 本地 STT/实时字幕 -> 携带本次进程内临时历史调用云端 LLM -> 本地 TTS -> 本机扬声器播放 -> 回到待机继续监听。

当前能力

  • owner_voice_pet run-live:真实常驻语音循环。
  • owner_voice_pet run-live --once:只跑一轮,便于验收。
  • .env 直接读取配置,不要求导出 shell 环境变量。
  • 唤醒词检测使用本地 sherpa-onnx KWS 模型,不走云端 ASR。
  • OWNER_SPEECH_PROVIDER=local:默认除 LLM 外全用本地语音链路;cloud 仅作为显式兼容选项。
  • 本地语音默认:wake/VAD/STT/实时字幕/TTS/降噪全部在本机执行,LLM 继续走 .env 中的云端配置。
  • 本地设备:sounddevice 读取麦克风,扬声器或 afplay 播放。
  • 本地模型:models/ 存放 sherpa-onnx wake/VAD/STT/denoiser 模型,目录不提交 Git。
  • Pipelinerun-live 使用 stage 化 VoiceAssistantPipeline,通过事件总线输出终端状态。
  • 主说话人端点:默认 OWNER_ENDPOINT_MODE=primary_speaker,本轮音色消失后结束录音,避免背景噪声拖慢 STT。
  • 临时上下文:同一次 run-live 进程内携带最近 user/assistant 历史,退出即清空。

首次准备

python3.11 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -e '.[speech]'
cp .env.example .env

然后编辑 .env,填写本地密钥和模型配置。当前默认示例:

OWNER_LLM_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1
OWNER_LLM_API_KEY=
OWNER_LLM_MODEL=mimo-v2.5
OWNER_LLM_API_STYLE=chat_completions
OWNER_REALTIME_TRANSCRIPT_ENABLED=1
OWNER_WAKE_PROVIDER=local_kws
OWNER_WAKE_KEYWORDS_FILE=
OWNER_WAKE_KWS_THRESHOLD=0.15
OWNER_WAKE_KWS_SCORE=1.0
OWNER_WAKE_ACK_TEXT=我在
OWNER_POST_PLAYBACK_DRAIN_MS=0
OWNER_PIPELINE_MODE=live_turn_based
OWNER_ENDPOINT_MODE=primary_speaker
OWNER_NOISE_FILTER_ENABLED=1
OWNER_NOISE_FILTER_PROVIDER=sherpa_onnx_gtcrn
OWNER_WAKE_DENOISE_ENABLED=0
OWNER_SPEAKER_PROFILE_MS=600
OWNER_SPEAKER_PROFILE_MIN_MS=120
OWNER_SPEAKER_ABSENT_MS=300
OWNER_SPEAKER_SIMILARITY_THRESHOLD=0.70
OWNER_SPEAKER_MIN_RMS=0.012
OWNER_VAD_PROVIDER=hybrid
OWNER_VAD_THRESHOLD=0.5
OWNER_VAD_MIN_DURATION_MS=250
OWNER_VAD_END_SILENCE_MS=350
OWNER_VAD_NO_SPEECH_TIMEOUT_MS=5000
OWNER_VAD_MAX_RECORDING_MS=12000
OWNER_SPEECH_PROVIDER=local
OWNER_ASR_MODEL=mimo-v2.5-asr
OWNER_TTS_MODEL=mimo-v2.5-tts
OWNER_TTS_VOICE=mimo_default
OWNER_CONTEXT_MODE=session_memory

OWNER_WAKE_PROVIDER=local_kws 表示唤醒词“小杰小杰”由本地模型检测。唤醒命中后会先本地播报 OWNER_WAKE_ACK_TEXT=我在,再开始听取问题。OWNER_SPEECH_PROVIDER=local 表示正式问题 STT、实时字幕和 TTS 都走本地模型或 macOS 本地能力;云端只接收 final 文本和本次会话历史用于 LLM 回复。

OWNER_NOISE_FILTER_ENABLED=1 表示唤醒后的正式问题阶段默认启用本地 GTCRN 降噪。降噪后的同一份音频会进入 VAD、实时字幕和 final STT;wake 阶段默认保持原始音频,OWNER_WAKE_DENOISE_ENABLED=0 可以避免降噪影响“小杰小杰”的 KWS 特征。

OWNER_REALTIME_TRANSCRIPT_ENABLED=1 表示录音期间会使用本地 streaming STT 实时显示中间转写,终端会输出 实时转写:...。为了过滤噪音,实时字幕默认不会显示 家确 这类很短的瞬时误识别;最终发送给 LLM 的内容仍只以 转写结果:... 为准。设置为 0 可以临时关闭实时显示。

本地模型

首次运行前必须准备本地语音模型;同一脚本会下载 wake、VAD、2025 中文 CTC STT 和 GTCRN denoiser 模型:

python3.11 scripts/download_speech_models.py --dir models
.venv/bin/python -m owner_voice_pet model-check --models-dir models

models/ 已在 .gitignore 中,不会提交大模型文件。

默认本地 STT 使用 sherpa-onnx 官方 2025 中文 CTC int8 模型,manifest 里的关键文件是 tokens.txtmodel.int8.onnx。旧 14M transducer 模型仍可通过旧 manifest 兼容,但不再作为默认实时字幕质量基线。

本地唤醒关键词文件位于 models/wake/keywords.txt。默认 OWNER_WAKE_KWS_THRESHOLD=0.15 已偏向灵敏;如果真人唤醒仍不灵敏,可以继续降到 0.10,若误唤醒变多再回调到 0.20

默认 OWNER_ENDPOINT_MODE=primary_speaker 会在本轮问题开头建立临时音色画像;OWNER_SPEAKER_PROFILE_MIN_MS=120 表示最少 120 毫秒有效语音即可让画像参与端点判断。当这个主说话人音色连续消失 OWNER_SPEAKER_ABSENT_MS=300 毫秒后,直接结束录音进入转写。它不保存长期声纹、不做主人注册、不跨进程记忆。

默认 OWNER_POST_PLAYBACK_DRAIN_MS=0 表示“我在”播放结束后只清理已经积压的输入队列,不再额外读取并丢弃后续音频,避免切掉正式问题开头。默认 OWNER_VAD_PROVIDER=hybrid 会同时使用本地 sherpa-onnx VAD 和能量阈值兜底,帮助开始录音;结束录音优先由主说话人端点控制。如果你想临时回退旧行为,可以设置 OWNER_ENDPOINT_MODE=vad。如果唤醒后你已经停说但还长时间显示“录音中”,优先调小 OWNER_SPEAKER_ABSENT_MS,例如 250;如果句尾容易被切掉,再调高到 400

设备检查

.venv/bin/python -m owner_voice_pet device-check

输出里 ok=true 表示已检测到可用麦克风和扬声器。

运行

模拟麦克风自动验收:

.venv/bin/python -m owner_voice_pet simulate-live --turns 2

这个命令不打开真实麦克风,会把生成的模拟麦克风音频帧喂进当前 VoiceAssistantPipeline,默认跑两轮唤醒到播放闭环。输出 JSON 中 success=true 表示两轮 wake、录音、实时字幕、final STT、临时上下文、LLM、TTS、播放和恢复待机都通过。模拟帧里故意包含 家确 这类短噪声 partial 和背景说话帧,用来验证实时字幕过滤和主说话人端点。

需要复现同一组模拟输入时:

.venv/bin/python -m owner_voice_pet simulate-live --turns 2 --write-fixture /tmp/owner-simulated-mic.jsonl
.venv/bin/python -m owner_voice_pet simulate-live --turns 2 --fixture /tmp/owner-simulated-mic.jsonl

单轮验收:

.venv/bin/python -m owner_voice_pet run-live --once

常驻重复对话:

.venv/bin/python -m owner_voice_pet run-live

运行后终端状态来自 pipeline event bus,会显示待机、唤醒命中、应答中、请说出问题、录音中、检测到用户语音、实时转写、用户语音结束、转写中、转写结果、思考中、播放中、恢复待机等状态。说“小杰小杰”,听到“我在”且看到“请说出问题”后再提问;听到回复后可以再次说“小杰小杰”继续下一轮。本次进程内会携带临时历史,程序退出后不保存。背景噪声下如果实时字幕仍偶发短错字,先看最终 转写结果;最终文本才会进入 LLM。

验证

.venv/bin/python -m compileall src tests scripts
.venv/bin/python -m unittest discover -s tests
.venv/bin/python -m owner_voice_pet simulate-live --turns 2
.venv/bin/python -m owner_voice_pet acceptance
.venv/bin/python -m owner_voice_pet validate-assets
.venv/bin/python -m owner_voice_pet security-check
.venv/bin/python -m owner_voice_pet model-check --models-dir models
.venv/bin/python -m owner_voice_pet device-check
openspec validate --all --strict

LLM smoke

.venv/bin/python -m owner_voice_pet llm-smoke --no-stream --message '用一句中文回复:小杰在线。'

安全约束

  • API key 只写入本地 .env.env 不提交 Git。
  • models/.venv/ 和临时音频文件不提交 Git。
  • 默认不持久化麦克风原始音频。
  • 默认不使用云端 ASR/TTS;云端 LLM 只接收最终用户文本和本次运行内临时历史。
  • security-check 会扫描已跟踪文本文件中的 sk-...tp-... 形式密钥。
S
Description
No description provided
Readme 866 KiB
Languages
Python 100%