7. Rozmowa z asystentem w konsoli

7.1. Porównaj konsolę i zapis wyników

Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy

Orientacyjny czas: 15 min · Tryb pracy: najpierw własna próba, potem przykład

Każda sesja tworzy osobny folder. sesja.json przechowuje konfigurację, wersję Ollama, identyfikatory modeli oraz skróty kodu i danych. Każda tura ma własny plik. Zapis używa trybu tworzenia nowego pliku, aby nie nadpisywać wcześniejszego dowodu.

Plik: konsola.py. Poniżej znajduje się kompletna wersja do porównania po własnej próbie.

"""Konsola oraz zapis wyników rzeczywistych wywołań."""
import argparse
import hashlib
import json
import uuid
from datetime import datetime, timezone
from lokalny import ROOT, CONFIG, api, require_models
from indeks import load_index
from narzedzia import katalog_dokumentow
from pamiec import nowy_stan
from asystent import krok
from rag import format_sources


def nowy_zapis(prefix, selector):
    stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
    folder = ROOT / "wyniki" / f"{prefix}_{stamp}_{uuid.uuid4().hex[:8]}"
    folder.mkdir(parents=True, exist_ok=False)
    files = list(ROOT.glob("*.py")) + [ROOT / "config.json", ROOT / "dane/chunks.jsonl"]
    if (ROOT / "dane/index.json").exists():
        files.append(ROOT / "dane/index.json")
    snapshot = {"selector": selector, "config": CONFIG, "created_at": stamp,
        "models": require_models([CONFIG["chat_model"], CONFIG["embedding_model"]]),
        "ollama": api("/api/version"),
        "sha256": {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() for p in files}}
    zapisz(folder / "sesja.json", snapshot)
    return folder


def zapisz(path, data):
    with path.open("x", encoding="utf-8") as output:
        json.dump(data, output, ensure_ascii=False, indent=2, allow_nan=False)


def pokaz(result, debug=False):
    print(f"\nAsystent [{result['status']}]: {result['answer']}")
    if result["sources"] and result.get("reason") != "sources_returned":
        print("\nŹródła przekazane do odpowiedzi:\n" + format_sources(result["sources"]))
    if result["source_origin"]:
        print("Pochodzenie źródeł: tura", result["source_origin"]["turn_id"])
    for warning in result["warnings"]:
        print("Uwaga:", warning)
    if debug:
        print(json.dumps({k: result[k] for k in ("turn_id", "standalone_question", "history_used", "decision", "reason")}, ensure_ascii=False, indent=2))


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--wybor", choices=("reguly", "model"), default=CONFIG.get("selector", "reguly"))
    args = parser.parse_args()
    records, index = load_index()
    catalog = katalog_dokumentow(records)
    folder = nowy_zapis("rozmowa", args.wybor)
    state, debug = nowy_stan(), False
    print("Asystent dokumentów | wybór:", args.wybor)
    print("/exit /clear /history /sources /documents /debug | zapis:", folder)
    while True:
        try:
            message = input("\nTy: ").strip()
        except (EOFError, KeyboardInterrupt):
            print("\nKoniec rozmowy.")
            break
        if message == "/exit":
            break
        if message == "/clear":
            state = nowy_stan()
            print("Nowa rozmowa: pamięć i ostatnie źródła wyczyszczone. Pliki wcześniejszych wyników pozostają.")
            continue
        if message == "/history":
            print(json.dumps(state["history"], ensure_ascii=False, indent=2))
            continue
        if message == "/documents":
            print(json.dumps(catalog, ensure_ascii=False, indent=2))
            continue
        if message == "/debug":
            debug = not debug
            print("Podgląd decyzji:", debug)
            continue
        if message == "/sources":
            message = "Pokaż źródła"
        elif message.startswith("/"):
            print("Nieznana komenda. Użyj /exit /clear /history /sources /documents /debug.")
            continue
        result = krok(message, state, records, index, catalog, args.wybor)
        zapisz(folder / f"{result['conversation_id']}_{result['turn_id']:03d}_{result['result_id']}.json", result)
        pokaz(result, debug)


if __name__ == "__main__":
    try:
        main()
    except (OSError, ValueError, RuntimeError, KeyError) as exc:
        raise SystemExit("Nie można uruchomić asystenta: " + str(exc))

Zmiana config.json działa po ponownym uruchomieniu programu. Wersja modelu embeddingowego jest sprawdzana również względem indeksu. Dla rzetelnego porównania nie aktualizuj modeli ani plików w trakcie trwającej sesji. Jeśli wystąpi błąd zapisu wyników, uruchom program w katalogu z uprawnieniem zapisu.

Materiał uzupełniający do modułów 1–6 · wersja 1.0 · 30.09.2026