LlamaIndex, framework zbudowany wokół dokumentów
LlamaIndex robi jedną rzecz porządnie: łączy model językowy z Twoimi danymi. Wczytuje dokumenty, dzieli je, indeksuje, a potem odpowiada na pytania na ich podstawie. Framework jest otwarty i darmowy, a płatna jest warstwa chmurowa do parsowania trudnych plików.
Czym różni się od LangChain
Oba narzędzia się przenikają i oba potrafią zbudować to samo, ale wychodzą z innego punktu.
LangChain zaczyna od modelu i pytania, jak spiąć go z narzędziami, pamięcią i przepływem sterowania. LlamaIndex zaczyna od dokumentów i pytania, jak je zorganizować, żeby model dostawał właściwe fragmenty. Ta różnica widać w domyślnych ustawieniach: pierwsze narzędzie daje więcej klocków i wymaga decyzji, drugie ma sensowne wartości domyślne dla wyszukiwania i pozwala je stopniowo zmieniać.
Praktyczny podział wygląda tak. Jeśli rdzeniem produktu jest wyszukiwanie po dokumentach, zacznij od LlamaIndexa, bo dojdziesz do działającego wyniku szybciej. Jeśli rdzeniem jest agent podejmujący decyzje i wywołujący narzędzia, LangChain albo LangGraph pasują lepiej. Wiele projektów używa obu, bo nie wykluczają się w jednym kodzie.
Pojęcia, które warto znać od razu
Cztery nazwy wracają w całej dokumentacji i bez nich trudno czytać przykłady.
Dokument to wczytany plik razem z metadanymi. Węzeł to fragment dokumentu po podziale, czyli jednostka, którą model faktycznie dostaje. Indeks to struktura, która pozwala te węzły znaleźć, najczęściej oparta o wektory. Silnik zapytań spina retriever z modelem i zwraca odpowiedź razem ze źródłami.
Warto od razu zrozumieć, że węzeł jest jednostką, na której wszystko się opiera. To on trafia do bazy wektorowej, on jest zwracany przy wyszukiwaniu i on ląduje w promptcie. Jeśli podział na węzły przecina zdanie w połowie albo oddziela nagłówek tabeli od jej treści, żadna późniejsza warstwa tego nie naprawi. Dlatego pierwsze godziny pracy nad systemem najlepiej poświęcić na obejrzenie kilkunastu wygenerowanych węzłów zamiast na dobieranie modelu.
Ostatni element bywa niedoceniany. Odpowiedź zawiera odwołania do węzłów, z których powstała, więc możesz pokazać użytkownikowi cytat i odnośnik do dokumentu. W zastosowaniach firmowych to często warunek wdrożenia, bo odpowiedź bez źródła jest bezużyteczna dla osoby, która musi ją zweryfikować.
Pierwszy RAG
pip install llama-index
export OPENAI_API_KEY=...from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
dokumenty = SimpleDirectoryReader("./dane").load_data()
indeks = VectorStoreIndex.from_documents(dokumenty)
silnik = indeks.as_query_engine(similarity_top_k=4)
odpowiedz = silnik.query("Jaki jest termin zwrotu towaru?")
print(odpowiedz)
for zrodlo in odpowiedz.source_nodes:
print(zrodlo.metadata["file_name"], round(zrodlo.score, 3))Cztery linie wystarczają do działającego wyszukiwania, co bywa mylące. Ta wersja trzyma indeks w pamięci procesu, więc znika po restarcie i nie nadaje się do produkcji. Do trwałego przechowywania podpinasz bazę wektorową, na przykład Pinecone albo Postgresa z rozszerzeniem wektorowym z Supabase.
from llama_index.vector_stores.postgres import PGVectorStore
from llama_index.core import StorageContext
magazyn = PGVectorStore.from_params(
database="dane", host="localhost", port=5432,
user="postgres", password=haslo, table_name="fragmenty", embed_dim=1536
)
kontekst = StorageContext.from_defaults(vector_store=magazyn)
indeks = VectorStoreIndex.from_documents(dokumenty, storage_context=kontekst)Samo llama-index nie ciągnie integracji z bazami ani z chmurą. Powyższy przykład wymaga pakietu llama-index-vector-stores-postgres, a LlamaParse z następnej sekcji pakietu llama-cloud-services. Import bez doinstalowania kończy się błędem, który łatwo wziąć za pomyłkę w nazwie klasy.
Parsowanie, czyli gdzie naprawdę leży problem
Większość projektów RAG nie przewraca się na wyborze modelu, tylko na wczytywaniu dokumentów. PDF z tabelą finansową, skan umowy, prezentacja z wykresami czy dokument z układem dwukolumnowym po naiwnym wczytaniu zamieniają się w tekst, którego kolejność nie ma sensu.
Objaw jest charakterystyczny: model odpowiada pewnie i błędnie, bo dostał fragment, w którym liczby z jednej kolumny skleiły się z opisami z drugiej. Wina leży w kroku pierwszym, a szuka się jej w promptcie.
LlamaParse to komercyjna odpowiedź na ten problem. Rozpoznaje układ strony, zachowuje strukturę tabel i zwraca markdown zamiast płaskiego tekstu.
Po stronie otwartej ten sam problem rozwiązuje Unstructured, które rozpoznaje typ pliku i rozbija go na elementy z zachowaniem struktury. Wersja biblioteczna nie kosztuje nic i uruchomisz ją u siebie, co bywa rozstrzygające przy dokumentach, które nie mogą opuścić firmy.
from llama_cloud_services import LlamaParse
parser = LlamaParse(result_type="markdown")
dokumenty = parser.load_data("./umowa.pdf")Wynik w markdownie ma jeszcze jedną zaletę, niezależną od jakości odczytu. Nagłówki i tabele zapisane strukturalnie pozwalają dzielić dokument po sekcjach zamiast po liczbie znaków, więc fragment odpowiada logicznej całości, a nie przypadkowemu wycinkowi. Przy dokumentacji technicznej i regulaminach to zwykle daje większą poprawę niż jakakolwiek zmiana po stronie wyszukiwania.
Zanim za to zapłacisz, sprawdź swoje pliki darmowym czytnikiem. Dokumenty tekstowe generowane z edytora zwykle wczytują się poprawnie i nie wymagają niczego więcej. Dopiero skany i pliki z rozbudowanym układem uzasadniają koszt.
Strategie wyszukiwania
Domyślne ustawienie zwraca cztery najbardziej podobne fragmenty i w wielu przypadkach wystarcza. Gdy nie wystarcza, masz kilka kierunków, z których każdy rozwiązuje inny problem.
| Podejście | Co poprawia | Koszt | Kiedy sięgnąć |
|---|---|---|---|
| Zwiększenie liczby fragmentów | Szansa, że właściwy fragment się znajdzie | Dłuższy prompt, wyższa cena | Odpowiedzi są niepełne |
| Reranking | Kolejność wyników | Dodatkowe wywołanie modelu | Właściwy fragment jest w wynikach, ale nisko |
| Wyszukiwanie hybrydowe | Zapytania z nazwami własnymi i numerami | Konfiguracja bazy | Użytkownicy wpisują kody i wersje |
| Filtrowanie po metadanych | Zawężenie do właściwego zbioru | Wymaga dyscypliny przy indeksowaniu | Dokumenty dzielą się na działy albo lata |
| Podsumowania nad grupami dokumentów | Pytania ogólne o całość | Wyższy koszt indeksowania | Pytania typu "o czym są te raporty" |
Kolejność wdrażania, która zwykle się opłaca: najpierw popraw podział dokumentów, potem dołóż reranking, dopiero na końcu komplikuj architekturę. Zmiana rozmiaru fragmentu bywa skuteczniejsza niż trzy kolejne warstwy nad wyszukiwaniem.
Workflows, czyli agenci zdarzeniowi
Gdy proces przestaje być pojedynczym zapytaniem, LlamaIndex oferuje Workflows: kroki reagujące na zdarzenia, z których każdy może wyemitować kolejne. Model programowania przypomina kolejkę komunikatów bardziej niż graf.
To osobny temat, któremu warto poświęcić więcej miejsca, bo model zdarzeniowy zmienia sposób myślenia o procesie. Szerzej opisują go Workflows w LlamaIndeksie razem z obsługą kroków równoległych, punktów wstrzymania i wznawiania przebiegu po błędzie.
from llama_index.core.workflow import Workflow, step, StartEvent, StopEvent, Event
class ZnalezionoEvent(Event):
fragmenty: list
class ObslugaPytania(Workflow):
@step
async def szukaj(self, ev: StartEvent) -> ZnalezionoEvent:
return ZnalezionoEvent(fragmenty=retriever.retrieve(ev.pytanie))
@step
async def odpowiedz(self, ev: ZnalezionoEvent) -> StopEvent:
return StopEvent(result=model.complete(zbuduj_prompt(ev.fragmenty)))Zaleta tego podejścia to naturalna równoległość: jeśli dwa kroki reagują na to samo zdarzenie, wykonają się jednocześnie. Wada to trudniejsze prześledzenie przebiegu, bo kolejność nie jest zapisana wprost, tylko wynika z typów zdarzeń.
Warstwa agentowa jest w wersji testowej i sama w sobie nie jest płatna. Płacisz za moduły, z których agent korzysta, czyli za parsowanie i ekstrakcję.
Aktualizacja dokumentów bez psucia indeksu
Pierwsza wersja systemu zwykle zakłada, że dokumenty są stałe. Po miesiącu okazuje się, że nie są, i wtedy zaczynają się kłopoty, bo w indeksie leżą obok siebie dwie wersje tego samego regulaminu.
Rozwiązaniem jest identyfikator wynikający z treści, a nie losowy. Jeśli węzeł dostaje identyfikator zbudowany z nazwy pliku i numeru fragmentu, ponowne wczytanie dokumentu nadpisuje stare wpisy zamiast dokładać nowe.
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import SentenceSplitter
potok = IngestionPipeline(
transformations=[SentenceSplitter(chunk_size=800, chunk_overlap=120), embedding],
vector_store=magazyn,
docstore=docstore
)
potok.run(documents=dokumenty)Potok z magazynem dokumentów porównuje skrót treści z tym, co już zapisane, i przetwarza wyłącznie to, co się zmieniło. Przy tysiącu plików, z których zmienia się pięć dziennie, różnica w koszcie i czasie jest zasadnicza.
Przy usuwaniu dokumentów zadbaj o osobną ścieżkę. Plik skasowany ze źródła nie znika sam z indeksu, więc bez świadomego usunięcia węzłów model będzie cytował dokument, którego już nie ma. To szczególnie kłopotliwe przy danych osobowych i przy dokumentach wycofanych z obiegu.
Warto też zapisywać w metadanych datę wczytania i wersję dokumentu. Wtedy przy sprzecznych odpowiedziach da się ustalić, skąd wziął się starszy fragment, zamiast zgadywać.
Cennik warstwy chmurowej
Framework nie kosztuje nic. Chmura rozlicza się kredytami, a tysiąc kredytów kosztuje 1,25 USD, tyle samo w regionie amerykańskim i europejskim.
| Operacja | Koszt w kredytach za stronę | Uwagi |
|---|---|---|
| Parsowanie bez modelu | 1 | Dokumenty tekstowe o prostym układzie |
| Parsowanie w trybie oszczędnym | 3 | Zalecane domyślnie, użycie mniejszego modelu |
| Ekstrakcja w trybie premium | 60 | Wyciąganie danych ze skomplikowanych układów |
Nowe konta dostają dziesięć tysięcy kredytów miesięcznie, czyli równowartość 12,50 USD, i to wystarcza na prototyp. Przetworzenie dziesięciu tysięcy stron w trybie oszczędnym zużywa trzydzieści tysięcy kredytów, więc kosztuje 37,50 USD jednorazowo, bo płacisz za wczytanie, a nie za późniejsze zapytania.
Sama liczba kredytów to jednak połowa obrazu, bo możliwość dokupienia kolejnych zależy od planu.
| Plan | Abonament | Kredyty w cenie | Dokupywanie ponad limit |
|---|---|---|---|
| Free | 0 USD | 10 tys. | brak, trzeba przejść wyżej |
| Starter | 50 USD miesięcznie | 40 tys. | do 500 USD miesięcznie |
| Pro | 500 USD miesięcznie | 400 tys. | do 5 000 USD miesięcznie |
| Enterprise | wycena indywidualna | ustalane | rabat ilościowy, pięciokrotnie wyższe limity zapytań |
Konto darmowe po wyczerpaniu puli po prostu przestaje przetwarzać, bo rozliczenia za zużycie na tym poziomie nie ma. Pierwszym progiem jest więc 50 USD miesięcznie, a nie płynne dopłacanie za nadwyżkę.
Przy szacowaniu warto policzyć nie tylko pierwsze wczytanie, ale też ponowne przetwarzanie po zmianie strategii podziału. Zmiana rozmiaru fragmentu wymaga przeliczenia embeddingów, a przy skanach czasem także ponownego parsowania, więc eksperymenty na dużym zbiorze mają swoją cenę. Tu ratuje cache: plik, który LlamaParse już raz przetworzył, przy ekstrakcji obciąża konto wyłącznie krokiem wyciągania danych, czyli piętnastoma kredytami zamiast sześćdziesięciu w trybie premium. Wynik leży w cache tylko 48 godzin i można go wyłączyć, więc eksperymenty rozłożone na tydzień zapłacą pełną stawkę. Testuj na próbce kilkuset stron, a pełne przetwarzanie uruchamiaj dopiero po wybraniu ustawień.
Ta jednorazowość jest istotna przy planowaniu. Koszt parsowania ponosisz przy indeksowaniu i przy aktualizacjach dokumentów, natomiast codzienny rachunek generują embeddingi i wywołania modelu.
LlamaIndex kontra alternatywy
| Narzędzie | Mocna strona | Słabość | Kiedy wybrać |
|---|---|---|---|
| LlamaIndex | Wyszukiwanie po dokumentach, parsowanie trudnych plików, dobre domyślne ustawienia | Mniej rozbudowana warstwa agentowa | Produkt oparty o bazę wiedzy |
| LangChain | Agenci, narzędzia, jeden interfejs nad dostawcami | Więcej decyzji do podjęcia przy RAG | Aplikacja łącząca modele i narzędzia |
| Haystack | Czytelne potoki, dojrzała ewaluacja | Mniejsza społeczność | Zespół z doświadczeniem w NLP |
| Własna implementacja | Pełna kontrola, brak zależności | Wszystko piszesz sam | Prosty przypadek, jeden typ dokumentów |
Ostatni wiersz zasługuje na uwagę, bo bywa niedoceniany. Wczytanie plików, policzenie embeddingów i zapytanie do bazy wektorowej to około stu linii kodu. Framework zaczyna się opłacać przy różnorodnych formatach plików i przy potrzebie zmiany strategii wyszukiwania bez przepisywania całości. Przy wyborze biblioteki warto sprawdzić też, czy ktoś ją jeszcze rozwija: Embedchain obiecywał dokładnie to samo w kilku linijkach, zatrzymał się na wydaniu z marca 2025 roku, a jego repozytorium przekierowuje dziś do projektu zajmującego się pamięcią rozmowy, czyli zupełnie innym problemem.
Jak sprawdzić, czy to w ogóle działa
System RAG bez pomiaru wygląda dobrze na demie i zawodzi na prawdziwych pytaniach. Ocena rozpada się na dwie warstwy i trzeba je rozdzielić, bo mierzy się je inaczej.
Pierwsza to skuteczność wyszukiwania. Bierzesz trzydzieści realnych pytań, dla każdego wskazujesz dokument, który powinien znaleźć się w wynikach, i liczysz, w ilu procentach przypadków się tam znalazł. Ta liczba nie zależy od modelu, więc mierzysz ją raz i porównujesz przy każdej zmianie podziału dokumentów.
Druga to jakość odpowiedzi przy poprawnym kontekście. Tu sprawdzasz, czy model trzyma się dostarczonych fragmentów, czy dopowiada. Najprostszy sposób to przejrzeć trzydzieści odpowiedzi ręcznie i policzyć te, które zawierają informację spoza źródeł.
def trafnosc(zestaw, k=4):
trafione = sum(
1 for pytanie, oczekiwany in zestaw
if oczekiwany in [w.metadata["file_name"] for w in retriever.retrieve(pytanie)[:k]]
)
return trafione / len(zestaw)Pytania zbieraj z prawdziwych rozmów z użytkownikami, nie wymyślaj przy biurku. Pytanie ułożone przez osobę, która zna dokumentację, brzmi jak jej spis treści i trafia zbyt łatwo, przez co wynik jest zawyżony i nic nie mówi o realnym użyciu.
Zestaw trzymaj w repozytorium i uruchamiaj po każdej zmianie: innym modelu embeddingów, innym rozmiarze fragmentu, dołożonym rerankingu. Bez tego każda dyskusja o tym, czy nowa wersja jest lepsza, kończy się wymianą wrażeń.
Typowe błędy
Pierwszy to indeksowanie bez metadanych. Fragment bez informacji o źródle, dacie i dziale nie da się później filtrować ani pokazać użytkownikowi z odnośnikiem.
Drugi to indeks trzymany w pamięci na produkcji. Działa do pierwszego restartu, po którym aplikacja przelicza wszystko od nowa albo odpowiada, że nic nie wie.
Trzeci to brak strategii aktualizacji dokumentów. Zmieniona umowa dodana ponownie zostawia w indeksie starą wersję, a model dostaje dwie sprzeczne odpowiedzi i wybiera losowo.
Czwarty to ocenianie systemu po odpowiedziach modelu zamiast po skuteczności wyszukiwania. Zmierz najpierw, jak często właściwy fragment trafia do wyników, bo bez tego poprawianie promptu jest zgadywaniem.
Piąty to przyjęcie domyślnego rozmiaru fragmentu dla wszystkich typów dokumentów. Regulamin dzieli się po paragrafach, transkrypcja rozmowy po wypowiedziach, a dokumentacja techniczna po sekcjach.
FAQ
Czy LlamaIndex jest darmowy?
Framework jest otwarty i darmowy, także komercyjnie. Płatna jest warstwa chmurowa do parsowania i ekstrakcji, rozliczana kredytami, gdzie tysiąc kredytów kosztuje 1,25 USD. Plan Free daje dziesięć tysięcy kredytów miesięcznie bez możliwości dokupienia kolejnych, a pierwszy plan płatny kosztuje 50 USD miesięcznie za czterdzieści tysięcy kredytów.
LlamaIndex czy LangChain?
Do wyszukiwania po dokumentach szybciej dojdziesz do wyniku LlamaIndexem, bo ma lepsze domyślne ustawienia w tym obszarze. Do agentów podejmujących decyzje i wywołujących narzędzia lepiej pasuje LangChain. Można używać obu w jednym projekcie.
Czy działa z modelami innymi niż OpenAI?
Tak, obsługuje modele od różnych dostawców oraz modele lokalne, w tym te pobrane z Hugging Face. Zmiana sprowadza się do podmiany obiektu modelu i modelu embeddingów w konfiguracji.
Czy muszę używać LlamaParse?
Nie, framework wczyta pliki darmowymi czytnikami. LlamaParse ma sens przy skanach, tabelach i układach wielokolumnowych, gdzie zwykłe wczytanie gubi strukturę. Sprawdź najpierw swoje pliki, bo dokumenty tekstowe zwykle nie wymagają niczego więcej.
Czy jest wersja dla TypeScriptu?
Tak, istnieje odpowiednik dla TypeScriptu, który pokrywa podstawowe scenariusze RAG. Nowe funkcje pojawiają się najpierw w wersji pythonowej, więc przy nietypowych integracjach sprawdź, czy odpowiednik już istnieje.
Dokumentacja stoi na developers.llamaindex.ai, a cennik chmury na llamaindex.ai.