Przejdź do głównej treści
Wróć do bloga
Inne 8 min czytania

Automatyczna ekstrakcja danych z dokumentów PDF i obrazów w n8n: jak budować przepływy OCR i strukturyzacji treści z AI

10 lipca 2026 Michał Kasprzyk Aktualizacja: 13 lipca 2026

Ekstrakcja danych z PDF i obrazów: kiedy OCR, a kiedy wystarczy tekst

Dokumenty PDF dzielą się na dwie kategorie, które determinują wybór metody ekstrakcji. PDF z osadzonym tekstem (generowane cyfrowo) pozwala na bezpośrednie odczytanie zawartości bez OCR. PDF skanowane oraz zdjęcia dokumentów wymagają rozpoznawania optycznego znaków. Projektując przepływ w n8n, najpierw decyduję o metodzie ekstrakcji na podstawie typu pliku.

Decyzja wygląda tak:

  • PDF cyfrowy → węzeł odczytujący tekst bezpośrednio (np. biblioteka pdf-parse wywołana przez węzeł Code lub dedykowany węzeł Read PDF)
  • PDF skanowany / obraz (JPG, PNG, TIFF) → silnik OCR → tekst → AI do strukturyzacji
  • PDF mieszany (część cyfrowa, część skanowana) → ekstrakcja tekstowa + OCR dla stron bez tekstu → scalenie wyników

W n8n rozróżnienie realizuję węzłem Switch: sprawdzam, czy po bezpośredniej ekstrakcji tekstowej długość wyniku przekracza sensowny próg. Jeśli tekst jest pusty lub fragmentaryczny, kieruję plik do gałęzi OCR.

Wybór silnika OCR pod kątem dokumentów i języka polskiego

Silniki OCR różnią się trafnością rozpoznawania, obsługą języków i kosztem. W przepływach n8n korzystam z trzech głównych opcji:

Tesseract (lokalny) — darmowy, obsługuje język polski po doinstalowaniu pakietu pol.traineddata. Nadaje się do dokumentów o dobrej jakości skanu z wyraźną czcionką. W n8n wywołuję go przez węzeł Execute Command lub własny węzeł n8n-community. Wymaga preprocessing obrazu (progowanie, korekta pochylenia) dla akceptowalnej trafności.

Cloud OCR (Google Vision, Azure Document Intelligence, AWS Textract) — wyższa trafność rozpoznawania, wbudowana obsługa układu dokumentu (layout analysis), tabele i formularze. W n8n łączę się przez węzły HTTP Request z odpowiednim credential. Koszt zależy od liczby stron i wybranego modelu.

Modele multimodalne LLM (GPT-4o, Claude, Gemini) — rozpoznają tekst bezpośrednio z obrazu bez osobnego kroku OCR. Przydatne dla dokumentów o złożonym układzie, ale droższe przy masowym przetwarzaniu i podatne na halucynacje przy gęstych tabelach.

Reguła wyboru w moich przepływach:

  • Proste dokumenty, duże wolumeny → Tesseract + preprocessing
  • Faktury, formularze z tabelami → Azure Document Intelligence lub AWS Textract
  • Nieliczne dokumenty o złożonym układzie → model multimodalny z rygorystyczną walidacją

Architektura przepływu ekstrakcji w n8n

Kompletny przepływ składa się z pięciu etapów:

  1. Pobranie pliku — webhook, monitorowanie folderu (Google Drive, S3), lub węzeł polling
  2. Klasyfikacja i routing — rozpoznanie typu dokumentu i wybranie metody ekstrakcji
  3. Ekstrakcja tekstu — bezpośrednia lub przez OCR
  4. Strukturyzacja AI — prompt LLM z wymuszeniem formatu JSON
  5. Walidacja i zapis — sprawdzenie poprawności struktury, zapis do bazy lub CRM

Każdy etap projektuję jako osobny sub-workflow, co ułatwia testowanie i ponowne wykorzystanie. Główny workflow wywołuje sub-workflow przez węzeł Execute Workflow.

Preprocessing obrazów przed OCR

