LlamaIndex w praktyce: dokumenty, indeks, zapytania

Michał Feliksik
3 min read
LlamaIndex w praktyce: dokumenty, indeks, zapytania

Wprowadzenie

W pierwszej części ustaliliśmy teorię: RAG to indeksowanie dokumentów pociętych na chunki z embeddingami oraz odpowiadanie na pytania z pomocą znalezionych fragmentów. Dziś przekładamy te pojęcia na konkretne klasy biblioteki LlamaIndex — omawiamy dokładnie te (i tylko te) elementy, z których w trzeciej części złożymy działające API.

Czym jest LlamaIndex?

LlamaIndex to biblioteka Pythona, która implementuje cały przepływ RAG: wczytywanie dokumentów, cięcie na chunki, liczenie embeddingów, budowę indeksu i odpytywanie z pomocą LLM. Bez niej każdy z tych kroków trzeba by napisać ręcznie; z nią — cały minimalny RAG mieści się w kilkunastu linijkach.

Instalacja w środowisku wirtualnym:

1
2
python -m venv .venv
.venv\Scripts\pip install llama-index-core llama-index-llms-openrouter llama-index-embeddings-huggingface

Trzy pakiety, bo LlamaIndex jest modularny: rdzeń (core), integracja z modelem językowym przez OpenRouter i lokalny model embeddingów.

Document — dokument w rękach biblioteki

Podstawowa jednostka danych. Nasze API dostanie ścieżkę do pliku Markdown, więc wczytamy jego treść i opakujemy w Document:

1
2
3
4
5
from pathlib import Path
from llama_index.core import Document

tekst = Path("notatki.md").read_text(encoding="utf-8")
dokument = Document(text=tekst, metadata={"plik": "notatki.md"})

metadata to zwykły słownik — LlamaIndex przenosi go na chunki, dzięki czemu przy odpowiedzi wiadomo, z którego pliku pochodzi znaleziony fragment.

Settings — jaki model liczy embeddingi, jaki odpowiada

RAG używa dwóch różnych modeli i warto je świadomie rozdzielić:

  • model embeddingów — zamienia tekst na wektory; u nas mały, darmowy model uruchamiany lokalnie,
  • LLM — generuje odpowiedź na podstawie znalezionych fragmentów; u nas model wywoływany przez OpenRouter, czyli jedno API dające dostęp do wielu modeli różnych dostawców.

Globalna konfiguracja przez Settings:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import os
from llama_index.core import Settings
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openrouter import OpenRouter

Settings.embed_model = HuggingFaceEmbedding(
    model_name="sentence-transformers/all-MiniLM-L6-v2"
)
Settings.llm = OpenRouter(
    api_key=os.environ["OPENROUTER_API_KEY"],   # klucz TYLKO ze zmiennej środowiskowej
    model="anthropic/claude-haiku-4.5",
)

Zwróć uwagę na os.environ — klucz API nie pojawia się w kodzie ani w repozytorium. Przed uruchomieniem ustawiamy go w terminalu ($env:OPENROUTER_API_KEY = "..." w PowerShell).

VectorStoreIndex — indeks w pamięci

Serce całości. Przyjmuje listę dokumentów i robi wszystko, o czym była mowa w teorii — tnie na chunki, liczy embeddingi, zapisuje w indeksie:

1
2
3
from llama_index.core import VectorStoreIndex

index = VectorStoreIndex.from_documents([dokument])

Domyślnie indeks żyje w pamięci — dokładnie tak, jak zaplanowaliśmy: zero konfiguracji baz danych, po restarcie indeksujemy od nowa. Kolejne dokumenty można dodawać do istniejącego indeksu:

1
index.insert(kolejny_dokument)

Query engine — zadajemy pytanie

Ostatni klocek zamienia indeks w kompletny mechanizm pytanie-odpowiedź:

1
2
3
4
5
6
query_engine = index.as_query_engine(similarity_top_k=3)
odpowiedz = query_engine.query("Jak zresetować hasło?")

print(odpowiedz.response)          # tekst odpowiedzi z LLM
for node in odpowiedz.source_nodes:
    print(node.metadata, node.score)   # skąd pochodzi fragment i jak dobrze pasował

Jedno wywołanie query() wykonuje cały przepływ z części pierwszej: embedding pytania → znalezienie similarity_top_k=3 najbliższych chunków → wysłanie ich z pytaniem do LLM → zwrócenie odpowiedzi. W source_nodes dostajemy użyte fragmenty — dzięki metadanym widać, z którego pliku pochodzą.

Podsumowanie

Cztery klocki: Document (treść pliku), Settings (model embeddingów lokalnie + LLM przez OpenRouter, klucz ze zmiennej środowiskowej), VectorStoreIndex (indeks w pamięci) i query engine (pytanie → odpowiedź ze źródłami). To dosłownie wszystko, czego potrzebuje najprostszy RAG. W ostatniej części serii opakujemy te klocki w API na FastAPI: jeden endpoint do indeksowania plików Markdown z dysku, drugi do zadawania pytań.