如果要做一個可以自然對話的 AI Companion,第一件事不是讓它回答,而是先讓它「聽懂」。
今天要完成的流程是:
使用者說話
↓
麥克風收到聲音
↓
VAD 判斷人聲開始 / 結束
↓
Whisper Speech-to-Text
↓
轉成中文文字
這次我使用 Apple M5 Pro,搭配 MLX Whisper,在本機完成第一版語音辨識。
STT(Speech-to-Text)就是把語音轉成文字。
完整流程會是:
使用者說話
↓
STT
↓
LLM
↓
TTS
↓
數位人
在高齡陪伴情境中,直接說話比打字更自然,所以 STT 會是整個語音互動的入口。
這次主要考慮中文辨識、本機執行,以及後續即時互動的速度,因此使用:
mlx-community/whisper-large-v3-turbo
最基本的辨識方式:
import mlx_whisper
MODEL = "mlx-community/whisper-large-v3-turbo"
result = mlx_whisper.transcribe(
"data/audio/test.wav",
path_or_hf_repo=MODEL,
language="zh",
condition_on_previous_text=False,
)
print(result["text"].strip())
接著使用 sounddevice 取得麥克風輸入:
stream = sd.InputStream(
device=device,
samplerate=sample_rate,
channels=1,
dtype="float32",
callback=audio_callback,
)
這樣就完成:
麥克風 → Whisper → Text
第一版必須錄完才能開始辨識,所以接著加入 Rolling Transcription,每隔約 2 秒更新一次文字:
Partial:
哈囉,你好……
Partial:
哈囉,你好!今天我要開始測試我的 AI Companion。
接著再加入 Silero VAD(Voice Activity Detection):
pip install silero-vad
VAD 負責判斷使用者什麼時候開始、停止說話;Whisper 則負責辨識說了什麼。
不過 Rolling 每 2 秒才更新一次,如果使用者剛好在兩次更新之間說完,最後一小段可能還沒被辨識。
例如:
我今天不太舒服。
漏掉句尾就可能改變整句意思。
所以最後把兩者用途分開:
Rolling STT → 顯示即時字幕
VAD 判斷說完 → 完整辨識一次 → Final Text
未來真正送給 LLM 的會是 Final Text,而不是最後一次 Partial。
測試環境:
本次測試約為:
| 項目 | 結果 |
|---|---|
| 模型預熱 | 約 0.97 秒 |
| 第一個 Partial | 約 2.3 秒 |
| 4.5 秒音訊辨識 | 約 1.31 秒 |
| RTF | 約 0.29 |
RTF 計算方式:
RTF = 處理時間 / 音訊長度
= 1.31 / 4.5
≈ 0.29
RTF 小於 1,代表目前模型處理速度比音訊本身快。
現在已經完成:
直接說話
→ Rolling STT
→ VAD 判斷說完
→ Final Text
下一步,就要讓 AI 開口說話。