ZWID_Pierwszy film AI za darmo - 5 kredytów na start, potem od 3 zł. Bez abonamentu.1. film za 0 złZrób film →
✨ Świeża dostawa•nowe kody na kinetyka.pl: Gemini Pro 18 mies. 170 zł•Cursor, ElevenLabs, Lovable i więcej, roczne plany AI w ułamku ceny Zobacz →
Przejdź do treści
Artykuły

Lokalny czat z PDF po polsku: RAG w Ollamie, skrypt i test

Działający lokalny czat z dokumentem PDF po polsku: gotowy skrypt w Pythonie, Ollama i bge-m3. Sprawdziłem 10 pytań do ustawy o prawach konsumenta: bez dokumentu, z RAG i z całą ustawą w kontekście.

12 min czytania
Napis Czat z PDF bez internetu - RAG w Ollamie: skrypt i test 10 pytań

👁 115 przeczytań

// w skrócie
  • RAG w Ollamie podniósł trafność odpowiedzi Gemmy 4 12B z 2 do 9 na 10 pytań dotyczących ustawy o prawach konsumenta.
  • Alternatywą dla RAG jest wklejenie całego dokumentu do modelu z kontekstem 65 536 tokenów, co dało Gemmie 4 12B wynik 10 na 10 przy 9,7 GB pamięci karty.
  • Model bge-m3 (1,2 GB, licencja MIT) obsługuje ponad 100 języków w tym polski i służy do zamiany fragmentów tekstu na wektory podczas wyszukiwania.

Lokalny czat z PDF-em po polsku zrobisz w Ollamie i jednym skrypcie Pythona: dokument nie opuszcza komputera, a model odpowiada z numerem artykułu. Sprawdziłem to na ustawie o prawach konsumenta (Dz.U. 2026 poz. 1244, 24 strony) i 10 pytaniach z odpowiedzią w przepisach. 3 października 2026 roku wyszukiwanie fragmentów (RAG) podniosło trafność Gemmy 4 12B z 2 do 9 na 10. 17 października 2026 roku sprawdziłem drugą drogę: cała ustawa wklejona do modelu dała 10 na 10 przy 9,7 GB pamięci karty. Skrypt, pytania i wyniki są niżej, a szerszy przegląd programów i modeli jest w przewodniku Lokalne AI.

Stan na 17 października 2026

  • Ollama 0.40.5 (wydanie stabilne z 14 października 2026), licencja MIT, działa bez konta.
  • Wyszukiwanie: model bge-m3 w Ollamie (1,2 GB, licencja MIT, ponad 100 języków, w tym polski).
  • Odpowiedzi: Gemma 4 12B (Apache 2.0), Llama 3.1 8B (Llama 3.1 Community License), Bielik 11B v3 (Apache 2.0). Wszystkie można używać komercyjnie, Llamę z warunkami licencji Mety.
  • Dokument: tekst jednolity ustawy z 30 maja 2014 r. o prawach konsumenta. Akty prawne nie są chronione prawem autorskim (art. 4 ustawy o prawie autorskim).
  • Koszt: zero złotych za API. Po pobraniu modeli całość działa bez internetu.

Odpowiedź w skrócie

  • Krótki dokument (do kilkudziesięciu stron): wklej go w całości do modelu z długim kontekstem. Gemma 4 12B z kontekstem 65 536 tokenów odpowiedziała poprawnie na 10 z 10 pytań.
  • Długi dokument albo cały folder plików: użyj RAG, czyli wyszukiwania fragmentów przed odpowiedzią. W moim teście dało 8-9 trafnych odpowiedzi na 10 przy 7-9 GB pamięci karty.
  • Bez Pythona: ten sam mechanizm mają Open WebUI, LM Studio i GPT4All. Opis w sekcji o gotowych programach.

Na czym polega czat z dokumentem (RAG)

