RAG w praktyce – jak sprawić, by ChatGPT odpowiadał tylko na podstawie dokumentów Twojej firmy?

Szymon Ptak
6 min read
RAG w praktyce – jak sprawić, by ChatGPT odpowiadał tylko na podstawie dokumentów Twojej firmy?

Dlaczego standardowy ChatGPT to za mało?

Wyobraź sobie, że pytasz ChatGPT o termin wykonania zamówienia nr 883/06/2026. Model nie ma dostępu do Twojego systemu ERP. Nie zna Twoich umów. Nie widzi korespondencji z klientem. Ale i tak odpowie – zmyśli numer zamówienia, datę i klienta. Brzmi wiarygodnie, jest poprawne gramatycznie… i kompletnie nieprawdziwe.

To właśnie problem halucynacji LLM – modele językowe generują odpowiedzi na podstawie statystycznych wzorców z danych treningowych, a nie na podstawie faktów z Twojej firmy.

Rozwiązaniem jest RAG – Retrieval-Augmented Generation.

RAG to architektura, w której model językowy przed wygenerowaniem odpowiedzi przeszukuje wskazaną bazę dokumentów i opiera się wyłącznie na znalezionych w niej informacjach.

🧠 Jak RAG eliminuje halucynacje?

Bez RAGZ RAG
Model zgadujeModel szuka w dokumentach
Odpowiada z pamięciOdpowiada z cytatem źródłowym
Brak kontroli nad źródłamiKażda odpowiedź ma ścieżkę audytu
Aktualizacja = ponowny treningAktualizacja = dodanie pliku

Architektura RAG – jak to działa pod maską?

System RAG składa się z trzech głównych komponentów:

1️⃣ Indeksowanie dokumentów (offline)

Dokumenty firmowe (PDF, DOCX, e-maile, umowy, faktury) są przetwarzane przed pierwszym zapytaniem:

┌─────────────┐      ┌────────────────┐      ┌──────────────┐
│  Dokumenty   │ ───▶ │  Embedding     │ ───▶ │  Wektorowa   │
│  firmowe     │      │  model         │      │  baza danych │
│  (PDF, DOCX) │      │  (text → wektor)│      │  (chromadb,  │
└─────────────┘      └────────────────┘      │   pgvector)  │
                                              └──────────────┘

Każdy dokument jest dzielony na fragmenty (chunks), a każdy fragment zamieniany na wektor liczbowy (embedding). Wektory są przechowywane w bazie wektorowej, gotowe do przeszukiwania semantycznego.

2️⃣ Wyszukiwanie (retrieval)

Gdy użytkownik zadaje pytanie:

  • Pytanie jest zamieniane na embedding
  • Baza wektorowa zwraca k najbardziej podobnych fragmentów dokumentów
  • Te fragmenty stają się kontekstem dla modelu

3️⃣ Generowanie odpowiedzi (augmented generation)

Model otrzymuje prompt złożony z kontekstu i pytania:

ODPOWIADAJ WYŁĄCZNIE NA PODSTAWIE PONIŻSZYCH DOKUMENTÓW.
JEŚLI NIE ZNAJDZIESZ ODPOWIEDZI, NAPISZ "NIE WIEM".

DOKUMENTY:
[Fragment 1] Umowa z dnia 12.03.2026: termin dostawy 45 dni...
[Fragment 2] Aneks nr 3: wydłużenie terminu do 60 dni...
[Fragment 3] ...

PYTANIE: Jaki jest termin dostawy dla zamówienia 883?

Wdrożenie RAG krok po kroku

Poniżej kompletny, działający przykład w Pythonie – od dokumentu do odpowiedzi.

Krok 1: Instalacja zależności

1
pip install langchain chromadb openai pypdf sentence-transformers

Krok 2: Ładowanie i dzielenie dokumentów

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from langchain.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# Załaduj wszystkie PDF z folderu firmowego
loader = DirectoryLoader(
    "./dokumenty_firmowe/",
    glob="**/*.pdf",
    loader_cls=PyPDFLoader
)
documents = loader.load()

# Podziel na fragmenty po 500 znaków z overlapem 50
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", ". ", " ", ""]
)
chunks = text_splitter.split_documents(documents)
print(f"Utworzono {len(chunks)} fragmentów z {len(documents)} dokumentów")

