Wybierz region
pl
  • PL
  • EN
Wydrukuj

Co to jest RAG, wstęp do Langchain

W tym wpisie skupimy się na podstawowym wprowadzeniu do biblioteki. Zrobimy to z użyciem języka Python, chociaż sam Langchain oferuje również implementacje w JS.

Sztuczna inteligencja (AI), przetwarzanie języka naturalnego (NLP) oraz duże modele językowe (LLM) odgrywają kluczową rolę w rozwoju technologicznym. Jedną z najbardziej innowacyjnych technik, które zyskują na popularności, jest Retrieval-Augmented Generation (RAG). Technologia ta łączy wyszukiwanie informacji z generowaniem tekstu, co pozwala na tworzenie bardziej precyzyjnych i aktualnych odpowiedzi. W tym wpisie dokładnie przyjrzymy się, czym jest RAG, jak działa oraz jakie korzyści przynosi. Następnie wprowadzimy bibliotekę Langchain, która umożliwia łatwą implementację RAG w różnych zastosowaniach. Zapraszamy do lektury, która nie tylko rozwinie twoją wiedzę, ale także zainspiruje do praktycznego wykorzystania tych technologii.

Co to jest RAG?

Retrieval-Augmented Generation (RAG) to technika łącząca dwa kluczowe elementy przetwarzania języka naturalnego: wyszukiwanie informacji (retrieval) i generowanie tekstu (generation). Tradycyjne modele generowania tekstu opierają się na danych, które zostały w nich wytrenowane. RAG rozszerza te możliwości poprzez dodanie etapu wyszukiwania, który pozwala modelowi na dostęp do zewnętrznych źródeł informacji w czasie rzeczywistym. Dzięki temu model może generować odpowiedzi, które są bardziej trafne i aktualne.

RAG jest wykorzystywane w różnych aplikacjach, takich jak chatboty, systemy rekomendacyjne, narzędzia do analizy dokumentów, a nawet w medycynie do wspomagania decyzji klinicznych. Na przykład, chatbot wykorzystujący RAG może nie tylko odpowiadać na pytania użytkowników na podstawie swojej bazy wiedzy, ale także wyszukiwać najnowsze informacje w Internecie, aby udzielić bardziej precyzyjnych i aktualnych odpowiedzi.

Jak działa RAG?

RAG działa w dwóch głównych etapach:

  • Etap wyszukiwania (retrieval): Na tym etapie system przeszukuje bazy danych lub internet w celu znalezienia odpowiednich informacji związanych z zadanym pytaniem. Wykorzystuje do tego zaawansowane algorytmy wyszukiwania, które analizują treść i kontekst zapytania, aby znaleźć najbardziej trafne wyniki.
  • Etap generowania (generation): Po zebraniu odpowiednich informacji, model AI generuje tekst na podstawie wyszukanych danych. Dzięki temu odpowiedzi są nie tylko bardziej trafne, ale również mogą zawierać najnowsze informacje, co jest szczególnie ważne w szybko zmieniających się dziedzinach.

Wstęp do Langchain

Langchain to biblioteka zaprojektowana, aby ułatwić implementację RAG oraz innych zaawansowanych technik NLP. Langchain oferuje zestaw narzędzi i funkcji, które umożliwiają programistom łatwe tworzenie i zarządzanie modelami AI zdolnymi do przetwarzania i generowania języka naturalnego. Posiada także gotowe implementacje integracji z popularnymi LLMami takimi jak ChatGPT.

W tym wpisie skupimy się na podstawowym wprowadzeniu do biblioteki. Zrobimy to z użyciem języka Python, chociaż sam Langchain oferuje również implementacje w JS.

Struktura Langchain: Splitter, Chunks i inne pojęcia

Aby lepiej zrozumieć, jak Langchain pomaga w implementacji RAG, przyjrzyjmy się kilku kluczowym pojęciom i narzędziom oferowanym przez tę bibliotekę.

