RAG z LangChain i LangGraph: od czego zacząć

Michał Feliksik
3 min read
RAG z LangChain i LangGraph: od czego zacząć

Wprowadzenie

W poprzedniej serii zbudowaliśmy najprostszy RAG na LlamaIndex. Tym razem to samo zadanie rozwiążemy drugim najpopularniejszym zestawem narzędzi: LangChain i LangGraph. Plan jest identyczny jak ostatnio — dziś teoria i mapa pojęć, w drugiej części klocki z kodem, a w trzeciej kompletne API indeksujące pliki Markdown z dysku i odpowiadające na pytania przez OpenRouter. Jak zawsze: najprostszy możliwy wariant, bez wymyślnych strategii.

Przypomnienie: jak działa RAG

Dla porządku, w trzech zdaniach (szczegóły w pierwszej części poprzedniej serii): model językowy nie zna Twoich dokumentów, więc zamiast go dotrenowywać, RAG wyszukuje fragmenty pasujące do pytania i dokleja je do promptu. Wyszukiwanie działa na embeddingach — wektorach liczb reprezentujących znaczenie tekstu — a dokumenty są wcześniej pocięte na mniejsze fragmenty (chunki) i zapisane w indeksie. Ten schemat jest uniwersalny; zmienia się tylko biblioteka, którą go składamy.

LangChain — skrzynka z klockami

LangChain to biblioteka Pythona z gotowymi komponentami do budowy aplikacji z LLM. W odróżnieniu od LlamaIndex, który jest wyspecjalizowany w indeksowaniu i odpytywaniu dokumentów, LangChain jest bardziej „rozłożony na części" — dostajemy osobne, wymienne klocki:

  • model czatu — jednolity interfejs do LLM-ów różnych dostawców (u nas: przez OpenRouter),
  • model embeddingów — zamiana tekstu na wektory,
  • text splitter — cięcie dokumentu na chunki,
  • vector store — magazyn wektorów z wyszukiwaniem podobieństwa (u nas: w pamięci),
  • prompt template — szablon promptu, do którego wstawimy znalezione fragmenty i pytanie.

Tam, gdzie LlamaIndex ukrywał te kroki za jednym VectorStoreIndex i query(), LangChain każe je złożyć samodzielnie. To trochę więcej kodu, ale też pełna widoczność tego, co się dzieje na każdym etapie.

LangGraph — przepływ jako graf

Skoro kroki składamy sami, trzeba czymś opisać ich kolejność. Do tego służy LangGraph — biblioteka (od twórców LangChain), w której aplikację definiuje się jako graf stanów:

  • stan (state) — wspólny „formularz" przekazywany między krokami; u nas: pytanie, znalezione fragmenty, odpowiedź,
  • węzły (nodes) — funkcje Pythona, z których każda czyta stan i dopisuje do niego swój wynik,
  • krawędzie (edges) — strzałki mówiące, który węzeł wykonuje się po którym.

Nasz RAG będzie najprostszym możliwym grafem — dwa węzły połączone w linię:

START → [retrieve: znajdź fragmenty] → [generate: zapytaj LLM] → END

Czy do dwóch kroków graf jest niezbędny? Nie — dałoby się to zapisać jako dwie zwykłe funkcje. Ale graf pokazuje swoją wartość w momencie rozbudowy: dodanie kroku weryfikacji odpowiedzi, pętli doprecyzowującej pytanie czy rozgałęzienia to dopisanie węzła i krawędzi, a nie przepisywanie logiki. Ucząc się grafu na prostym przykładzie, dostajemy fundament pod bardziej złożone agentowe aplikacje.

Co będzie potrzebne z Pythona?

Nic nowego względem poprzednich serii — wszystko omawialiśmy w podstawach Pythona i drugiej ich części:

  • słowniki — stan grafu w LangGraph to w praktyce słownik; węzły zwracają słowniki z aktualizacjami,
  • funkcje — każdy węzeł grafu to zwykła funkcja,
  • listy — znalezione fragmenty dokumentów,
  • JSON, venv, zmienne środowiskowe — API, izolacja zależności i klucz do OpenRouter poza kodem, jak zawsze.

Podsumowanie

LangChain daje wymienne klocki (model, embeddingi, splitter, vector store, prompt), a LangGraph spina je w czytelny graf: retrieve → generate. Koncepcyjnie to dokładnie ten sam RAG, który znamy z serii o LlamaIndex — różnica polega na tym, że tym razem sami widzimy i kontrolujemy każdy krok. W następnej części każdy z tych klocków dostanie swój kod.