Podręcznik uczestnika z objaśnieniami, przykładami i ćwiczeniami do samodzielnej nauki do Modułu 6

5. R4. Odpowiedzi, źródła i zachowanie asystenta

5.1. Ranking z tego samego wywołania i import M5

ENHANCER+ · Moduł 6 · Ewaluacja RAG · Podrozdział

Jeżeli po uzyskaniu odpowiedzi ponownie uruchomisz wyszukiwanie, otrzymasz nowy wynik. Nawet przy takich samych parametrach nie jest to zapis wejścia tamtej generacji. Potrzebujesz danych przechwyconych w chwili rzeczywistego wywołania RAG.

Co zachowuje M5 i co dodaje M6

M5 zwraca odpowiedź, źródła, statusy i diagnostykę, ale jego interfejs narzędzia nie zachowuje pełnego rankingu przed filtrem. M6 korzysta z pełnego wyniku backendu M4 i przechwytuje go, zanim narzędzie ograniczy pola. Dotychczasowy interfejs pozostaje zgodny, a pełny obiekt jest zapisany w raw_rag.

Przepływ pojedynczej tury wygląda tak: wiadomość → resolver i selektor M5 → adapter narzędzia → jedno wywołanie RAG → odpowiedź M5 oraz kopia pełnego wyniku RAG. Na podstawie tej kopii powstaje rekord retrieval i jego result_id trafia do retrieval_result_id rekordu asystenta.

R4 · Do pliku kod/moj_m6.py · zastąp całą definicję

def assistant_step(user_message, state, *, top_k=3, similarity_threshold=None,
                   use_selector=True, prompt_version='rag_v1'):
    captured = []
    original = assistant_backend.TOOL_REGISTRY['answer_from_documents']
    def adapter(question, top_k=3, similarity_threshold=None):
        raw = answer_from_documents(
            question, top_k, similarity_threshold, prompt_version=prompt_version)
        captured.append(deepcopy(raw))
        fields = ('result_id', 'status', 'reason', 'answer', 'sources', 'warnings',
                  'diagnostics', 'calls', 'index_id', 'mode', 'embedding_model',
                  'llm_model', 'top_k', 'similarity_threshold', 'prompt_version')
        return {k: deepcopy(raw.get(k)) for k in fields}
    assistant_backend.TOOL_REGISTRY['answer_from_documents'] = adapter
    try:
        result = assistant_backend.assistant_step(
            user_message, state, top_k=top_k, similarity_threshold=similarity_threshold,
            use_selector=use_selector)
    finally:
        assistant_backend.TOOL_REGISTRY['answer_from_documents'] = original
    result['raw_rag'] = captured[0] if captured else None
    return result

finally przywraca oryginalną funkcję także wtedy, gdy wystąpi wyjątek. Zagnieżdżony adapter przekazuje prompt_version do answer_from_documents. Ta funkcja jest dostarczona w m6_tools i przekazuje parametr do backendu RAG. Sam zapis nowej nazwy promptu w konfiguracji byłby niewystarczający.

Ten adapter zmienia rejestr funkcji na czas jednej synchronicznej tury. Jest przeznaczony do notebooka działającego sekwencyjnie. Nie używaj go bez przebudowy w równoległym serwerze, gdzie inna rozmowa mogłaby w tym czasie odczytać rejestr.

Gdy nie było RAG

Odmowa działania, prośba o doprecyzowanie i odczyt poprzednich źródeł mogą zakończyć turę bez nowego wyszukiwania. W takim wyniku raw_rag i retrieval_result_id pozostają puste. Nie wstawiaj do nich ostatniego rankingu tylko po to, aby wszystkie rekordy wyglądały tak samo.

Źródła poprzedniej odpowiedzi mają własne pochodzenie: rezultat show_last_sources wskazuje turę lub wynik, z którego je odczytano. To inne powiązanie niż ranking wygenerowany przez bieżącą turę. Sprawdzaj je osobno, szczególnie po błędzie lub zmianie tematu.

Uruchom serię asystenta

Notebook warsztatu · przykład wywołania

ids = (ref.read_json(ROOT / 'dane/przypadki/assistant_checkpoint_subset.json')
       if MODE == 'checkpoint' else None)
assistant_results = w.run_assistant(cases, config, E, test_ids=ids)
for row in assistant_results:
    print(row['test_id'], row['raw_result']['status'],
          row['raw_result']['reason'], row['retrieval_result_id'])
ref.save_json(E / 'R4_selected_result_ids.json',
              [r['result_id'] for r in assistant_results])

Gotowa pętla z dodatku używa Twojego assistant_step i build_retrieval_record. Dla każdego niezależnego przypadku tworzy nowy stan M5, a wewnątrz scenariusza przekazuje rzeczywiste odpowiedzi do kolejnych tur. Wynik ma nowy identyfikator M6, zachowuje raw_result_id M5 i osobny identyfikator wyniku wewnętrznego RAG.

R4 offline obejmuje T003, T007, T008, T010 i T021. Pełne nowe T101/T106 oraz cały S001 wymagają Ollama. To, że istnieje checkpoint embeddingu pytania T106 do R3, nie oznacza, że istnieje checkpoint każdego żądania selektora w R4.

Pełny import historycznych wyników

Notebook warsztatu · przykład wywołania

imported = ref.import_m5(
    ROOT / 'dane/import_M5/functional_tests.jsonl', E)
print('Nowo importowane rekordy historyczne:', len(imported))
# Zachowane są raw_result, raw_result_id, source_case_id i source_sha256.
# mapping_status=unmapped: brak automatycznego przypisania kontraktów M6.

Import zachowuje cały rekord historyczny w raw_result, jego oryginalny result_id, numer tury, pytanie, odpowiedź, decyzję, źródła i diagnostykę. Nowy rekord otrzymuje przestrzeń identyfikatorów M5-..., sumę kontrolną źródła i pochodzenie historical_ollama_M5. Powtórny import pomija już zapisane rekordy.

Brak pełnego rankingu w historycznym wyniku pozostaje brakiem. Nie rekonstruuj go i nie nazywaj rekonstrukcji dowodem tamtej generacji. Historyczne przypadki służą analizie błędów, ale bez jawnego zgodnego kontraktu nie są automatycznie oceniane jako M6-T101 czy M6-S001.

Kontrola R4: sprawdź, że ranking powiązanego rekordu jest identyczny z raw_result.raw_rag.retrieved_results, a jego raw_rag_result_id zgadza się z identyfikatorem przechwyconego wyniku. Test kontrolera wykonuje tę kontrolę bez drugiego retrievalu.

Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026