1. Chunk: Chunki to małe fragmenty tekstu powstałe w wyniku podziału dokumentu przez splitter. Praca z mniejszymi fragmentami tekstu ułatwia zarządzanie danymi i zwiększa efektywność przetwarzania informacji. Chunki mogą być przetwarzane indywidualnie, co umożliwia modelowi lepsze zrozumienie kontekstu i szybsze wyszukiwanie odpowiednich informacji.

2. Splitter: Splitter to narzędzie używane do dzielenia dużych dokumentów na mniejsze części, czyli właśnie Chunki. Jest to szczególnie przydatne w sytuacjach, gdy dokument jest zbyt duży, aby model mógł go przetworzyć na raz (wynika to z ograniczeń kontekstu). Splittery mogą dzielić dokumenty na podstawie różnych kryteriów, takich jak liczba słów, liczba zdań, paragrafy itp. Dla bardziej zaawansowanych zagadnień tak proste splitery nie są wystarczające. Często nie wiemy z jak sformatowanym dokumentem mamy do czynienia dlatego coraz częściej stosuje się splittery semantyczne, które dzielą dokument na fragmenty starając się zachować sens wypowiedzi (tak żeby chunk nie był urwany w połowie akapitu). Wówczas wykorzystywane są do tego LLMy.

Przykład bardzo prostego Splittera w Langchain:

```

from langchain_text_splitters import CharacterTextSplitter

 

document = """

This is the first paragraph. It has several sentences.

This is the second paragraph. It also has several sentences.

"""

 

text_splitter = CharacterTextSplitter(

    separator=" ",

    chunk_size=15,

    chunk_overlap=5,

    length_function=len,

    is_separator_regex=False,

)

 

chunks = text_splitter.split_text(document)

 

print(chunks)

```

 

```

['This is the', 'the first', 'paragraph. It', 'It has several', 'sentences.\nThis', 'is the second', 'paragraph. It', 'It also has', 'has several', 'sentences.']

```

W tym przykładzie wykorzystaliśmy najprostszy splitter, czyli CharacterTextSplitter, który do dzielenia używa po prostu znaku (w tym przypadku spacji). Maksymalny rozmiar chunka wynosi 15 znaków. Określiliśmy również dodatkowy parametr, czyli chunk_overlap. Możemy porównać to do swego rodzaju „zawijania”. Pomaga to zachowywać ciągłość podzielonych chunków dla tego typu splittera. Widzimy w przykładzie, że niektóre słowa (jeżeli mają 5 znaków lub mniej) powtarzają się w chunkach.

3. Embeddings: Embeddings to reprezentacje tekstu w formie wektorów, które model AI może łatwo przetwarzać. Są one kluczowe dla wyszukiwania i porównywania tekstów, ponieważ umożliwiają modelowi zrozumienie znaczenia i kontekstu słów i zdań. Langchain oferuje narzędzia do tworzenia embeddings z tekstu. Gdy tworzymy chatboty, które mają odpowiadać na bazie naszych dokumentów, kluczowe jest aby odpowiednio dopasować pytanie użytkownika do pasujących treści w dokumentacji.

Do stworzenia wektorów użyjemy gotowej integracji z OpenAI. OpenAI posiada dedykowane modele do tego typu zagadnień. W celu wykorzystania OpenAI należy wygenerować konto na platformie, a następnie stworzyć tzw. API Key. Alternatywnie można stworzyć taki klucz w plaformie Azure (dla studentów jest pula kredytów za darmo).

Przykład w pythonie:

embedded_query = embeddings_model.embed_query("Tekst do zanurzenia w wektorach. Ten tekst jest całkiem śmieszny.")

 

print(embedded_query)

```

 

```

