Strona główna / Artykuły / Porównanie RAG, RAG bez wektorów oraz GraphRAG

Porównanie RAG, RAG bez wektorów oraz GraphRAG

Klasyczny RAG wektorowy, wyszukiwanie bez wektorów leksykalnych oraz GraphRAG: dzielenie na fragmenty, embeddingi, BM25, grafy wieloetapowe i sytuacje, w których każde podejście okazuje się przydatne.

1834 słów

Jak dzisiejsze modele językowe odpowiadają na podstawie materiałów, które nigdy nie pojawiły się podczas treningu.

RAG w jednym zdaniu

Retrieval Augmented Generation to dokładnie to, co oznacza ta skrótowość. Najpierw pobiera się materiały związane z pytaniem użytkownika, a następnie model pisze odpowiedź, wykorzystując te materiały wraz z instrukcjami. Sekwencja działań to najpierw pobieranie danych, a dopiero potem generowanie odpowiedzi.

Dlaczego to konieczne?

Modele znają tylko to, co zawierało ich dane treningowe. Wszystko inne jest dla nich niewidoczne.

Załóżmy rzadką książkę liczącą 3000 stron, która prawie w ogóle nie pojawia się w internecie i nigdy nie trafiła do żadnego korpusu treningowego. Jeśli zapytasz o nią model, otrzymasz puste odpowiedzi. Nawet jeśli użyjesz narzędzi do pobierania danych, nadal nie uzyskasz żadnych informacji – nie ma nic, co dałoby się wydobyć.

RAG wypełnia tę lukę. Załaduj PDF do procesu przetwarzania. Podczas wyszukiwania pobierz najbardziej istotne fragmenty i dołącz je jako kontekst. Zadanie modelu sprowadza się do: udzielania odpowiedzi na podstawie tego tekstu, wykorzystując umiejętności językowe do sformułowania odpowiedzi.

Nie wymagane jest dokładne dostosowywanie modelu. Wystarczy odpowiedni kontekst we właściwym momencie.

Skelet procesu przetwarzania

Najpierw następuje indeksowanie, a ono zaczyna się od dzielenia tekstu na fragmenty.

Strategie dzielenia na fragmenty

PDF liczące tysiące stron nie powinno być przechowywane w bazie wektorowej jako jeden duży plik. Trzeba je podzielić, aby każdy fragment mógł być osobno przechowywany i wykorzystywany.

Powszechne sposoby dzielenia:

Na stronę. Jedna strona → jeden fragment (3 000 stron → 3 000 fragmentów). Skuteczne, gdy chce się uzyskać dokładne i precyzyjne wyniki.

Na paragraf. Dokładniejsze cięcia przy granicach paragrafów. Może to sprawić wrażenie większej precyzji, ale rozmiary są bardzo zróżnicowane (200 tokenów obok 2000). Nierówne długości powodują nierównomierne embeddingi i potajemnie psują funkcjonowanie systemu wyszukiwania.

Stałe okna. Stałe limity tokenów — zazwyczaj 512 — niezależnie od miejsca cięcia. Jednolite rozmiary zapewniają bardziej porównywalne embeddingi. Gdy nie ma pewności, większość zespołów wybiera tę opcję.

from langchain_text_splitters import CharacterTextSplitter
from langchain_core.documents import Document

