Bemerkung

Hallo, willkommen in der SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community auf Facebook! Tauchen Sie gemeinsam mit anderen Enthusiasten tiefer in Raspberry Pi, Arduino und ESP32 ein.

Warum mitmachen?

  • Experten-Support : Lösen Sie After-Sales-Probleme und technische Herausforderungen mit Hilfe unserer Community und unseres Teams.

  • Lernen & Teilen : Tauschen Sie Tipps und Tutorials aus, um Ihre Fähigkeiten zu verbessern.

  • Exklusive Vorschauen : Erhalten Sie frühzeitigen Zugriff auf neue Produktankündigungen und exklusive Einblicke.

  • Sonderrabatte : Profitieren Sie von exklusiven Rabatten auf unsere neuesten Produkte.

  • Festliche Aktionen und Gewinnspiele : Nehmen Sie an Gewinnspielen und saisonalen Aktionen teil.

👉 Bereit, mit uns zu entdecken und zu erschaffen? Klicken Sie auf [here] und treten Sie noch heute bei!

6. Lokaler Sprach-Chatbot

In dieser Lektion kombinieren Sie alles, was Sie gelernt haben — Spracherkennung ( STT ) , Text-to-Speech ( TTS ) und ein lokales LLM ( Ollama ) — um einen vollständig offline laufenden Sprach-Chatbot zu bauen, der auf Ihrem Fusion HAT läuft.

Der Ablauf ist einfach :

  1. Zuhören — Das Mikrofon erfasst Ihre Sprache und transkribiert sie mit Vosk .

  2. Denken — Der Text wird an ein lokales LLM gesendet, das über Ollama läuft ( z. B. llama3.2:3b ).

  3. Sprechen — Der Chatbot antwortet laut mit Piper TTS .

Dadurch entsteht ein freihändig bedienbarer Konversationsroboter , der in Echtzeit verstehen und antworten kann.


Bevor Sie beginnen

Stellen Sie sicher, dass Sie Folgendes vorbereitet haben :

  • Testen Sie Piper TTS ( 1. Piper testen ) und wählen Sie ein funktionierendes Sprachmodell aus.

  • Testen Sie Vosk STT ( 2. Vosk testen ) und wählen Sie das richtige Sprachpaket ( z. B. en-us ).

  • Installieren Sie Ollama ( 1. Ollama ( LLM ) installieren und Modell herunterladen ) auf Ihrem Pi oder einem anderen Computer und laden Sie ein Modell wie llama3.2:3b herunter ( oder ein kleineres wie moondream:1.8b , wenn der Speicher begrenzt ist ).


Code ausführen

  1. Öffnen Sie das Beispielskript :

    cd ~/fusion-hat/examples/
    sudo nano local_voice_chatbot.py
    
  2. Passen Sie die Parameter nach Bedarf an :

    • stt = Vosk(language="en-us") : Ändern Sie dies so, dass es zu Ihrem Akzent / Sprachpaket passt ( z. B. en-us, zh-cn, es ).

    • tts.set_model("en_US-amy-low") : Ersetzen Sie dies durch das Piper-Sprachmodell, das Sie in 1. Piper testen überprüft haben.

    • llm = Ollama(ip="localhost", model="llama3.2:3b") : Aktualisieren Sie sowohl ip als auch model entsprechend Ihrem Setup.

      • ip : Wenn Ollama auf dem gleichen Pi läuft, verwenden Sie localhost. Wenn Ollama auf einem anderen Computer in Ihrem LAN läuft, aktivieren Sie Expose to network in Ollama und setzen Sie ip auf die LAN-IP dieses Computers.

      • model : Muss exakt mit dem Modellnamen übereinstimmen, den Sie in Ollama heruntergeladen / aktiviert haben.

  3. Führen Sie das Skript aus :

    cd ~/fusion-hat/examples/
    sudo python3 local_voice_chatbot.py
    
  4. Nach dem Start sollten Sie Folgendes sehen :

    • Der Bot begrüßt Sie mit einer gesprochenen Willkommensnachricht.

    • Er wartet auf Spracheingabe.

    • Vosk transkribiert Ihre Sprache in Text.

    • Der Text wird an Ollama gesendet, das eine Antwort zurück streamt.

    • Die Antwort wird bereinigt ( versteckte Begründungen werden entfernt ) und anschließend von Piper laut gesprochen.

    • Beenden Sie das Programm jederzeit mit Ctrl+C .


Code

import re
import time
from fusion_hat.llm import Ollama
from fusion_hat.stt import Vosk
from fusion_hat.tts import Piper

# Initialize speech recognition
stt = Vosk(language="en-us")

# Initialize TTS
tts = Piper()
tts.set_model("en_US-amy-low")

# Instructions for the 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."

# Initialize Ollama connection
llm = Ollama(ip="localhost", model="llama3.2:3b")
llm.set_max_messages(20)
llm.set_instructions(INSTRUCTIONS)

# Utility: clean hidden reasoning
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🎤 Listening... (Press Ctrl+C to stop)")

            # Collect final transcript from Vosk
            text = ""
            for result in stt.listen(stream=True):
                if result["done"]:
                    text = result["final"].strip()
                    print(f"[YOU] {text}")
                else:
                    print(f"[YOU] {result['partial']}", end="\r", flush=True)

            if not text:
                print("[INFO] Nothing recognized. Try again.")
                time.sleep(0.1)
                continue

            # Query Ollama with 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("")

            # Clean and speak
            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] Stopping...")
    finally:
        tts.say("Goodbye!")
        print("Bye.")

if __name__ == "__main__":
    main()

Code-Analyse

Imports und globale Einrichtung

import re
import time
from fusion_hat.llm import Ollama
from fusion_hat.stt import Vosk
from fusion_hat.tts import Piper