Model nie zna Twojego pliku. Można mu go wkleić w całości, ale ustawa o prawach konsumenta to 28,5 tysiąca tokenów (tyle zliczyła Ollama 17 października), a lokalne programy domyślnie dają modelowi 4-8 tysięcy. Dlatego stosuje się RAG. Teorię opisałem w tekście RAG krok po kroku, a wektory w tekście o embeddingach.

  1. Cięcie. Skrypt tnie ustawę po artykułach, a długie artykuły na części po 1400 znaków z zakładką 200. Wyszło 120 fragmentów.
  2. Wektory. Każdy fragment zamieniam na wektor modelem bge-m3 i zapisuję na dysku.
  3. Wyszukiwanie. Pytanie też zamieniam na wektor i biorę 5 najbardziej podobnych fragmentów.
  4. Odpowiedź. Model dostaje tylko te fragmenty i polecenie: odpowiadaj na ich podstawie, podaj numer artykułu, a jeśli odpowiedzi nie ma, napisz to.

Jak zacząć krok po kroku

  1. Zainstaluj Ollamę z ollama.com i pobierz dwa modele: ollama pull bge-m3 (wyszukiwanie) oraz ollama pull gemma4:12b-it-qat (odpowiedzi, 7,2 GB).
  2. Zainstaluj Pythona 3.10 lub nowszego i dwie biblioteki: pip install pypdf numpy. Ja miałem Pythona 3.13, pypdf 6.16 i numpy 2.5.
  3. Pobierz skrypt czat_z_pdf.py (pełny kod niżej) i zapisz obok PDF-a.
  4. Uruchom: python czat_z_pdf.py dokument.pdf i zadawaj pytania. Enter bez tekstu kończy rozmowę.
python czat_z_pdf.py prawa-konsumenta.pdf
python czat_z_pdf.py prawa-konsumenta.pdf "Ile mam dni na zwrot towaru kupionego w internecie?"
$env:MODEL = "llama3.1:8b"; python czat_z_pdf.py prawa-konsumenta.pdf
$env:PELNY = "1"; python czat_z_pdf.py prawa-konsumenta.pdf

Pierwsze uruchomienie liczy wektory dla całego dokumentu. U mnie trwało to 55 s dla 120 fragmentów. Kolejne startują od razu, bo wektory leżą w pliku .bge-m3.npz obok PDF-a. Zmienna PELNY=1 pomija wyszukiwanie i wkleja cały dokument z kontekstem 65 536 tokenów.

Skrypt nie czyta skanów. Jeśli PDF to zdjęcia stron, najpierw przepuść go przez OCR. Dla dokumentów innych niż ustawy zmień funkcję kawalki: zamiast cięcia po artykułach tnij po nagłówkach albo akapitach.

Mój test: 10 pytań do ustawy o prawach konsumenta

Każde pytanie ma odpowiedź w konkretnym przepisie, więc wynik da się sprawdzić bez zgadywania. Każdy model odpowiadał dwa razy: bez dokumentu (z tego, co pamięta z treningu) i z RAG. Odpowiedź uznawałem za trafną, jeśli zawierała właściwą liczbę albo rozstrzygnięcie, a każdą sprawdziłem ręcznie z tekstem ustawy. Temperatura 0, kontekst 8192 tokeny, Ollama 0.35.1, RTX 4090, 3 października 2026.

PytanieOdpowiedźPrzepisZnaleziony
1. Termin odstąpienia od umowy zawartej przez internet14 dniart. 27 ust. 1tak
2. Termin przy nieumówionej wizycie sprzedawcy w domu30 dniart. 27 ust. 2tak
3. Kiedy wygasa prawo, gdy sklep o nim nie poinformowałpo 12 miesiącach od zwykłego terminuart. 29 ust. 1tak
4. Termin odpowiedzi na reklamację i skutek milczenia14 dni, milczenie = uznanieart. 7atak
5. Czas na odesłanie towaru po odstąpieniu14 dniart. 34 ust. 1tak
6. Czy sklep zwraca dopłatę za droższą dostawęnieart. 33nie
7. Domniemanie niezgodności towaru z umową2 lataart. 43ctak
8. Odstąpienie od ubezpieczenia zawartego na odległość30 dniart. 40 ust. 2tak
9. Próg kwoty umów poza lokalem50 złart. 3 ust. 1 pkt 10nie
10. Czy jednoosobowa firma ma prawa konsumentatak, gdy umowa nie ma charakteru zawodowegoart. 7aatak

