RAG systémy: principy fungování a minimální implementace v Pythonu
Jazykové modely jsou omezené daty, na kterých byly trénovány: jejich znalosti jsou statické, neodrážejí aktuální události a trpí halucinacemi – generováním vymyšlených faktů. Kontextové okno tento problém ještě zhoršuje, protože při překročení limitu odstraňuje část vstupních dat. Retrieval-Augmented Generation (RAG) tento problém řeší tím, že během generování získává relevantní kontext z externích zdrojů, snižuje chyby a poskytuje odkazy na zdroje.
RAG propojuje dokumenty, databáze nebo repozitáře kódu, díky čemuž jsou odpovědi ověřitelné a aktuální. To je klíčové pro specializované domény, kde model nebyl trénován.
Rozdělování dokumentů na chunk
Rozdělování na chunk (chunking) znamená rozdělení textů na fragmenty (chunky) pro indexování. LLM pracuje s omezeným množstvím chunků, takže kvalita rozdělení určuje dostupnost faktů.
Velké chunky přidávají šum, zvyšují náklady a snižují přesnost vyhledávání. Malé chunky ztrácí kontext a oddělují související prvky. Strategie:
- Pevná délka znaků nebo tokenů.
- Sémantické hranice (odstavce, nadpisy).
- Překrytí (overlap) pro zachování spojitosti.
Parametry se ladí empiricky podle korpusu a dotazů.
Fáze fungování RAG
RAG zahrnuje předzpracování, retrieval a generování:
- Indexování: chunky se převádějí na vektory a ukládají do vektorové databáze.
- Retrieval: dotaz se vektorizuje, získávají se top-relevantní chunky.
- Augmentace: výsledky se řadí, čistí od duplicit a šumu.
- Generování: kontext se přidá k dotazu pro LLM.
V základním Naive RAG je zpracování minimální. Advanced RAG přidává přeformulování dotazu a reranking. Modular RAG používá moduly: směrování, fúzi výsledků.
Srovnání s doladěním
RAG rozšiřuje přístup k znalostem bez změny modelu, na rozdíl od doladění (fine-tuning), které vyžaduje data a zdroje. Prompt engineering optimalizuje dotazy, ale nepřidává externí data.
| Přístup | Výhody | Nevýhody |
|--------|-------------|------------|
| RAG | Aktuálnost, nízké náklady, citace | Závisí na kvalitě retrievalu |
| Doladění | Hluboká adaptace | Náročnost na zdroje, zastaralost |
RAG je ideální pro dynamická data, doladění pro statické domény.
Minimální RAG pipeline: praxe
Implementujeme Naive RAG v Pythonu s Wikipedií (500 článků) a Qwen2.5-3B-Instruct. Používáme langchain, Chroma, sentence-transformers.
Krok 1: Načtení a chunking
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ů:", len(chunks))
Získáme ~17892 chunků po 800 znacích s overlapem 100.
Krok 2: Vektorový index
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 pro embeddingy, Chroma pro úložiště.
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--- Výsledek {i} ---")
print("Nadpis:", doc.metadata["title"])
print(textwrap.fill(doc.page_content[:1000], width))
search("Kdo je Puškin?")
Vrátí relevantní fragmenty z článků o Puškinovi.
Krok 4: Generování
Přidejte LLM pro generování na základě retrieved chunků (kód vynechán pro stručnost, kompletní ve zdroji).
Co je důležité
- RAG snižuje halucinace díky externímu kontextu, přičemž model zůstává nezměněn.
- Kvalita závisí na chunkingu a embeddingech: laděte podle domény.
- Naive RAG pro start, Advanced/Modular pro produkci.
- Integrace s langchain usnadňuje prototypování.
- Testujte na reálných korpusech pro optimalizaci.
— Editorial Team
Zatím žádné komentáře.