Zpět na domů

Vývoj konzolového LLM-chatu v Pythonu s Ollama a LiteLLM

Návod krok za krokem k vytvoření interaktivního konzolového chatu s lokálními LLM v Pythonu. Prozkoumejte systémové prompty, zpracování chyb a optimalizaci s Ollama a LiteLLM.

Interaktivní LLM-chat v Pythonu: Ollama a LiteLLM pro vývojáře
Advertisement 728x90

Vývoj interaktivního konzolového LLM chatu v Pythonu s Ollamou a LiteLLM

V době rostoucího zájmu o lokální velké jazykové modely (LLM) a jejich integraci do vlastních aplikací se stává klíčovým nejen spuštění modelu, ale i vytvoření plnohodnotného interaktivního rozhraní. Tento článek, který je pokračováním série, se zaměřuje na transformaci základního skriptu pro jednorázový dotaz na LLM do funkčního konzolového chatu v Pythonu. Projdeme si klíčové kroky pro strukturování kódu, implementaci systémových promptů, přidání interaktivity a zpracování chyb, a to vše s využitím kombinace Ollama a LiteLLM. Cílem je ukázat, jak přejít od jednoduchého spuštění příkazu k vytvoření robustní a ovladatelné aplikace, která dokáže komunikovat s uživatelem v reálném čase, a také jak připravit základ pro další rozšíření funkcionality, včetně správy kontextu dialogu.

Od jednorázového skriptu k interaktivní aplikaci

Předchozí část našeho průvodce ukázala základní integraci lokální LLM, spuštěné přes Ollamu, s Python kódem pomocí knihovny LiteLLM. Výsledkem byl skript schopný odeslat jeden předem definovaný dotaz modelu a zobrazit obdrženou odpověď, po čemž se program ukončil. Pro počáteční seznámení s technologií to stačí, avšak pro vytvoření jakékoli praktické aplikace je takový přístup nevhodný. Skutečný chatbot musí zajistit nepřetržitou interakci: přijímat vstup od uživatele, zpracovávat jej, získávat odpověď od modelu a znovu čekat na další otázku, dokud se uživatel nerozhodne relaci ukončit. Právě tuto zásadní transformaci nyní realizujeme, přeměníme statický skript na dynamický konzolový chat.

Klíčové vylepšení, která budou implementována v této části, zahrnují:

Google AdInline article slot
  • Vydělení logiky dotazu na LLM do samostatné, znovupoužitelné funkce.
  • Použití systémového promptu pro definování role a chování asistenta.
  • Organizace komunikačního cyklu, který uživateli umožňuje klást mnoho otázek za sebou.
  • Implementace měření doby odezvy modelu pro hodnocení výkonu.
  • Základní zpracování výjimek pro zvýšení odolnosti aplikace.

Pochopení formátu zpráv, se kterými pracují LLM API, je základem pro efektivní interakci. Modely nepracují s jedním řádkem textu; zpracovávají seznam zpráv, z nichž každá má specifickou roli:

  • system: Toto je instrukce pro model, která definuje jeho osobnost, styl odpovědi, omezení nebo důležitá kontextová data. Uživatel tuto zprávu přímo nevidí, je generována na úrovni kódu.
  • user: Zpráva nebo otázka pocházející od uživatele.
  • assistant: Odpověď vygenerovaná samotným modelem. V budoucích iteracích se sem budou přidávat předchozí odpovědi modelu pro udržení kontextu.

Architektura konzolového LLM chatu v Pythonu

Nyní přejdeme k implementaci. Upravíme soubor main.py, abychom zapouzdřili logiku interakce s LLM a zorganizovali cyklus uživatelského vstupu. Níže je uveden aktualizovaný kód, který můžete použít k nahrazení obsahu vašeho main.py.

# -*- 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 = "Ty užitečný asistent. Odpovídej krátce a k věci."
 
 
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⏱ Doba generace: {duration:.2f} sek")
        return response.choices[0].message.content
    except Exception as e:
        print(f"\n❌ Chyba při dotazu: {e}")
        return None
 
 
