Zurück zur Startseite

Entwicklung eines Konsolen-LLM-Chats in Python mit Ollama und LiteLLM

Schritt-für-Schritt-Anleitung zur Erstellung eines interaktiven Konsolen-Chats mit lokalen LLMs in Python. Lernen Sie alles über Systemprompts, Fehlerbehandlung und Optimierung mit Ollama und LiteLLM.

Interaktiver LLM-Chat in Python: Ollama und LiteLLM für Entwickler
Advertisement 728x90

Interaktiven Konsolen-LLM-Chat in Python mit Ollama und LiteLLM erstellen

Im Zuge des wachsenden Interesses an lokalen Large Language Models (LLMs) und deren Integration in benutzerdefinierte Anwendungen ist es entscheidend, nicht nur ein Modell auszuführen, sondern auch eine voll funktionsfähige interaktive Schnittstelle zu schaffen. Dieser Artikel, eine Fortsetzung unserer Serie, konzentriert sich darauf, ein grundlegendes Skript für eine einzelne LLM-Abfrage in einen funktionalen Konsolen-Chat in Python zu verwandeln. Wir werden die wichtigsten Schritte zur Code-Strukturierung, Implementierung von System-Prompts, Hinzufügen von Interaktivität und Fehlerbehandlung behandeln, indem wir die leistungsstarke Kombination aus Ollama und LiteLLM nutzen. Ziel ist es zu zeigen, wie man von einer einfachen Befehlsausführung zu einer robusten und verwaltbaren Anwendung gelangt, die Echtzeit-Benutzerinteraktion ermöglicht, und die Grundlage für weitere funktionale Erweiterungen, einschließlich der Verwaltung des Dialogkontexts, zu legen.

Vom Einzelschuss-Skript zur interaktiven Anwendung

Der vorherige Teil unseres Leitfadens demonstrierte die grundlegende Integration eines lokalen LLM, das über Ollama gehostet wird, mit Python-Code unter Verwendung der LiteLLM-Bibliothek. Das Ergebnis war ein Skript, das eine einzelne vordefinierte Abfrage an das Modell senden und die Antwort anzeigen konnte, wonach das Programm beendet wurde. Obwohl dies für eine erste Einführung in die Technologie ausreichend ist, ist dieser Ansatz für jede reale Anwendung unpraktisch. Ein echter Chatbot muss eine kontinuierliche Interaktion bieten: Benutzereingaben entgegennehmen, verarbeiten, eine Antwort vom Modell erhalten und dann auf die nächste Frage warten, bis der Benutzer die Sitzung beendet. Diese grundlegende Transformation werden wir genau umsetzen und ein statisches Skript in einen dynamischen Konsolen-Chat verwandeln.

Zu den wichtigsten Verbesserungen, die in diesem Teil eingeführt werden, gehören:

Google AdInline article slot
  • Extrahieren der LLM-Abfragelogik in eine separate, wiederverwendbare Funktion.
  • Verwenden eines System-Prompts, um die Rolle und das Verhalten des Assistenten zu definieren.
  • Organisieren einer Konversationsschleife, die es dem Benutzer ermöglicht, mehrere Fragen nacheinander zu stellen.
  • Implementieren einer Messung der Antwortzeit zur Bewertung der Leistung.
  • Grundlegende Fehlerbehandlung zur Verbesserung der Anwendungsrobustheit.

Das Verständnis des Nachrichtenformats, mit dem LLM-APIs arbeiten, ist grundlegend für eine effektive Interaktion. Modelle arbeiten nicht mit einer einzelnen Textzeile; sie verarbeiten eine Liste von Nachrichten, jede mit einer spezifischen Rolle:

  • system: Dies ist eine Anweisung für das Modell, die seine Persönlichkeit, seinen Antwortstil, seine Einschränkungen oder wichtige Kontextdaten definiert. Der Benutzer sieht diese Nachricht nicht direkt; sie wird auf Code-Ebene generiert.
  • user: Die Nachricht oder Frage, die vom Benutzer stammt.
  • assistant: Die vom Modell selbst generierte Antwort. In zukünftigen Iterationen werden hier frühere Modellantworten hinzugefügt, um den Kontext aufrechtzuerhalten.

Architektur eines Konsolen-LLM-Chats in Python

Kommen wir nun zur Implementierung. Wir werden die Datei main.py ändern, um die LLM-Interaktionslogik zu kapseln und die Benutzereingabeschleife zu organisieren. Unten ist der aktualisierte Code, den Sie verwenden können, um den Inhalt Ihrer main.py zu ersetzen.

# -*- coding: utf-8 -*-
import time
from typing import Optional
from litellm import completion
 
MODEL = "ollama_chat/qwen2.5:3b"
API_BASE = "http://localhost:11434"
SYSTEM_PROMPT = "You are a helpful assistant. Answer concisely and to the point."
 