def perform_fixed_size_chunking(document, chunk_size=1000, chunk_overlap=200😞
    """
    Performs fixed-size chunking on a document with specified overlap.

    Args:
        document (str): The text document to process
        chunk_size (int): The target size of each chunk in characters
        chunk_overlap (int): The number of characters of overlap between chunks

    Returns:
        list: The chunked documents with metadata
    """
    # Create the text splitter with optimal parameters
    text_splitter = CharacterTextSplitter(
        separator="\n\n",
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        length_function=len
    )

    # Split the text into chunks
    chunks = text_splitter.split_text(document)
    print(f"Document split into {len(chunks)} chunks")

    # Convert to Document objects with metadata
    documents = []
    for i, chunk in enumerate(chunks):
        doc = Document(
            page_content=chunk,
            metadata={
                "chunk_id": i,
                "total_chunks": len(chunks),
                "chunk_size": len(chunk),
                "chunk_type": "fixed-size"
            }
        )
        documents.append(doc)

    return documents

# Example usage
if __name__ == "__main__":

    # Create the dummy document
    document = create_dummy_document()

    # Process with fixed-size chunking
    chunked_docs = perform_fixed_size_chunking(
        document,
        chunk_size=1000,
        chunk_overlap=200
    )

    # Display results
    print("\n----- CHUNKING RESULTS -----")
    print(f"Total chunks: {len(chunked_docs)}")

    # Print an example chunk
    print("\n----- EXAMPLE CHUNK -----")
    middle_chunk_idx = len(chunked_docs) // 2
    example_chunk = chunked_docs[middle_chunk_idx]
    print(f"Chunk {middle_chunk_idx} content ({len(example_chunk.page_content)} characters):")
    print("-" * 40)
    print(example_chunk.page_content)
    print("-" * 40)
    print(f"Metadata: {example_chunk.metadata}")

    # For integration with Databricks Vector Search
    print("\nThese documents are ready for embedding and storage in Databricks Vector Search")
    print("Example next steps:")
    print("1. Create embeddings using the Databricks embedding endpoint")
    print("2. Store documents and embeddings in Delta table")
    print("3. Create Vector Search index for retrieval")

Embeddingi

Embedding przekształca tekst (słowo, zdanie, stronę) w gęsty wektor wysokowymiarowy, który koduje znaczenie, dzięki czemu podobne idee grupują się razem.

Cztery fazy w RAG

  1. Strona korpusu. Wstawia embedding do każdego fragmentu tekstu; przechowuje indeks oraz wektor.
  2. Strona zapytania. Wstawia embedding do wpisu użytkownika, aby żądanie miało semantyczny „odpis”.
  3. Wyszukiwanie. Szuka w bazie najbliższych sąsiadów — zwykle na podstawie podobieństwa kosinowego.
  • Zwiększenie kontekstu. Dołącz uzyskane fragmenty jako dodatkowy kontekst; model odpowiada na podanie + kontekst.
  • Gdzie znajdują się wektory

    To specjalistyczne magazyny dla embeddingów, a nie ogólne bazy danych relacyjnych lub obiektowych. Powszechne są AlloyDB, Pinecone i Qdrant; wiele zespołów używa pgvector w PostgreSQL.

    Gdzie tradycyjne podejście RAG z wektorami napotyka trudności

    1. Cięcia mogą ignorować znaczenie. Powiązane okna mogą być oddzielone; większe nakładanie się czasami pomaga, ale nie jest rozwiązaniem na wszystko.
    2. Podobieństwo może pomijać parafrazy — „sprzedaż spadła” w porównaniu z „przedsiębiorstwo jest w upadku” może nie znaleźć się blisko siebie we przestrzeni wektorowej.
    3. Fakty wieloetapowe zawodzą, gdy przyczyna i skutek znajdują się w różnych fragmentach, a pobierany jest tylko jeden z nich.
    4. Budowa, przechowywanie, indeksowanie i ponowne indeksowanie embeddingów jest kosztowne.

    Również: baza danych wektorowych nie jest synonimem RAG. Jest to tylko jeden typ backendu do wyszukiwania. Wersja RAG bez wektorów zachowuje model „pobierz, a następnie utwórz”, eliminując jednocześnie wyszukiwanie za pomocą embeddingów.

    Dlaczego unikać wektorów? Koszt tworzenia i ponownego indeksowania embeddingów, słabe zachowanie przy dokładnym dopasowaniu identyfikatorów, numerów i kodów błędów oraz konieczność utrzymania dodatkowej infrastruktury do ich obsługi.

    Rozwiązania bez wektorów stanowią całą rodzinę technologii, a nie jeden konkretny rozwiązanie:

    1. Wyszukiwanie leksykalne. BM25, Postgres tsvector, Elasticsearch — dokładne terminy są skuteczniejsze niż niejasna semantyka w przypadku SKU, cytatów i linii z logów.
    from rank_bm25 import BM25Okapi
    
    def vectorless_retrieve(query, corpus_chunks, top_k=3):
        """
        Lexical retrieval over raw text chunks - no embeddings, no vector DB.
        """
        tokenized_corpus = [chunk.lower().split() for chunk in corpus_chunks]
        bm25 = BM25Okapi(tokenized_corpus)
    
        tokenized_query = query.lower().split()
        scores = bm25.get_scores(tokenized_query)
    
        ranked = sorted(zip(corpus_chunks, scores), key=lambda x: x[1], reverse=True)
        return [chunk for chunk, score in ranked[:top_k]]
    
    1. Wyszukiwanie za pomocą agentów/narzędzi. Brak wstępnego indeksowania; model analizuje treść, wywołuje API wyszukiwania lub otwiera odpowiednie sekcje na żądanie, podobnie jak agent programistyczny przegląda repozytorium. Wyszukiwanie w czasie rzeczywistym, oparte na rozumowaniu.
  • Dopisywanie dużych fragmentów kontekstu. Dzięki ogromnym oknom kontekstowym małe korpusy mogą być uwzględnione w zapytaniu. To nie klasyczne wyszukiwanie, ale daje podobne rezultaty dla niewielkich korpusów.
  • Hybrydowe ponowne sortowanie. Najpierw tworzy się tani wykaz słów kluczowych, a następnie model przestawia je według istotności — szybkość działania dzięki słowom kluczowym z pewną niuansą semantyczną, bez konieczności wcześniejszego tworzenia pełnego indeksu wektorowego.
  • Ograniczenia modeli bez wektorów

    Słowa kluczowe nadal nie radzą sobie z parafrazami — czasem gorzej niż modele wektorowe. Pętle agentywne zwiększają opóźnienie i liczbę tokenów na zapytanie. Ogromne korpusy nadal wymagają dobrze skonstruowanego indeksu wektorowego. Modele bez wektorów zazwyczaj przeważają w małych/średnich skali lub gdy dokładność jest ważniejsza od niejednoznaczności.

    GraphRAG

    Klasyczny RAG może mylić przyczynę z skutkiem w różnych fragmentach tekstu. Modele bez wektorów zastępują wektory słowami kluczowymi lub dużymi fragmentami kontekstu. Żadne z nich nie modeluje wzajemnych powiązań między ideami. Graph RAG adresuje tę lukę.

    Zamiast pytać „który fragment jest najbliżej?”, zapytaj „jak te koncepcje są ze sobą powiązane?”. Odpowiedzią jest graf wiedzy.

    Indeksowanie — rozwijanie grafu

    Najpierw pomiń fragmenty/wbudowania. Prześlij dokument przez model, który wyodrębnia entity i relacje. Wynikiem są węzły i krawędzie.

    W przypadku tej długiej książki teoretycznej możesz otrzymać węzły takie jak Marx, Engels, Kapitał, Manifest Komunistyczny, wartość dodatkowa, materializm dialektyczny, a krawędzie takie jak napisał, współnapisał, wprowadza-koncepcję.

    Entity stają się węzłami; relacje stają się krawędziami. Mapujesz znaczenie, a nie dzielisz strony.

    Zgrupuj ściśle powiązane węzły w społeczności (popularny jest model Leiden), a następnie streszcz każdą społeczność za pomocą kolejnego przetworzenia modelu.

    Działaj na dwóch poziomach:

    • Węzły dla konkretnych faktów i bezpośrednich powiązań
    • Społeczności dla tematów i streszczeń

    Krótkie pytania → węzły. Szerokie pytania dotyczące „głównych idei?” → streszczenia społeczności. Jeden indeks, dwa tryby wyszukiwania.

    Zadawanie zapytań — przemierzanie grafu

    Pytanie: „Kto współtworzył dzieła z Marksem i co napisali razem?”

    Klasyczne RAG ma trudności: współautorstwo znajduje się w jednym fragmencie, a prace są setki stron dalej; trzeba mieć nadzieję, że embeddingi będą zgodne.

    RAG oparty na grafie działa w następujący sposób:

    1. Zidentyfikować Marksa jako punkt odniesienia
    2. Naładować węzeł i krawędzie związane z Marksem
    3. Pójść ścieżką „współtworzył z” → Engels
    4. Pójść ścieżką „napisał” → Manifest, Stan klasy robotniczej, powiązane prace

    Kierunkowe krawędzie utrzymują relacje w nienaruszonym stanie. Przyczyna i skutek, które w klasycznym RAG są rozdzielone, stają się połączonymi węzłami. Taki wzorzec wielokrotnych skoków jest trudny do prawidłowego przetworzenia zarówno w prostym RAG, jak i w RAG bez wektorów.

    Zebrawszy węzły, krawędzie oraz streszczenia społeczności, tworzy się kontekst; wynik generuje się jak zwykle.

    from graphrag import GraphRAGPipeline
    
    # Indexing — runs once
    pipeline = GraphRAGPipeline(llm="claude-3", graph_store="neo4j")
    pipeline.index(documents=["book.pdf"])
    # Under the hood: entity extraction → graph build → community detection → summaries
    
    # Querying
    result = pipeline.query(
        "Who co-wrote with Marx and what did they write together?",
        mode="global"   # uses community summaries for broad questions
        # mode="local"  # uses node-level traversal for specific facts
    )
    print(result.answer)
    print(result.sources)   # returns actual nodes + edges used, fully traceable
    

    mode nie ma charakteru estetycznego. Implementacje (włącznie z otwartym stackiem Microsoftu) rozróżniają podejście globalne od lokalnego, ponieważ są to różne strategie.

    Koszty GraphRAG

    Ekstrakcja jest kosztowna. Konieczne jest przeczytanie całego dokumentu; długie książki zużywają wiele tokenów; implicite odniesienia („jak omówiono wcześniej”) mogą nigdy nie stać się krawędziami grafu.

    Jakość grafu ogranicza jakość wyników. Zła ekstrakcja → zły graf → słabe rezultaty wyszukiwania. Rozwiązania często polegają na ponownym przeczytaniu wszystkiego, co jest problematyczne w skali większej.

    W praktyce należy wybrać narzędzie do wyszukiwania w zależności od typu zapytania, zamiast narzucać jeden konkretny sposób na każde pytanie.

    Podsumowanie: używaj klasycznego RAG do wyszukiwania semantycznego, rozwiązań bez wektorów, gdy potrzebujesz dokładności lub mniej zasobów infrastrukturalnych, oraz Graph RAG, gdy kluczowe są relacje między elementami.

    Wybór stylu wyszukiwania bez dogmatów

    Pożyteczna sekwencja decyzyjna wygląda w ten sposób.

    Zacznij od klasycznego RAG wektorowego, gdy twój korpus jest duży, języki są bardzo zróżnicowane i zazwyczaj potrzebujesz przybliżonych sąsiadów semantycznych. Inwestuj od razu w jakość dzielenia na fragmenty oraz procesy aktualizacji wektorów; to one decydują o jakości.

    Zastosuj techniki niewektorowe, gdy dokładne identyfikatory są ważniejsze niż parafraza, gdy korpus jest na tyle mały, by można było użyć wyszukiwania leksykalnego lub długiego kontekstu, albo gdy nie chcesz ponosić kosztów infrastruktury wektorowej. BM25 i podobne rozwiązania nie są „przestarzałe” — to odpowiednie narzędzia do SKU, cytatów i ciągów błędów.

    Zastosuj Graph RAG, gdy pytanie dotyczące produktu ma charakter relacyjny: kto był powiązany z kim, który koncept wprowadził jaką ideę, która społeczność podsumowuje dany temat. Spodziewaj się wyższych kosztów indeksowania i traktuj jakość ekstrakcji jako kluczowy element.

    Wiele zespołów ostatecznie stosuje podejście hybrydowe: pierwszy etap to analiza leksykalna, drugi – wektorowa, a dla podzbioru domen wieloetapowych używa się grafów. Nie chodzi o wybór jednej konkretnej metody, lecz o dopasowanie narzędzia do rzeczywistego sposobu występowania błędów.

    Uwagi operacyjne pomijane przez prezentacje

    Rozkłady ponownego indeksowania są istotne. Stare embeddingi potajemnie pogarszają jakość systemu RAG opartego na wektorach, nawet jeśli model pozostaje niezmieniony. Ustawienia pokrycia są ważne, gdy sąsiednie okna zawierają podobne znaczenia. Filtry metadanych są konieczne, gdy użytkownicy nie powinni widzieć danych innych użytkowników. Zestawy oceny są istotne, gdy twierdzimy o „lepszej” efektywności bez użycia etykiet.

    Dla Graph RAG należy planować ponowne próby wydobywania informacji, częściowe aktualizacje grafów oraz ponowne streszczanie treści w przypadku zmian w dokumentach. W przypadku agentic retrieval trzeba uwzględnić limity użycia narzędzi oraz zasady czasowe, aby ciekawy model nie mógł zużyć całej ilości tokenów przy jednej zapytaniu.