Krok 3: Tworzenie embeddingów i bazy wektorowej

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma

# Model embeddingów – polski, lokalny, darmowy
embeddings = HuggingFaceEmbeddings(
    model_name="sdadas/st-polish-paraphrase-from-mpnet"
)

# Baza wektorowa – lokalna, nie wymaga serwera
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db"
)
vectorstore.persist()
print("Baza wektorowa gotowa.")

Krok 4: Zapytanie RAG

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI

# Retriever – pobiera 4 najbardziej pasujące fragmenty
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# Model LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# Łańcuch RAG
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=retriever,
    return_source_documents=True
)

# Zadaj pytanie
response = qa_chain("Jaki jest termin płatności faktury FV/12/2026?")
print("Odpowiedź:", response["result"])
print("\n--- ŹRÓDŁA ---")
for doc in response["source_documents"]:
    print(f" 📄 {doc.metadata['source']}: ...{doc.page_content[:200]}...")

Typowe pułapki RAG i jak ich uniknąć

❌ Problem 1: Za duże chunki

Fragment 2000 znaków zawiera 5 różnych wątków – retrieval traci precyzję.

Rozwiązanie: Trzymaj chunki 300–800 znaków. Dla dokumentów prawnych stosuj podział po paragrafach, nie po znakach.

❌ Problem 2: Retrieval zwraca nieistotne fragmenty

Wyszukiwanie semantyczne oparte na podobieństwie wektorów nie zawsze trafia.

Rozwiązanie: Zastosuj hybrid search – połącz wyszukiwanie wektorowe z pełnotekstowym (BM25). W LangChain: EnsembleRetriever.

❌ Problem 3: Prompt nie blokuje halucynacji

Model nadal „dopowiada" spoza kontekstu.

Rozwiązanie: Twardy system prompt:

Jesteś asystentem firmowym. Odpowiadasz WYŁĄCZNIE na podstawie
dostarczonych dokumentów. Jeśli odpowiedzi nie ma w dokumentach,
odpowiadasz: "Nie znalazłem tej informacji w dokumentach firmy."
NIGDY nie dodawaj informacji spoza kontekstu.

❌ Problem 4: Dokumenty zawierają sprzeczne informacje

Umowa mówi 30 dni, aneks mówi 60 dni – model wybiera losowo.

Rozwiązanie: Dodaj metadane z datą każdego dokumentu i sortuj wyniki po dacie. W prompcie wymuś priorytetyzację nowszych dokumentów.


Co możesz zyskać dzięki RAG?

ObszarBez RAGZ RAG
Obsługa klientaPracownik szuka 15 min w dokumentachAI odpowiada w 3 sekundy z cytatem
Dział prawnyPrzeglądanie setek stron umów ręcznieZapytanie: „znajdź klauzule o karach" → wynik w sekundę
Helpdesk ITWiki nieczytana, odpowiedzi z głowyAI przeszukuje Wiki + zgłoszenia historyczne
Compliance„Chyba tak robiliśmy…"Każda odpowiedź ma ślad źródłowy

RAG w Agidot – jak to robimy u klientów

W Agidot wdrażamy RAG jako element większych systemów – od prostego asystenta dokumentów po złożone silniki decyzyjne weryfikujące wnioski kredytowe.

Nasz stack:

  • Dokumenty: PDF, DOCX, e-maile, skany (OCR), transkrypcje rozmów
  • Chunking: adaptacyjny, dostosowany do typu dokumentu
  • Embeddings: polskie modele open-source (bez zależności od OpenAI)
  • Vector DB: pgvector (PostgreSQL) – jedna baza mniej do zarządzania
  • LLM: GPT-4o, Claude lub lokalny model przez Ollama (dla klientów wymagających offline)
  • Orkiestracja: LangChain + FastAPI

💡 Case study z produkcji: Firma faktoringowa używa RAG do automatycznej analizy umów i wyciągania kluczowych parametrów (terminy, kary, warunki wypowiedzenia). System skraca czas analizy umowy z 45 minut do poniżej 60 sekund.


Chcesz wdrożyć RAG w swojej firmie?

Jeśli masz setki lub tysiące dokumentów, z których Twój zespół ręcznie wyszukuje informacje – RAG to rozwiązanie dla Ciebie.

🚀 Skontaktuj się z nami – pokażemy Ci, jak to działa na Twoich własnych dokumentach.