Jakość OCR zależy od jakości obrazu wejściowego. W n8n realizuję preprocessing w węźle Code z użyciem biblioteki Sharp:

  • Konwersja do skali szarości — redukcja szumu kolorowego
  • Zwiększenie kontrastu — progowanie Otsu lub ręczny próg
  • Korekta pochylenia (deskew) — detekcja kąta nachylenia i obrót
  • Skalowanie DPI — OCR działa najlepiej przy 300 DPI; jeśli obraz ma mniej, skaluję w górę

Przykład logiki w węźle Code:

const sharp = require('sharp');
const inputBuffer = items[0].binary.data;

const processed = await sharp(inputBuffer)
  .grayscale()
  .normalize()
  .threshold(128)
  .toBuffer();

items[0].binary.data = processed;
return items;

Bez tego kroku Tesseract na skanach o niskiej jakości zwraca tekst z dużą liczbą błędów, co obciąża kolejny etap korekty przez AI.

Strukturyzacja tekstu przez LLM: wymuszanie formatu JSON

Surowy tekst z OCR to ciąg znaków bez struktury. Aby przekształcić go w dane użyteczne dla systemu (CRM, ERP, baza), stosuję prompt LLM z wymuszeniem odpowiedzi w formacie JSON.

Przykład promptu do ekstrakcji danych z faktury:

Jesteś systemem ekstrakcji danych z dokumentów.
Na podstawie poniższego tekstu OCR wyciągnij następujące pola i zwróć WYŁĄCZNIE poprawny JSON:

{
  "numer_faktury": "",
  "data_wystawienia": "YYYY-MM-DD",
  "nip_sprzedawcy": "",
  "nazwa_sprzedawcy": "",
  "kwota_netto": 0,
  "kwota_vat": 0,
  "kwota_brutto": 0,
  "waluta": "PLN",
  "pozycje": [
    {"nazwa": "", "ilosc": 0, "cena_jednostkowa": 0, "wartosc": 0}
  ]
}

Tekst OCR:
{{ $json.ocr_text }}

Zasady:
- Jeśli wartość nie występuje w tekście, wstaw null
- Kwoty zapisz jako liczby, nie ciągi znaków
- Daty w formacie ISO
- Nie dodawaj komentarzy, tylko JSON

W n8n konfiguruję węzeł LLM z parametrem response_format: json_object (dla modeli OpenAI) lub odpowiednikiem. Po odpowiedzi parsuję JSON w węźle Code i przechwytuję ewentualne błędy parsowania.

Obsługa błędów parsowania JSON z odpowiedzi LLM

Modele LLM czasami zwracają JSON z błędami: brakujące przecinki, tekst przed lub po bloku JSON, ucięte odpowiedzi. W n8n buduję mechanizm naprawy:

let responseText = $json.response;

// Usuń markdown code fences jeśli model je dodał
responseText = responseText.replace(/```json\n?/g, '').replace(/```/g, '');

// Próba parsowania
try {
  const parsed = JSON.parse(responseText.trim());
  return { json: parsed };
} catch (e) {
  // Próba ekstrakcji JSON z otaczającego tekstu
  const jsonMatch = responseText.match(/\{[\s\S]*\}/);
  if (jsonMatch) {
    try {
      return { json: JSON.parse(jsonMatch[0]) };
    } catch (e2) {
      // Zwróć błąd do gałęzi retry
      throw new Error('JSON parse failed after extraction attempt');
    }
  }
  throw new Error('No JSON found in LLM response');
}

Jeśli parsowanie nie powiedzie się, przepływ trafia do gałęzi retry z węzłem Error Trigger. Konfiguruję maksymalnie 2 ponowne próby z krótkim opóźnieniem. Po wyczerpaniu prób dokument trafia do kolejki do ręcznej weryfikacji.

Walidacja wyekstrahowanych danych

Strukturyzacja JSON to połowa sukcesu. Druga połowa to weryfikacja, czy dane są spójne i sensowne. Buduję reguły walidacji w węźle Code:

Reguły formatu:

  • NIP: dokładnie 10 cyfr (lub 13 z prefiksem kraju)
  • Data: format ISO, data nie z przyszłości
  • Kwoty: typ number, kwota_brutto ≈ kwota_netto + kwota_vat (z tolerancją zaokrągleń)

Reguły spójności biznesowej:

  • Suma wartości pozycji ≈ kwota_netto
  • Waluta z listy dozwolonych (PLN, EUR, USD, GBP)
  • Numer faktury pasuje do wzorca (np. FV/\d{2,4}/\d+)