def main() -> None:
    print("🤖 Lokální AI asistent spuštěn.")
    print("Zadejte otázku nebo 'konec' pro ukončení.\n")
 
    while True:
        user_input = input("👤 Vy: ").strip()
 
        if user_input.lower() in ("konec", "exit", "quit"):
            print("Na shledanou!")
            break
 
        if not user_input:
            print("⚠️ Zadejte otázku.")
            continue
 
        print("\n🤔 Model přemýšlí...")
        answer = send_request_to_llm(user_input)
 
        if answer is not None:
            print(f"\n🦙 AI: {answer}\n")
        else:
            print("\n⚠️ Nepodařilo se získat odpověď. Zkontrolujte, zda je Ollama spuštěna.\n")
 
 
if __name__ == "__main__":
    main()

Analýza klíčových komponent kódu

Podívejme se podrobněji, jak funguje každý blok tohoto skriptu.

Google AdInline article slot

1. Konfigurační konstanty:

Na začátku souboru jsou definovány konstanty: MODEL, API_BASE a SYSTEM_PROMPT. Tento přístup umožňuje snadno měnit parametry konfigurace, například použít jiný model LLM nebo upravit chování asistenta, aniž byste se museli ponořit do logiky hlavního kódu. SYSTEM_PROMPT zde hraje klíčovou roli, neboť nastavuje počáteční instrukce pro model, které ovlivňují celkový tón a styl jeho odpovědí.

2. Funkce send_request_to_llm:

Google AdInline article slot

Tato funkce (def send_request_to_llm(user_message: str) -> Optional[str]:) je ústředním prvkem pro interakci s LLM. Přijímá zprávu uživatele, formuje kompletní seznam zpráv (včetně SYSTEM_PROMPT a user_message) a odesílá jej přes litellm.completion do lokálního modelu Ollama.

  • Zapouzdření: Logika odesílání požadavku a přijímání odpovědi je plně zapouzdřena, což činí hlavní cyklus main čistším a srozumitelnějším.
  • Měření času: Vestavěné měření času (time.time()) umožňuje vyhodnotit výkon modelu, ukazující, kolik sekund trvala generace odpovědi. To je obzvláště užitečné při experimentování s různými modely nebo parametry.
  • Zpracování chyb: Blok try...except zajišťuje základní odolnost proti chybám. V případě chyby při dotazu na LLM (například pokud Ollama není dostupná) funkce vrátí None, namísto vyvolání kritické chyby, což umožňuje funkci main elegantně zpracovat situaci a pokračovat v chatu.

3. Funkce main a interaktivní cyklus:

Funkce main spravuje uživatelské rozhraní a celkový tok chatu.

  • Nekonečný cyklus (while True): Vytváří nepřetržitou chatovací relaci, která pokračuje, dokud se uživatel explicitně nerozhodne ukončit.
  • Uživatelský vstup: input("👤 Vy: ").strip() očekává vstup od uživatele. Metoda .strip() odstraňuje počáteční a koncové mezery, čímž zabraňuje odesílání prázdných nebo neplatných dotazů.
  • Příkazy pro ukončení: Kontrola user_input.lower() in ("konec", "exit", "quit") umožňuje uživateli ukončit program pomocí různých variant ukončovacího příkazu.
  • Prázdný vstup: Kontrola if not user_input: zabraňuje odesílání prázdných zpráv modelu a vyzývá uživatele k zadání otázky.
  • Interakce: main volá send_request_to_llm s uživatelským vstupem a zpracovává přijatou odpověď, zobrazuje ji v konzoli nebo hlásí chybu. Rozdělení odpovědnosti mezi main (správa rozhraní) a send_request_to_llm (interakci s modelem) je základním principem dobrého návrhu softwaru.

Správa chování LLM prostřednictvím systémového promptu

