Słów parę o RAG.
Obietnica jest kusząca: „podłącz” chatbota do swojej bazy dokumentów PDF, a on będzie na nie odpowiadał.
Teoretycznie działa, ale często zawodzi, dlaczego? Ponieważ w 90% przypadków systemy RAG zawodzą nie na etapie generowania odpowiedzi, ale na etapie przygotowania danych. Wrzucono do nich „surowe” pliki, licząc na to, że AI „jakoś sobie poradzi”.
Dziś nie pokażę Wam gotowego skryptu do skopiowania. Pokażę Wam coś znacznie cenniejszego: strategię architektoniczną budowy inteligentnego systemu do przetwarzania dokumentów. To podejście hybrydowe, które łączy klasyczną precyzję z kontekstowym rozumieniem Dużych Modeli Językowych (LLM).
Trzy strategie „chunkowania” – i dlaczego dwie z nich są mogą być błędne
Głównym problemem w RAG jest „chunking”, czyli dzielenie długich dokumentów na mniejsze fragmenty. Model językowy ma ograniczony kontekst, więc musimy mu podać tylko te fragmenty, które są istotne dla pytania.
- Podejście Naiwne / Lazy RAG (Prosty Podział): Większość tutoriali pokazuje prosty podział: „podziel tekst co 500 znaków”. W przypadku dokumentu technicznego lub aktu prawnego jest to katastrofa. Taki podział tnie zdania w połowie, rozdziela definicje od przykładów, a co najgorsze – traci cały kontekst strukturalny. Fragment „stosuje się odpowiednio” nie znaczy nic bez informacji, że pochodzi z
Działu IV,Rozdziału 2,Art. 15a,ust. 3. - Podejście „Tylko AI” (Brute Force): Druga skrajność to wysłanie całego dokumentu (np. 100 stron PDF) do super-modelu (np. GPT-5) z poleceniem: „podziel mi to inteligentnie”. To może zadziałać, ale będzie:
- Drogie: Przetwarzanie milionów tokenów kosztuje.
- Wolne: Odpowiedź zajmie wiele minut, nie sekundy.
- Nieprzewidywalne: Model za każdym razem może to zrobić nieco inaczej.
- Podejście Hybrydowe (Strategia i Precyzja): To jest podejście, które projektuję. Traktujemy system jak wyspecjalizowany zespół ekspertów:
- LLM / SLM analizuje strukturę dokumentu, aby wybrać odpowiednią strategie regexów
- Szybki Analityk (np. Regex): Przelatuje przez tekst w milisekundy i tworzy „mapę” dokumentu, znajdując wszystkie strukturalne punkty zaczepienia (Działy, Rozdziały, Artykuły).
- Ekspert Kontekstowy (LLM): Wkracza tylko wtedy, gdy jest absolutnie niezbędny. Używamy go do „chirurgicznych” zadań.
Oto jak przełożyć tę strategię na architekturę.
Krok 1: Fundament – Konwersja zachowująca strukturę
Zanim zaczniemy cokolwiek dzielić, musimy wydobyć tekst z pliku PDF. Ale nie chcemy „gołego” tekstu. Chcemy zachować strukturę – nagłówki, listy, pogrubienia.
Dlatego zamiast prostego pdf.read(), używamy narzędzi takich jak pymupdf4llm, które konwertują PDF na format Markdown.
Python
# Koncepcja: Konwersja PDF do Markdown
# Używamy biblioteki, która rozumie układy stron
import pymupdf4llm
try:
# To daje nam tekst z zachowanymi nagłówkami (#), listami (*) i pogrubieniami (**)
md_text = pymupdf4llm.to_markdown("moj_dokument.pdf")
except Exception as e:
print(f"Błąd konwersji: {e}")
Markdown jest idealny, bo **Art. 1.** czy # DZIAŁ I to dla nas jasne sygnały strukturalne, które wykorzystamy w kolejnym kroku.
Krok 2: Mapa Terenu – Szybkie Skanowanie Strukturalne
Teraz, zamiast wysyłać cały tekst do LLM, używamy szybkiego i precyzyjnego modułu parsera (np. opartego o wyrażenia regularne), aby zmapować szkielet dokumentu.
Chcemy błyskawicznie znaleźć wszystkie wystąpienia nagłówków strukturalnych:
Python
# Koncepcja: Definicje wzorców strukturalnych
# Kompilujemy Regexy raz, dla maksymalnej wydajności
import re
DZIAL_RE = re.compile(r"^(DZIAŁ\s+[IVXLCDM]+[A-Z]*)", re.MULTILINE)
ROZDZIAL_RE = re.compile(r"^(Rozdział\s+\d+[a-z]*)", re.MULTILINE)
ARTYKUL_RE = re.compile(r"(^|\n)(?<!\w)(Art\.\s*\d+[a-z]*\s*\.)", re.IGNORECASE | re.MULTILINE)
# ... i tak dalej dla innych elementów ...
Przeskanowanie tymi wzorcami 100-stronicowego dokumentu zajmuje ułamki sekundy. Wynikiem jest „spis treści”: lista wszystkich Działów, Rozdziałów i Artykułów wraz z ich pozycją w tekście. Mamy mapę terenu, zanim LLM w ogóle wszedł do gry.
Krok 3: Chirurgiczna Precyzja – Kiedy Wkracza LLM?
Teraz dopiero uruchamiamy AI. Ale nie na całym dokumencie. Zlecamy mu tylko dwa precyzyjne, „chirurgiczne” zadania, z którymi Regex sobie nie poradzi.
Zadanie 1: Zrozumienie Tytułu
Regex nie „zrozumie”, że „USTAWA z dnia 29 września 1994 r. o rachunkowości” oznacza, że logicznym tytułem jest „o rachunkowości”. LLM potrafi to zrobić. Wysyłamy do modelu tylko początek dokumentu z bardzo konkretnym poleceniem:
Plaintext
# Przykład Promptu: Ekstrakcja Tytułu
Przeanalizuj początek tego aktu prawnego. Zidentyfikuj główny tytuł aktu
(np. "o podatku dochodowym od osób fizycznych").
Fragment tekstu:
---
[...pierwsze 3000 znaków tekstu...]
---
Odpowiedz TYLKO w formacie JSON:
{
"tytul_aktu": "..."
}
Oszczędność tokenów jest ogromna, a wynik precyzyjny.
Zadanie 2: Inteligentny Podział Złożonych Artykułów
Główna logika systemu wie (z Kroku 2), gdzie zaczyna się i kończy każdy artykuł. Kiedy napotyka artykuł, który jest bardzo długi (np. > 800 znaków), wie, że prosty podział zawiedzie.
Wtedy zleca zadanie LLM, wysyłając do niego tylko treść tego jednego artykułu:
Plaintext
# Przykład Promptu: Podział Artykułu
Jesteś ekspertem prawnym. Podziel poniższy tekst artykułu na logiczną hierarchię:
ustępy (np. "1.", "2."), punkty (np. "1)", "2)"), litery (np. "a)", "b)").
Zachowaj pełną treść.
Tekst do podziału:
---
[...treść Art. 15...]
---
Zwróć TYLKO listę obiektów JSON:
[
{ "typ": "ustep", "numer": "1", "tresc": "..." },
{ "typ": "punkt", "numer": "1)", "tresc": "...", "nadrzedny_typ": "ustep", "nadrzedny_numer": "1" }
]
To jest serce strategii hybrydowej. Regex wykonał 90% roboty (znalezienie artykułu), a LLM wykonuje 10% pracy analitycznej – ale takiej, której Regex by nie podołał.
Krok 4: Architektura na Przyszłość – Abstrakcja i Odporność
Dobry system IT od prototypu odróżniają dwie rzeczy: elastyczność i odporność na błędy.
1. Elastyczność (Abstrakcja Dostawcy LLM): Nasz główny system nie powinien „wiedzieć”, czy korzysta z darmowej Ollamy na lokalnym komputerze, czy z płatnego Azure OpenAI. Projektujemy to z użyciem klasy abstrakcyjnej.
Python
# Koncepcja: Architektura modułowa
from abc import ABC, abstractmethod
class DostawcaLLM(ABC):
"""Interfejs dla każdego dostawcy modelu językowego."""
@abstractmethod
def wykonaj_polecenie(self, prompt: str) -> str:
pass
class DostawcaOllama(DostawcaLLM):
"""Konkretna implementacja dla Ollama."""
def wykonaj_polecenie(self, prompt: str) -> str:
# ... logika komunikacji z Ollama ...
return odpowiedz_llm
class DostawcaAzureOpenAI(DostawcaLLM):
"""Implementacja dla Azure."""
def wykonaj_polecenie(self, prompt: str) -> str:
# ... logika komunikacji z API Azure ...
return odpowiedz_llm
To jest myślenie architektoniczne. Jeśli jutro klient powie „przechodzimy na Azure”, zmieniamy jedną linijkę przy inicjalizacji systemu, a cała reszta logiki działa bez zmian.
2. Odporność (Logika fallback): Co, jeśli LLM zwróci bzdury, pustą odpowiedź albo JSON, którego nie da się sparsować? System prototypowy by się „wywalił”. System produkcyjny musi być na to gotowy.
Nasza główna logika musi zawierać mechanizmy zapasowe (fallback). Jeśli inteligentny podział artykułu (Krok 3.2) się nie powiedzie:
- System loguje błąd.
- Nie przerywa przetwarzania.
- Zamiast tego stosuje metodę zapasową (np. traktuje cały tekst artykułu jako jeden, duży chunk).
Lepiej mieć jeden duży, ale poprawny kontekstowo chunk, niż dopuścić do awarii całego systemu. To jest dojrzałe, inżynierskie podejście do zarządzania ryzykiem.
Podsumowanie: Architektura ponad Hype
Budując system w ten sposób, otrzymujemy rozwiązanie, które jest:
- Precyzyjne: Dzięki świadomym kontekstu chunkom, nasz przyszły system RAG będzie dostarczał dokładne odpowiedzi.
- Wydajne: Regex wykonuje większość ciężkiej pracy, a LLM jest używany tylko tam, gdzie to konieczne, oszczędzając czas i pieniądze.
- Odporne: System jest gotowy na błędy LLM i potrafi sobie z nimi radzić bez awarii.
- Elastyczne: Dzięki abstrakcji dostawcy, możemy łatwo zmienić „mózg” AI pod spodem.
To jest właśnie moje podejście do technologii, ukształtowane przez lata praktyki – zarówno w IT, jak i w karate. Nie chodzi o to, by używać najnowszych, krzykliwych narzędzi. Chodzi o to, by strategicznie wybrać właściwe narzędzia i użyć ich z precyzją, budując systemy, które są niezawodne, skalowalne i naprawdę rozwiązują problemy biznesowe.

0 komentarzy