← Toate proiectele
Live Actualizat: Invalid Date

Asistent vocal local, bidirecțional ($0/lună)

Un asistent vocal complet local pe Windows, fără niciun cost de cloud: microfonul intră în STT (faster-whisper), textul ajunge la un LLM gratuit (NVIDIA NIM, fixat pe gpt-oss), iar răspunsul este rostit prin TTS Piper. Conversația este bidirecțională și scurtă: agentul răspunde vorbit în 1-3 propoziții, cu activare prin cuvânt-cheie ("Claude ..."), barge-in și filtre anti-halucinație. Bucla este construită ca daemon, cu pre-încălzire la pornire și model fixat, astfel încât latența să rămână mică.

Rezultatul practic: 400-600ms round-trip la cald și aproximativ 1s până la primul audio, la un cost de operare de $0/lună. Stiva evită PyAudio (care nu se compilează pe Python 3.14) și folosește sounddevice plus faster-whisper. Opțional, agentul poate comanda o sesiune Claude Code live, scriind comenzile vorbite într-un fișier-punte și rostind răspunsurile primite înapoi.

Citește studiul de caz complet →

Problema și motivația

Asistenții vocali comerciali au două costuri ascunse: trimit audio în cloud și se facturează la utilizare. Pentru un flux de lucru personal, pe Windows, pe o singură mașină cu GPU, niciunul dintre aceste compromisuri nu este necesar. Modelele de recunoaștere a vorbirii și de sinteză rulează deja bine local, iar un LLM suficient de bun este disponibil pe un lane gratuit. Scopul a fost concret: un asistent vocal bidirecțional care ascultă, înțelege, gândește și răspunde rostit, fără să trimită nimic în afară și fără cost lunar de operare.

Restricția de cost a modelat întreaga arhitectură. Pentru ca operarea să rămână la $0/lună, fiecare etapă trebuia să fie fie complet locală (STT, TTS), fie pe un lane gratuit (LLM-ul prin NVIDIA NIM). Această decizie a eliminat din start orice serviciu de cloud cu plată per cerere și a transformat problema într-una de inginerie pe resurse locale: cum obții latență mică și răspunsuri utile dintr-o stivă gratuită.

Arhitectura și cum funcționează

Sistemul este o singură buclă, scrisă în Python, care leagă trei etape. Microfonul este citit prin sounddevice, fără PyAudio. Audio-ul capturat trece prin faster-whisper pentru speech-to-text. Textul rezultat este trimis la un LLM gratuit prin NVIDIA NIM (lane-ul cc_free), iar răspunsul este redat prin Piper TTS cu vocea en_US-amy-medium. Pe scurt: microfon → STT (faster-whisper) → LLM (NVIDIA, gpt-oss) → TTS (Piper).

Interacțiunea este gândită să fie scurtă și naturală. Prompt-ul cere răspunsuri de 1-3 propoziții, ca să nu obosească ascultătorul și ca să mențină round-trip-ul mic. Asistentul este implicit activat prin cuvânt-cheie: rostești "Claude ..." ca să te audă, iar "stop" îl închide. Acest gating evită ca microfonul deschis să răspundă la orice zgomot din cameră. În modul open-mic (gating dezactivat) răspunde la tot, motiv pentru care se recomandă căștile.

Agentul rulează ca daemon, într-o fereastră de consolă proprie, ca să fie vizibile prompturile și ca microfonul și difuzorul să funcționeze corect. La pornire își pre-încălzește atât STT-ul cât și TTS-ul (aproximativ 7 secunde), apoi intră în starea de ascultare. Întregul comportament este reglabil din variabile de mediu, ceea ce a permis testarea iterativă a unui daemon prin cicluri rapide de kill, editare și relansare, fără a modifica codul de fiecare dată.

Decizii tehnice și de ce

Prima decizie importantă a fost alegerea bibliotecilor audio. PyAudio nu se compilează pe Python 3.14, pentru că nu există wheel cp314, iar stiva RealtimeSTT depinde de el. Soluția a fost înlocuirea cu sounddevice plus faster-whisper, care se instalează curat și acoperă același rol. Lecția generală, aplicabilă în orice proiect: când o dependență hard nu are wheel pe versiunea ta de Python, schimb-o cu un echivalent instalat în loc să forțezi compilarea.

A doua decizie a fost fixarea modelului LLM. Lane-ul NVIDIA brut rotește un pool de modele, iar uneori nimerește un model care răspunde în 20-40 de secunde, ceea ce este inacceptabil pentru voce. Prin fixarea explicită pe gpt-oss (variabila CC_VOICE_NVIDIA_MODEL = openai/gpt-oss-120b), latența LLM a coborât la circa 0,6s, față de 3,4s pe lane-ul implicit. Aceasta este una dintre cele mai mari economii de latență din întregul sistem, obținută printr-o singură decizie de configurare.