Bindet die drei Teilsysteme ein, die Sie zuvor aufgebaut haben: Vosk für Speech-to-Text ( STT ), Ollama für das LLM und Piper für Text-to-Speech ( TTS ).

STT initialisieren ( Vosk )

stt = Vosk(language="en-us")

Lädt das Vosk-Modell für US-Englisch. Ändern Sie den Sprachcode ( z. B. zh-cn, es ), damit er zu Ihrem Sprachpaket passt, um eine bessere Genauigkeit zu erzielen.

TTS initialisieren ( Piper )

tts = Piper()
tts.set_model("en_US-amy-low")

Erstellt eine Piper-Engine und wählt eine bestimmte Stimme aus. Wählen Sie ein Modell, das Sie in 1. Piper testen getestet haben. Stimmen mit geringerer Qualität sind schneller und benötigen weniger CPU.

LLM-Anweisungen und Begrüßungszeile

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."

Zwei zentrale UX-Entscheidungen :

  • Halten Sie Antworten kurz und direkt ( hilft bei der TTS-Verständlichkeit ).

  • Verbieten Sie ausdrücklich versteckte „chain-of-thought“-Tags, um störende Ausgaben zu reduzieren.

Mit Ollama verbinden und Gesprächsumfang festlegen

llm = Ollama(ip="localhost", model="llama3.2:3b")
llm.set_max_messages(20)
llm.set_instructions(INSTRUCTIONS)
  • ip="localhost" setzt voraus, dass der Ollama-Server auf dem gleichen Pi läuft. Wenn er auf einem anderen LAN-Rechner läuft, tragen Sie dessen LAN-IP ein und aktivieren Sie Expose to network in Ollama.

  • set_max_messages(20) hält die Gesprächshistorie kurz. Verringern Sie diesen Wert, wenn Speicher / Latenz knapp ist.

Versteckte Begründungen / Tags vor dem Sprechen entfernen

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()

Einige Modelle geben möglicherweise interne Tags aus ( z. B. <think>… ). Diese Funktion entfernt sie, damit Ihre TTS nur die endgültige Antwort spricht.

Tipp: Wenn Sie andere Artefakte auf dem Bildschirm sehen ( weil Sie rohe Tokens streamen ), sorgt diese Funktion bereits dafür, dass die gesprochene Ausgabe sauber bleibt.

Hauptschleife: einmal begrüßen, dann zuhören → denken → sprechen

print(WELCOME)
tts.say(WELCOME)

Begrüßt den Benutzer über Terminal und Lautsprecher. Passiert einmal beim Start.

Zuhören ( Streaming-STT mit Live-Teilergebnissen )

print("\n🎤 Listening... (Press Ctrl+C to stop)")

text = ""
for result in stt.listen(stream=True):
    if result["done"]:
        text = result["final"].strip()
        print(f"[YOU] {text}")
    else:
        print(f"[YOU] {result['partial']}", end="\r", flush=True)
  • stream=True liefert Teil-Transkripte für unmittelbares Feedback und ein finales Transkript, wenn die Äußerung endet.

  • Der final erkannte Text wird in text gespeichert und einmal ausgegeben.

Schutz: Wenn nichts erkannt wurde, überspringen Sie den LLM-Aufruf :

if not text:
    print("[INFO] Nothing recognized. Try again.")
    time.sleep(0.1)
    continue

Dadurch werden leere Prompts an das Modell vermieden ( spart Zeit und Tokens ).

Denken ( LLM ) mit gestreamter Ausgabe

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("")
  • Sendet das finale Transkript an das lokale LLM und gibt Tokens aus, sobald sie eintreffen , für geringe Latenz.

  • Gleichzeitig sammeln Sie die vollständige Antwort in reply_accum für die Nachbearbeitung.

Hinweis: Wenn Sie rohe Tokens lieber nicht anzeigen möchten, setzen Sie stream=False und geben Sie nur die finale Zeichenkette aus.

Sprechen ( erst bereinigen, dann einmal TTS )

clean = strip_thinking(reply_accum)
if clean:
    tts.say(clean)
else:
    tts.say("Sorry, I didn't catch that.")
  • Bereinigt den finalen Text, um versteckte Tags zu entfernen, und spricht dann genau einmal .

  • TTS auf einen einzigen Durchlauf zu beschränken, vermeidet wiederholte Prompts wie „[LLM] / [SAY]“.

Beenden und Aufräumen

except KeyboardInterrupt:
    print("\n[INFO] Stopping...")
finally:
    tts.say("Goodbye!")
    print("Bye.")

Verwenden Sie Ctrl+C , um zu stoppen. Der Bot sagt kurz auf Wiedersehen, um ein sauberes Beenden zu signalisieren.


Fehlerbehebung & FAQ

  • Modell ist zu groß ( Speicherfehler )

    Verwenden Sie ein kleineres Modell wie moondream:1.8b oder führen Sie Ollama auf einem leistungsstärkeren Computer aus.

  • Keine Antwort von Ollama

    Stellen Sie sicher, dass Ollama läuft ( ollama serve oder Desktop-App geöffnet ). Wenn remote, aktivieren Sie Expose to network und prüfen Sie die IP-Adresse.

  • Vosk erkennt Sprache nicht

    Überprüfen Sie, ob Ihr Mikrofon funktioniert. Versuchen Sie bei Bedarf ein anderes Sprachpaket ( zh-cn , es usw. ).

  • Piper stumm oder Fehler

    Bestätigen Sie, dass das gewählte Sprachmodell heruntergeladen wurde und in 1. Piper testen getestet ist.

  • Antworten zu lang oder am Thema vorbei

    Bearbeiten Sie INSTRUCTIONS und fügen Sie hinzu: „Keep answers short and to the point.“