[-0.008956722915172577, -0.006244521122425795, -0.005217758938670158, 0.01135250087827444, -0.008388452231884003, 0.004436386749148369, -0.01654442958533764, -0.02594672702252865, i tak dalej

```

4. Vector store: Vector Store to struktura danych, która przechowuje wektory (embeddings) oraz umożliwia szybkie i efektywne wyszukiwanie najbliższych sąsiadów dla danego zapytania wektorowego. Vector Store jest kluczowy w aplikacjach takich jak wyszukiwanie informacji, rekomendacje, klasteryzacja danych i wiele innych.

W późniejszym przykładzie użyjemy gotowej implantacji Vetor Store in-memory. W dużych produkcyjnych rozwiązaniach użylibyśmy dedykowanej bazy wektorowej.

5. Retriver: Retrievery to komponenty odpowiedzialne za wyszukiwanie odpowiednich informacji z bazy (właśnie wektorowej) danych lub zewnętrznych źródeł na podstawie zapytania użytkownika.

6. Generator: Generatory to komponenty odpowiedzialne za tworzenie tekstu na podstawie informacji znalezionych przez Retriever. Generatory wykorzystują duże modele językowe do tworzenia odpowiedzi, które są zgodne z kontekstem i treścią zapytania użytkownika. Dzięki temu otrzymujemy treść z dokumentacji opakowaną w formę odpowiedzi na pytanie użytkownika.

7. Chain: Chain to mechanizm, który pozwala na łączenie różnych komponentów w sekwencję przetwarzania. Każdy krok w chain jest niezależnym komponentem, który wykonuje określoną operację na danych, a wynik jednego kroku jest przekazywany jako wejście do następnego. Na potrzeby dzisiejszej analizy użyjemy gotowego chaina, należy pamiętać jednak że Chain trzeba stworzyć zgodnie z potrzebą i przeważnie gotowe rozwiązania nie dadzą nam największej skuteczności.

Znając już kroki jakie należy wykonać przejdźmy do napisania bardzo prostego RAGa w Pythonie. W przykładzie użyjemy gotowych komponentów. Po więcej informacji odsyłamy do dokumentacji Langchain, która pozwala się zapoznać z konkretnymi przykładami użycia w innych sytuacjach.

```

from langchain.document_loaders import PyPDFLoader

from langchain_text_splitters import CharacterTextSplitter

from langchain_openai import OpenAIEmbeddings

from langchain_community.vectorstores import FAISS

from langchain import hub

from langchain_openai import ChatOpenAI

from langchain_core.runnables import RunnablePassthrough

from langchain_core.output_parsers import StrOutputParser

# 1. Załadowanie dokumentu PDF

loader = PyPDFLoader(file_path="Voicebot.pdf")

document = loader.load()

# 2. Podział dokumentu na części (Splitter)

text_splitter = CharacterTextSplitter(

    separator="\n\n",

    chunk_size=1000,

    chunk_overlap=200,

    length_function=len,

    is_separator_regex=False,

)

chunks = text_splitter.split_documents(document)

 # 3. Wygenerowanie wektorów dla każdej części (Embeddings)

embeddings_model = OpenAIEmbeddings(api_key="NASZ_KLUCZ") 

# 4. Zapisanie wektorów w pamięci (Embeddings -> VectorStore)

vectorstore = FAISS.from_documents(chunks, embeddings_model) 

# 5. Utworzenie retrievera (VectorStore -> Retriever)

retriever = vectorstore.as_retriever()

# 6. Utworzenie chaina

## 6.1. Wybranie modelu językowego (Generator)

llm = ChatOpenAI(model="gpt-3.5-turbo-0125", api_key="NASZ_KLUCZ")

 

def format_docs(docs):

    return "\n\n".join(doc.page_content for doc in docs)

 

## 6.2. i 7. Konfiguracja komponentów chaina (Chain, używamy gotowego, wstawiamy wartości)

 

rag_chain = (

        {"context": retriever | format_docs, "question": RunnablePassthrough()}

        | hub.pull("rlm/rag-prompt")

        | llm

        | StrOutputParser()

)

 

# 8. Wywołanie naszego RAGa

response = rag_chain.invoke("Tworzenie organizacji. Jak to się robi?")

print(response)

```

W powyższym przykładzie wykorzystaliśmy dokumentację jednego z naszych produktów, czyli Voicebot. Podzieliśmy dokument na chunki za pomocą prostego splittera, a następnie utworzyliśmy Vector Store oraz Retriver. Do wygenerowania odpowiedzi użyliśmy modelu gpt-3.5-turbo, a następnie utworzyliśmy Chain spinający nasze komponenty. Dla takiego przykładu uzyskaliśmy odpowiedź:

```

Aby utworzyć nową organizację, administrator musi zalogować się po raz pierwszy i podać dane opisujące organizację. Następnie musi wybrać projekt, aby uzyskać dostęp do wszystkich funkcjonalności. Zarządzanie użytkownikami w organizacji odbywa się poprzez ekran organizacji, gdzie można sprawdzić informacje o użytkownikach i edytować ich ustawienia.

```

Znając rozwiązanie mogę powiedzieć że nie jest to najbardziej wyczerpująca odpowiedź ale jak w pełni poprawna w kontekście naszego produktu. Wychodząc od tak prostego RAGa można

rozpocząć pracę nad bardziej skomplikowaną implementacją, która będzie umożliwiała uzyskanie długich i wyczerpujących odpowiedzi nawet na mało precyzyjne pytania użytkowników.

Podsumowanie

W dzisiejszym dynamicznym świecie technologii, sztuczna inteligencja (AI), przetwarzanie języka naturalnego (NLP) i duże modele językowe (LLM) odgrywają kluczową rolę. Retrieval-Augmented Generation (RAG) to technika łącząca wyszukiwanie informacji z generowaniem tekstu, co pozwala na tworzenie bardziej precyzyjnych i aktualnych odpowiedzi. Dzięki RAG modele AI mogą korzystać z zewnętrznych źródeł informacji, generując trafniejsze i bardziej aktualne odpowiedzi.

Langchain to biblioteka, która ułatwia implementację RAG i innych zaawansowanych technik NLP. W naszym wpisie omówiliśmy podstawowe pojęcia takie jak chunk, splitter, embeddings, vector store, retriever, generator oraz chain.

Co osiągnęliśmy?
  • Załadowaliśmy dokument PDF: Wykorzystaliśmy PyPDFLoader do wczytania pliku.
  • Podzieliliśmy dokument na części: Użyliśmy CharacterTextSplitter do podziału tekstu na mniejsze fragmenty.
  • Stworzyliśmy embeddings: OpenAIEmbeddings pomogło nam przekształcić tekst w wektory.
  • Przechowaliśmy wektory: Użyliśmy FAISS do przechowywania wektorów w pamięci.
  • Utworzyliśmy retrievera: Dzięki retrieverowi mogliśmy szybko wyszukiwać informacje.
  • Skonfigurowaliśmy chain: Połączyliśmy wszystkie komponenty, aby stworzyć sekwencję przetwarzania danych.

Dzięki temu prostemu przykładowi stworzyliśmy podstawowy RAG, który odpowiada na pytania na podstawie dostarczonego dokumentu.

To, co zaprezentowaliśmy tutaj, to zaledwie wierzchołek góry lodowej. Langchain oferuje wiele zaawansowanych funkcji i możliwości, które pozwalają na tworzenie bardziej skomplikowanych i efektywnych rozwiązań. Możesz eksperymentować z różnymi splitterami, tworzyć własne embeddings, używać bardziej zaawansowanych vector stores, a także budować złożone sekwencje przetwarzania danych.

Zachęcam do eksperymentowania z tą biblioteką, dzięki temu więcej się nauczycie.


Michał Torzewicz

Wywodzący się ze start-upu Architekt z pioniu Business Intelligence, na co dzień związany z technologiami głosowymi i wykorzystaniem narzędzi ML w aplikacjach biznesowych. W wolnym czasie spędza czas w towarzystwie znajomych lub podróżuje kupując bilety na ostatnią chwilę (ahoj przygodo!).


Wydrukuj