Powrót do strony głównej

Systemy RAG w Pythonie: implementacja krok po kroku

Artykuł wyjaśnia zasady działania systemów RAG, które łączą generatywne modele językowe z zewnętrznymi źródłami wiedzy, minimalizując halucynacje. Przedstawiono porównanie z dostrajaniem oraz praktyczną implementację Naive RAG w Pythonie z wykorzystaniem Wikipedii, langchain i Chroma. Omówiono kluczowe etapy: chunkowanie, indeksowanie wektorowe i wyszukiwanie.

🔥 RAG w Pythonie: od teorii do działającego kodu!
Advertisement 728x90

## 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.

Google AdInline article slot

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ę:

Google AdInline article slot
  • 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 |

Google AdInline article slot

|--------|-------------|------------|

| 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

Advertisement 728x90

Czytaj dalej