Przykład walidacji:

const data = $json;
const errors = [];

// Walidacja NIP
if (data.nip_sprzedawcy && !/^\d{10}$/.test(data.nip_sprzedawcy.replace(/[^0-9]/g, ''))) {
  errors.push('NIP sprzedawcy nieprawidłowy');
}

// Walidacja sumy pozycji
if (data.pozycje && data.pozycje.length > 0) {
  const sumaPozycji = data.pozycje.reduce((sum, p) => sum + (p.wartosc || 0), 0);
  if (Math.abs(sumaPozycji - data.kwota_netto) > 0.01) {
    errors.push(`Suma pozycji (${sumaPozycji}) ≠ kwota netto (${data.kwota_netto})`);
  }
}

// Walidacja kwot
const expectedBrutto = data.kwota_netto + data.kwota_vat;
if (Math.abs(expectedBrutto - data.kwota_brutto) > 0.01) {
  errors.push('Kwota brutto niezgodna z netto + VAT');
}

if (errors.length > 0) {
  // Oznacz dokument do weryfikacji ręcznej
  return { json: { ...data, _validation_errors: errors, _requires_review: true } };
}

return { json: { ...data, _validation_errors: [], _requires_review: false } };

Dokumenty z błędami walidacji kieruję do kolejki weryfikacji (np. Google Sheet lub Trello) z dopisaną listą problemów. Dokumenty poprawne trafiają bezpośrednio do systemu docelowego.

Obsługa dokumentów wielostronicowych i tabel

PDF wielostronicowe wymagają podziału na strony przed OCR. W n8n realizuję to w węźle Code z biblioteką pdf-lib:

const pdfLib = require('pdf-lib');
const pdfBytes = items[0].binary.data;
const pdfDoc = await pdfLib.PDFDocument.load(pdfBytes);
const pageCount = pdfDoc.getPageCount();

const pages = [];
for (let i = 0; i < pageCount; i++) {
  const newPdf = await pdfLib.PDFDocument.create();
  const [page] = await newPdf.copyPages(pdfDoc, [i]);
  newPdf.addPage(page);
  const pageBytes = await newPdf.save();
  pages.push({ pageIndex: i, data: Buffer.from(pageBytes) });
}

return pages.map((p, i) => ({
  json: { page_number: i + 1, total_pages: pageCount },
  binary: { data: p.data }
}));

Każdą stronę przetwarzam osobno, co pozwala na:

  • Równoległe wywołania OCR (węzeł Split In Batches z rozmiarem partii)
  • Przypisanie numeru strony do wyniku
  • Obsługę stron w różnych orientacjach (pionowa/pozioma)

Tabele z faktur i formularzy najlepiej ekstrahować przez Azure Document Intelligence lub AWS Textract, które zwracają strukturę komórek. Jeśli korzystam z Tesseract, tabele wymagają dodatkowego promptu AI z instrukcją rekonstrukcji układu kolumnowego.

Human-in-the-loop dla dokumentów o niskiej pewności

Nie każdy dokument da się przetworzyć automatycznie z akceptowalną pewnością. Buduję mechanizm weryfikacji ludzkiej dla przypadków brzegowych:

  1. Próg pewności OCR — jeśli silnik zwraca confidence score poniżej ustalonego progu, dokument trafia do kolejki ręcznej
  2. Błędy walidacji — dokumenty z niepustą listą _validation_errors
  3. Nowe typy dokumentów — pierwszy dokument danego wzorca kieruję do weryfikacji przed automatyzacją

W n8n realizuję to węzłem IF sprawdzającym flagę _requires_review. Dokumenty do weryfikacji wysyłam na Slack lub Microsoft Teams z linkiem do oryginalnego pliku i wyekstrahowanymi danymi. Po akceptacji przez operatora przepływ kontynuuje zapis do systemu docelowego.

Checklist jakości przepływu ekstrakcji