A treia decizie a vizat API-ul Piper. De la Piper 1.4 încolo, sinteza se face cu voice.synthesize_wav(text, wav_file); apelul vechi synthesize(text, wf) ridică eroarea "channels not specified". Alinierea la noul API a fost necesară pentru ca redarea audio să funcționeze deloc.

Detalii de inginerie: anti-fragilitate și performanță

Un asistent vocal local trebuie să fie robust la zgomot și la modurile de eșec ale modelelor. Cea mai supărătoare problemă a fost halucinația din liniște: pe segmente fără vorbire, Whisper produce fraze fantomă tipice, de genul "subscribe to my channel" sau "thank you very much". Soluția a fost un filtru pe trei niveluri: un gate pe energia microfonului (RMS), o durată minimă de vorbire și un filtru de fraze cunoscute. Pragul RMS este reglabil (CC_VOICE_RMS = 0,030, coborât spre 0,020 la microfoane slabe), iar pragurile de durată minimă (CC_VOICE_MIN_BLOCKS, CC_VOICE_MIN_S) asigură că și cuvinte scurte ca "stop" sunt totuși recunoscute fără a deschide ușa zgomotului.

Performanța a fost o luptă cu contenția pe GPU. Recall-ul de memorie (grounding pe baza unui scratchpad de proiect, Hivemind) folosește embeddings pe același GPU unic ca Whisper, deci adaugă aproximativ 2,8s de latență când este pornit. Din acest motiv recall-ul este oprit implicit și se activează doar când utilitatea contează mai mult decât viteza. Pentru STT, modelul base.en este aproape la fel de precis ca small.en pe comenzi scurte, dar de circa 2,8 ori mai rapid, deci este alegerea implicită. Sinteza Amy și ascultarea simultană creează probleme de barge-in pe difuzoare, unde sunetul propriu se întoarce în microfon; pe căști barge-in-ul funcționează curat, iar pe difuzoare se recomandă dezactivarea lui.

Pentru cost și fiabilitate la nivel de LLM, lane-ul are un comportament de cascadă. cc.auto (gpt-oss) returnează gol pe prompturi lungi, așa că lane-urile sunt înlănțuite (qwen_cloud → nvidia → auto), păstrând răspunsuri utile fără a renunța la gratuitate. Astfel costul rămâne $0/lună chiar și când un lane individual cedează.

Puntea către Claude Code

Pe lângă conversația normală, agentul poate, opțional, să comande o sesiune Claude Code live. Cu CC_VOICE_BRIDGE pornit, o comandă rostită de forma "Claude, " este scrisă într-un fișier-punte (voice_cc_command.jsonl) și anunțată în consolă cu un marcaj CC-COMMAND. O sesiune Claude Code monitorizează acel fișier, scrie răspunsul în voice_cc_reply.jsonl, iar agentul vocal îl rostește înapoi. Important pentru siguranță: comenzile de acțiune trec în continuare prin toate gardurile de tip HARD RULE, deci vocea nu este o cale de ocolire a confirmărilor. Puntea este oprită implicit, ca interacțiunea de bază să rămână pur conversațională.

Rezultat și status curent

Sistemul este un build funcțional, nu un prototip. La cald, round-trip-ul complet este de 400-600ms, iar timpul până la primul audio este de aproximativ 1s, obținut printr-o trecere de optimizare a latenței: pre-încălzire la boot, model fixat și un timp de așteptare la finalul vorbirii de 0,4s. Costul de operare este $0/lună, pentru că STT și TTS sunt locale, iar LLM-ul rulează pe un lane gratuit. Stiva completă este Python, faster-whisper pentru STT, Piper pentru TTS, sounddevice pentru I/O audio, NVIDIA NIM pentru LLM, plus NumPy, pe Windows cu CUDA.

Toate aceste detalii de build sunt documentate, ca să nu fie reînvățate: incompatibilitatea PyAudio cu Python 3.14, schimbarea de API la Piper, halucinațiile din liniște, contenția pe GPU și problema de barge-in pe difuzoare. Asistentul a pornit ca un comportament matur dintr-un sistem de instincte interne și a fost extras într-un instrument local de sine stătător și reutilizabil, cu butoane de reglaj din mediu pentru activare, model, praguri audio și punte. În forma curentă, este o demonstrație concretă că o experiență vocală bidirecțională, cu latență mică, se poate construi în întregime din componente locale și gratuite.

Cronologie

  • 09/06/2026 Build inițial al asistentului vocal bidirecțional și extragerea lui într-un instrument local de sine stătător (faster-whisper STT, Piper TTS, LLM gratuit NVIDIA).
  • 09/06/2026 Trecere de optimizare a latenței: pre-încălzire la boot, fixarea modelului pe gpt-oss (0,6s vs 3,4s) și hang de 0,4s, ajungând la circa 1s până la primul audio.