def send_request_to_llm(user_message: str) -> Optional[str]:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_message},
    ]
    try:
        start_time = time.time()
        response = completion(
            model=MODEL,
            messages=messages,
            api_base=API_BASE,
            request_timeout=120,
        )
        duration = time.time() - start_time
        print(f"\n⏱ Generation time: {duration:.2f} sec")
        return response.choices[0].message.content
    except Exception as e:
        print(f"\n❌ Error during request: {e}")
        return None
 

def main() -> None:
    print("🤖 Local AI assistant started.")
    print("Enter your question or 'exit' to quit.\n")
 
    while True:
        user_input = input("👤 You: ").strip()
 
        if user_input.lower() in ("ende", "exit", "quit"):
            print("Goodbye!")
            break
 
        if not user_input:
            print("⚠️ Please enter a question.")
            continue
 
        print("\n🤔 Model is thinking...")
        answer = send_request_to_llm(user_input)
 
        if answer is not None:
            print(f"\n🦙 AI: {answer}\n")
        else:
            print("\n⚠️ Failed to get a response. Check if Ollama is running.\n")
 

if __name__ == "__main__":
    main()

Analyse der wichtigsten Code-Komponenten

Werfen wir einen genaueren Blick darauf, wie jeder Block dieses Skripts funktioniert.

Google AdInline article slot

1. Konfigurationskonstanten:

Am Anfang der Datei werden Konstanten definiert: MODEL, API_BASE und SYSTEM_PROMPT. Dieser Ansatz ermöglicht eine einfache Änderung der Konfigurationsparameter, wie z.B. die Verwendung eines anderen LLM-Modells oder die Anpassung des Verhaltens des Assistenten, ohne sich in die Kernlogik des Hauptcodes vertiefen zu müssen. Hier spielt SYSTEM_PROMPT eine entscheidende Rolle, indem es anfängliche Anweisungen für das Modell festlegt, die den Gesamtton und Stil seiner Antworten beeinflussen.

2. Die Funktion send_request_to_llm:

Google AdInline article slot

Diese Funktion (def send_request_to_llm(user_message: str) -> Optional[str]:) ist das zentrale Element für die Interaktion mit dem LLM. Sie nimmt eine Benutzernachricht entgegen, konstruiert eine vollständige Liste von Nachrichten (einschließlich SYSTEM_PROMPT und user_message) und sendet diese über litellm.completion an das lokale Ollama-Modell.

  • Kapselung: Die Logik zum Senden einer Anfrage und Empfangen einer Antwort ist vollständig gekapselt, was die Hauptschleife main sauberer und verständlicher macht.
  • Zeitmessung: Die integrierte Zeitmessung (time.time()) ermöglicht die Bewertung der Modellleistung und zeigt an, wie viele Sekunden die Generierung einer Antwort gedauert hat. Dies ist besonders nützlich, wenn mit verschiedenen Modellen oder Parametern experimentiert wird.
  • Fehlerbehandlung: Der try...except-Block bietet eine grundlegende Fehlertoleranz. Tritt während der LLM-Anfrage ein Fehler auf (z.B. wenn Ollama nicht verfügbar ist), gibt die Funktion None zurück, anstatt einen kritischen Fehler auszulösen, wodurch die main-Funktion die Situation elegant handhaben und den Chat fortsetzen kann.

3. Die Funktion main und die interaktive Schleife:

Die main-Funktion verwaltet die Benutzeroberfläche und den gesamten Chat-Fluss.

  • Endlosschleife (while True): Erstellt eine kontinuierliche Chat-Sitzung, die bestehen bleibt, bis der Benutzer explizit beschließt, sie zu beenden.
  • Benutzereingabe: input("👤 You: ").strip() wartet auf Benutzereingaben. Die Methode .strip() entfernt führende und nachfolgende Leerzeichen und verhindert so, dass leere oder fehlerhafte Abfragen gesendet werden.
  • Beenden-Befehle: Die Überprüfung user_input.lower() in ("ende", "exit", "quit") ermöglicht es dem Benutzer, das Programm mit verschiedenen Beenden-Befehlsoptionen zu beenden.
  • Leere Eingabe: Die Überprüfung if not user_input: verhindert, dass leere Nachrichten an das Modell gesendet werden, und fordert den Benutzer auf, eine Frage einzugeben.
  • Interaktion: main ruft send_request_to_llm mit der Benutzereingabe auf und verarbeitet die empfangene Antwort, indem sie diese in der Konsole ausgibt oder einen Fehler meldet. Die Trennung der Verantwortlichkeiten zwischen main (Schnittstellenverwaltung) und send_request_to_llm (Modellinteraktion) ist ein grundlegendes Prinzip guten Software-Designs.

LLM-Verhalten über System-Prompt steuern

