Budowa asystenta dokumentów
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
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