Powrót do strony głównej

Historia wiadomości w czacie LLM Python Ollama

Artykuł opisuje implementację historii wiadomości w konsolowym czacie LLM na Python z Ollama i LiteLLM. Dodawana jest conversation_history z przycinaniem, pełna struktura messages. Podany jest kod i przykłady dialogów z kontekstem.

Kontekst w lokalnym czacie LLM: kod i przykłady
Advertisement 728x90

Implementacja kontekstu i historii wiadomości w lokalnym czacie LLM w Pythonie

Czat konsolowy oparty na LiteLLM i Ollama z poprzedniej części przetwarza zapytania niezależnie. Model nie widzi poprzednich wypowiedzi, co psuje dialog: odwołania do wcześniejszych odpowiedzi są ignorowane. Rozwiązaniem jest przechowywanie historii wiadomości w liście Pythona i przekazywanie jej z każdym nowym zapytaniem.

To przekształca sekwencję wywołań w spójną rozmowę. Model otrzymuje pełny kontekst: instrukcję systemową, poprzednie pary user/assistant oraz bieżące dane wejściowe.

Struktura wiadomości dla kontekstu

Format wiadomości jest standardowy dla API zgodnych z OpenAI:

Google AdInline article slot
  • system: stała instrukcja, dodawana osobno
  • user: zapytanie użytkownika
  • assistant: odpowiedź modelu

Historia zawiera tylko user/assistant. Przy wysyłaniu:

[
    {"role": "system", "content": SYSTEM_PROMPT},
    *conversation_history,
    {"role": "user", "content": user_message}
]

Kolejność jest kluczowa: system określa zachowanie, historia daje kontekst, nowy user to bieżące zapytanie.

Zaktualizowany kod main.py

Pełny skrypt z historią i przycinaniem kontekstu:

Google AdInline article slot
# -*- 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 = "Jesteś pomocnym asystentem. Odpowiadaj zwięźle i na temat."

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"\nCzas generowania: {duration:.2f} sek")
        return response.choices[0].message.content
    except Exception as e:
        print(f"\nBłąd przy zapytaniu: {e}")
        return None


def main() -> None:
    print("Lokalny asystent AI z pamięcią uruchomiony.")
    print("Wprowadź pytanie lub 'wyjście' aby zakończyć.\n")

    conversation_history = []

    while True:
        user_input = input("Ty: ").strip()

        if user_input.lower() in ("wyjście", "exit", "quit"):
            print("Do widzenia!")
            break

        if not user_input:
            print("Wprowadź pytanie.")
            continue

        print("\nModel myśli...")
        answer = send_request_to_llm(user_input, conversation_history)

        if answer is not None:
            print(f"\nAI: {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("\nNie udało się uzyskać odpowiedzi. Sprawdź, czy Ollama jest uruchomiona.\n")


if __name__ == "__main__":
    main()

Kluczowe zmiany w logice

  • conversation_history: lista w main() przechowuje pary wiadomości
  • send_request_to_llm: przyjmuje history, rozwija przez *
  • Zapisywanie par: po odpowiedzi dodawane są user i assistant
  • trim_history: przycina do MAX_HISTORY_MESSAGES (6 sztuk — 3 dialogi)

| Komponent | Przed aktualizacją | Po aktualizacji |

|-----------|---------------|------------------|

| messages | system + user | system + history + user |

Google AdInline article slot

| Pamięć | Brak | Lista w RAM |

| Rozmiar | Nieograniczony | Do 6 wiadomości |

Przycinanie zapobiega przepełnieniu kontekstu: stare wiadomości zwiększają tokeny, spowalniając inference.

Testowanie kontekstu

Dialog z zależnymi zapytaniami:

  • Ty: Wymień trzy frameworki webowe w Pythonie
  • AI: Django, Flask, FastAPI
  • Ty: Opowiedz szczegółowo o drugim
  • AI: Flask — lekki microframework...

Model poprawnie odnosi się do „drugiego”, widząc poprzednią odpowiedź.

Ograniczenia obecnej implementacji

  • Historia w RAM: znika przy ponownym uruchomieniu
  • Stały limit: 6 wiadomości (można dostosować)
  • Brak trwałości: dla produkcji potrzebny Redis/DB

Dla bota Telegram lub aplikacji webowej logika pozostaje: history w sesji użytkownika.

Co jest ważne

  • Format wiadomości: system osobno, history tylko user/assistant
  • Przycinanie historii: [-limit:] zachowuje ostatnie pary
  • Podział odpowiedzialności: zapytania LLM w send_request_to_llm, stan w main()
  • Limity czasu: request_timeout=120 dla długich kontekstów
  • Logowanie: czas generowania do debugowania wydajności

— Editorial Team

Advertisement 728x90

Czytaj dalej