Kolumna „Znaleziony” mówi, czy właściwy przepis był wśród 5 fragmentów, które wyszukiwanie dało modelowi.

Wyniki: bez dokumentu i z RAG

ModelBez dokumentuZ RAGCzas z RAG
Gemma 4 12B2/109/100,4-1,6 s
Llama 3.1 8B2/109/100,8-3,6 s
Bielik 11B v3 (Q8_0)7/108/102,1-6,2 s

Czas to pełna odpowiedź z wyszukiwaniem, z modelem już w pamięci karty. Pierwsze pytanie trwało od 151 do 521 s, bo modele wczytywały się z wolnego dysku USB.

  • RAG podnosi trafność z 2 do 9 na 10 w Gemmie 4 12B i Llamie 3.1 8B. Bez dokumentu obie myliły terminy (14 zamiast 30 dni przy wizycie w domu i przy ubezpieczeniu), a Gemma napisała, że reklamacja bez odpowiedzi jest „odrzucona”, choć ustawa mówi odwrotnie.
  • Bielik zna polskie prawo sam z siebie. Bez dokumentu odpowiedział dobrze na 7 pytań, w tym o próg 50 zł, którego żaden inny model nie znał. Z RAG doszedł do 8, ale w pytaniu o droższą dostawę sam sobie zaprzeczył: zaczął od „tak, sklep musi zwrócić całą opłatę”, a potem przytoczył wyjątek z ustawy.
  • Na pytanie 9 z RAG nie odpowiedział żaden model.l. Wyszukiwanie nie znalazło art. 3, bo próg 50 zł jest schowany w długiej liście wyłączeń. Modele uczciwie napisały „nie znalazłem tego w dokumencie” zamiast zgadywać.
  • Numery artykułów podawały wszystkie modele, choć Gemma raz dopisała do prawidłowego art. 34 nieistniejące tu „art. 55”. Numer przepisu trzeba sprawdzić, zanim się na nim oprze.

A gdyby wkleić całą ustawę?

3 października ta próba skończyła się błędem serwera w Ollamie 0.35.1, więc 17 października powtórzyłem ją w Ollamie 0.40.5. Gemma 4 12B dostała cały tekst ustawy (94 tys. znaków, 28 542-28 564 tokeny razem z pytaniem) i te same 10 pytań przy kontekście 65 536 tokenów.

SposóbTrafnePamięć kartyCzas odpowiedzi
RAG, 5 fragmentów (3.10)9/108,9 GB0,4-1,6 s
Cała ustawa w kontekście (17.10)10/109,7 GB1,2-2,3 s

Pamięć przy RAG zmierzyłem 17 października razem z modelem bge-m3. Pierwsze pytanie z całą ustawą trwało 30,7 s: 24,9 s zajęło wczytanie modelu z dysku USB, a 4,8 s przeczytanie dokumentu. Kolejne odpowiedzi przychodziły w 1,2-2,3 s, bo Ollama trzyma przetworzony początek rozmowy w pamięci i przy tym samym dokumencie liczy tylko nowe pytanie.

Wniosek: przy jednym dokumencie do kilkudziesięciu stron i modelu z długim kontekstem wklejenie całości jest prostsze i dokładniejsze. Gemma 4 zajmuje przy długim kontekście mało dodatkowej pamięci, dlatego zmieściła się w niecałych 10 GB. RAG wygrywa przy wielu plikach, przy dokumentach dłuższych niż kontekst modelu i na kartach, na których długi kontekst się nie mieści.

Ile pamięci karty potrzeba

