Zbudowałem RAG od zera w Pythonie, aby go zrozumieć

Przez sześć miesięcy używałem LangChain w środowisku produkcyjnym. Nie potrafiłem wyjaśnić, jak to działało. Nie wiedziałem, dlaczego wybrałem konkretne metryki ani jak tekst stawał się wektorami. Biblioteka ukrywała logikę.

Aby to naprawić, usunąłem framework. Napisałem potok (pipeline) RAG od zera, używając 500 linii czystego Pythona.

Oto czego nauczyłem się, budując ten stos (stack) ręcznie.

Problem z „czarnymi skrzynkami”

Używając bibliotek wysokiego poziomu, tracisz kontrolę. Widziałem, jak modele halucynują fakty lub podają błędne cytowania. Nie potrafiłem stwierdzić, czy błąd leżał w chunkerze, modelu embeddingowym czy w prompcie.

Kiedy budujesz to sam, każda warstwa jest możliwa do sprawdzenia. Możesz wypisać dokładne chunki wysyłane do LLM. Możesz dokładnie zobaczyć, w którym miejscu zdanie zostaje podzielone.

Pięć warstw RAG

RAG to nie jeden algorytm. To pięć różnych procesów ułożonych jeden na drugim:

  • Chunking: Decydowanie, jak dzielić tekst.
  • Embedding: Zamiana tekstu na matematykę.
  • Retrieval: Znajdowanie odpowiednich fragmentów.
  • Prompt Construction: Mówienie modelowi, jak ma się zachowywać.
  • Generation: Uzyskiwanie końcowej odpowiedzi.

Lekcje z budowy

1. Chunking to najważniejszy krok Większość samouczków to pomija. Jeśli nie użyjesz nakładania się (overlap), stracisz kontekst na granicach fragmentów. Użyłem okna przesuwnego (sliding window) z nakładaniem się na poziomie znaków. Dzięki temu model widzi powiązanie między dwoma chunkami.

2. Metryki odległości mają znaczenie Spędziłem godziny na debugowaniu złych wyników wyszukiwania. Problemem nie były dane. Problemem była metryka. ChromaDB domyślnie używa odległości L2. Do wyszukiwania semantycznego potrzebujesz podobieństwa cosinusowego (Cosine similarity). Jedna linia kodu zmieniła wszystko.

3. Prompty wymagają ograniczeń LLM to narzędzie do uzupełniania treści, a nie wyrocznia. Jeśli zadasz niejasne pytanie, model wymyśli odpowiedź. Nauczyłem się używać ścisłego szablonu odmowy. Powiedziałem modelowi: „Jeśli kontekst nie zawiera odpowiedzi, powiedz, że nie wiesz”. To zmniejszyło liczbę halucynacji z 40% do 5%.

4. Przetwarzaj zapytania w paczkach (batching) Wysyłanie jednego zapytania HTTP na każdy chunk jest wolne. Wysyłanie ich w paczkach jest znacznie szybsze. Pozwala to lokalnemu modelowi na potokowe przetwarzanie pracy.

5. Testuj od dołu do góry Nie pisz testów na samym końcu. Najpierw przetestuj swój chunker. Potem przetestuj swój embedder. Na końcu przetestuj swój magazyn (store). Jeśli zostawisz testy na koniec, będziesz testować błędy zamiast logiki.

Jeśli masz wrażenie, że nie rozumiesz w pełni swojego stosu AI, zbuduj go samodzielnie. Kod nie jest celem. Celem jest myślenie.

Source: https://dev.to/avinash_zala_1c6f5e7c4af9/i-built-rag-from-scratch-in-python-to-understand-it-heres-what-i-learned-33kf

Optional learning community: https://t.me/GyaanSetuAi