Eines der mächtigsten Werkzeuge bei der Arbeit mit LLMs ist der System-Prompt. Er ermöglicht es Ihnen, dem Modell eine spezifische Rolle, einen Kommunikationsstil oder eine Reihe von Einschränkungen zuzuweisen, wodurch seine Antworten erheblich verändert werden, ohne das Modell selbst oder seine Parameter zu ändern. Das Experimentieren mit SYSTEM_PROMPT ist eine hervorragende Möglichkeit zu verstehen, wie subtil Sie das Verhalten des Assistenten für bestimmte Aufgaben anpassen können. Schauen wir uns einige Beispiele an, wie das Ändern einer einzigen Zeile den Dialog drastisch beeinflussen kann:

  • `SYSTEM_PROMPT = "You are a sarcastic assistant. Respond with irony."` — Das Modell wird mit Humor und Witz antworten.
  • `SYSTEM_PROMPT = "You are a strict teacher. Correct errors in questions."` — Der Assistent wird nicht nur antworten, sondern auch grammatikalische oder sachliche Ungenauigkeiten in den Anfragen aufzeigen.
  • `SYSTEM_PROMPT = "Respond only in English. Always provide a code example."` — Das Modell wird sich strikt an die englische Sprache halten und versuchen, wenn zutreffend, Codebeispiele in seine Antworten aufzunehmen.

Diese Beispiele zeigen, wie ein System-Prompt ein statisches Modell in ein flexibles Werkzeug verwandelt, das sich an verschiedene Anwendungsfälle anpassen kann. Dies ist entscheidend für die Erstellung spezialisierter Chatbots, sei es als technische Assistenten, kreative Partner oder Bildungswerkzeuge.

Wichtige Erkenntnisse

In diesem Entwicklungsstadium hat unser Konsolen-LLM-Chat die folgenden wichtigen Ergebnisse erzielt:

  • Interaktivität: Der Benutzer kann einen kontinuierlichen Dialog führen, Fragen stellen und in Echtzeit Antworten erhalten.
  • Kontrolliertes Verhalten: Der System-Prompt ermöglicht eine einfache Konfiguration der Rolle und des Kommunikationsstils des Modells.
  • Code-Modularität: Die LLM-Interaktionslogik ist von der Benutzeroberflächenlogik getrennt, was die weitere Entwicklung und Fehlerbehebung vereinfacht.
  • Leistung: Die integrierte Messung der Antwortgenerierungszeit hilft, die Geschwindigkeit des Modells zu überwachen.
  • Zuverlässigkeit: Eine grundlegende Fehlerbehandlung verhindert Programmabstürze aufgrund von Verbindungs- oder Modellproblemen.

Häufige Herausforderungen und Lösungen

Bei der Arbeit mit lokalen LLMs und Konsolenanwendungen können einige häufige Probleme auftreten:

  • Programm friert nach Eingabe einer Frage ein: Dies geschieht meist, weil Ollama nicht läuft oder das Modell noch nicht vollständig in den Speicher geladen wurde. Stellen Sie sicher, dass Ollama aktiv läuft (ollama list in Ihrem Terminal) und geben Sie dem Modell Zeit zum Initialisieren.
  • Zu knappe Antworten vom Modell: Wenn der SYSTEM_PROMPT „Antworten Sie prägnant“ angibt, wird das Modell diese Anweisung befolgen. Für detailliertere Antworten ändern Sie den System-Prompt, indem Sie diese Einschränkung entfernen oder explizit nach „einer detaillierten Antwort mit Beispielen“ fragen.
  • KeyboardInterrupt beim Drücken von Strg+C:** Dies ist ein Standardverhalten von Python. Obwohl für ein Lernprojekt nicht kritisch, können Sie in der Produktion main() in einen try/except KeyboardInterrupt-Block einschließen, um ein saubereres Herunterfahren zu ermöglichen.
  • Kodierungsprobleme (verzerrte Zeichen in Windows): Dies kann durch das Setzen der Terminal-Kodierung auf UTF-8 gelöst werden, zum Beispiel mit dem Befehl $OutputEncoding = [System.Text.Encoding]::UTF8 in PowerShell.

Nächste Schritte: Auf dem Weg zu einem vollwertigen Dialog

Obwohl unser Konsolen-Chat deutlich funktionaler geworden ist, fehlt ihm derzeit „Gedächtnis“. Jede Anfrage wird unabhängig verarbeitet, und das Modell erinnert sich nicht an frühere Dialogrunden. Das bedeutet, wenn Sie fragen: „Können Sie dasselbe, aber kürzer machen?“, wird das Modell nicht verstehen, worauf sich „dasselbe“ bezieht. Im nächsten Teil werden wir untersuchen, wie man die Nachrichtenhistorie implementiert, indem der gesamte Dialogkontext mit jeder neuen Anfrage übergeben wird, was ihn zu einem wirklich intelligenten Gesprächspartner machen wird.

— Editorial Team

Advertisement 728x90

Weiterlesen