Automatyczna ekstrakcja danych z dokumentów PDF i obrazów w n8n: jak budować przepływy OCR i strukturyzacji treści z AI
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:
- Pobranie pliku — webhook, monitorowanie folderu (Google Drive, S3), lub węzeł polling
- Klasyfikacja i routing — rozpoznanie typu dokumentu i wybranie metody ekstrakcji
- Ekstrakcja tekstu — bezpośrednia lub przez OCR
- Strukturyzacja AI — prompt LLM z wymuszeniem formatu JSON
- 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:
- Próg pewności OCR — jeśli silnik zwraca confidence score poniżej ustalonego progu, dokument trafia do kolejki ręcznej
- Błędy walidacji — dokumenty z niepustą listą
_validation_errors - 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 mniePowiązane artykuły
Strona one-page czy wielopodstronicowa — jaką strukturę wybrać dla firmy usługowej
Strona one-page czy z podstronami? Sprawdź, jak struktura strony firmy usługowej wpływa na widoczność w Google i zapytania, oraz kiedy jedna strona wystarczy.
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.
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.
Potrzebujesz strony internetowej?
Skontaktuj się ze mną, aby omówić Twój projekt. Pierwsza konsultacja jest bezpłatna.
Zamów bezpłatną wycenę