Zmierzyłem 17 października różnicę wskazań nvidia-smi przed i w trakcie odpowiedzi, z bge-m3 i modelem czatu załadowanymi naraz, kontekst 8192 tokeny.

Model odpowiedziPamięć karty z bge-m3Karta
Llama 3.1 8B (Q4_K_M)7,0 GB8 GB
Gemma 4 12B (Q4_0 QAT)8,9 GB12 GB
Bielik 11B v3 (Q4_K_M)9,7 GB12 GB
Bielik 11B v3 (Q8_0)14,1 GB16 GB

Sam bge-m3 zajmował w Ollamie 0,66 GB. Ranking modeli po polsku z pomiarami pamięci na karty 8-24 GB jest w tekście najlepszy lokalny model AI po polsku.

Co poprawić, gdy odpowiedzi są złe

Nie przepłacaj za te subskrypcje

Prowadzę sklep z rocznymi dostępami do narzędzi AI - te same konta, o których piszę wyżej, tylko taniej niż w cenniku producenta.

Zobacz, co jest dostępne

  • Najpierw sprawdź wyszukiwanie, nie model. Jeśli właściwego fragmentu nie ma wśród znalezionych, żaden model nie odpowie dobrze. Wypisz wynik funkcji szukaj przy kłopotliwym pytaniu.
  • Weź model wyszukiwania, który zna polski. bge-m3 działa dobrze. Angielski all-MiniLM-L6-v2, domyślny w Open WebUI, gubi polskie odmiany.
  • Tnij po strukturze dokumentu. Przy cięciu po artykułach właściwy przepis był wśród znalezionych w 8 pytaniach na 10, przy cięciu co 1400 znaków w 6 (3 października).
  • Zwiększ TOPK przy pytaniach ogólnych. TOPK=8 doda więcej fragmentów kosztem dłuższego polecenia.

Gotowe programy zamiast skryptu

ProgramJak działa z dokumentamiDla kogo
Open WebUIbazy wiedzy z wielu plików, w przeglądarcekilka osób, wiele dokumentów
LM StudioPDF, DOCX i TXT w rozmowie: krótki wkleja w całości, długi przeszukujejedna osoba, bez konfiguracji
GPT4Allfunkcja LocalDocs przeszukuje wskazany folderprosty start na słabszym sprzęcie
Ten skryptkażdy krok widoczny i do zmianydokumenty prawne, własne cięcie

W Open WebUI zmień model wyszukiwania w ustawieniach administratora (Documents) na bge-m3 z Ollamy i przelicz dokumenty od nowa. Więcej o drugim prostym programie w tekście Jan i GPT4All.

Dla kogo to jest

  • Dla firm i kancelarii, które nie mogą wysyłać umów i pism do chmury.
  • Dla każdego z kartą 8-12 GB, kto chce przeszukiwać regulaminy, instrukcje czy ustawy po polsku.
  • Nie dla skanów i tabel. Skrypt czyta tylko warstwę tekstu PDF-a, a odpowiedzi do spraw ważnych zawsze trzeba sprawdzić w podanym przepisie.

Gotowy skrypt: czat_z_pdf.py

Około 100 linii, bez frameworków. Sprawdziłem go 17 października 2026 na czystym folderze (tylko PDF i skrypt) z Ollamą 0.40.5 na Windowsie 11, w trybie RAG i w trybie PELNY=1. Plik do pobrania: czat_z_pdf.py.

# -*- coding: utf-8 -*-
"""Lokalny czat z PDF-em po polsku (RAG) na Ollamie. Bez internetu, bez kluczy.
Wymaga: pip install pypdf numpy ; ollama pull bge-m3 ; ollama pull gemma4:12b-it-qat
Użycie:
  python czat_z_pdf.py dokument.pdf                    -> tryb rozmowy
  python czat_z_pdf.py dokument.pdf "Twoje pytanie"    -> jedna odpowiedź
Zmienne środowiskowe:
  MODEL  - model do odpowiedzi (domyślnie gemma4:12b-it-qat)
  EMB    - model wyszukiwania (domyślnie bge-m3)
  TOPK   - ile fragmentów dać modelowi (domyślnie 5)
  PELNY  - 1 = zamiast wyszukiwania wklej cały dokument (krótkie pliki, kontekst 65 536 tokenów)"""
