3. Pętla rozmowy nad RAG

Jedno wywołanie ask_rag() z M4 nie jest jeszcze interfejsem rozmowy. Użytkownik potrzebuje kolejnego pytania, zakończenia pracy i czytelnego wyniku. W R2 pytania pozostają niezależne; historii jeszcze nie interpretujemy.

1. Sprawdź wejście przed wywołaniem modelu

Walidator zwraca oczyszczony tekst albo zgłasza określony błąd. Najpierw sprawdza typ, ponieważ liczba nie ma metody strip(). Potem usuwa spacje na brzegach, wykrywa puste wejście i sprawdza długość. Limit dotyczy znaków, nie tokenów modelu.

Do pliku kod/moj_m5.py: validate_chat_message()

def validate_chat_message(message: object, max_chars=MAX_MESSAGE_CHARS) -> str:
    if not isinstance(message, str):
        raise TypeError('invalid_message')
    clean = message.strip()
    if not clean:
        raise ValueError('empty_message')
    if len(clean) > max_chars:
        raise ValueError('message_too_long')
    return clean

Test walidacji: bez LLM

assert m.validate_chat_message("  Stan DocAssist?  ") == "Stan DocAssist?"
for value in ["   ", "x" * 1001, 42]:
    try:
        m.validate_chat_message(value)
    except (TypeError, ValueError) as error:
        print(type(error).__name__, str(error))

Oczekiwane kody to empty_message, message_too_long i invalid_message. Interfejs przechwytuje znane błędy wejścia i pozwala spróbować ponownie. Nie każdy wyjątek oznacza awarię całej sesji.

2. Zachowaj znaczenie wyniku RAG

Gotowe answer_from_documents() wywołuje m4_tools.ask_rag(). Przenosi status, reason, odpowiedź, źródła, diagnostykę, parametry i ID indeksu. Nie traktuje liczby źródeł jako oceny prawdziwości. Funkcja wyświetlająca pokazuje tekst oraz informacje potrzebne do analizy.

Do pliku kod/moj_m5.py: print_chat_result()

def print_chat_result(result, debug=False):
    print('DocAssist:', result.get('answer') or NO_DATA_MESSAGE)
    print('Status:', result.get('status', result.get('event')))
    if result.get('warnings'):
        print('Ostrzeżenia:', ', '.join(result['warnings']))
    if 'sources' in result:
        print('ŹRÓDŁA PRZEKAZANE MODELOWI\n' + format_sources(result.get('sources') or []))
    if debug:
        print(json.dumps(result.get('diagnostics', {}), ensure_ascii=False, indent=2))

Etykieta „ŹRÓDŁA PRZEKAZANE MODELOWI” jest celowa. Lista sources opisuje kontekst dopuszczony do generowania. Nie dowodzi, że wszystkie twierdzenia są potwierdzone ani że model wykorzystał każdy fragment. W ćwiczeniu zachowujemy widoczny status i ostrzeżenia.

3. Zbuduj pętlę

Do pliku kod/moj_m5.py: run_chat()

def run_chat(*, debug=False, similarity_threshold=DEFAULT_SIMILARITY_THRESHOLD, input_fn=input):
    """R2: niezależne wywołania RAG, bez interpretacji historii."""
    while True:
        raw = input_fn('Ty: ')
        try:
            message = validate_chat_message(raw)
        except (TypeError, ValueError) as e:
            print('DocAssist:', e)
            continue
        if message.casefold() in EXIT_COMMANDS:
            break
        try:
            print_chat_result(answer_from_documents(message, similarity_threshold=similarity_threshold), debug=debug)
        except Exception:
            logger.exception('Nieobsłużony błąd tury')
            print('Nie udało się obsłużyć pytania.')

Kolejność to odczyt, walidacja, rozpoznanie zakończenia, RAG i prezentacja. continue przechodzi do następnej wiadomości po błędzie; break kończy pętlę. Parametr input_fn domyślnie wskazuje input, lecz w teście może czytać z listy. Dzięki temu sprawdzisz interfejs bez ręcznego wpisywania danych.

Test pętli: komunikat o pustym wejściu, potem wyjście

wiadomosci = iter(["   ", "/exit"])
m.run_chat(input_fn=lambda prompt: next(wiadomosci))

Żadne pytanie nie dochodzi tu do RAG, więc nie potrzeba modelu. Do swobodnej rozmowy skonfiguruj moduł w trybie Ollama, a potem wywołaj m.run_chat(). Checkpoint obejmuje tylko zapisane żądania. Nie przełączaj trybu automatycznie po błędzie.

4. Ten sam TLS, dwie różne ścieżki

Pytanie brzmi: „Jak skonfigurować certyfikat TLS dla usługi docassist-api?”. W pilotażu top_k=3 i próg None dopuszczają kontekst, lecz bez procedury TLS. Model zwraca „Brak informacji w dostarczonym kontekście.”. Przy progu 1.0 wszystkie wyniki są odrzucone przed generacją.

Warunki Wynik RAG Interpretacja
Próg None answered, tekstowa odmowa, sources niepuste. Powstał tekst, ale nie procedura TLS.
Próg 1.0 insufficient_context / below_threshold, sources puste. Brak kontekstu po filtrze; bez wywołania LLM odpowiedzi.

Ćwiczenie 2. Rozdziel odmowę od filtrowania

  1. Uzupełnij trzy funkcje R2 i uruchom test etapu.
  2. W notebooku R2 wykonaj cztery próby RAG oraz trzy próby wejścia. Porównaj status, reason, similarity_threshold i sources dla TLS.
  3. Zapisz obserwację, konkretne pole jako dowód oraz wniosek.
  4. Wyjaśnij skutek umieszczenia kontroli /exit po wywołaniu RAG.

ENHANCER+ · Politechnika Warszawska · podręcznik do samodzielnej pracy · M5