Przed wdrożeniem przepływu na produkcję weryfikuję:

  • Routing PDF cyfrowych vs skanowanych działa poprawnie na próbce minimum 50 dokumentów
  • Preprocessing obrazów poprawia trafność OCR mierzalnie (porównanie przed/po na tej samej próbce)
  • Prompt LLM zwraca poprawny JSON dla co najmniej 90% testowanych dokumentów danego typu
  • Mechanizm naprawy JSON przechwytuje i naprawia typowe błędy formatowania
  • Reguły walidacji biznesowej wyłapują niespójności w kwotach, datach i identyfikatorach
  • Retry logic obsługuje timeouty API i limity zapytań bez utraty dokumentów
  • Dokumenty o niskiej pewności trafiają do kolejki weryfikacji, nie są gubione
  • Sub-workflow są wersjonowane i testowane niezależnie
  • Logi zawierają źródło dokumentu, wynik OCR, odpowiedź LLM i wynik walidacji dla audytu

Koszty i optymalizacja

Ekstrakcja z użyciem OCR i LLM generuje koszty na dwóch etapach: silnik OCR i wywołanie modelu. Strategie optymalizacji:

  • Buforowanie wyników OCR — ten sam dokument nie powinien być poddawany OCR ponownie. Przechowuję wynik w bazie z hashem pliku jako kluczem
  • Routing modeli — proste dokumenty (standardowe faktury) kieruję do tańszego modelu, złożone do modelu o wyższej trafności
  • Batch processing — grupowanie zapytań OCR i LLM zmniejsza narzut na połączenia HTTP
  • Lokalny OCR dla dużych wolumenów — Tesseract nie generuje kosztów API, ale wymaga serwera z odpowiednią mocą obliczeniową

Koszty monitoruję przez węzły Code zliczające tokeny i strony, zapisując wyniki do bazy w celu comiesięcznej analizy.

Integracja wyników z systemami docelowymi

Po walidacji wyekstrahowane dane trafiają do systemu docelowego. W n8n korzystam z gotowych węzłów integracyjnych lub HTTP Request:

  • CRM — tworzenie lub aktualizacja rekordu firmy na podstawie NIP
  • System księgowy — import faktur z mapowaniem pól na schemat docelowego API
  • Baza danych — zapis do PostgreSQL lub Google BigQuery z deduplikacją po numerze dokumentu
  • Google Sheets / Excel — dla procesów wymagających ręcznego dosłuchu

Deduplikację realizuję po numerze dokumentu i NIP sprzedawcy przed zapisem, aby uniknąć podwójnego importu tej samej faktury.


Ekstrakcja danych z dokumentów PDF i obrazów to proces, który projektuję pod konkretne typy dokumentów i wymogi biznesowe. Jeśli potrzebujesz przepływu automatycznej ekstrakcji dopasowanego do Twoich dokumentów i systemów, mogę zaprojektować i wdrożyć rozwiązanie w n8n — od wyboru silnika OCR po integrację z CRM lub systemem księgowym.

👨‍💻

Michał Kasprzyk

Tworzę nowoczesne strony internetowe dla firm z całej Polski. Specjalizuję się w szybkich, bezpiecznych i zoptymalizowanych pod SEO witrynach.

Prowadzę też osobną działalność jako agent ubezpieczeniowy (mkasprzyk.pl) — bez związku z usługami Qualix.

Więcej o mnie

Powiązane artykuły

Inne

Automatyzacja procesów w małej firmie usługowej z n8n — co zautomatyzować i jak zacząć

Automatyzacja procesów w małej firmie usługowej z n8n — sprawdź, jakie zadania zautomatyzować, jak zaplanować workflow i jak uniknąć częstych błędów.

Inne

Oprogramowanie dedykowane dla firm: kiedy gotowe systemy przestają wystarczać?

Dowiedz się, kiedy oprogramowanie dedykowane dla firm jest lepsze od gotowych systemów SaaS i jak uniknąć vendor lock-in w swoim biznesie.

Inne

Modernizacja czy nowa strona: kiedy wymiana jest rozsądniejsza niż łatanie?

Dowiedz się, kiedy warto zmodernizować stronę, a kiedy bezpieczniej zbudować nową witrynę od podstaw dla małej firmy usługowej.

Potrzebujesz strony internetowej?

Skontaktuj się ze mną, aby omówić Twój projekt. Pierwsza konsultacja jest bezpłatna.

Zamów bezpłatną wycenę
Napisz na WhatsApp