import sys, os, re, json, pathlib, urllib.request
import numpy as np
from pypdf import PdfReader

OLLAMA = os.environ.get('OLLAMA_URL', 'http://127.0.0.1:11434')
MODEL = os.environ.get('MODEL', 'gemma4:12b-it-qat')
EMB = os.environ.get('EMB', 'bge-m3')
TOPK = int(os.environ.get('TOPK', '5'))
PELNY = os.environ.get('PELNY', '0') == '1'

def api(path, body):
    r = urllib.request.Request(OLLAMA + path, data=json.dumps(body).encode(), headers={'Content-Type': 'application/json'})
    return json.load(urllib.request.urlopen(r, timeout=900))

def tekst_z_pdf(plik):
    t = 'n'.join(p.extract_text() or '' for p in PdfReader(plik).pages)
    t = re.sub(r'(w)-n(w)', r'12', t)              # dzielenie wyrazów na końcu linii
    t = re.sub(r'[ t]+', ' ', t)
    t = re.sub(r'n(?!Art. |d+. |d+) )', ' ', t)   # sklejam linie, zostawiam początki artykułów i punktów
    return t

def kawalki(t, maks=1400, zakladka=200):
    # najpierw tnę po artykułach, długie artykuły dzielę dalej z zakładką
    czesci = re.split(r'n(?=Art. d+[a-z]*. )', t)
    out = []
    for c in czesci:
        c = c.strip()
        if not c: continue
        naglowek = (re.match(r'Art. d+[a-z]*.', c) or [''])[0]
        i = 0
        while i < len(c):
            kawal = c[i:i + maks]
            if i > 0 and naglowek: kawal = f'({naglowek} - ciąg dalszy) ' + kawal
            out.append(kawal)
            i += maks - zakladka
    return out

def indeks(plik):
    cache = pathlib.Path(plik).with_suffix(f'.{EMB}.npz')
    if cache.exists():
        d = np.load(cache, allow_pickle=True); return list(d['k']), d['v']
    k = kawalki(tekst_z_pdf(plik))
    v = []
    for i in range(0, len(k), 16):
        v += api('/api/embed', {'model': EMB, 'input': k[i:i + 16]})['embeddings']
    v = np.array(v, dtype=np.float32); v /= np.linalg.norm(v, axis=1, keepdims=True)
    np.savez(cache, k=np.array(k, dtype=object), v=v)
    return k, v

def szukaj(pyt, k, v, n=TOPK):
    q = np.array(api('/api/embed', {'model': EMB, 'input': [pyt]})['embeddings'][0], dtype=np.float32)
    q /= np.linalg.norm(q)
    idx = np.argsort(-(v @ q))[:n]
    return [k[i] for i in idx]

SYSTEM = ('Odpowiadasz po polsku wyłącznie na podstawie fragmentów dokumentu podanych niżej. '
          'Podaj numer artykułu, z którego bierzesz odpowiedź. Jeśli fragmenty nie zawierają odpowiedzi, '
          'napisz: "Nie znalazłem tego w dokumencie." Odpowiadaj krótko, w 1-3 zdaniach.')

def odpowiedz(pyt, k, v, model=MODEL, ctx=8192, caly=None):
    if caly is not None:
        fr, kontekst, ctx = [], f'DOKUMENT:n{caly}', 65536
    else:
        fr = szukaj(pyt, k, v)
        kontekst = 'nn'.join(f'[Fragment {i + 1}]n{f}' for i, f in enumerate(fr))
    msg = [{'role': 'system', 'content': SYSTEM}, {'role': 'user', 'content': f'{kontekst}nnPytanie: {pyt}'}]
    body = {'model': model, 'messages': msg, 'stream': False, 'options': {'num_ctx': ctx, 'temperature': 0}}
    try: r = api('/api/chat', {**body, 'think': False})
    except Exception: r = api('/api/chat', body)
    return r['message']['content'], fr

