Nachrichtenverlauf und Kontext in einem lokalen LLM-Chat mit Python implementieren
Der auf LiteLLM und Ollama basierende Konsolen-Chat aus dem vorherigen Teil verarbeitet Anfragen unabhängig voneinander. Das Modell kann vorherige Nachrichten nicht sehen, was den Dialog unterbricht: Verweise auf frühere Antworten werden ignoriert. Die Lösung besteht darin, den Nachrichtenverlauf in einer Python-Liste zu speichern und ihn mit jeder neuen Anfrage zu übergeben.
Dadurch wird eine Abfolge von Aufrufen zu einem zusammenhängenden Gespräch. Das Modell erhält den vollständigen Kontext: die Systemanweisung, frühere Nutzer/Assistent-Paare und die aktuelle Eingabe.
Nachrichtenstruktur für Kontext
Das Nachrichtenformat ist standardmäßig für OpenAI-kompatible APIs:
system: eine dauerhafte Anweisung, separat hinzugefügtuser: die Anfrage des Nutzersassistant: die Antwort des Modells
Der Verlauf enthält nur Nutzer/Assistent-Paare. Beim Senden:
[
{"role": "system", "content": SYSTEM_PROMPT},
*conversation_history,
{"role": "user", "content": user_message}
]
Die Reihenfolge ist entscheidend: System legt das Verhalten fest, Verlauf liefert Kontext, neue Nutzereingabe ist die aktuelle Anfrage.
Aktualisierter main.py-Code
Vollständiges Skript mit Verlauf und Kontextkürzung:
# -*- 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 = "Du bist ein hilfreicher Assistent. Antworte präzise und auf den Punkt."
MAX_HISTORY_MESSAGES = 6
def trim_history(history: list, limit: int) -> list:
if len(history) <= limit:
return history
return history[-limit:]
def send_request_to_llm(user_message: str, history: list) -> Optional[str]:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
*history,
{"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"\nGenerierungszeit: {duration:.2f} Sek")
return response.choices[0].message.content
except Exception as e:
print(f"\nAnfragefehler: {e}")
return None
def main() -> None:
print("Lokaler KI-Assistent mit Gedächtnis gestartet.")
print("Gib eine Frage ein oder 'exit' zum Beenden.\n")
conversation_history = []
while True:
user_input = input("Du: ").strip()
if user_input.lower() in ("exit", "quit"):
print("Auf Wiedersehen!")
break
if not user_input:
print("Bitte gib eine Frage ein.")
continue
print("\nModell denkt nach...")
answer = send_request_to_llm(user_input, conversation_history)
if answer is not None:
print(f"\nKI: {answer}\n")
conversation_history.append({"role": "user", "content": user_input})
conversation_history.append({"role": "assistant", "content": answer})
conversation_history = trim_history(conversation_history, MAX_HISTORY_MESSAGES)
else:
print("\nAntwort konnte nicht abgerufen werden. Prüfe, ob Ollama läuft.\n")
if __name__ == "__main__":
main()
Wichtige Logikänderungen
- conversation_history: Liste in main() speichert Nachrichtenpaare
- send_request_to_llm: akzeptiert Verlauf, erweitert via
* - Paare speichern: nach einer Antwort werden Nutzer und Assistent hinzugefügt
- trim_history: kürzt auf MAX_HISTORY_MESSAGES (6 Einträge — 3 Dialoge)
| Komponente | Vor Update | Nach Update |
|-----------|---------------|--------------|
| messages | system + user | system + history + user |
| Speicher | Keiner | Liste im RAM |
| Größe | Unbegrenzt | Bis zu 6 Nachrichten |
Kürzung verhindert Kontextüberlauf: alte Nachrichten erhöhen Tokens und verlangsamen Inferenz.
Kontext testen
Dialog mit abhängigen Anfragen:
- Du: Nenne drei Python-Webentwicklungs-Frameworks
- KI: Django, Flask, FastAPI
- Du: Erzähl mir mehr über das zweite
- KI: Flask ist ein leichtgewichtiges Microframework...
Das Modell bezieht sich korrekt auf "das zweite", indem es die vorherige Antwort sieht.
Grenzen der aktuellen Implementierung
- Verlauf im RAM: geht beim Neustart verloren
- Feste Grenze: 6 Nachrichten (konfigurierbar)
- Keine Persistenz: erfordert Redis/Datenbank für Produktion
Für einen Telegram-Bot oder Web-App bleibt die Logik gleich: Verlauf in Nutzersitzung.
Was wichtig ist
- Nachrichtenformat: system separat, Verlauf nur Nutzer/Assistent
- Verlaufskürzung: [-limit:] bewahrt aktuelle Paare
- Trennung der Verantwortlichkeiten: LLM-Anfragen in send_request_to_llm, Zustand in main()
- Timeouts: request_timeout=120 für lange Kontexte
- Protokollierung: Generierungszeit für Performance-Debugging
— Editorial Team
Noch keine Kommentare.