Jedním z nejmocnějších nástrojů při práci s LLM je systémový prompt. Umožňuje modelu přiřadit určitou roli, styl komunikace nebo sadu omezení, což výrazně mění jeho odpovědi bez nutnosti měnit samotný model nebo jeho parametry. Experimentování s SYSTEM_PROMPT je skvělý způsob, jak pochopit, jak jemně lze chování asistenta přizpůsobit konkrétním úkolům. Podívejme se na několik příkladů, jak změna jednoho řádku může zásadně ovlivnit dialog:

  • SYSTEM_PROMPT = "Jsi sarkastický asistent. Odpovídej s ironií." — model bude odpovídat s humorem a břitkostí.
  • SYSTEM_PROMPT = "Jsi přísný učitel. Opravuj chyby v otázkách." — asistent bude nejen odpovídat, ale také upozorňovat na gramatické nebo faktické nepřesnosti v dotazech.
  • SYSTEM_PROMPT = "Odpovídej pouze v češtině. Vždy uveď příklad kódu." — model se bude striktně držet češtiny a snažit se do svých odpovědí zahrnovat příklady kódu, pokud je to relevantní.

Tyto příklady ukazují, jak systémový prompt transformuje statický model v flexibilní nástroj, schopný se přizpůsobit různým scénářům použití. To je kriticky důležité pro vytváření specializovaných chatbotů, ať už jde o technického asistenta, kreativního partnera nebo vzdělávací nástroj.

Co je důležité

V současné fázi vývoje dosáhl náš konzolový LLM chat následujících důležitých výsledků:

  • Interaktivita: Uživatel může vést nepřetržitý dialog, klást otázky a dostávat odpovědi v reálném čase.
  • Řiditelné chování: Systémový prompt umožňuje snadno nastavit roli a styl komunikace modelu.
  • Modularita kódu: Logika interakce s LLM je oddělena od logiky uživatelského rozhraní, což zjednodušuje další vývoj a ladění.
  • Výkon: Vestavěné měření času generování odpovědi pomáhá monitorovat rychlost práce modelu.
  • Spolehlivost: Základní zpracování chyb zabraňuje havarijnímu ukončení programu při problémech s připojením nebo modelem.

Typické potíže a jejich řešení

Při práci s lokálními LLM a konzolovými aplikacemi se mohou vyskytnout některé běžné problémy:

  • Zaseknutí programu po zadání otázky: Nejčastěji k tomu dochází, protože Ollama není spuštěna nebo model ještě není plně načten do paměti. Ujistěte se, že Ollama aktivně běží (ollama list v terminálu) a dejte modelu čas na inicializaci.
  • Příliš krátké odpovědi od modelu: Pokud je v SYSTEM_PROMPT uvedeno "odpovídej stručně", model se bude řídit touto instrukcí. Pro podrobnější odpovědi změňte systémový prompt, odstraňte toto omezení nebo explicitně požádejte "uveď podrobnou odpověď s příklady".
  • KeyboardInterrupt při stisknutí Ctrl+C: Toto je standardní chování Pythonu. Pro vzdělávací projekt to není kritické, ale v produkčním prostředí lze main() obalit do try/except KeyboardInterrupt pro elegantnější ukončení.
  • Problémy s kódováním (nesmyslné znaky ve Windows): Lze vyřešit nastavením kódování terminálu na UTF-8, například příkazem $OutputEncoding = [System.Text.Encoding]::UTF8 v PowerShellu.

Další kroky: k plnohodnotnému dialogu

Přestože se náš konzolový chat stal výrazně funkčnějším, zatím nemá "paměť". Každý dotaz je zpracováván nezávisle a model si nepamatuje předchozí repliky dialogu. To znamená, že pokud se zeptáte "Můžeš udělat to samé, ale stručněji?", model nepochopí, o co jde. V další části se podíváme na to, jak implementovat historii zpráv, předáváním celého kontextu dialogu v každém novém dotazu, což jej promění ve skutečně inteligentního společníka.

— Editorial Team

Advertisement 728x90

Číst dál