## Systemy RAG: zasady działania i minimalna implementacja w Pythonie
Modele językowe są ograniczone danymi treningowymi: wiedza jest statyczna, nie odzwierciedla bieżących wydarzeń i podatna na halucynacje — generowanie wymyślonych faktów. Okno kontekstowe pogarsza problem, odrzucając część danych wejściowych po przekroczeniu limitu. Retrieval-Augmented Generation (RAG) rozwiązuje to, pobierając relewantny kontekst z zewnętrznych źródeł podczas generowania, zmniejszając błędy i zapewniając odnośniki do źródeł.
RAG podłącza dokumenty, bazy danych lub repozytoria kodu, czyniąc odpowiedzi weryfikowalnymi i aktualnymi. To kluczowe dla specjalistycznych dziedzin, na których model nie był trenowany.
Chunkowanie dokumentów
Chunkowanie to podział tekstów na fragmenty (chunks) do indeksowania. LLM przetwarza ograniczony zestaw chunków, dlatego jakość podziału decyduje o dostępności faktów.
Duże chunks dodają szum, zwiększają koszty i obniżają precyzję wyszukiwania. Małe rozrywają kontekst, dzieląc powiązane elementy. Strategie:
- Stała długość znaków lub tokenów.
- Granice semantyczne (akapity, nagłówki).
- Nakładanie (overlap) dla zachowania spójności.
Parametry dobiera się empirycznie pod korpus i zapytania.
Etapy działania RAG
RAG obejmuje preprocesing, retrieval i generację:
- Indeksowanie: chunky są przekształcane na wektory i zapisywane w bazie wektorowej.
- Retrieval: zapytanie jest wektoryzowane, pobierane są top-relewantne chunky.
- Augmentacja: wyniki są rankowane, oczyszczane z duplikatów i szumu.
- Generacja: kontekst jest dodawany do zapytania dla LLM.
W podstawowym Naive RAG przetwarzanie jest minimalne. Advanced RAG dodaje reformulację zapytania i reranking. Modular RAG wykorzystuje moduły: routing, fuzję wyników.
Porównanie z fine-tuningiem
RAG rozszerza dostęp do wiedzy bez zmiany modelu, w przeciwieństwie do fine-tuningu, który wymaga danych i zasobów. Prompt engineering optymalizuje zapytania, ale nie dodaje zewnętrznych danych.
| Podejście | Zalety | Wady |
|--------|-------------|------------|
| RAG | Aktualność, niskie koszty, cytaty | Zależne od jakości retrieval |
| Fine-tuning | Głęboka adaptacja | Wysokie zużycie zasobów, starzenie się |
RAG nadaje się do dynamicznych danych, fine-tuning do statycznych dziedzin.
Minimalny potok RAG: praktyka
Zaimplementujemy Naive RAG w Pythonie z Wikipedią (500 artykułów) i Qwen2.5-3B-Instruct. Użyjemy langchain, Chroma, sentence-transformers.
Krok 1: Ładowanie i chunkowanie
import torch
from datasets import load_dataset
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
device = "cuda" if torch.cuda.is_available() else "cpu"
dataset = load_dataset(
"wikimedia/wikipedia",
"20231101.ru",
split="train[:500]"
)
documents = [
Document(
page_content=row["text"],
metadata={"title": row["title"]}
)
for row in dataset
]
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=100
)
chunks = splitter.split_documents(documents)
print("Chunków:", len(chunks))
Otrzymujemy ~17892 chunków po 800 znaków z overlapem 100.
Krok 2: Indeks wektorowy
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
embedding_model = HuggingFaceEmbeddings(
model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
model_kwargs={"device": device}
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding_model
)
Model multilingual MiniLM do embeddingów, Chroma do przechowywania.
Krok 3: Retrieval
import textwrap
def search(query, k=3, width=80):
results = vectorstore.similarity_search(query, k=k)
for i, doc in enumerate(results, 1):
print(f"\n--- Wynik {i} ---")
print("Tytuł:", doc.metadata["title"])
print(textwrap.fill(doc.page_content[:1000], width))
search("Kto to jest Puszkin?")
Zwraca relewantne fragmenty z artykułów o Puszkinie.
Krok 4: Generacja
Dodaj LLM do generowania na podstawie pobranych chunków (kod pominięty dla zwięzłości, pełny w źródle).
Co ważne
- RAG zmniejsza halucynacje dzięki zewnętrznemu kontekstowi, bez zmiany modelu.
- Jakość zależy od chunkowania i embeddingów: dobieraj pod dziedzinę.
- Naive RAG na start, Advanced/Modular do produkcji.
- Integracja z langchain ułatwia prototypowanie.
- Testuj na realnych korpusach dla optymalizacji.
— Editorial Team
Brak komentarzy.