Note
Bienvenue sur la communauté Facebook SunFounder Raspberry Pi, Arduino & ESP32 !
Obtenez du support technique et de l’aide au dépannage.
Apprenez et partagez des projets, des astuces et des tutoriels.
Accédez en avant-première aux aperçus et mises à jour des produits.
Profitez de réductions exclusives et de cadeaux.
👉 Rejoignez-nous ici : [here]
6. Chatbot Vocal Local
Dans cette leçon, vous allez combiner tout ce que vous avez appris — reconnaissance vocale (STT), synthèse vocale (TTS) et un LLM local (Ollama) — pour construire un chatbot vocal entièrement hors ligne qui fonctionne sur votre Pironman 5 Pro MAX.
Le flux de travail est simple :
Écouter — Le microphone capture votre discours et le transcrit avec Vosk.
Penser — Le texte est envoyé à un LLM local fonctionnant sur Ollama (par exemple,
llama3.2:3b).Parler — Le chatbot répond à voix haute en utilisant Piper TTS.
Cela crée un robot conversationnel mains libres capable de comprendre et de répondre en temps réel.
Avant de commencer
Assurez-vous d’avoir préparé les éléments suivants :
Testé Piper TTS (1. Tester Piper) et choisi un modèle vocal fonctionnel.
Testé Vosk STT (Tester Vosk) et choisi le bon pack linguistique (par exemple,
en-us).Installé Ollama (1. Installer Ollama (LLM) et télécharger un modèle) sur votre Pi ou un autre ordinateur, et téléchargé un modèle comme
llama3.2:3b(ou un plus petit commemoondream:1.8bsi la mémoire est limitée).
Exécuter le code
Ouvrez le script d’exemple :
cd ~/sunfounder-voice-assistant/examples/ sudo nano local_voice_chatbot.py
Mettez à jour les paramètres si nécessaire :
stt = Vosk(language="en-us"): Modifiez ceci pour correspondre à votre accent/pack linguistique (par exemple,en-us,zh-cn,es).tts.set_model("en_US-amy-low"): Remplacez par le modèle vocal Piper que vous avez vérifié dans 1. Tester Piper.llm = Ollama(ip="localhost", model="llama3.2:3b"): Mettez à jour à la foisipetmodelselon votre propre configuration.ip: Si Ollama fonctionne sur le même Pi, utilisezlocalhost. Si Ollama fonctionne sur un autre ordinateur de votre LAN, activez Exposer au réseau dans Ollama et définissezipsur l’adresse IP LAN de cet ordinateur.model: Doit correspondre exactement au nom du modèle que vous avez téléchargé/activé dans Ollama.
Exécutez le script :
cd ~/sunfounder-voice-assistant/examples/ sudo python3 local_voice_chatbot.py
Après l’exécution, vous devriez voir :
Le bot vous salue avec un message de bienvenue vocal.
Il attend une entrée vocale.
Vosk transcrit votre discours en texte.
Le texte est envoyé à Ollama, qui diffuse une réponse.
La réponse est nettoyée (suppression du raisonnement caché) et prononcée à voix haute par Piper.
Arrêtez le programme à tout moment avec
Ctrl+C.
Code
import re
import time
from sunfounder_voice_assistant.llm import Ollama
from sunfounder_voice_assistant.stt import Vosk
from sunfounder_voice_assistant.tts import Piper
# Initialiser la reconnaissance vocale
stt = Vosk(language="en-us")
# Initialiser la TTS
tts = Piper()
tts.set_model("en_US-amy-low")
# Instructions pour le LLM
INSTRUCTIONS = (
"You are a helpful assistant. Answer directly in plain English. "
"Do NOT include any hidden thinking, analysis, or tags like <think>."
)
WELCOME = "Hello! I'm your voice chatbot. Speak when you're ready."
# Initialiser la connexion Ollama
llm = Ollama(ip="localhost", model="llama3.2:3b")
llm.set_max_messages(20)
llm.set_instructions(INSTRUCTIONS)
# Utilitaire : supprimer le raisonnement caché
def strip_thinking(text: str) -> str:
if not text:
return ""
text = re.sub(r"<\s*think[^>]*>.*?<\s*/\s*think\s*>", "", text, flags=re.DOTALL|re.IGNORECASE)
text = re.sub(r"<\s*thinking[^>]*>.*?<\s*/\s*thinking\s*>", "", text, flags=re.DOTALL|re.IGNORECASE)
text = re.sub(r"```(?:\s*thinking)?\s*.*?```", "", text, flags=re.DOTALL|re.IGNORECASE)
text = re.sub(r"\[/?thinking\]", "", text, flags=re.IGNORECASE)
return re.sub(r"\s+\n", "\n", text).strip()
def main():
print(WELCOME)
tts.say(WELCOME)
try:
while True:
print("\n🎤 Écoute... (Appuyez sur Ctrl+C pour arrêter)")
# Récupérer la transcription finale de Vosk
text = ""
for result in stt.listen(stream=True):
if result["done"]:
text = result["final"].strip()
print(f[VOUS] {text}")
else:
print(f"[VOUS] {result['partial']}", end="\r", flush=True)
if not text:
print("[INFO] Rien n'a été reconnu. Réessayez.")
time.sleep(0.1)
continue
# Interroger Ollama avec streaming
reply_accum = ""
response = llm.prompt(text, stream=True)
for next_word in response:
if next_word:
print(next_word, end="", flush=True)
reply_accum += next_word
print("")
# Nettoyer et parler
clean = strip_thinking(reply_accum)
if clean:
tts.say(clean)
else:
tts.say("Sorry, I didn't catch that.")
time.sleep(0.05)
except KeyboardInterrupt:
print("\n[INFO] Arrêt en cours...")
finally:
tts.say("Goodbye!")
print("Au revoir.")
if __name__ == "__main__":
main()
Analyse du code
Importations et configuration globale
import re
import time
from sunfounder_voice_assistant.llm import Ollama
from sunfounder_voice_assistant.stt import Vosk
from sunfounder_voice_assistant.tts import Piper
Importe les trois sous-systèmes que vous avez construits précédemment : Vosk pour la reconnaissance vocale (STT), Ollama pour le LLM, et Piper pour la synthèse vocale (TTS).
Initialiser la STT (Vosk)
stt = Vosk(language="en-us")
Charge le modèle Vosk pour l’anglais américain.
Changez le code de langue (par exemple, zh-cn, es) pour correspondre à votre pack vocal pour une meilleure précision.
Initialiser la TTS (Piper)
tts = Piper()
tts.set_model("en_US-amy-low")
Crée un moteur Piper et sélectionne une voix spécifique. Choisissez un modèle que vous avez testé dans 1. Tester Piper. Les voix de qualité inférieure sont plus rapides et utilisent moins de CPU.
Instructions LLM et message de bienvenue
INSTRUCTIONS = (
"You are a helpful assistant. Answer directly in plain English. "
"Do NOT include any hidden thinking, analysis, or tags like <think>."
)
WELCOME = "Hello! I'm your voice chatbot. Speak when you're ready."
Deux choix clés pour l’expérience utilisateur :
Garder les réponses courtes et directes (aide à la clarté de la TTS).
Interdire explicitement les balises de « chaîne de pensée » cachées pour réduire les sorties bruitées.
Se connecter à Ollama et définir le périmètre de conversation
llm = Ollama(ip="localhost", model="llama3.2:3b")
llm.set_max_messages(20)
llm.set_instructions(INSTRUCTIONS)
ip="localhost"suppose que le serveur Ollama fonctionne sur le même Pi. S’il fonctionne sur une autre machine du LAN, mettez l”adresse IP LAN de cet ordinateur et activez Exposer au réseau dans Ollama.set_max_messages(20)conserve un historique de conversation court. Réduisez ceci si la mémoire/la latence est limitée.
Supprimer le raisonnement / balises cachés avant de parler
def strip_thinking(text: str) -> str:
if not text:
return ""
text = re.sub(r"<\s*think[^>]*>.*?<\s*/\s*think\s*>", "", text, flags=re.DOTALL|re.IGNORECASE)
text = re.sub(r"<\s*thinking[^>]*>.*?<\s*/\s*thinking\s*>", "", text, flags=re.DOTALL|re.IGNORECASE)
text = re.sub(r"```(?:\s*thinking)?\s*.*?```", "", text, flags=re.DOTALL|re.IGNORECASE)
text = re.sub(r"\[/?thinking\]", "", text, flags=re.IGNORECASE)
return re.sub(r"\s+\n", "\n", text).strip()
Certains modèles peuvent émettre des balises de style interne (par exemple, <think>…).
Cette fonction les supprime afin que votre TTS ne prononce que la réponse finale.
Astuce : Si vous voyez d’autres artefacts à l’écran (parce que vous diffusez des jetons bruts), cette fonction garantit déjà que la sortie parlée reste propre.
Boucle principale : saluer une fois, puis écouter → penser → parler
print(WELCOME)
tts.say(WELCOME)
Saluer l’utilisateur via le terminal et le haut-parleur. Se produit une fois au démarrage.
Écouter (STT en streaming avec partiels en direct)
print("\n🎤 Écoute... (Appuyez sur Ctrl+C pour arrêter)")
text = ""
for result in stt.listen(stream=True):
if result["done"]:
text = result["final"].strip()
print(f"[VOUS] {text}")
else:
print(f"[VOUS] {result['partial']}", end="\r", flush=True)
stream=Trueproduit des transcriptions partielles pour un retour immédiat et une transcription finale lorsque l’énoncé se termine.Le texte reconnu final est stocké dans
textet imprimé une fois.
Garde : Si rien n’a été reconnu, vous sautez l’appel LLM :
if not text:
print("[INFO] Rien n'a été reconnu. Réessayez.")
time.sleep(0.1)
continue
Cela évite d’envoyer des invites vides au modèle (économie de temps et de jetons).
Penser (LLM) avec impression en streaming
reply_accum = ""
response = llm.prompt(text, stream=True)
for next_word in response:
if next_word:
print(next_word, end="", flush=True)
reply_accum += next_word
print("")
Envoie la transcription finale au LLM local et imprime les jetons à leur arrivée pour une faible latence.
Pendant ce temps, vous accumulez la réponse complète dans
reply_accumpour le post-traitement.
Remarque : Si vous préférez ne pas afficher les jetons bruts, définissez stream=False et imprimez simplement la chaîne finale.
Parler (nettoyer d’abord, puis TTS une fois)
clean = strip_thinking(reply_accum)
if clean:
tts.say(clean)
else:
tts.say("Sorry, I didn't catch that.")
Nettoie le texte final pour supprimer les balises cachées, puis parle exactement une fois.
Garder la TTS sur un seul passage évite les invites répétées comme « [LLM] / [SAY] ».
Sortie et nettoyage
except KeyboardInterrupt:
print("\n[INFO] Arrêt en cours...")
finally:
tts.say("Goodbye!")
print("Au revoir.")
Utilisez Ctrl+C pour arrêter. Le bot dit un court au revoir pour signaler une sortie propre.
Dépannage et FAQ
Le modèle est trop gros (erreur de mémoire)
Utilisez un modèle plus petit comme
moondream:1.8bou exécutez Ollama sur un ordinateur plus puissant.Pas de réponse d’Ollama
Assurez-vous qu’Ollama est en cours d’exécution (
ollama serveou application de bureau ouverte). Si distant, activez Exposer au réseau et vérifiez l’adresse IP.Vosk ne reconnaît pas la parole
Vérifiez que votre microphone fonctionne. Essayez un autre pack linguistique (
zh-cn,es, etc.) si nécessaire.Piper silencieux ou erreurs
Confirmez que le modèle vocal choisi est téléchargé et testé dans 1. Tester Piper.
Réponses trop longues ou hors sujet
Modifiez
INSTRUCTIONSpour ajouter : « Keep answers short and to the point. »