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í:
- 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.
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:
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...exceptzajišť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 funkcimainelegantně 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:
mainvolásend_request_to_llms uživatelským vstupem a zpracovává přijatou odpověď, zobrazuje ji v konzoli nebo hlásí chybu. Rozdělení odpovědnosti mezimain(správa rozhraní) asend_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 listv terminálu) a dejte modelu čas na inicializaci. - Příliš krátké odpovědi od modelu: Pokud je v
SYSTEM_PROMPTuvedeno "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". KeyboardInterruptpř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í lzemain()obalit dotry/except KeyboardInterruptpro 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]::UTF8v 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
Zatím žádné komentáře.