if __name__ == '__main__':
    sys.stdout.reconfigure(encoding='utf-8')
    if len(sys.argv) < 2: print(__doc__); sys.exit(1)
    if PELNY:
        caly, k, v = tekst_z_pdf(sys.argv[1]), None, None
        print(f'Dokument: {len(caly)} znaków w całości, model {MODEL}')
    else:
        caly = None; k, v = indeks(sys.argv[1])
        print(f'Dokument: {len(k)} fragmentów, model {MODEL}, wyszukiwanie {EMB}')
    if len(sys.argv) > 2:
        print(odpowiedz(sys.argv[2], k, v, caly=caly)[0]); sys.exit()
    while True:
        try: pyt = input('nPytanie (Enter kończy): ').strip()
        except EOFError: break
        if not pyt: break
        print(odpowiedz(pyt, k, v, caly=caly)[0])

Najczęstsze pytania

Czy da się zrobić czat z PDF po polsku bez internetu?

Tak. Po pobraniu modeli Ollama, bge-m3 i skrypt działają offline. Dokument nie jest nigdzie wysyłany.

Jakie AI do PDF wybrać lokalnie?

W moim teście najlepiej wypadła Gemma 4 12B: 9 na 10 z RAG i 10 na 10 z całym dokumentem w kontekście. Llama 3.1 8B też miała 9 na 10 z RAG i mieści się na karcie 8 GB. Bielik 11B najlepiej znał prawo bez dokumentu, ale z dokumentem raz sam sobie zaprzeczył.

Ile pamięci karty potrzeba do RAG lokalnie?

Według mojego pomiaru z 17 października: 7,0 GB z Llamą 3.1 8B, 8,9 GB z Gemmą 4 12B i 9,7 GB z Bielikiem 11B Q4_K_M, zawsze razem z bge-m3. Cała 24-stronicowa ustawa w kontekście Gemmy 4 12B zajęła 9,7 GB.

Czy lokalny RAG jest dokładny?

Na tyle, na ile dobre jest wyszukiwanie. Modele z RAG odpowiedziały trafnie na 8-9 pytań z 10, a jedyne pytanie, przy którym wyszukiwanie nie znalazło przepisu, wszystkie oznaczyły jako „nie znalazłem”.

Czy mogę przeszukiwać kilka dokumentów naraz?

Tak. Połącz fragmenty z kilku plików w jedną listę i zapisuj przy każdym nazwę pliku, żeby model mógł ją podać. Przy setkach dokumentów lepsza jest baza wektorowa, np. wbudowana w Open WebUI.

Google · Twoje źródłaPromptowy wyżej w Twoim Google - jednym kliknięciemDodaj do preferowanych źródeł →

Źródła i data sprawdzenia

Sprawdzone 17 października 2026.

// Newsletter

Cały tydzień w AI, w jednym mailu

Wybrane premiery, narzędzia i analizy. Raz w tygodniu, prosto do skrzynki.

Zapisz się za darmo →
Za darmo. Wypisujesz się jednym kliknięciem.
// czytaj też

Podobne tematy na Promptowym

Piotr Olszewski

Piotr Olszewski

AUTOR I WYDAWCA

Piotr Olszewski - twórca i autor Promptowego, polskiego serwisu o sztucznej inteligencji. Codziennie śledzi premiery modeli, narzędzia i regulacje AI, i tłumaczy je prostym, konkretnym językiem.

// mapa strony

🛒 Sklep Kinetyka Google AI Gemini Pro 170 zł CapCut Pro 460 zł/rok Lovable Pro 450 zł/rok Zobacz wszystko →
promptowy w liczbach 0tekstów w archiwum0newsów z ostatnich 7 dni0modeli wideo w obserwatorium0zagadek w grach
× ‹ powiększenie ›