Podręcznik uczestnika z objaśnieniami, przykładami i ćwiczeniami do samodzielnej nauki do Modułu 6
| Site: | SEZAM - System Edukacyjnych Zasobów Akademickich i Multimedialnych |
| Course: | Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy |
| Book: | Podręcznik uczestnika z objaśnieniami, przykładami i ćwiczeniami do samodzielnej nauki do Modułu 6 |
| Printed by: | Gość |
| Date: | Tuesday, 3 November 2026, 9:50 AM |
Table of contents
- 1. Jak korzystać z podręcznika
- 2. R1. Plan ewaluacji i warunki akceptacji
- 3. R2. Kontrakty przypadków i scenariuszy
- 4. R3. Ranking i metryki wyszukiwania
- 5. R4. Odpowiedzi, źródła i zachowanie asystenta
- 6. R5. Od objawu do potwierdzonej przyczyny
- 7. R6. Raport, odtwarzalność i demonstracja
- 8. Złożenie projektu i rozwiązywanie problemów
- 9. Odpowiedzi i wskazówki do ćwiczeń
- 10. Słownik pojęć i granice interpretacji
- 11. Źródła ćwiczeń i mapa pakietu
1. Jak korzystać z podręcznika
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Moduł 6 prowadzi od działającego DocAssist do oceny, którą można sprawdzić i powtórzyć. Zbudujesz zestaw testowy, zapiszesz wyniki wyszukiwania i odpowiedzi, ocenisz je względem źródeł, zbadasz jedną zmianę i przygotujesz raport projektu. Książka zawiera objaśnienia oraz pełne definicje funkcji potrzebnych w warsztacie. Nie musisz równocześnie oglądać prezentacji.
Co jest potrzebne do samodzielnej pracy
Do czytania i rozwiązywania zadań analitycznych wystarczy ta książka. Końcowy dodatek „Źródła ćwiczeń i mapa pakietu” zawiera aktualne fragmenty korpusu, do których odwołują się przykłady. Do uruchomienia kodu potrzebujesz pakietu M12_M6_Student.zip, dodatku Modul_6_RAG_warsztat_kod.zip i środowiska Python z NumPy oraz JupyterLab. Pobierz je przed przejściem do pracy offline.
Standardowe notebooki R1–R6 pakietu używają gotowego m6_tools.py. Nowy warsztat używa moj_m6.py, w którym uzupełniasz trzynaście funkcji. Funkcje pomocnicze, zapis JSON i połączenie z M3–M5 są już dostarczone. Książka wyjaśnia, co robią, ale nie wymaga przepisywania całej aplikacji.
| Etap | Co budujesz | Jak sprawdzasz rezultat |
|---|---|---|
| R1 | Niezmienną konfigurację bazową | Próba podmiany istniejącej bazy zostaje zatrzymana |
| R2 | Dodatkową walidację kontraktów i przygotowanie pytania | Wadliwe referencje i brak samodzielnego pytania są wykrywane |
| R3 | Hit@k, pokrycie grup, rekord i wykonanie retrievalu | Metryki zgadzają się z jawnym rankingiem, bez generacji |
| R4 | Zapis RAG z tego samego wywołania i kontrolę recenzji | Odpowiedź wskazuje właściwy ranking i ocenę |
| R5 | Porównanie jednej zmiany i analizę trzech wykonań | Wykrywasz regresję, błąd krytyczny i zmienność |
| R6 | Wiersz macierzy dowodów | Wniosek prowadzi do konkretnego wyniku i recenzji |
Przygotuj własny warsztat
Rozpakuj pakiet Studenta. Z dodatku warsztatowego przenieś zawartość katalogu Student do swojego katalogu Student. Dodatek ma nowe nazwy plików. Jeżeli już pracowałeś z moj_m6.py, zachowaj własną wersję przed ponownym kopiowaniem.
Student/
kod/
m3_tools.py, m4_tools.py, m5_tools.py
m6_tools.py gotowy punkt odniesienia
moj_m6.py trzynaście funkcji do uzupełnienia
sprawdz_moj_m6.py kontrole etapów
notebooki/
M12_M6_warsztat_ksiazki.ipynb
dane/semantic_index/
checkpointy/
evaluation/ standardowe ćwiczenia
wyniki/warsztat_M6/ niezależne przebiegi warsztatu
W moj_m6.py zastępuj całą definicję oznaczoną WARSZTAT, zachowując nazwę, argumenty i wcięcia. W książce każda definicja ma wskazany etap. Kod gotowych pętli wykonawczych pozostaw na miejscu: wywołują one funkcje z Twojego modułu, więc wyniki dotyczą Twojej implementacji.
Środowisko i cykl pracy
Jeżeli środowisko M5 działa, użyj go ponownie. W nowym środowisku uruchom poniższe polecenia w terminalu otwartym w Student. Na Linux/macOS użyj python3 oraz .venv/bin/python.
py -m venv .venv
.venv\Scripts\python.exe -m pip install -r requirements.txt
.venv\Scripts\python.exe -m jupyterlab
Otwórz notebook M12_M6_warsztat_ksiazki.ipynb. Po każdej zmianie pliku zapisz go i wykonaj komórkę z importlib.reload. Zwykły powtórny import nie wczytuje zmienionego kodu. Kontroler etapu poda listę brakujących definicji albo wynik sprawdzeń. Komunikat „nieukończone” nie jest zaliczeniem testu.
Notebook warsztatu · przykład wywołania
from pathlib import Path
from uuid import uuid4
import sys, json, importlib
ROOT = Path.cwd().resolve()
if not (ROOT / 'konfiguracja.json').exists():
ROOT = ROOT.parent
assert (ROOT / 'konfiguracja.json').exists(), 'Otwórz notebook z Student/notebooki.'
sys.path.insert(0, str(ROOT / 'kod'))
import moj_m6 as w
import m6_tools as ref
import sprawdz_moj_m6 as check
importlib.reload(w)
MODE = 'checkpoint'
config = w.configure(ROOT, mode=MODE)
cases = w.load_cases(ROOT)
WORK = ROOT / 'wyniki' / 'warsztat_M6' / uuid4().hex
E = WORK / 'evaluation'
E.mkdir(parents=True)
print('Katalog tego przebiegu:', WORK)
Zmienna ROOT zawsze wskazuje pakiet Studenta. WORK jest nowym katalogiem danego przebiegu warsztatu. Jego podkatalog evaluation zawiera pliki o tych samych nazwach co standardowe ćwiczenia, lecz nie nadpisuje wcześniejszej pracy. Przy ponownym otwarciu rozpoczętej sesji wskaż istniejący WORK, zamiast tworzyć kolejny, jeśli chcesz tylko dokończyć recenzje.
Co znaczy offline
Tryb checkpoint odtwarza zapisane żądania z wcześniejszego pilota. W R3 dostępnych jest dziewięć wyszukiwań z listy checkpoint_subset.json. W R4 lista assistant_checkpoint_subset.json obejmuje pięć sprawdzonych przypadków. To dwa różne zakresy. Brak checkpointu nie uruchamia modelu automatycznie.
Nowe pytanie, nowy prompt i pełne scenariusze asystenta wymagają trybu ollama oraz lokalnych modeli. Internet nie musi być potrzebny podczas generowania, jeżeli biblioteki i modele pobrano wcześniej. Czytanie książki i analiza dostarczonych wyników pozostają dostępne całkowicie offline.
Aktualny korpus
DocAssist jest fikcyjny. W M6 pracujesz na rzeczywistym pakiecie M3–M5: cztery identyfikatory dokumentów, dziesięć fragmentów i wektory o 1024 wymiarach. W książce M1 występowały inne przykłady, m.in. journalctl i kopia katalogu przed aktualizacją. Nie przenoś tych procedur do oceny M6. Tutaj podstawą jest tekst aktualnego indeksu, przytoczony w dodatku źródłowym. Polecenia systemowe występują jako treść dokumentów, a asystent ich nie wykonuje.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
2. R1. Plan ewaluacji i warunki akceptacji
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Dobra demonstracja potwierdza, że pewna ścieżka zadziałała w określonej sytuacji. Plan ewaluacji określa, jakie sytuacje sprawdzisz, po czym rozpoznasz błąd i jak szeroki wniosek możesz wyciągnąć. Plan zapisujesz przed oglądaniem odpowiedzi, aby nie dopasowywać kryteriów do uzyskanych rezultatów.
Od potrzeby użytkownika do warunku testu
Użytkownik DocAssist chce znaleźć lokalną procedurę i jej źródło. Wynik musi więc być poprawny wobec dokumentacji, obejmować wymagane warunki i wskazywać fragment pozwalający skontrolować treść. Sama płynność językowa nie wystarczy. Jeśli użytkownik prosi o wykonanie restartu, poprawne zachowanie jest inne: aplikacja odmawia wykonania i może wyjaśnić granicę swoich możliwości.
Rozłóż jakość na warstwy. Wyszukiwanie znajduje fragmenty. Filtr i składanie kontekstu wybierają materiał dla modelu. Generowanie tworzy tekst. Warstwa rozmowy ustala znaczenie zaimków i właściwe źródła poprzedniej odpowiedzi. Warstwa narzędzi wybiera i wykonuje dozwoloną funkcję. Niepowodzenie całej tury może mieć początek w dowolnym z tych miejsc.
| Pytanie oceniającego | Dowód | Czego dowód jeszcze nie potwierdza |
|---|---|---|
| Czy znaleziono właściwy fragment? | Ranking i oczekiwane chunk_id | Że fragment trafił do kontekstu |
| Czy kontekst wystarcza? | Pełne teksty po filtrze | Że model wykorzystał wszystkie fakty |
| Czy odpowiedź ma oparcie? | Twierdzenie oraz wspierający je fragment | Że odpowiedź jest kompletna |
| Czy wykonano właściwe narzędzie? | Decyzja, argumenty i wynik wykonania | Że odpowiedź realizuje całą intencję |
| Czy zmiana pomaga? | Te same kontrakty przed i po zmianie | Że poprawa obejmuje niebadane zastosowania |
Cel, kryterium, metryka i próg
Cel opisuje potrzebę, np. „operator ma poznać procedurę po nieudanym uruchomieniu”. Kryterium mówi, co oceniasz, np. kompletność. Metryka nadaje wynikowi mierzalną postać, np. ocena 0–2 albo pokrycie wymaganych grup. Próg akceptacji mówi, jaki wynik uznasz za wystarczający. Ustal również warunki krytyczne, które przesądzają o niezaliczeniu niezależnie od innych ocen.
Przykładowy plan może wymagać poprawnego statusu i narzędzia, wszystkich obowiązkowych faktów, braku twierdzeń zabronionych i poprawnych źródeł. To propozycja dla tego projektu, nie regulamin oceniania kursu. Nie wybieraj progu dopiero wtedy, gdy zobaczysz średnią.
Przykład planu do zapisania
Cel: sprawdzić, czy DocAssist odpowiada zgodnie z korpusem M3–M5.
Zakres: wyszukiwanie, kontekst, odpowiedź, źródła, rozmowa i narzędzia.
Poza zakresem: wykonywanie operacji systemowych, wdrożenie produkcyjne.
Baza: top_k=3, brak progu score, rag_v1, parametry pakietowe.
Zestaw: 24 przypadki / 13 kategorii / 26 tur.
Offline: podzbiory R3 i R4, jawnie oddzielone od importu M5.
Ocena: rubryki 0–2; ND tylko dla kryteriów niewłaściwych dla przypadku.
Krytyczny warunek T101: przywrócenie zatwierdzonej kopii po niepowodzeniu.
Decyzja: krytyczne naruszenie daje fail; brak dowodu daje inconclusive.
Stabilność modelu: trzy nowe wykonania Ollama, komplet wyników i ocen.
Uzupełnij evaluation_plan.md w swoim WORK/evaluation, korzystając z szablonu pakietowego. Dopisz autora, zakres faktycznie wykonywanych prób, sposób rozstrzygania ocen spornych i warunki wyboru konfiguracji końcowej. Jeśli nie możesz wykonać części prób, zaplanuj sposób opisania tego ograniczenia.
Zbuduj ochronę konfiguracji bazowej
configure(ROOT, mode=...) odczytuje indeks i sprawdza środowisko. Funkcja zwraca faktyczne parametry obsługiwane przez pakiet. Twoja funkcja zapisuje je raz. Identyczny ponowny zapis jest dozwolony, a inna konfiguracja w tym samym pliku zostaje odrzucona.
R1 · Do pliku kod/moj_m6.py · zastąp całą definicję
def save_baseline(path, config):
check_config(config)
path = Path(path)
if path.exists():
if read_json(path) != config:
raise ValueError('Istnieje inna baza. Rozpocznij osobny eksperyment.')
return read_json(path)
save_json(path, config)
return deepcopy(config)
Notebook warsztatu · przykład wywołania
w.save_baseline(E / 'baseline_config.json', config)
ref.initialize_document(ROOT, 'evaluation_plan.md',
E / 'evaluation_plan.md')
print('Uzupełnij plan przed ocenianiem wyników.')
Sprawdzenie tej funkcji nie wymaga modelu. Zapisz bazę, utwórz kopię ze zmienionym top_k i spróbuj zapisać ją pod tą samą nazwą. Oczekujesz wyjątku, a plik powinien nadal zawierać pierwotną wartość. Wariant eksperymentalny zapiszesz później jako candidate_config.json.
Ćwiczenie R1
Napisz cel i regułę decyzji dla pytania o treść zgłoszenia awarii. Oddziel kryterium kompletności od warunku zakazującego umieszczania haseł w zgłoszeniu. Następnie uzupełnij save_baseline i uruchom kontrolę R1.
Sprawdź się: potrafisz powiedzieć, co oznacza pass, fail i inconclusive, zanim zobaczysz odpowiedź. W bazie nie wpisujesz wartości z przykładowego slajdu, lecz odczytujesz rzeczywiste ustawienia.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
2.1. Konfiguracja, wersje i pochodzenie danych
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Podrozdział
Konfiguracja ma pozwolić ustalić, jaki system naprawdę badano. Dwa pliki nazwane „baseline” mogą wskazywać inne modele, indeksy lub prompty. Dlatego rozdziel parametry wykonywania, identyfikatory danych i opis organizacyjny eksperymentu.
Tag modelu i digest
Tag bge-m3:latest jest nazwą używaną w żądaniu. Digest identyfikuje konkretną zawartość modelu. Sam tag nie gwarantuje, że na innym komputerze za miesiąc będzie używany ten sam model. W pakiecie digest embeddingu pochodzi z index_metadata.json. Digest LLM odczytywany jest z backendu albo manifestu checkpointów. Nie wymyślaj brakującej identyfikacji.
Indeks obejmuje trzy powiązane pliki: embeddings.npy, chunks.jsonl i index_metadata.json. Manifest podaje wymiar, liczbę rekordów, metrykę, normalizację i sumy kontrolne. Zmiana jednego pliku bez pozostałych może spowodować przypisanie wektora do niewłaściwego tekstu. Niezgodność modelu embeddingowego wymaga właściwego indeksu albo jego przebudowy zgodnie z M3.
Schemat slajdu a schemat pakietu
Slajdy pokazują dokumentację konfiguracji w postaci zagnieżdżonych obiektów. W wykonywalnym pakiecie M6 modele mają płaskie pola, a ich digests są osobną mapą. Zachowaj schemat oczekiwany przez kod. Dodatkowy opis eksperymentu zapisuj obok, bez podmieniania typu pola używanego przez wykonawcę.
| Znaczenie | Zapis pakietu M6 | Jak wykorzystać w raporcie |
|---|---|---|
| Tag embeddingu | embedding_model | Pełna nazwa modelu pytania i indeksu |
| Digest embeddingu | model_digest | Identyfikacja z manifestu indeksu |
| Digests modeli | model_digests[tag] | Osobno embedding i LLM |
| Indeks | index_id | Powiązanie z manifestem i plikami |
| Parametry RAG | top_k, similarity_threshold, prompt_version | Faktyczne argumenty wykonania |
| Generacja | temperature, seed, num_predict, num_ctx | Obsługiwane wartości pakietowe |
| Rozmowa | history_window, use_selector | Zakres historii i sposób wyboru narzędzi |
Warstwa check_config dopuszcza tylko parametry rzeczywiście obsługiwane przez dostarczony adapter. Nie wystarczy zmienić liczby w JSON, by zmienić zachowanie modelu. Eksperymenty z nową temperaturą lub modelem wymagają najpierw rozszerzenia ścieżki wykonania i jej kontroli. W tym warsztacie zmieniasz przede wszystkim top_k, próg albo wersję promptu.
Zapisz opis środowiska
Poniższy kod odczytuje metadane i sumy kontrolne. Zapisz wynik w environment_manifest.json w katalogu swojego eksperymentu. Nie dopisuj go do konfiguracji podawanej wykonawcy, bo compare_configs traktuje każdą różnicę parametru jako zmianę.
Notebook warsztatu · przykład wywołania
import hashlib, platform
def sha256(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
index_dir = ROOT / ref.read_json(ROOT / 'konfiguracja.json')['index_dir']
manifest = ref.read_json(index_dir / 'index_metadata.json')
snapshot = {
'python': platform.python_version(),
'mode': MODE,
'index_manifest': manifest,
'test_cases_sha256': sha256(ROOT / 'evaluation/test_cases.jsonl'),
'code_sha256': sha256(ROOT / 'kod/moj_m6.py'),
'config': config,
}
ref.save_json(E / 'environment_manifest.json', snapshot)
Wersję DocAssist odczytujesz z metadanych dokumentów, a wersję zestawu z kontraktów. Wersja fikcyjnej aplikacji, wersja korpusu, wersja zestawu i wersja schematu JSON mają różne znaczenia. Gdy korpus nie ma osobnej deklaracji wersji, identyfikuj go sumą pliku fragmentów i index_id, zamiast wpisywać dowolny numer.
Skąd pochodzą wyniki
| Pochodzenie | Co oznacza | Jakich wniosków nie wyciągać |
|---|---|---|
| assistant_run, mode=ollama | Nowa tura z lokalnymi wywołaniami | Nie uogólniaj jednego wyniku na cały model |
| assistant_run, mode=checkpoint | Odtworzona tura aplikacji z zapisanymi odpowiedziami | Nie mierz bieżącej szybkości ani niezależnej zmienności |
| isolated_retrieval | Osobny test wyszukiwania | Nie przypisuj rankingu późniejszej odpowiedzi |
| same_assistant_call | Ranking przechwycony podczas danej tury RAG | Nie zakładaj poprawności treści tylko dlatego, że zapis jest pełny |
| historical_ollama_M5 | Pełny import dawnego wyniku | Nie zmieniaj go automatycznie w nowy test M6 |
Datę importu można zapisać osobno. Oryginalny czas wykonania pozostaje w raw_result. Zachowaj oba konteksty czasowe, jeśli są potrzebne do raportu. W zapisie checkpointu pole original_elapsed_s dotyczy historycznego wywołania, a current_elapsed_s czasu odtworzenia. Żadne z nich samo nie opisuje nowej inferencji na Twoim komputerze.
Kontrola R1: druga osoba powinna umieć wskazać konkretny indeks, modele, kontrakty i pochodzenie wyników bez pytania autora, co miał na myśli.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
3. R2. Kontrakty przypadków i scenariuszy
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Kontrakt testu opisuje, co ma się wydarzyć dla konkretnego wejścia. Zamiast jednej wzorcowej odpowiedzi zapisujesz wymagane fakty, dopuszczalne źródła, oczekiwane zachowanie i treści zabronione. Dzięki temu poprawna parafraza nie przegrywa tylko dlatego, że różni się słowami od klucza.
Zestaw startowy i zakres
Pakiet zawiera 24 przypadki w 13 kategoriach. Jeden z nich jest scenariuszem z trzema turami, więc pełne wykonanie asystenta obejmuje 26 tur. Minimum 20 przypadków i 8 kategorii jest propozycją dydaktyczną przyjętą w materiałach. Różnorodność obejmuje m.in. pytania proste, parafrazy, procedury, wiele źródeł, brak danych, działania zabronione, metadane, doprecyzowanie i odwołania do poprzednich źródeł.
Oczekiwania w pakiecie są propozycją do świadomego zatwierdzenia. Otwórz źródła i sprawdź fakty. Jeżeli zmieniasz kontrakt, zmień wersję zestawu i wykonaj powiązane próby ponownie. Nie poprawiaj oczekiwania tylko dlatego, że model odpowiedział inaczej.
Pełny rekord zamiast skrótu ze slajdu
Poniżej znajduje się cały przypadek T001 odczytany z pakietu. W JSON wartości logiczne zapisuje się małymi literami, a brak wartości jako null. W Pythonie odpowiadają im True, False i None.
Pełny kontrakt z pakietu Studenta
{
"test_id": "M6-T001",
"test_set_version": "m6-1.0",
"category": "direct_question",
"question": "Jak sprawdzić stan usługi docassist-api?",
"initial_history": [],
"expected_status": "answered",
"expected_reason": "answer_generated",
"expected_tool": {
"name": "answer_from_documents",
"arguments": {
"question": "Jak sprawdzić stan usługi docassist-api?"
}
},
"expected_sources": [
{
"document_id": "DOC-INSTR",
"section": "Ponowne uruchomienie usługi",
"required_chunk_ids": [
"DOC-INSTR-dd6055d675-0002"
],
"alternative_chunk_ids": []
}
],
"required_facts": [
"systemctl is-active docassist-api",
"active"
],
"optional_facts": [],
"forbidden_claims": [],
"critical_conditions": [],
"applicable_criteria": {
"context": [
"trafnosc",
"pokrycie",
"wystarczalnosc",
"niski_szum",
"spojnosc",
"identyfikowalnosc"
],
"answer": [
"poprawnosc",
"kompletnosc",
"oparcie",
"precyzja",
"adekwatnosc",
"ostroznosc"
]
},
"retrieval_question": "Jak sprawdzić stan usługi docassist-api?",
"contract_note": "Przeczytaj źródła i zatwierdź oczekiwania przed wykonaniem. Oceny wyników wypełniasz samodzielnie."
}
question jest wiadomością użytkownika. retrieval_question jest samodzielnym pytaniem do izolowanego R3. initial_history opisuje jawne wejście historii. applicable_criteria wyznacza kryteria, które rzeczywiście mają sens w tym przypadku. Puste forbidden_claims nie znosi ogólnych zasad pracy aplikacji; oznacza, że kontrakt nie dodaje własnych szczegółowych zakazów. Możesz je doprecyzować przed pomiarem, np. zakazać twierdzenia, że sprawdzanie stanu samo restartuje usługę.
Jak czytać grupy źródeł
Każdy obiekt expected_sources jest jedną wymaganą grupą dowodów. Wszystkie required_chunk_ids tej grupy muszą być obecne, chyba że w kontrakcie wskazano pojedynczy alternatywny fragment, który samodzielnie zastępuje całą grupę. Fragment pomocniczy nie jest takim substytutem. W dostarczonym zestawie listy alternatyw są puste.
Rozważ dwie grupy: procedurę restartu i procedurę przywrócenia kopii. Znalezienie pierwszej grupy daje częściowe pokrycie. Nie oznacza, że pytanie o obie sytuacje ma już pełną podstawę odpowiedzi. Jeśli informacja jest rozbita na dwa fragmenty jednej grupy, znalezienie tylko jednego może dać Hit@k, ale jeszcze nie spełnia tej grupy.
Dwa przypadki, których nie wolno zlewać
M6-T101 pyta o czynności po zmianie konfiguracji oraz postępowanie po nieudanym uruchomieniu. Wymaga restartu, kontroli stanu, interpretacji active, przywrócenia zatwierdzonej kopii i zgłoszenia administratorowi. Przywrócenie kopii jest warunkiem krytycznym. To szersze pytanie niż historyczne „Co zrobić po zmianie konfiguracji DocAssist?”. Zmiana samej etykiety starego wyniku nie czyni go próbą T101.
M6-T106 pyta o TLS i narzuca similarity_threshold=1.0. Test zakłada, że żaden score z danego przebiegu nie osiąga 1,0. Po spełnieniu tej przesłanki cały kontekst zostaje odrzucony i oczekujesz insufficient_context z below_threshold. Nie oczekujesz generacji odpowiedzi RAG. Selektor narzędzia może wcześniej wykonać własne wywołanie modelu. Jeśli score osiągnie próg, oznacz niespełniony warunek testu, zamiast przypisywać systemowi niepowodzenie.
Pytanie bez odpowiedzi przy wyłączonym progu to inny test. System może pobrać tematycznie podobne fragmenty i dopiero w wygenerowanej treści poprawnie stwierdzić brak informacji. Techniczny status answered nie wyklucza takiej poprawnej odpowiedzi ograniczającej zakres.
Scenariusz wieloturowy
M6-S001 zaczyna nową sesję. Pierwsza tura pyta o zmianę konfiguracji, druga o „jej stan”, a trzecia o źródła odpowiedzi. Każda tura ma osobne oczekiwania i osobną recenzję. W drugiej turze prawidłowo rozwiązane pytanie samodzielne powinno prowadzić do nowego RAG. W trzeciej show_last_sources korzysta z zapisanych źródeł ostatniego RAG bez ponownego wyszukiwania.
Podczas wykonania R4 historia zawiera rzeczywiste odpowiedzi aplikacji. Nie zastępuj błędnej odpowiedzi wzorcem, żeby dalsza tura wyglądała poprawnie. W R3 używasz jawnego retrieval_question ze scenariusza: izolujesz wyszukiwanie od błędu rozwiązywania odniesienia. Wyniki obu doświadczeń odpowiadają na różne pytania.
Ćwiczenie R2
Wymyśl parafrazę pytania o informacje wymagane w zgłoszeniu. Na podstawie DOC-REG zbuduj kontrakt: fakty, źródło, status, narzędzie i twierdzenie zabronione. Nadaj nowy identyfikator i wersję zestawu. Zapisz też, dlaczego nowe pytanie może wymagać trybu Ollama.
Sprawdź się: nie wpisujesz oczekiwanego fragmentu do testu braku danych tylko po to, aby obliczyć Hit@k. Potrafisz wskazać różnicę między pojedynczym przypadkiem a scenariuszem, zachowując osobne oczekiwania dla tur.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
3.1. Walidacja zestawu i przygotowanie pytania
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Podrozdział
Walidator powinien zatrzymać test, którego nie można jednoznacznie wykonać lub ocenić. Nie powinien obiecywać, że sprawdził prawdziwość wszystkich faktów. Struktura danych podlega kontroli automatycznej, a zgodność kontraktu ze źródłem wymaga lektury człowieka.
Co kontroluje pakiet
Pełny walidator w m6_tools sprawdza m.in. wymagane pola, unikalność identyfikatorów, typy danych, liczebność zestawu, obecność T101/T106/S001, kolejne numery tur, źródła, narzędzia i kryteria. Raport ma pole valid oraz listę errors. Niepoprawny typ danych powinien dać błąd raportu, a nie ciche pominięcie przypadku.
Twoja funkcja rozszerza tę kontrolę o związki między polami: brak narzędzia nie powinien jednocześnie planować RAG, wymagane fragmenty powinny zgadzać się z sekcją, a wewnątrz grupy nie powinno być powtórzeń. Ten podział pozwala korzystać ze wspólnego kontraktu kursowego i uczyć się walidacji zależności.
R2 · Do pliku kod/moj_m6.py · zastąp całą definicję
def validate_test_cases(cases, chunks):
report = reference.validate_test_cases(cases, chunks)
if not report['valid']:
return report
known = {c['chunk_id']: c for c in chunks}
errors = report['errors']
for t in flatten_cases(cases):
prefix = f"{t['test_id']}/{t['turn_id']}: "
tool = t['expected_tool']
if tool is None and t['retrieval_question'] is not None:
errors.append(prefix + 'Brak narzędzia, ale zaplanowano RAG.')
if tool and tool['name'] == 'answer_from_documents':
if t['retrieval_question'] is None:
errors.append(prefix + 'Brak pytania do izolowanego R3.')
for group in t['expected_sources']:
ids = group['required_chunk_ids'] + group.get('alternative_chunk_ids', [])
if len(ids) != len(set(ids)):
errors.append(prefix + 'Powtórzony fragment w grupie.')
# Sekcja identyfikuje główne źródło. Alternatywa może mieć inną sekcję.
for cid in group['required_chunk_ids']:
if known[cid]['metadata'].get('section') != group.get('section'):
errors.append(prefix + 'Sekcja niezgodna z ' + cid)
if t['critical_conditions'] and not t['required_facts']:
errors.append(prefix + 'Warunek krytyczny wymaga jawnego oczekiwania.')
report['valid'] = not errors
return report
Notebook warsztatu · przykład wywołania
chunks = ref.read_jsonl(ROOT / 'dane/semantic_index/chunks.jsonl')
validation = w.validate_test_cases(cases, chunks)
ref.save_json(E / 'test_cases_validation.json', validation)
assert validation['valid'], validation['errors']
(E / 'test_cases.jsonl').write_text(
''.join(json.dumps(c, ensure_ascii=False) + '\n' for c in cases),
encoding='utf-8')
contracts = {(t['test_id'], t['turn_id']): t for t in w.flatten_cases(cases)}
Jeżeli raport jest niepoprawny, popraw zestaw i ponów kontrolę. Nie usuwaj błędnych wierszy z wyników dopiero na końcu. Zapis raportu umożliwia później wykazanie, jaki zestaw rzeczywiście poddano wykonaniu.
Czego walidator nie rozstrzyga
Identyfikator może istnieć i mieć właściwy dokument, a mimo to fragment może nie odpowiadać na pytanie. Lista required_facts może zawierać prawdziwe zdanie, które nie jest potrzebne w tym przypadku. Nawet poprawne typy danych nie potwierdzają, że warunek krytyczny jest dobrze wybrany. Dlatego przed pomiarem otwórz wszystkie wymagane źródła i spróbuj zbudować odpowiedź wyłącznie z ich treści.
Przy migracji pytań z M3 zachowaj pierwotny identyfikator w dodatkowym polu, np. source_question_id. Nowy test M6 dostaje własny test_id. Historia pochodzenia nie zastępuje sprawdzenia, że intencja, źródła i warunki są nadal te same.
Przygotowanie pytania do R3
W tym warsztacie przygotowanie jest jawne i deterministyczne: odczytujesz retrieval_question z kontraktu. Nie prosisz LLM o parafrazę. Taka decyzja pozwala zbadać wyszukiwanie przy stałym pytaniu. Ocena resolvera rozmowy pozostaje zadaniem R4.
R2 · Do pliku kod/moj_m6.py · zastąp całą definicję
def prepare_search_question(test_case):
question = test_case.get('retrieval_question')
if question is None:
return None
if not isinstance(question, str) or not question.strip():
raise ValueError('Pytanie retrieval_question musi być niepustym tekstem.')
return question
None oznacza, że tura nie wymaga izolowanego retrievalu, np. prośba o wcześniej zapisane źródła. Nie zamieniaj tego na pusty tekst i nie uruchamiaj wyszukiwania „na wszelki wypadek”. Nie usuwaj również białych znaków lub nie poprawiaj sformułowania pytania po jego zatwierdzeniu bez nowej wersji kontraktu: dla checkpointu nawet mała zmiana wejścia może oznaczać inne żądanie.
Sprawdź nie tylko poprawny przypadek
W kontrolowanej kopii danych zmień jeden chunk_id na nieistniejący. Następnie powtórz test_id, usuń T101 i dodaj nieznany argument narzędzia. Każda próba powinna zakończyć się valid=False. Nie modyfikuj oryginalnego pliku podczas tych sprawdzeń. Po kontroli uruchom walidację niezmienionego zestawu i oczekuj valid=True.
Sprawdź się: raport błędu wskazuje konkretną przyczynę, a funkcja przygotowania pytania nie wykonuje połączenia z modelem ani nie zmienia samodzielnie intencji testu.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
4. R3. Ranking i metryki wyszukiwania
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
W R3 pytasz, czy system znalazł dowody potrzebne do odpowiedzi. Nie uruchamiasz generowania. Utrzymujesz stałe pytanie samodzielne, indeks i parametry wyszukiwania, dzięki czemu nie mieszasz problemu retrievalu z problemem formułowania odpowiedzi.
Od wektora do uporządkowanej listy
retrieve korzysta z funkcji M3 i M4. Tworzy embedding pytania modelem zgodnym z indeksem, porównuje go z wektorami fragmentów i zwraca pierwsze top_k wyników. Znormalizowany rekord ma chunk_id, text, metadata i score. W aktualnym indeksie używana jest metryka cosine i normalizacja wektorów.
Wartość score służy do uporządkowania i filtrowania fragmentów. Wynik 0,81 nie oznacza 81% prawdopodobieństwa poprawnej odpowiedzi. Fragment o wysokim podobieństwie może dotyczyć tego samego systemu, ale nie zawierać potrzebnej procedury. Nie porównuj surowych score między różnymi modelami i indeksami jako wspólnej skali jakości.
Hit@1, Hit@k i pozycja
Dla jednego pytania Hit@k odpowiada na pytanie, czy w pierwszych k pozycjach znajduje się co najmniej jeden wymagany albo jawnie dopuszczony fragment. Wynik jest logiczny. Dopiero dla wielu pytań liczysz odsetek. expected_rank oznacza pozycję pierwszego takiego fragmentu w zapisanym rankingu, liczoną od 1.
| Oczekiwany fragment | Ranking | Hit@1 | Hit@3 | Pozycja |
|---|---|---|---|---|
| B | A, B, C | false | true | 2 |
| B | B, A, C | true | true | 1 |
| D | A, B, C | false | false | null |
| Brak oczekiwanego źródła | A, B, C | null | null | null |
To dane kontrolowane do nauki metryki, nie wynik modelu. Przy top_k=3 nie wolno obliczyć Hit@5 z trzech zachowanych pozycji. Możesz zwiększyć top_k w nowej serii albo wcześniej jawnie zapisać szerszy ranking. Brak fragmentu w pierwszej trójce nie dowodzi jego braku na pozycji czwartej.
R3 · Do pliku kod/moj_m6.py · zastąp całą definicję
def hit_at_k(test_case, ranking, k, recorded_top_k):
if type(k) is not int or not 1 <= k <= recorded_top_k:
raise ValueError('k musi mieścić się w zapisanym top_k.')
expected = {
cid
for group in test_case['expected_sources']
for cid in group['required_chunk_ids'] + group.get('alternative_chunk_ids', [])
}
if not expected:
return None
return any(row['chunk_id'] in expected for row in ranking[:k])
recorded_top_k opisuje szerokość żądanego i zachowanego rankingu. Jeżeli indeks ma mniej rekordów niż k, lista może być krótsza i nadal kompletna dla danego żądania. Nie uzupełniaj jej fikcyjnymi pozycjami. Dla pytań bez oczekiwanego źródła funkcja zwraca None: to N/D, nie niepowodzenie retrievalu.
Trafienie fragmentu a pokrycie grup
Hit@k nie mierzy kompletności odpowiedzi wieloźródłowej. Załóżmy, że test wymaga dwóch grup. Pierwsza zawiera restart i kontrolę stanu, druga przywrócenie kopii. Obecność wyłącznie pierwszej daje Hit@k=true, any_group=true, all_groups=false i pokrycie grup 0,5.
Wymagane fragmenty jednej grupy łączysz warunkiem „wszystkie”. Grupy także muszą być spełnione wszystkie, jeśli oceniasz pełne pokrycie. Alternatywa zastępuje całą daną grupę, więc nie wymagasz jednocześnie wszystkich alternatyw. Taką semantykę zapisujesz jawnie przed testem.
R3 · Do pliku kod/moj_m6.py · zastąp całą definicję
def source_metrics(test_case, ranking, context):
groups = test_case['expected_sources']
def coverage(items):
ids = {row['chunk_id'] for row in items}
hits = [
set(g['required_chunk_ids']) <= ids
or bool(set(g.get('alternative_chunk_ids', [])) & ids)
for g in groups
]
if not hits:
return {'any_group': None, 'all_groups': None, 'group_coverage': None}
return {
'any_group': any(hits),
'all_groups': all(hits),
'group_coverage': sum(hits) / len(hits),
}
return {'ranking': coverage(ranking), 'context': coverage(context)}
Funkcja liczy metryki osobno dla rankingu oraz końcowego kontekstu. Możliwa jest sytuacja: ranking.all_groups=true, ale context.all_groups=false. Wtedy wyszukiwanie znalazło źródło, a dalszy etap go nie przekazał. Przyczyny szukasz w progu lub filtrowaniu, a nie od razu w modelu embeddingowym.
Agregacja i właściwy mianownik
Dla jednego wybranego run_id odfiltruj błędy wykonania. Następnie zbierz wartości Hit@k dla przypadków z oczekiwanym źródłem. Nie dopisuj zer za None, pominięte tury bez RAG i błędy techniczne. Raportuj ich liczby oddzielnie, bo samo wykluczenie bez ujawnienia może upiększyć wynik.
Poniższy fragment wykonaj po utworzeniu retrieval_results w następnej części „Zapis pełnego rankingu i wykonanie R3”. Notebook warsztatowy zachowuje tę kolejność.
Notebook warsztatu · przykład wywołania
completed = [r for r in retrieval_results if r['status'] == 'completed']
values = [r['hit_at_k'] for r in completed if r['hit_at_k'] is not None]
print({
'hits': sum(values),
'evaluated_questions': len(values),
'hit_rate': sum(values) / len(values) if values else None,
'not_applicable': sum(r['hit_at_k'] is None for r in completed),
'not_completed': len(retrieval_results) - len(completed),
})
Średnia odwrotność pozycji, MRR, jest rozszerzeniem: dla znalezionego fragmentu liczysz 1 / expected_rank, dla nieznalezionego 0, a przypadki bez oczekiwanego fragmentu wyłączasz. Jeśli zachowujesz wyłącznie top-k, nazwij tę miarę MRR@k. Nie przedstawiaj jej jako oceny rankingu obejmującego cały indeks.
Ćwiczenie R3
Dla dwóch wymaganych grup zbuduj ranking, który daje Hit@1=true i all_groups=false. Następnie dodaj brakujący dowód na pozycji czwartej. Porównaj metryki przy top_k równym 3 i 5. Uzupełnij funkcje metryk i sprawdź, że pytanie bez oczekiwanego fragmentu zwraca None.
Sprawdź się: potrafisz wyjaśnić wysokie Hit@k przy niewystarczającym kontekście. W każdym odsetku podajesz licznik, mianownik oraz zakres k.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
4.1. Zapis pełnego rankingu i wykonanie R3
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Podrozdział
Wynik jest użytecznym dowodem, gdy zachowuje dane pozwalające odtworzyć ocenę. Lista samych nazw źródeł nie pokazuje kolejności, score ani fragmentów odrzuconych przez filtr. Zapisuj więc ranking przed progiem i kontekst po jego zastosowaniu.
Cztery identyfikatory
test_id identyfikuje pytanie lub scenariusz, turn_id turę, run_id wykonanie serii, a result_id konkretny wynik. Dwa wykonania tego samego testu mają wspólny test_id, lecz inne result_id. contract_hash identyfikuje treść kontraktu danej tury, dzięki czemu zmiana oczekiwań nie może zostać niezauważona podczas łączenia ocen.
Wiersz retrieval_runs.jsonl zawiera pełny tekst i metadane wyników. Zajmuje więcej miejsca, ale nie wymaga zgadywania, jaki fragment miał model. deepcopy chroni zapis w pamięci przed późniejszą modyfikacją listy rankingu. Nie chroni pliku przed ręczną edycją, dlatego surowe wyniki zachowuj bez zmian.
R3 · Do pliku kod/moj_m6.py · zastąp całą definicję
def build_retrieval_record(test_case, ranking, context, config, run_id,
*, origin='isolated_retrieval', question=None):
expected = {
cid for g in test_case['expected_sources']
for cid in g['required_chunk_ids'] + g.get('alternative_chunk_ids', [])
}
rank = next((i for i, r in enumerate(ranking, 1)
if r['chunk_id'] in expected), None)
return {
'schema_version': 'm6.1', 'result_id': uuid4().hex,
'run_id': run_id, 'created_at': datetime.now(timezone.utc).isoformat(),
'test_id': test_case['test_id'], 'turn_id': test_case['turn_id'],
'test_set_version': test_case['test_set_version'],
'contract_hash': fingerprint(test_case),
'origin': origin, 'mode': config['mode'], 'config': deepcopy(config),
'search_question': question or prepare_search_question(test_case),
'ranking': deepcopy(ranking), 'context': deepcopy(context),
'metrics': source_metrics(test_case, ranking, context),
'hit_at_1': hit_at_k(test_case, ranking, 1, config['top_k']),
'hit_at_k': hit_at_k(test_case, ranking, config['top_k'], config['top_k']),
'metric_k': config['top_k'], 'expected_rank': rank,
'metric_reason': None if expected else 'no_expected_chunk',
'status': 'completed',
}
Funkcja zachowuje schemat pakietu i dopisuje hit_at_1, hit_at_k, metric_k, expected_rank oraz przyczynę nieoceniania metryki. Nazwy metrics.ranking.any_group i all_groups odpowiadają znaczeniu retrieval_any_hit i retrieval_all_hit na slajdach. To jawne mapowanie schematu, nie dwie różne miary.
Próg i filtr struktury
Filtr score odrzuca wyniki poniżej progu. Wartość None wyłącza ten filtr, ale nie wyłącza sprawdzania pustego tekstu, metadanych i powtórzonych fragmentów. prepare_context_results nadal wybiera użyteczne strukturalnie rekordy. Oddziel brak wyników (no_results), odrzucenie wszystkich przez próg (below_threshold) i brak rekordów użytecznych strukturalnie (no_usable_results).
R3 · Do pliku kod/moj_m6.py · zastąp całą definicję
def evaluate_retrieval_turn(test_case, config, run_id):
check_config(config)
effective = dict(config, **test_case.get('config_overrides', {}))
question = prepare_search_question(test_case)
if question is None:
return None
try:
ranking = rag_backend.retrieve(question, top_k=effective['top_k'])
context, reason = rag_backend.select_context_results(
ranking, effective['similarity_threshold'])
record = build_retrieval_record(test_case, ranking, context, effective, run_id)
record['context_reason'] = reason
if test_case['test_id'] == 'M6-T106' and any(r['score'] >= 1 for r in ranking):
record.update(status='precondition_failed', error='T106 wymaga score < 1.')
return record
except Exception as exc:
record = build_retrieval_record(test_case, [], [], effective, run_id)
record.update(status='execution_error', error=str(exc), metrics=None,
hit_at_1=None, hit_at_k=None, expected_rank=None,
metric_reason='execution_error')
return record
Wyjątek techniczny tworzy rekord execution_error, a metryki stają się nieoceniane. Gdyby pozostawić puste listy i zwykłe zero Hit@k, uszkodzony checkpoint wyglądałby jak poprawnie wykonane, lecz nietrafne wyszukiwanie. Zapis precondition_failed w T106 sygnalizuje inną sytuację: nie zaszło założenie wymagane do tej kontrolowanej próby.
Uruchomienie serii i odczyt wyniku
Gotowa pętla run_retrieval z dodatku nadaje jeden run_id serii, wywołuje Twoje evaluate_retrieval_turn i natychmiast dopisuje rekordy. Tury z retrieval_question=None pomija. Poniższy kod uruchamia sprawdzony podzbiór offline.
Notebook warsztatu · przykład wywołania
ids = (ref.read_json(ROOT / 'dane/przypadki/checkpoint_subset.json')
if MODE == 'checkpoint' else None)
retrieval_results = w.run_retrieval(cases, config, E, test_ids=ids)
for row in retrieval_results:
print(row['test_id'], row['turn_id'], row['status'],
row['hit_at_1'], row['hit_at_k'], row['metrics'])
ref.save_json(E / 'R3_selected_result_ids.json',
[r['result_id'] for r in retrieval_results])
Nie uruchamiaj całego zestawu z założeniem, że każdy tekst ma checkpoint. Gdy tworzysz własne pytania, wybierz świadomie Ollama i rozpocznij osobną serię. Zachowaj listę planowanych przypadków, wykonanych rekordów, pominiętych tur oraz błędów technicznych.
Rzeczywisty przykład T004
Pytanie o nieudane uruchomienie wymaga fragmentu DOC-INSTR-dd6055d675-0003. W dostarczonym pilocie R3 przy top_k=3 pojawiają się fragment restartu, testu po zmianie oraz kontroli znaków. Brakuje procedury przywrócenia kopii. Mimo podobieństwa tematycznego pokrycie wymaganej grupy wynosi 0. Przy top_k=5 grupa pojawia się w zapisanym rankingu i kontekście.
Ten wynik pochodzi z odtworzenia danych dostarczonego indeksu. Nie przenoś go na T101 tylko dlatego, że oba pytania wspominają niepowodzenie. Nie dowodzi również, że wygenerowana odpowiedź po zmianie top_k będzie kompletna. To osobna próba R4.
Kontrola R3: potrafisz otworzyć jeden wiersz JSONL i wskazać konkretne wejście, ranking, filtr, kontekst, konfigurację i kontrakt. Liczby wynikają z tych danych, a nie z opisu słownego eksperymentu.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
4.2. Ręczna ocena fragmentów i kontekstu
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Podrozdział
Metryka oparta na identyfikatorach jest szybka, ale nie rozumie treści. Ręczna ocena odpowiada na pytanie, dlaczego dany fragment pomaga albo przeszkadza. Przeczytaj wszystkie pozycje top-k, także odrzucone przez próg. Inaczej nie ustalisz, czy filtr usunął szum, czy ważny dowód.
Pięć klas przydatności
| Klasa | Jak rozpoznać | Przykład dla pytania o nieudane uruchomienie |
|---|---|---|
| relevant | Zawiera wymagany fakt | Przywrócenie zatwierdzonej kopii i zgłoszenie administratorowi |
| supporting | Uzupełnia temat, lecz nie jest wymaganym dowodem | Dodatkowa informacja diagnostyczna, jeśli kontrakt jej nie wymaga |
| topical_insufficient | Ten sam temat, brak potrzebnego kroku | Zwykły restart bez postępowania po niepowodzeniu |
| irrelevant | Nie wnosi informacji do zadania | Tekst kontrolny polskich znaków |
| conflicting_or_outdated | Jest sprzeczny lub dotyczy innej obowiązującej wersji | Nieaktualna procedura przecząca aktualnej instrukcji |
Ostatni przykład opisuje klasę, a nie dodatkowy dokument w dostarczonym korpusie. Nie wymyślaj nieaktualnego źródła w wynikach. Jeżeli budujesz kontrolowany przykład sprzeczności, nadaj mu osobną etykietę demonstracyjną.
Zapis klasyfikacji może mieć postać dodatkowego pliku chunk_reviews.jsonl: result_id, pozycja, chunk_id, klasa i uzasadnienie. Ten plik uzupełnia ocenę całego kontekstu. Uzasadnienie powinno wymieniać konkretny obecny albo brakujący fakt, np. „brak instrukcji przywrócenia kopii”, zamiast „fragment słaby”.
Rubryka kontekstu
| Kryterium | 0 | 1 | 2 |
|---|---|---|---|
| trafnosc | Większość poza zadaniem | Wynik mieszany | Fragmenty odpowiadają na pytanie |
| pokrycie | Brak kluczowych faktów | Część faktów | Wszystkie fakty wymagane kontraktem |
| wystarczalnosc | Konieczne istotne domysły | Drobne braki | Odpowiedź możliwa wprost ze źródeł |
| niski_szum | Dominują zbędne treści | Część treści zbędna | Prawie cały kontekst użyteczny |
| spojnosc | Sprzeczne fragmenty | Niejasne wersje lub warunki | Brak sprzeczności |
| identyfikowalnosc | Brak identyfikatorów | Metadane częściowe | Źródła i lokalizacje można odtworzyć |
Każde kryterium opisuje inny aspekt. Kontekst może mieć pełne metadane i niskie pokrycie. W przykładzie T004 można uzasadnić profil 1, 0, 0, 1, 2, 2: część treści jest tematycznie związana, kluczowej procedury brak, występuje szum, ale nie ma jawnej sprzeczności i źródła są identyfikowalne. Jest to propozycja kalibracji, nie automatyczny wynik programu.
N/D oraz ocena jeszcze niewypełniona
W tekście używamy N/D, w JSON "ND". Wartość null w formularzu oznacza, że człowiek jeszcze nie wystawił oceny. Nie jest synonimem N/D ani zera. O tym, które kryterium dotyczy testu, decyduje kontrakt w applicable_criteria.context, a nie wygoda oceniającego po zobaczeniu wyniku.
W pakiecie przypadki bez oczekiwanych źródeł mają pustą listę właściwych kryteriów kontekstu. Możesz mimo tego zapisać klasyfikację fragmentów i obserwację o pozornej podstawie odpowiedzi. Jeśli chcesz nadać im liczbową rubrykę, zdefiniuj taką regułę przed pomiarem i nadaj nową wersję kontraktu.
Utwórz formularz, a potem go uzupełnij
Notebook warsztatu · przykład wywołania
chosen = [r for r in retrieval_results
if r['test_id'] in ('M6-T001', 'M6-T004', 'M6-T020')]
context_drafts = [
ref.review_template(r, contracts[(r['test_id'], r['turn_id'])], 'context')
for r in chosen
]
draft_path = E / 'retrieval_reviews_draft.json'
if not draft_path.exists():
ref.save_json(draft_path, context_drafts)
else:
saved_ids = {v['result_id'] for v in ref.read_json(draft_path)}
assert saved_ids == {r['result_id'] for r in chosen}, (
'Formularz dotyczy innego przebiegu. Zachowaj go i wybierz właściwe wyniki.')
# Otwórz plik i uzupełnij oceny, autora, uzasadnienia oraz decyzję.
SAVE_CONTEXT_REVIEWS = False
if SAVE_CONTEXT_REVIEWS:
by_id = {r['result_id']: r for r in retrieval_results}
for review in ref.read_json(E / 'retrieval_reviews_draft.json'):
r = by_id[review['result_id']]
ref.save_review(E / 'retrieval_reviews.jsonl', review, r,
contracts[(r['test_id'], r['turn_id'])])
Ponowne wykonanie komórki nie nadpisuje istniejącego formularza. Jeśli uruchomisz nową serię R3, identyfikatory się zmienią: zachowaj poprzednią pracę i użyj nowego katalogu albo wróć do wyników właściwego przebiegu.
Szablon nie wpisuje sam ocen 2. Wybierz minimum trzy różne konteksty, np. T001, T004 i T020. Dla każdego oceń wszystkie właściwe kryteria, uzasadnij N/D, sprawdź warunki krytyczne i nadaj verdict. Nie oznaczaj jako ukończonego formularza, który nadal zawiera null lub pusty komentarz.
Funkcja pakietowa save_review kontroluje spójność formularza. W R4 zbudujesz własną dodatkową kontrolę rodzaju wyniku. Wyniki i recenzje połączysz przez result_id oraz zgodny contract_hash. Plik retrieval_results.jsonl jest odtwarzalnym widokiem tego połączenia. Surowe retrieval_runs.jsonl pozostają bez zmian.
Małe zadanie kalibracyjne
Oceń dwa konteksty: pierwszy zawiera tylko wymaganą procedurę, drugi tę samą procedurę i trzy długie fragmenty poboczne. Wyjaśnij, dlaczego pokrycie może pozostać równe 2, a niski szum spaść. Następnie porównaj oceny z drugą osobą. Różnicę rozstrzygnij przez wskazanie konkretnych zdań, a nie przez uśrednienie dwóch liczb bez dyskusji.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
5. R4. Odpowiedzi, źródła i zachowanie asystenta
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Teraz oceniasz to, co otrzymał użytkownik. Wiesz już, jakie fragmenty były dostępne. Sprawdzasz więc oddzielnie prawdziwość twierdzeń, kompletność odpowiedzi, sposób użycia źródeł i wykonanie intencji rozmowy. Status answered opisuje ścieżkę aplikacji, a nie końcową ocenę jakości.
Rozbij odpowiedź na sprawdzalne twierdzenia
Dla każdego ważnego zdania wskaż fragment, który je potwierdza. Stosuj etykiety: potwierdzone, częściowo potwierdzone, niepotwierdzone, sprzeczne, zbyt ogólne albo poza zakresem. Jedna odpowiedź może łączyć wszystkie te rodzaje zdań. Prawdziwy wstęp nie usprawiedliwia dopisania nieistniejącego parametru w dalszej części.
| Twierdzenie | Ocena wobec aktualnego korpusu |
|---|---|
| Po zmianie config.yaml wykonaj restart i sprawdź is-active | Potwierdzone przez sekcję Ponowne uruchomienie usługi |
| Wynik kontroli powinien być active | Potwierdzone przez tę samą sekcję |
| Gdy uruchomienie się nie uda, przywróć zatwierdzoną kopię i zgłoś problem | Potwierdzone przez sekcję Kopia konfiguracji |
| Ustaw tls_auto_enable na true | Niepotwierdzone; taki parametr nie wynika z korpusu |
| Zgłoszenie zostanie na pewno rozwiązane w 30 minut | Niepotwierdzone; regulamin nie gwarantuje czasu rozwiązania |
Nie pomyl ostatniego zdania z timeout_seconds=30: to inny temat i jednostka. Wysoka precyzja liczby nie oznacza poprawności jej zastosowania.
Rubryka odpowiedzi
| Kryterium | Co sprawdzasz |
|---|---|
| poprawnosc | Czy twierdzenia i działanie są zgodne ze źródłem i kontraktem |
| kompletnosc | Czy podano wszystkie wymagane fakty i warunki |
| oparcie | Czy treść wynika z rzeczywiście przekazanego kontekstu |
| precyzja | Czy nazwy, wartości, polecenia i warunki są jednoznaczne |
| adekwatnosc | Czy odpowiedź realizuje bieżącą intencję |
| ostroznosc | Czy system ujawnia brak danych i respektuje granice działania |
Stosuj 0 — niespełnione, 1 — częściowo, 2 — spełnione, ze wskazaniem dowodu. Dla odmowy wykonania działania i doprecyzowania pakiet zwykle przewiduje poprawność i ostrożność, a pozostałe kryteria oznacza ND. Nie wymagaj od poprawnej odmowy kompletnej procedury, której użytkownik nie powinien otrzymać jako wykonanego działania.
Błąd krytyczny pozostaje osobną decyzją
Odpowiedź demonstracyjna na T101 brzmi: „Po zmianie konfiguracji uruchom ponownie usługę”. Samo zdanie może być prawdziwe, ale brakuje kontroli stanu, kopii po niepowodzeniu i zgłoszenia administratorowi. Pominięcie krytycznego warunku kopii oznacza niezaliczenie przypadku. Średnia sześciu ocen nie może tego ukryć.
Na slajdach znacznik nosi nazwę critical_fail. W pakiecie recenzji M6 jego odpowiednikiem jest critical_error. W zapisach wykonywalnych używaj critical_error, a w raporcie możesz opisać go jako krytyczne niepowodzenie. Nie twórz dwóch niezależnych flag o różnych wartościach.
Znacznik wymaga uzasadnienia: wskaż zdanie albo pominięty warunek, źródło i skutek dla użytkownika. Nie uznawaj za krytyczny samego długiego stylu, innej kolejności akapitów czy braku informacji, której kontrakt nie wymagał.
Źródła: format i znaczenie
Kontrola formalna sprawdza, czy etykieta, np. [S1], istnieje i wskazuje dostępne źródło. Kontrola semantyczna sprawdza, czy to źródło potwierdza dane twierdzenie. Poprawna etykieta przy błędnym fragmencie nie zapewnia oparcia. Z kolei błąd formatu cytowania nie dowodzi automatycznie, że wszystkie fakty odpowiedzi są zmyślone.
W historycznej turze 1 M5 procedura restartu i kontroli stanu ma oparcie w źródłach. Odpowiedź kończy się jednak zbiorczą informacją z S1 i S2, bez wymaganych etykiet [S1], [S2] przy twierdzeniach. Diagnostyka zgłasza missing_citations. To dobry przykład rozdzielenia jakości treści od formatu i przypisania źródeł.
Rozmowa i narzędzia
Sprawdź najpierw oryginalną wiadomość, potem standalone_question, decyzję selektora, argumenty i wykonane narzędzie. Jeżeli pytanie zależne rozwinięto poprawnie, a wybrano złą funkcję, diagnoza dotyczy wyboru narzędzia. Nie nazywaj tego automatycznie błędem pamięci rozmowy.
W historycznej turze 2 M5 pytanie „Jak sprawdzić jej stan?” rozwinięto do pytania o usługę, lecz selektor wybrał show_last_sources. Wynik miał status answered, ale nie realizował intencji. Ten rzeczywisty przykład pokazuje, dlaczego sama liczba zakończonych tur zawyża ocenę użyteczności.
Ćwiczenie R4
Oceń demonstracyjną odpowiedź T101 powyżej. Wskaż osobno prawdziwe stwierdzenie, brakujące fakty i krytyczne pominięcie. Następnie przeanalizuj historyczną turę 2 M5 i ustal najwcześniejszy etap, dla którego masz bezpośredni dowód błędu.
Sprawdź się: nie przypisujesz całej odpowiedzi jednej etykiety tylko dlatego, że zaczyna się poprawnie. Umiesz odróżnić brak kontekstu, poprawne zgłoszenie braku informacji i błąd narzędzia.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
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
5.2. Recenzje, decyzje i łączenie wyników
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Podrozdział
Surowy wynik opisuje wykonanie. Recenzja opisuje ocenę człowieka względem kontraktu. Zachowuj je w oddzielnych plikach: jedna osoba może ponownie ocenić ten sam wynik, lecz nie powinna w tym celu przepisywać odpowiedzi modelu ani zmieniać jego diagnostyki.
Tożsamość recenzji
result_id musi wskazywać dokładnie oceniony rekord. test_id, turn_id i contract_hash muszą się z nim zgadzać. Sam podobny tekst pytania nie jest wystarczającym kluczem połączenia. W historii M5 i nowych testach M6 mogą występować podobne pytania przy innej historii, konfiguracji i oczekiwaniach.
W warsztacie rozróżniasz review_kind=context oraz review_kind=answer. Kontekst oceniasz na rekordzie retrieval, a odpowiedź i zachowanie na rekordzie asystenta. Poniższa funkcja pilnuje tej granicy i korzysta z pełnej walidacji pakietowej.
R4 · Do pliku kod/moj_m6.py · zastąp całą definicję
def save_review(path, review, result, test_case):
# Pakiet waliduje pola, N/D, autora, uzasadnienia, kontrakt i błąd krytyczny.
# Ta warstwa dodatkowo sprawdza spójność tożsamości i etap oceny.
identity = ('result_id', 'test_id', 'turn_id')
if any(review.get(k) != result.get(k) for k in identity):
raise ValueError('Recenzja wskazuje inną turę lub wynik.')
kind = review.get('review_kind')
if kind == 'context' and 'ranking' not in result:
raise ValueError('Ocena kontekstu wymaga rekordu retrieval.')
if kind == 'answer' and 'raw_result' not in result:
raise ValueError('Ocena odpowiedzi wymaga rekordu asystenta.')
if test_case['test_id'] != result['test_id'] or test_case['turn_id'] != result['turn_id']:
raise ValueError('Przekazano kontrakt innej tury.')
return reference.save_review(path, review, result, test_case)
Pakiet odrzuca nieukończone oceny, brak autora, puste uzasadnienia, niewłaściwe ND, niezgodny kontrakt, krytyczny błąd połączony z verdict innym niż fail oraz próby niewykonane poprawnie. Nie rozstrzyga, czy człowiek trafnie ocenił fakt. Walidacja recenzji jest kontrolą spójności, a nie automatycznym recenzentem merytorycznym.
Przykład kompletnej oceny odmowy
T007 prosi o wykonanie restartu. Oczekiwane zachowanie to odmowa, a nie uruchomienie polecenia. W dostępnym wyniku checkpoint asystent wyjaśnia, że opisuje procedury, ale nie wykonuje operacji systemowych. Dla tej obserwacji można uzasadnić ocenę poprawności i ostrożności równą 2. Pozostałe kryteria są ND według kontraktu.
Notebook warsztatu · przykład wywołania
r = next(r for r in assistant_results if r['test_id'] == 'M6-T007')
t = contracts[('M6-T007', 1)]
print(r['raw_result']['answer'])
assert r['raw_result']['status'] == 'rejected'
assert r['raw_result']['reason'] == 'forbidden_action'
answer_review = ref.review_template(r, t, 'answer')
answer_review.update(
reviewer='Uczestnik — własny przegląd T007',
scores={k: 2 if k in ('poprawnosc', 'ostroznosc') else 'ND'
for k in ref.ANSWER},
justification={k: ('Aplikacja odmawia wykonania operacji i wyjaśnia granicę.'
if k in ('poprawnosc', 'ostroznosc')
else 'Kryterium nie dotyczy odmowy według kontraktu T007.')
for k in ref.ANSWER},
critical_error=False,
critical_reason='W zapisanej turze nie wykonano żądanego działania.',
verdict='pass',
)
# Zapisz dopiero po przeczytaniu własnego wyniku i sprawdzeniu treści oceny.
SAVE_T007_REVIEW = False
if SAVE_T007_REVIEW:
w.save_review(E / 'assistant_reviews.jsonl', answer_review, r, t)
Przykład jest konkretną oceną zachowania T007, a nie szablonem ustawiającym wszystkim wynikom najwyższe oceny. Przeczytaj własny raw_result przed zapisem. Gdy otrzymasz inne zachowanie, zmień uzasadnienie i decyzję stosownie do dowodów. Oprócz odmowy oceń co najmniej jedną odpowiedź RAG i jedno doprecyzowanie.
pass, fail i inconclusive
pass oznacza spełnienie jawnych wymagań danego kontraktu, fail stwierdzone naruszenie, a inconclusive brak wystarczających dowodów do rozstrzygnięcia. Błąd środowiska zapisujesz jako błąd wykonania, nie jako ocenę merytoryczną zero. inconclusive nie powinno służyć do ukrywania oczywistego naruszenia warunku krytycznego.
Nie wyciągaj ostatecznej decyzji wyłącznie z progu średniej. Przykład: oceny 2, 0, 2, 2, 2, 1 mogą ukrywać pominięcie obowiązkowej procedury. Osobny znacznik krytyczny i wskazanie naruszonego wymagania są bardziej informacyjne niż sama suma.
Połącz dane i policz braki
Notebook warsztatu · przykład wywołania
raw = ref.read_jsonl(E / 'retrieval_runs.jsonl')
reviews = ref.read_jsonl(E / 'retrieval_reviews.jsonl')
joined = ref.join_reviews(raw, reviews)
(E / 'retrieval_results.jsonl').write_text(
''.join(json.dumps(row, ensure_ascii=False) + '\n' for row in joined),
encoding='utf-8')
print('Ocenione wyniki retrieval:', len(joined), 'z', len(raw))
Widok połączenia obejmuje wyłącznie wyniki, które mają ukończone recenzje. Pozostałych nie traktuj jako zaliczonych. Podaj w raporcie liczbę wykonanych wyników, ocenionych wyników i brakujących recenzji. Przy wielu run_id wybierz jawny zakres porównania. Nie licz kilku ocen tego samego pytania jako kilku nowych przypadków testowych.
Jeśli drugi recenzent ma ocenić ten sam wynik, zapisz jego ocenę w osobnym pliku albo zaprojektuj wersjonowanie recenzji. Domyślny zapis blokuje duplikat result_id w jednym pliku, aby przypadkowe ponowne uruchomienie komórki nie podwoiło wagi wyniku.
Sprawdź się: próba połączenia oceny z wynikiem innej tury kończy się błędem. Poprawne ND nie zwiększa ani nie zmniejsza średniej, a brak oceny pozostaje widocznym brakiem.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
6. R5. Od objawu do potwierdzonej przyczyny
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
„Asystent odpowiedział źle” jest początkiem analizy. Diagnoza wymaga ustalenia, jaki fakt był potrzebny, gdzie powinien być dostępny i na którym etapie został utracony lub zniekształcony. Oddziel obserwację od hipotezy. Dowód może potwierdzać brak fragmentu w top-k, ale jeszcze nie wyjaśniać, dlaczego ranking go pominął.
Przejdź przez ślad od początku
Zacznij od korpusu: czy informacja w ogóle istnieje i jest aktualna? Następnie sprawdź ekstrakcję i podział na fragmenty, obecność rekordu w indeksie, pytanie samodzielne, ranking, wynik progu, końcowy kontekst, odpowiedź i przypisanie źródeł. W rozmowie dodaj kontrolę historii, selekcji, walidacji i wykonania narzędzia.
Nie diagnozuj błędu generowania tylko na podstawie braku faktu w odpowiedzi. Jeżeli faktu nie było w kontekście, wcześniejszą potwierdzoną usterką może być wyszukiwanie. Jeśli fakt był obecny, a model go pominął, dopiero wtedy masz podstawę do badania promptu lub generowania. W jednej turze mogą współistnieć różne problemy, więc rejestr może zawierać kilka error_id wskazujących jeden result_id.
Trzy rzeczywiste przypadki do analizy
| Dowód | Obserwacja | Najwcześniejszy potwierdzony etap | Hipoteza do sprawdzenia |
|---|---|---|---|
| T004, R3, top_k=3 | Nie ma fragmentu procedury awaryjnej | retrieval | Wymagany fragment leży poza pierwszą trójką |
| Historyczna tura_1 M5 | Treść procedury ma oparcie, ale brak prawidłowych etykiet cytowań | sources_presented | Model nie zachował wymaganego formatu źródeł |
| Historyczna tura_2 M5 | Resolver wskazał usługę, selektor wybrał pokazanie źródeł | tool_selection | Selektor błędnie rozpoznał intencję pytania |
Historyczny tls_domyslnie odpowiada „Brak informacji w dostarczonym kontekście.” Nie jest dowodem halucynacji tylko dlatego, że pobrano kontekst o DocAssist. Możesz osobno skrytykować przydatność retrievalu i uznać ostrożność wygenerowanej treści za poprawną. To rozróżnienie chroni przed błędną diagnozą całego systemu.
Rejestr błędu
stage wskazuje etap, a problem_code typ problemu ze słownika. W pakiecie zapisujesz jeden kod na rekord. Na slajdzie występuje również skrót z listą problem_codes: jeśli problemów jest kilka, w tym schemacie utwórz kilka rekordów albo przed pomiarem wprowadź jawnie nową wersję schematu. Nie zamieniaj bez zapowiedzi napisu na listę, której narzędzia nie obsługują.
Notebook warsztatu · przykład wywołania
r = next(r for r in retrieval_results if r['test_id'] == 'M6-T004')
error = ref.draft_error(r, 'retrieval', 'retrieval_miss')
error.update(
symptom='Brak procedury awaryjnej w top-3.',
evidence='W rankingu nie ma DOC-INSTR-dd6055d675-0003.',
hypothesis='Wymagana grupa leży poniżej bieżącego top_k.',
user_impact='Kontekst nie wystarcza do opisania przywrócenia kopii.',
proposed_change='Sprawdzić top_k=5 bez innych zmian.',
regression_test_ids=['M6-T004', 'M6-T020'],
)
# Potwierdź dowód w swoim rankingu przed zapisaniem.
ref.save_json(E / 'error_draft.json', error)
Przykład ma pola objawu, dowodu, hipotezy, skutku i proponowanej zmiany. Uzasadnij je na podstawie rzeczywistego result_id. Twierdzenie „model embeddingowy jest zły” jest zbyt szerokie wobec jednego nietrafionego fragmentu. Trafniejsza hipoteza brzmi: „wymagany fragment leży niżej niż bieżące top_k; sprawdzę szerszy ranking przy tym samym pytaniu i indeksie”.
Klasy problemów i zależności
Zmiana dokumentu, chunkingu lub modelu embeddingowego wymaga spójnego nowego indeksu i ponownego wykonania etapów zależnych. Zmiana progu lub top_k wymaga nowego wyboru kontekstu, a jeśli oceniasz odpowiedzi, także ich ponownego uzyskania. Zmiana promptu może być badana na zamrożonym kontekście, jeśli celem jest izolowany wpływ instrukcji generowania. Pełna tura asystenta obejmuje jednak także selektor, którego zachowanie musi pozostać widoczne.
| Rodzaj zmiany | Co powtarzasz |
|---|---|
| Dokumenty, chunking, embedding | Przygotowanie indeksu oraz wszystkie późniejsze etapy |
| top_k, próg | Retrieval/kontekst oraz odpowiedzi, jeśli są przedmiotem oceny |
| Prompt odpowiedzi | Generację i ocenę na jawnie ustalonej podstawie |
| Selektor lub resolver | Całe scenariusze rozmowy wraz z narzędziami |
| Format prezentacji źródeł | Kontrolę formatowania i mapowania, a nie dowolny nowy ranking |
Priorytet problemu
Najpierw zajmij się ryzykiem nieprawdziwej lub niedozwolonej instrukcji i krytycznymi warunkami. Potem analizuj błędy częste albo utrudniające realizację głównego zadania. Samo zwiększenie liczby poprawnych łatwych pytań nie równoważy pogorszenia jednego krytycznego scenariusza.
Ćwiczenie R5
Dla trzech przypadków z tabeli zapisz osobno objaw, bezpośredni dowód i hipotezę. Wskaż test, który mógłby obalić każdą hipotezę. Następnie wybierz jedną zmianę możliwą do zbadania offline i określ, których wniosków nadal nie będzie można wyciągnąć.
Sprawdź się: jeśli nie masz danych o wcześniejszym etapie, zapisujesz brak dowodu, a nie pewną przyczynę. Negatywny wynik eksperymentu także jest użytecznym rezultatem.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
6.1. Jedna zmiana, porównanie i regresja
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Podrozdział
Eksperyment powinien mieć hipotezę, bazę, wariant, ten sam zestaw przypadków i regułę decyzji. Hipotezę zapisz przed uruchomieniem. Jeśli jednocześnie zmienisz top_k, prompt i model, wynik pokaże zachowanie całego nowego systemu, ale nie wskaże wpływu pojedynczej zmiany.
Zbuduj kontrolę konfiguracji
R5 · Do pliku kod/moj_m6.py · zastąp całą definicję
def compare_configs(baseline, candidate):
changes = {k: [baseline.get(k), candidate.get(k)]
for k in set(baseline) | set(candidate)
if k != 'config_id' and baseline.get(k) != candidate.get(k)}
if len(changes) != 1:
raise ValueError('Eksperyment wymaga dokładnie jednej zmiany.')
return changes
config_id jest etykietą wariantu, więc samo nadanie nowej nazwy nie liczy się jako zmiana badanego czynnika. Pozostałe różnice pozostają istotne. Informacje organizacyjne, np. autor i data eksperymentu, zapisuj w karcie eksperymentu, a nie w porównywanych parametrach wykonywania.
Przykład top_k 3 wobec 5
Hipoteza brzmi: przy tej samej reprezentacji pytania i indeksie szerszy zestaw udostępni wymaganą grupę dla T004. W wariancie nie zmieniasz progu, promptu, modelu ani kontraktów. Zachowujesz oba przebiegi z osobnymi identyfikatorami.
Notebook warsztatu · przykład wywołania
baseline = ref.read_json(E / 'baseline_config.json')
candidate = dict(baseline, config_id='topk5', top_k=5)
changes = w.compare_configs(baseline, candidate)
ref.save_json(E / 'candidate_config.json', candidate)
ids = ref.read_json(ROOT / 'dane/przypadki/checkpoint_subset.json')
before = w.run_retrieval(cases, baseline, E, test_ids=ids)
after = w.run_retrieval(cases, candidate, E, test_ids=ids)
comparison = w.compare_retrieval(before, after)
ref.save_json(E / 'retrieval_comparison.json', comparison)
print([x for x in comparison if x['change'] != 'unchanged'])
Zmiana top_k w tym wyszukiwaniu poszerza zwracaną część uporządkowanej listy. Nie jest nowym sposobem liczenia podobieństwa. Jeżeli na granicy występują remisy albo implementacja wyszukiwania jest niedeterministyczna, zapisz także sposób ich rozstrzygania. W pakietowym indeksie pełne wyniki pozwalają skontrolować kolejność bez domysłów.
Sparuj wyniki przez kontrakt
Nie porównuj pierwszego wiersza jednego pliku z pierwszym wierszem drugiego. Użyj test_id, turn_id i contract_hash. Funkcja poniżej odrzuca różny zakres lub duplikaty. Dla każdej pary potwierdza jedną zmianę konfiguracji i liczy zmianę pokrycia końcowego kontekstu.
R5 · Do pliku kod/moj_m6.py · zastąp całą definicję
def compare_retrieval(before, after):
key = lambda r: (r['test_id'], r['turn_id'], r['contract_hash'])
left = {key(r): r for r in before}
right = {key(r): r for r in after}
if set(left) != set(right) or len(left) != len(before) or len(right) != len(after):
raise ValueError('Różne kontrakty lub duplikaty porównywanych tur.')
comparison = []
for k in left:
a, b = left[k], right[k]
compare_configs(a['config'], b['config'])
comparable = a['status'] == b['status'] == 'completed'
ma = a['metrics']['context']['group_coverage'] if comparable else None
mb = b['metrics']['context']['group_coverage'] if comparable else None
delta = mb - ma if ma is not None and mb is not None else None
comparison.append({
'test_id': k[0], 'turn_id': k[1],
'before_result_id': a['result_id'], 'after_result_id': b['result_id'],
'comparable': comparable, 'before_coverage': ma, 'after_coverage': mb,
'coverage_delta': delta,
'change': 'ND' if delta is None else
('improved' if delta > 0 else 'regressed' if delta < 0 else 'unchanged'),
})
return comparison
change=improved w tym pliku oznacza tylko poprawę pokrycia grup. Nie oznacza automatycznie lepszej odpowiedzi, niższego szumu ani krótszego czasu. Wiersz ND może wynikać z braku oczekiwanych grup lub z niewykonanej poprawnie próby. Pole comparable pozwala rozróżnić te sytuacje.
W dostarczonym porównaniu T004 przechodzi z pokrycia 0 do 1. Inne przypadki z oczekiwanymi źródłami w podzbiorze zachowują pełne pokrycie. T006 i T106 mają pokrycie N/D. Nie oznacza to, że cały wariant jest bezwarunkowo lepszy: oceń wszystkie dodatkowo przekazane fragmenty i powtórz odpowiedzi, jeśli chcesz wypowiedzieć się o jakości asystenta.
Próba z nowym promptem
Pakiet udostępnia rag_conditions_v1. Wariant dopowiada zachowanie warunków i kolejności oraz rozdzielenie zwykłej procedury od postępowania po niepowodzeniu. Nie wkleja do promptu wzorcowej odpowiedzi z kontraktu. Przekazywanie poprawnej odpowiedzi modelowi podczas testu zafałszowałoby ocenę.
Notebook warsztatu · przykład wywołania
RUN_PROMPT_EXPERIMENT = False # wymaga świadomego wyboru Ollama
if RUN_PROMPT_EXPERIMENT:
assert MODE == 'ollama', 'Nowy prompt wymaga nowych odpowiedzi.'
candidate_prompt = dict(config, config_id='conditions',
prompt_version='rag_conditions_v1')
w.compare_configs(config, candidate_prompt)
selected = ['M6-T101', 'M6-T003', 'M6-T006']
answers_before = w.run_assistant(cases, config, E, test_ids=selected)
answers_after = w.run_assistant(cases, candidate_prompt, E, test_ids=selected)
# Oceń oba zbiory ręcznie; sprawdź narzędzie i kontekst każdej pary.
Ten kod wymaga jawnego trybu Ollama. Używa tych samych kontraktów i pozostałych parametrów. Ponieważ pełna tura obejmuje selektor, sprawdź, czy oba wykonania użyły tej samej ścieżki oraz kontekstu. Jeżeli selektor wybrał inne narzędzie, para nie izoluje już samego promptu odpowiedzi. Zapisz to jako osobną obserwację. W badaniu izolowanej generacji można zamrozić ranking i użyć answer_from_results, ale należy jawnie nazwać taki zakres.
Regresja i decyzja
Regresja oznacza pogorszenie wcześniej spełnianego wymagania. Prowadź listy przypadków poprawionych, pogorszonych, bez zmian i nieocenionych. Sprawdź błędy krytyczne, a dopiero potem korzyści w średnich. Zestaw regresyjny powinien obejmować ważne ścieżki, także brak danych, odmowę, doprecyzowanie i rozmowę.
Zapisz w experiment_log.jsonl hipotezę, zmianę, identyfikatory przebiegów, plik porównania, skutki uboczne i decyzję. Wybraną konfigurację skopiuj do final_config.json. Gdy dowody nie wystarczają, zachowaj bazę i opisz decyzję jako tymczasową albo nierozstrzygniętą. Nie wybieraj wariantu tylko dlatego, że jedna odpowiedź brzmi lepiej.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
6.2. Powtarzanie prób i uczciwe podsumowanie
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Podrozdział
Retrieval dla stałego pytania, wektora, indeksu i deterministycznego algorytmu może być powtarzalny. Generowanie odpowiedzi i wybór narzędzia zależą od modelu. Niska temperatura i seed pomagają kontrolować warunki, ale nie dają ogólnej gwarancji identycznego wyniku na każdym środowisku.
Trzy nowe wykonania
W metodzie tego kursu ocenę stabilności testu zależnego od modelu opierasz na trzech nowych niezależnych wykonaniach. Każde ma nowy run_id i nową sesję dla niezależnego przypadku. Wewnątrz scenariusza zachowujesz ciąg rzeczywistych tur. Pojedyncza demonstracja pozostaje dopuszczalna, gdy nie wyciągasz z niej wniosku o stabilności.
Notebook warsztatu · przykład wywołania
RUN_STABILITY = False
if RUN_STABILITY:
assert MODE == 'ollama', 'Checkpoint nie mierzy zmienności modelu.'
repeated_results = []
for repetition in range(3):
repeated_results.extend(
w.run_assistant(cases, config, E, test_ids=['M6-T101']))
ref.save_json(E / 'stability_result_ids.json',
[r['result_id'] for r in repeated_results])
drafts = [ref.review_template(
r, contracts[(r['test_id'], r['turn_id'])], 'answer')
for r in repeated_results]
ref.save_json(E / 'stability_reviews_draft.json', drafts)
# Teraz przeczytaj trzy odpowiedzi i uzupełnij plik ocen.
# Nie uruchamiaj tej komórki ponownie, aby zapisać oceny.
Nie kopiuj jednego wyniku pod trzema identyfikatorami. Nie traktuj trzech odczytów checkpointu jako trzech nowych odpowiedzi. Nie wybieraj najlepszego tekstu spośród prób, pomijając pozostałe. Jeżeli jeden przebieg kończy się błędem środowiska, zgłoś go i uzupełnij serię zgodnie z planem; nie nadaj brakującej treści oceny zero.
Minimum i mediana osobno dla każdego kryterium
Dla ocen poprawności 2, 1, 2 minimum wynosi 1, a mediana 2. Minimum ujawnia słabszą próbę, a mediana typowy środkowy wynik. Nie mieszaj w jednej liście poprawności, szumu kontekstu i precyzji odpowiedzi. Kryteria mierzą inne rzeczy.
Policz liczbę błędów krytycznych, sprawdź zgodność statusów i zestawów źródeł. Stabilne statusy nie dowodzą stabilnej jakości, bo trzy różne teksty mogą mieć status answered. Stabilne źródła także nie gwarantują, że model właściwie je wykorzystał. Potrzebne są trzy osobne recenzje.
R5 · Do pliku kod/moj_m6.py · zastąp całą definicję
def summarize_repeats(results, reviews, *, allow_replay=False):
if not results:
raise ValueError('Brak wyników.')
review_map = {r['result_id']: r for r in reviews}
if len(review_map) != len(reviews):
raise ValueError('Powtórzona recenzja.')
groups = {}
for row in results:
if row.get('execution_status') != 'completed':
raise ValueError('Próba niewykonana poprawnie; raportuj ją osobno.')
if not allow_replay and (row['mode'] != 'ollama' or row['origin'] != 'assistant_run'):
raise ValueError('Stabilność modelu wymaga nowych wykonań Ollama.')
if row['result_id'] not in review_map:
raise ValueError('Brakuje oceny któregoś wykonania.')
rev = review_map[row['result_id']]
if rev['contract_hash'] != row['contract_hash'] or rev['review_kind'] != 'answer':
raise ValueError('Niezgodny kontrakt lub rodzaj recenzji.')
groups.setdefault((row['test_id'], row['turn_id']), []).append((row, rev))
summary = []
for key, pairs in groups.items():
signatures = {(r['contract_hash'], fingerprint(r['config'])) for r, _ in pairs}
if len(pairs) != 3 or len({r['run_id'] for r, _ in pairs}) != 3 or len(signatures) != 1:
raise ValueError('Potrzebne są trzy różne run_id przy tym samym kontrakcie i konfiguracji.')
scores = {}
for criterion in ANSWER:
values = [v['scores'][criterion] for _, v in pairs]
if any(v != 'ND' and (type(v) is not int or v not in (0, 1, 2)) for v in values):
raise ValueError('Nieukończona ocena.')
numeric = [v for v in values if type(v) is int]
scores[criterion] = {'n': len(numeric),
'minimum': min(numeric) if numeric else None,
'median': statistics.median(numeric) if numeric else None}
statuses = [r['raw_result']['status'] for r, _ in pairs]
sources = [sorted(s['chunk_id'] for s in r['raw_result'].get('sources', [])) for r, _ in pairs]
summary.append({'test_id': key[0], 'turn_id': key[1], 'scores': scores,
'critical_errors': sum(v['critical_error'] is True for _, v in pairs),
'pass_all': all(v['verdict'] == 'pass' for _, v in pairs),
'statuses_stable': len(set(statuses)) == 1,
'sources_stable': all(s == sources[0] for s in sources),
'new_measurements': all(r['mode'] == 'ollama' and r['origin'] == 'assistant_run' for r, _ in pairs)})
return summary
Funkcja domyślnie wymaga nowych wyników Ollama. Parametr allow_replay=True służy wyłącznie do sprawdzania obliczeń na kontrolowanych danych testowych. Taki wynik nie staje się badaniem zmienności modelu. Pole new_measurements zachowuje tę różnicę.
Trzy próby są niewielką obserwacją przyjętą na potrzeby kursu. Nie stanowią oszacowania niezawodności dla wszystkich użytkowników ani gwarancji bezpieczeństwa wdrożenia. W przypadkach krytycznych plan wymaga spełnienia warunku w każdej próbie; podaj ten fakt w raporcie bez rozszerzania wniosku poza badaną próbę.
Zapis trzech ocen i podsumowania
Komórka wykonująca próby tworzy stability_reviews_draft.json. Uzupełnij w nim wszystkie trzy recenzje, korzystając z ich result_id i zapisanych odpowiedzi. Następna komórka odczytuje wyniki z pliku, więc nie wymaga ponownego generowania. Włącz zapis dopiero po ocenie. Walidator zatrzyma niekompletne recenzje. Po udanym zapisie pozostaw przełącznik wyłączony, aby nie dopisywać tych samych ocen ponownie.
Notebook warsztatu · przykład wywołania
# Po uzupełnieniu pliku stability_reviews_draft.json.
SAVE_STABILITY_REVIEWS = False
if SAVE_STABILITY_REVIEWS:
result_ids = set(ref.read_json(E / 'stability_result_ids.json'))
selected_results = [r for r in ref.read_jsonl(E / 'assistant_results.jsonl')
if r['result_id'] in result_ids]
assert len(selected_results) == len(result_ids) == 3
by_id = {r['result_id']: r for r in selected_results}
selected_reviews = ref.read_json(E / 'stability_reviews_draft.json')
assert {v['result_id'] for v in selected_reviews} == result_ids
for review in selected_reviews:
r = by_id[review['result_id']]
w.save_review(E / 'assistant_reviews.jsonl', review, r,
contracts[(r['test_id'], r['turn_id'])])
summary = w.summarize_repeats(selected_results, selected_reviews)
ref.save_json(E / 'stability_summary.json', summary)
print(summary)
Mianowniki i brakujące dane
Rozdziel liczbę kontraktów, liczbę tur, liczbę wykonań i liczbę recenzji. Pełny zestaw ma 24 przypadki i 26 tur. Trzy wykonania wszystkich tur dają 78 wyników, ale nadal dotyczą 24 przypadków. Dodatkowy import M5 nie zwiększa liczby nowych testów M6.
Odsetek zaliczonych wyników musi mieć jawny mianownik. Możesz podać odsetek wśród ocenionych tur oraz pokrycie oceną całego planowanego zestawu. Jeżeli oceniono tylko najłatwiejsze przypadki, wysoki odsetek w tej grupie nie opisuje całego systemu. N/D wyłączasz z konkretnej metryki, a brak recenzji pokazujesz oddzielnie.
Czas i koszt
Do czasu całej tury używaj pomiaru obejmującego resolver, selektor, narzędzie, retrieval i generację w tej samej konfiguracji. W diagnostyce M5 dostępny jest duration_ms. Nie mieszaj tego czasu z czasem samego wywołania LLM ani z odczytem checkpointu. Jeśli porównujesz czasy nowych wykonań, opisz sprzęt, tryb, obciążenie, rozgrzanie modelu i zakres pomiaru.
Zestaw rozwojowy i sprawdzający
Jeżeli wielokrotnie dopasowujesz prompt do tych samych pytań, poprawa może dotyczyć głównie znanych przykładów. Zachowaj dodatkowe pytania sprawdzające, których nie używasz do wyboru wariantu. Rozdzielaj je według intencji i scenariuszy: prawie identyczna parafraza w obu częściach daje słabszą kontrolę uogólnienia. W małym pakiecie traktuj wyniki jako diagnozę i porównanie prototypu, nie ranking uniwersalnej jakości modelu.
Sprawdź się: potrafisz wskazać wszystkie trzy wyniki i recenzje, a wniosek nie ukrywa minimum, braków ani pojedynczego krytycznego niepowodzenia.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
7. R6. Raport, odtwarzalność i demonstracja
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Raport ma umożliwić osobie spoza projektu zrozumienie systemu, odtworzenie warunków i sprawdzenie wniosków. Nie wymaga przepisywania całych logów. Wymaga za to jasnych odwołań do konkretnych wyników i opisania granic tego, co rzeczywiście zmierzono.
Struktura raportu projektu
Zacznij od problemu użytkownika i zakresu. Następnie opisz dane, przygotowanie indeksu, przebieg pytania, rozmowę i narzędzia. Podaj konfigurację końcową oraz powód jej wyboru. W części ewaluacyjnej przedstaw kontrakty, kryteria, zakres wykonania, wyniki, błędy i eksperyment. Zakończ ograniczeniami i instrukcją odtworzenia.
| Plik | Co powinien wyjaśniać |
|---|---|
| project_report.md | Cel, architekturę, metodę, wynik i ograniczenia |
| evaluation_summary.md | Zakres prób, metryki, błędy krytyczne i decyzje |
| run_instructions.md | Jak uruchomić system i odtworzyć wybrane wyniki |
| demo_scenario.md | Co pokażesz, co ma się wydarzyć i jaki jest plan awaryjny |
| traceability_matrix.csv | Jak każdy istotny wniosek łączy się z dowodem |
Korzystaj z szablonów w pakiecie, ale zastąp pola do uzupełnienia własnymi ustaleniami. Stwierdzenie „testy wykonano poprawnie” nie zastępuje liczby prób ani identyfikatorów. Raportuj konfigurację wybraną jako końcową, a nie zestaw najlepszych odpowiedzi z różnych eksperymentów.
Zbuduj połączenie wniosku z dowodem
R6 · Do pliku kod/moj_m6.py · zastąp całą definicję
def build_traceability(claim_id, claim, result, review, result_file, review_file):
if not claim_id.strip() or not claim.strip():
raise ValueError('Podaj identyfikator i treść wniosku.')
for k in ('result_id', 'test_id', 'turn_id', 'contract_hash'):
if result.get(k) != review.get(k):
raise ValueError('Wynik i recenzja nie są ze sobą zgodne.')
for name in (result_file, review_file):
if Path(name).is_absolute() or '..' in Path(name).parts:
raise ValueError('Użyj ścieżki względnej wewnątrz projektu.')
return {'claim_id': claim_id, 'claim': claim, 'test_id': result['test_id'],
'turn_id': result['turn_id'], 'result_id': result['result_id'],
'result_file': result_file, 'review_file': review_file,
'origin': result['origin'], 'limitation': 'Wniosek dotyczy wskazanego wyniku i konfiguracji.'}
Funkcja kontroluje identyfikatory i ścieżki względne. Nie ocenia prawdziwości zdania claim. Wniosek „system zawsze odpowiada poprawnie” pozostaje nieuzasadniony, nawet gdy ma poprawny result_id. Treść musi ograniczać się do tego, co potwierdza wynik.
Notebook warsztatu · przykład wywołania
import csv
D = WORK / 'documentation'
D.mkdir(exist_ok=True)
for name in ('project_report.md', 'run_instructions.md', 'demo_scenario.md'):
ref.initialize_document(ROOT, name, D / name)
r = next(r for r in assistant_results if r['test_id'] == 'M6-T007')
# answer_review z przykładu trzeba najpierw sprawdzić i zapisać.
saved_reviews = ref.read_jsonl(E / 'assistant_reviews.jsonl')
review = next((v for v in saved_reviews if v['result_id'] == r['result_id']), None)
if review is not None:
row = w.build_traceability(
'WN-01', 'W odtworzonym T007 aplikacja odmówiła wykonania restartu.',
r, review, 'evaluation/assistant_results.jsonl',
'evaluation/assistant_reviews.jsonl')
with (D / 'traceability_matrix.csv').open('w', encoding='utf-8-sig', newline='') as f:
writer = csv.DictWriter(f, fieldnames=list(row))
writer.writeheader()
writer.writerow(row)
else:
print('Najpierw uzupełnij i zapisz recenzję T007.')
Przykładowe właściwe zdanie brzmi: „W odtworzonym przypadku T007 aplikacja odrzuciła żądanie wykonania restartu”. Ma wąski zakres i można wskazać dowód. Szersze zdanie o wszystkich działaniach zabronionych wymaga szerszego zestawu przypadków.
Dokumentacja indeksu i danych
Po zakończeniu edycji wszystkich funkcji ponownie zapisz manifest środowiska, aby suma kodu odpowiadała wersji użytej w końcowym przebiegu. Zachowaj również manifesty wcześniejszych eksperymentów.
Zapisz pochodzenie źródeł, ich wersje, sposób ekstrakcji i podziału, liczbę fragmentów, model embeddingowy, wymiar, metrykę i normalizację. W aktualnym pakiecie ważnym artefaktem jest kompletny katalog dane/semantic_index. Manifest i sumy kontrolne są częścią dowodu, że porównania dotyczyły tych samych danych.
Nie przedstawiaj dokumentacji fikcyjnej aplikacji jako instrukcji administracji rzeczywistym systemem uczelni. Własne dane opisuj tak, aby możliwy był audyt pochodzenia, bez publikowania sekretów i informacji prywatnych. W rozdziale kursowym pracujesz na dostarczonym korpusie dydaktycznym.
Instrukcja uruchomienia dla innej osoby
Podaj wymagane pliki, środowisko, polecenie uruchomienia Jupyter, wybór trybu, kolejność notebooków i miejsce wyników. Wyjaśnij, że uruchomienie nowych pytań wymaga Ollama i zgodnych modeli, a odtworzenie checkpointów ma ograniczony zakres. Zapisz, który run_id należy otworzyć, jeśli folder zawiera kilka serii.
Sprawdź instrukcję od nowej sesji. Nie polegaj na zmiennych istniejących w pamięci notebooka po wcześniejszej pracy. Rozpakowanie na innym stanowisku powinno zachować ścieżki względne. Pliki zależne od lokalnego katalogu autora albo kernel bez wymaganych bibliotek ujawniają brak odtwarzalności.
Demonstracja bez ukrywania ograniczeń
Przygotuj pokaz trwający około 5–7 minut, o ile prowadzący nie ustalił innego czasu. Przedstaw problem, jedną poprawną ścieżkę, przypadek graniczny i przykład błędu wraz z próbą poprawy. Pokaż fragment źródła i powiązany wynik, a nie tylko płynnie brzmiącą odpowiedź.
Plan awaryjny może korzystać z zapisanych wyników. Powiedz wtedy, z jakiego wykonania pochodzą. Nie przedstawiaj odtwarzania jako nowego uruchomienia modelu. Pokaz błędu z trafną diagnozą może lepiej udokumentować wiedzę niż starannie wybrana, pojedyncza odpowiedź bez śladu danych.
Ćwiczenie R6
Napisz trzy wnioski: jeden o wyszukiwaniu, jeden o zachowaniu asystenta i jeden o ograniczeniu badania. Do pierwszych dwóch dołącz konkretne result_id, pliki i recenzje. Trzeci oprzyj na jawnym zakresie niewykonanych prób. Uzupełnij build_traceability i sprawdź, że podmiana recenzji na ocenę innego wyniku zostaje wykryta.
Sprawdź się: inna osoba może otworzyć dowód, zrozumieć jego pochodzenie i ocenić, czy potwierdza Twój wniosek.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
8. Złożenie projektu i rozwiązywanie problemów
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Po sześciu etapach masz proces od kontraktu do wniosku: konfiguracja i plan → przypadki → surowe wyniki → ręczne oceny → diagnoza → porównanie → raport. Zachowaj tę kolejność również w swoim projekcie. Brakującego kontraktu lub rankingu nie da się zastąpić późniejszym, przekonującym opisem.
Co działa w Twoim module
| Obszar | Funkcje własne | Wsparcie dostarczone w pakiecie |
|---|---|---|
| Baza | save_baseline | configure, check_config, zapis JSON |
| Kontrakt | validate_test_cases, prepare_search_question | pełny walidator struktury i flatten_cases |
| Retrieval | hit_at_k, source_metrics, build_retrieval_record, evaluate_retrieval_turn | embedding, indeks, próg i pętla serii |
| Asystent | assistant_step, save_review | M5, answer_from_documents, pętla scenariuszy i walidacja recenzji |
| Eksperyment | compare_configs, compare_retrieval, summarize_repeats | pliki wyników oraz formularze |
| Dokumentacja | build_traceability | CSV, szablony i kontrola kompletności |
Wszystkie trzynaście definicji pokazano w książce. Gotowe pętle z moj_m6.py używają Twoich funkcji. Standardowe notebooki nadal korzystają z m6_tools.py, dzięki czemu masz niezależny punkt odniesienia. Nie podmieniaj kodu M3–M5 tylko po to, aby przejść testy warsztatu.
Artefakty końcowe
WORK/
evaluation/
evaluation_plan.md
baseline_config.json, candidate_config.json, final_config.json
test_cases.jsonl, test_cases_validation.json
retrieval_runs.jsonl, retrieval_reviews.jsonl, retrieval_results.jsonl
assistant_results.jsonl, assistant_reviews.jsonl
error_log.jsonl, error_analysis.md
experiment_log.jsonl, regression_suite.json
evaluation_summary.md
environment_manifest.json
documentation/
project_report.md, run_instructions.md
demo_scenario.md, traceability_matrix.csv
Warsztat zapisuje dane wykonania i formularze, ale nie tworzy automatycznie gotowych recenzji, wyboru końcowej konfiguracji ani prawdziwego raportu. To Twoje zadania. Skopiuj zatwierdzony test_cases do katalogu eksperymentu, zachowaj własny kod i zapisz wersję środowiska. Do oddawanego archiwum nie dodawaj .venv, wag modeli ani przypadkowych danych prywatnych.
Spójność nazewnictwa
| Na slajdzie lub w opisie | W wykonywalnym pakiecie | Znaczenie |
|---|---|---|
| retrieval_any_hit / retrieval_all_hit | metrics.ranking.any_group / all_groups | Pokrycie wymaganych grup w rankingu |
| context_any_required / context_all_required | metrics.context.any_group / all_groups | Pokrycie w końcowym kontekście |
| critical_fail | critical_error w recenzji | Krytyczne naruszenie kontraktu |
| problem_codes | problem_code w pojedynczym rekordzie błędu | Kod problemu; kilka rekordów może dotyczyć jednej tury |
| Model jako obiekt tag + digest | embedding_model, llm_model, model_digests | Identyfikacja modeli zgodna z kodem |
| N/D | ND w JSON recenzji, null w nieocenianej metryce | Brak zastosowania; odróżnij od nieukończonego formularza |
Dodatkowe pola Hit@k z warsztatu nie usuwają pól pakietu. Raport może odwoływać się do obu, jeśli podaje definicję i zakres. Identyfikatory przykładów ze slajdów nie są dowodem wykonania identycznego przypadku na Twoich danych.
Gdy coś nie działa
| Objaw | Najpierw sprawdź |
|---|---|
| Nie można importować moj_m6 | ROOT, folder kod i kernel notebooka |
| Nadal wykonywana jest stara funkcja | Czy zapisano plik i wykonano importlib.reload |
| NotImplementedError | Czy zastąpiono całą definicję z danego etapu |
| Brak checkpointu | Właściwa lista R3 lub R4, dokładny tekst i niezmieniony prompt |
| Niezgodność digestu | Komplet indeksu, manifest i model embeddingowy |
| N/D jest odrzucane | Czy kryterium figuruje w applicable_criteria danego kontraktu |
| Duplikat result_id recenzji | Czy komórka zapisu została uruchomiona ponownie |
| Porównanie odrzuca pary | test_id, turn_id, kontrakty, duplikaty i różnice konfiguracji |
| Brak final_config | Czy decyzję i uzasadnienie rzeczywiście zapisano po analizie |
Jeśli restartujesz kernel, zmienne results, config i contracts znikają. Odtwórz je z zapisanego katalogu i właściwych plików zamiast tworzyć nowy pomiar tylko po to, aby odnaleźć istniejącą recenzję. Po zmianie kodu albo kontraktu nowa próba powinna mieć nowy run_id.
Ostatnia kontrola
Sprawdź kompletność plików, zgodność referencji, uzasadnienie decyzji, zakres próby i sposób odtworzenia. Wskaż jawnie brak nowych pomiarów modelu, jeśli pracowałeś offline. Książka i warsztat uczą procesu oceny; nie nadają automatycznie prototypowi statusu gotowości do wdrożenia.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
9. Odpowiedzi i wskazówki do ćwiczeń
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Najpierw wykonaj zadania samodzielnie. Poniższe odpowiedzi wyjaśniają kryteria rozumowania. Przy ocenach semantycznych dopuszczalne są różnice liczbowe, jeśli wynikają z jasno opisanej rubryki i konkretnych dowodów. Nie przenoś ocen przykładu na nową odpowiedź bez jej przeczytania.
R1. Plan i warunek krytyczny
Dla pytania o zgłoszenie awarii celem jest uzyskanie listy informacji potrzebnych operatorowi. Kompletność obejmuje wersję systemu, czas, komunikat błędu, wykonane kroki i oczekiwany wynik. Zakaz umieszczania haseł i kluczy jest innym wymaganiem: odpowiedź może podawać pełną listę i mimo to zawierać niebezpieczny dodatek. Dlatego zapisz zakaz jako osobny warunek kontraktu, z odpowiednią regułą krytycznego niepowodzenia.
save_baseline powinno zaakceptować pierwszy zapis i identyczne ponowienie. Przy tej samej ścieżce i top_k zmienionym z 3 na 5 powinno zgłosić wyjątek. Plik bazy pozostaje niezmieniony. Jeżeli chcesz inny tryb albo konfigurację początkową, utwórz osobny katalog eksperymentu.
R2. Parafraza zgłoszenia
Przykładowe nowe pytanie brzmi: „Co mam przygotować, zanim zgłoszę problem z DocAssist?”. Oczekiwanym narzędziem jest answer_from_documents; oczekiwane fakty wynikają z DOC-REG, sekcji „Treść zgłoszenia”. Dokładny tytuł sekcji oraz chunk_id odczytaj z dodatku źródłowego, ponieważ są częścią kontraktu danych.
Twierdzeniem zabronionym może być „dołącz hasło do konta”. Nie dopisuj gwarantowanego czasu rozwiązania, bo regulamin go nie podaje. Nowe sformułowanie nie musi mieć zapisanego embeddingu ani odpowiedzi selektora, więc wykonanie może wymagać Ollama. Sam poprawny walidator nie dowodzi dostępności checkpointu.
Jeżeli dokument nie wspiera planowanego faktu, zmień oczekiwanie przed testem albo świadomie utwórz test braku wiedzy. Nie oczekuj zewnętrznej procedury tylko dlatego, że jest znana z innego modułu.
R3. Fragmenty i grupy
Kontrolowany przykład ma grupy A oraz B, po jednym wymaganym fragmencie. Ranking A, X, Y daje Hit@1=true, Hit@3=true, any_group=true, all_groups=false i group_coverage=0,5. Ranking A, X, Y, B, Z przy top_k=5 daje pełne pokrycie grup. Dodatkowe pozycje mogą zwiększać szum, dlatego poprawa pokrycia nie rozstrzyga oceny całego kontekstu.
Jeśli jedna grupa wymaga jednocześnie A1 i A2, obecność samego A1 daje Hit@k=true, ale jeszcze nie spełnia grupy. To celowo inne pytania metryczne. Gdy lista oczekiwanych grup jest pusta, wyniki Hit@k i pokrycia to N/D. Nie licz takich przypadków jako nietrafionych wyszukiwań.
W rzeczywistym T004 przy top_k=3 brakuje DOC-INSTR-dd6055d675-0003. Przy top_k=5 fragment pojawia się. Poprawny wniosek dotyczy pokrycia w określonym indeksie. Nie można jeszcze napisać, że wariant generuje lepszą odpowiedź.
R4. Odpowiedź częściowa i narzędzie
Demonstracyjne zdanie „Po zmianie konfiguracji uruchom ponownie usługę” ma podstawę w źródle, ale nie spełnia T101. Brak kontroli stanu, interpretacji active, kopii po niepowodzeniu i zgłoszenia administratorowi. Krytyczne pominięcie kopii wymaga critical_error=true i verdict=fail. Poprawność wypowiedzianego zdania nie jest tym samym co kompletność procedury.
W historycznej turze 2 M5 resolver prawidłowo rozwinął zaimek. Bezpośredni dowód problemu jest później: decyzja i wykonane narzędzie wskazują show_last_sources zamiast RAG odpowiadającego na pytanie. Najwcześniejszy potwierdzony etap to tool_selection. Nie ma podstaw, aby przypisywać ten konkretny błąd chunkingowi.
Dla T007 poprawna odmowa jest wynikiem pozytywnym, o ile treść i brak wykonania zgadzają się z kontraktem. Dla T008 prośba o doprecyzowanie przy pustej historii także może być pozytywna. Nie przyznawaj im automatycznie zera za brak procedury.
R5. Hipotezy i eksperyment
Hipotezę „właściwy fragment leży poniżej top_k” sprawdzasz szerszym rankingiem przy stałym pytaniu i indeksie. Jeśli go tam nadal nie ma, trzeba zbadać inne przyczyny. Hipotezę o formacie cytowań sprawdzasz na nowej generacji po jednej zmianie instrukcji, oceniając także znaczenie źródeł. Hipotezę o selektorze sprawdzasz na całym scenariuszu, kontrolując historię i rzeczywistą decyzję.
Offline można wykonać porównanie top_k 3/5 na zapisanych embeddingach. Nie można z tego wywnioskować nowej jakości generowania ani stabilności modelu. Przy trzech ocenach 2, 1, 2 minimum wynosi 1, mediana 2. Jeden błąd krytyczny w trzech próbach nie znika dlatego, że dwie pozostałe zakończyły się dobrze.
Regresja powinna wskazywać konkretne kryterium i wynik. Jeżeli wzrosło pokrycie, ale pogorszył się szum, nie zapisuj po prostu „lepiej”. Przedstaw oba skutki i zastosuj wcześniej ustaloną regułę decyzji.
R6. Wnioski o właściwym zakresie
Przykład o wyszukiwaniu: „W odtworzonym T004 wymagany fragment nie wystąpił w top-3, lecz był obecny po rozszerzeniu do top-5 przy tym samym indeksie”. Dowód powinien wskazywać dwa result_id i plik porównania. Nie zastępuj ich samym test_id.
Przykład o zachowaniu: „W zapisanej turze T007 aplikacja odmówiła wykonania restartu”. Powiąż go z wynikiem i ręczną oceną. Przykład ograniczenia: „Nie wykonano pełnej nowej serii Ollama, więc raport nie ocenia stabilności odpowiedzi T101”. Taki wniosek nie potrzebuje wymyślonego result_id; jego podstawą jest plan i lista wykonanych prób.
Macierz powinna pomagać czytelnikowi, a nie udawać dowód samą obecnością identyfikatora. Jeżeli nie ma zgodnej recenzji albo wynik pochodzi z innego kontraktu, najpierw uzupełnij materiał, potem formułuj wniosek.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
10. Słownik pojęć i granice interpretacji
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Testy i dane
| Pojęcie | Znaczenie w M6 |
|---|---|
| Baseline | Zamrożona konfiguracja stanowiąca punkt odniesienia |
| Kontrakt testu | Wejście oraz jawne wymagania i zakazy ustalone przed pomiarem |
| Przypadek | Niezależny test albo cały scenariusz z własnym test_id |
| Tura | Jedna wiadomość użytkownika i jej obsługa |
| Przebieg | Wykonanie serii z własnym run_id |
| Wynik | Konkretna obserwacja o własnym result_id |
| Digest | Identyfikacja zawartości modelu, odrębna od jego tagu |
| Fingerprint kontraktu | Skrót treści używany do wykrycia zmiany oczekiwań |
| Checkpoint | Zapisane wywołanie odtwarzane przy zgodnym żądaniu |
| Provenance / pochodzenie | Informacja, skąd i z jakich warunków pochodzi rekord |
Wyszukiwanie i kontekst
| Pojęcie | Znaczenie w M6 |
|---|---|
| Ranking | Uporządkowane wyniki wyszukiwania przed filtrowaniem |
| top_k | Liczba pozycji pobieranych do dalszego przetwarzania |
| Score | Miara podobieństwa wektorowego, nie prawdopodobieństwo poprawności |
| Próg podobieństwa | Granica odrzucania wyników według score |
| Hit@k | Obecność co najmniej jednego oczekiwanego fragmentu w pierwszych k pozycjach |
| expected_rank | Pozycja pierwszego oczekiwanego fragmentu w zapisanym rankingu |
| MRR@k | Średnia odwrotność pozycji przy ograniczeniu obserwacji do top-k |
| Grupa dowodów | Wymagane fragmenty albo jawny pełny substytut grupy |
| Pokrycie grup | Udział spełnionych grup wymaganych przez kontrakt |
| Końcowy kontekst | Materiał, który pozostał po filtrach i jest podstawą odpowiedzi |
| Szum | Treść zbędna wobec badanego pytania |
Ocena i eksperyment
| Pojęcie | Znaczenie w M6 |
|---|---|
| Rubryka | Zdefiniowane kryteria i znaczenia ocen |
| ND | Kryterium lub metryka nie dotyczy przypadku |
| null w formularzu | Ocena jeszcze niewypełniona |
| Błąd krytyczny | Naruszenie wymagania dyskwalifikującego wynik mimo innych dobrych ocen |
| Regresja | Pogorszenie wcześniej spełnianego wymagania po zmianie |
| Zmienność | Różnice między nowymi wykonaniami przy ustalonych warunkach |
| Mediana | Środkowa wartość uporządkowanych ocen |
| Macierz dowodów | Powiązanie wniosku z testem, wynikiem, recenzją i plikiem |
| Odtwarzalność | Możliwość ponownego wykonania opisanej procedury przy znanych warunkach |
Czego nie utożsamiać
Dostęp do źródła nie gwarantuje wykorzystania jego treści. Wysokie Hit@k nie gwarantuje pełnego pokrycia. Pełne pokrycie nie gwarantuje poprawnej generacji. Poprawne etykiety nie gwarantują semantycznego oparcia. Status answered nie gwarantuje realizacji intencji. Trzy udane próby nie gwarantują niezawodności we wszystkich zastosowaniach.
Każde z tych zdań wskazuje granicę pomiaru. Znając tę granicę, potrafisz zaproponować następny test i sformułować wniosek, który rzeczywiście wynika z danych.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026
11. Źródła ćwiczeń i mapa pakietu
ENHANCER+ · Moduł 6 · Ewaluacja RAG · Rozdział
Poniższe teksty są kopią dziesięciu fragmentów z aktualnego chunks.jsonl pakietu Studenta M6. Pozwalają rozwiązywać zadania analityczne bez otwierania prezentacji. Nie zastępują całego katalogu indeksu potrzebnego do uruchamiania kodu. Oceniając własną zmienioną bazę, użyj jej faktycznej wersji zamiast tej kopii.
DOC-INSTR · Ponowne uruchomienie usługi
DOC-INSTR-dd6055d675-0002
Źródło: instrukcja.txt · wersja: 1.4
## Ponowne uruchomienie usługi Po zmianie pliku /etc/docassist/config.yaml wykonaj kolejno: 1. Uruchom ponownie usługę: sudo systemctl restart docassist-api 2. Sprawdź stan: systemctl is-active docassist-api Prawidłowy wynik: active. Uwaga: wykonaj ponowne uruchomienie tylko po zmianie konfiguracji.
DOC-INSTR · Kopia konfiguracji
DOC-INSTR-dd6055d675-0003
Źródło: instrukcja.txt · wersja: 1.4
## Kopia konfiguracji Przed edycją zachowaj kopię pliku konfiguracyjnego w katalogu kopii. Nie zapisuj w zgłoszeniu haseł ani kluczy. W razie nieudanego uruchomienia przywróć zatwierdzoną kopię i zgłoś problem administratorowi.
DOC-REG · Dyżur operatora
DOC-REG-6a742ef524-0002
Źródło: regulamin.md · wersja: 1.4
## Dyżur operatora Operator przyjmuje zgłoszenia w dni robocze od 08:00 do 16:00. Poza tymi godzinami zgłoszenie czeka do następnego dyżuru. Wyjątek: awarię uniemożliwiającą pracę całego zespołu zgłoś dyżurnemu administratorowi zgodnie z wewnętrzną procedurą eskalacji.
DOC-REG · Treść zgłoszenia
DOC-REG-6a742ef524-0003
Źródło: regulamin.md · wersja: 1.4
## Treść zgłoszenia Podaj wersję systemu, czas zdarzenia, komunikat błędu i wykonane kroki. Nie dołączaj danych osobowych ani sekretów. Opisz oczekiwany wynik. Nie podajemy w tym regulaminie gwarantowanego czasu rozwiązania zgłoszenia.
DOC-PDF · Parametry stanowiska
DOC-PDF-51aa0fc583-0002
Źródło: docassist.pdf · wersja: 1.4
## Parametry stanowiska Dla tej fikcyjnej aplikacji przyjęto 8 GB RAM i 2 GB wolnego miejsca. Są to dane ćwiczenia, a nie wymagania modeli językowych. Przed instalacją sprawdź dostęp do zatwierdzonego pakietu.
DOC-PDF · Test po zmianie konfiguracji
DOC-PDF-51aa0fc583-0003
Źródło: docassist.pdf · wersja: 1.4
## Test po zmianie konfiguracji Po zmianie konfiguracji sprawdź uruchomienie usługi i zapisz wynik. Nie usuwaj łącznika w nazwie docassist-api.
DOC-PDF · Parametr oczekiwania
DOC-PDF-7bf28bc22b-0005
Źródło: docassist.pdf · wersja: 1.4
## Parametr oczekiwania Parametr: timeout_seconds. Wartość zalecana w tym przykładzie: 30 s. Zwiększ wartość dopiero po ustaleniu, że operacja trwa dłużej. Nie traktuj tego parametru jako gwarancji czasu odpowiedzi.
DOC-PDF · Zapis wyniku testu
DOC-PDF-7bf28bc22b-0006
Źródło: docassist.pdf · wersja: 1.4
## Zapis wyniku testu Zapisz datę próby, wersję dokumentu i komunikat diagnostyczny. Prawidłowy odczyt powinien zachować jednostkę s i wartość 30.
DOC-HELP · Błąd połączenia
DOC-HELP-cc64f35642-0002
Źródło: pomoc.html · wersja: 1.4
## Błąd połączenia Gdy aplikacja zgłasza brak połączenia, sprawdź stan usługi i adres w konfiguracji. Jeżeli usługa działa, zapisz dokładny komunikat i przekaż go administratorowi. Nie zmieniaj uprawnień plików bez ustalenia przyczyny.
DOC-HELP · Kontrola znaków
DOC-HELP-cc64f35642-0003
Źródło: pomoc.html · wersja: 1.4
## Kontrola znaków Tekst kontrolny: Zażółć gęślą jaźń. Zachowaj nazwy docassist-api i --no-pager oraz jednostki: 25°C, ±5%, ≤10 ms.
Gdzie są potrzebne funkcje
| Moduł | Rola i najważniejsze funkcje |
|---|---|
| m3_tools | load_index, search_local_index, normalize_search_results: indeks, podobieństwo, stabilny format wyników |
| m4_tools | retrieve, filter_results_for_context, prepare_context_results, build_context, answer_from_results, ask_rag: ścieżka RAG |
| m5_tools | create_initial_state, resolve_followup_question, assistant_step oraz narzędzia: rozmowa i kontrola wykonania |
| m6_tools | configure, walidator kontraktów, import_m5, review_template, save_review, join_reviews: referencyjne wsparcie ewaluacji |
| moj_m6 | Twoje trzynaście funkcji oraz dostarczone pętle run_retrieval i run_assistant |
| sprawdz_moj_m6 | check_stage: kontrola zachowania kolejnych etapów bez nowych wywołań modelu |
Własny warsztat korzysta z dokładnie tego samego korpusu, konfiguracji i checkpointów co pakiet. Gdy aktualizujesz pakiet, porównaj jego manifest z manifestem książki i ponów testy warsztatu. Sama zgodna nazwa pliku nie potwierdza zgodności implementacji.
Podstawa opracowania
Książka odpowiada zakresowi M12-M6-v6 oraz kartom i notebookom pakietu M6. Zachowuje dydaktyczny sposób prowadzenia czytelnika z książki M1: objaśnienie, przykład, ćwiczenie i samokontrola. Długość i podział zostały dopasowane do ewaluacji i dokumentacji, a nie do liczby stron M1.
Przykłady historyczne pochodzą z pełnego R6 M5, którego plik źródłowy i suma kontrolna znajdują się w dane/import_M5. Przykład T004 pochodzi z odtworzenia indeksu pakietowego. Kontrolowane przykłady A/B i oceny 2,1,2 są ilustracjami działania miary lub reguły, nie wynikami nowego wywołania LLM.
Do czytania nie są potrzebne strony zewnętrzne. Instrukcja publikacji dołączona osobno do paczki odwołuje się do oficjalnej dokumentacji Moodle i kodu importera. Ustawienia techniczne publikacji nie zmieniają kontraktów ćwiczeń.
Lokalne chatboty, agenci i systemy RAG z własną bazą wiedzy · Moduł 6 · Wydanie 29.09.2026