Paperless-ngx jsem testoval na vytěžování faktur a smluv. Funguje, hledá, třídí, ale jednu věc po mně pořád chtěl: ručně doplnit metadata. Název dokumentu, korespondenta, typ, datum vystavení, variabilní symbol, částku. U pěti faktur za měsíc je to pohoda, u padesáti už docela slušná časová ztráta.
Již z první recenze víte, že já vím, že existuje open-source nadstavba, která tuhle práci nechá udělat jazykovému modelu. Takže všemi dnes vyhypované AI. V principu jde o totéž, co dělají komerční nástroje na obsahovou analýzu typu ABBYY: přečti dokument, poznej, co je která hodnota, a vyplň ji do správného pole. Jen s tím rozdílem, že tady místo licence platíte za tokeny. Zní to jako přesně ta věc, která na papíře vypadá jednoduše: stáhnout, spustit, propojit. Realita byla klasicky mnohem dobrodružnější. Přestože na konci byl hepáč, tak po dlouhé době jsem opět okusil, co je to open-sourcový hell.
A ono to je vlastně dobře, člověk je zhýčkaný velkými řešeními, že vlastně vše funguje, a prkotiny považuje za bezmála blokátory. Je dobré se vrátit na zem, trocha pokory nezaškodí. Ale konec okecávek, jdeme na to:
Co je Paperless-GPT
Paperless-GPT je open-source nadstavba nad Paperless-ngx, která používá jazykové modely (OpenAI, Ollama a další OpenAI-kompatibilní backendy) ke dvěma věcem: kvalitnějšímu OCR přes vision modely a automatické extrakci metadat, jako jsou název dokumentu, korespondent, typ dokumentu, tagy i vlastní pole (custom fields). Testoval jsem to na virtuálním Ubuntu s běžícím Paperless-ngx v Dockeru, s cílem automaticky vytěžit z faktur nejen základní údaje, ale i vlastní pole — variabilní symbol, datum splatnosti a částku k úhradě.
Instalace
Repozitář na GitHubu nabízí i samostatný docker-compose.yml, ale pozor — ve výchozím stavu odkazuje jen na lokální Dockerfile, ne na hotový obraz. Pokud stáhnete jen tenhle jeden soubor bez zbytku repozitáře, build selže. Správný postup je naklonovat celý repozitář:
git clone https://github.com/icereed/paperless-gpt.git
cd paperless-gpt
Dál je potřeba vytvořit soubor .env, kam vložíte svůj klíč z GPT a z Paperless-ngx:
PAPERLESS_BASE_URL=http://localhost:8000
PAPERLESS_API_TOKEN=
LLM_PROVIDER=openai
LLM_MODEL=gpt-4o-mini
OPENAI_API_KEY=
LLM_LANGUAGE=Czech
OCR_LIMIT_PAGES=2
API token pro Paperless-ngx najdete v Nastavení → Můj profil. Sestavení image ze zdrojáků chvíli trvá (repozitář má přes 40 zdrojových souborů), ale poté kontejner naběhne bez problémů:
docker compose up -d --build
Ono to nejede…
První chyba přišla hned po startu… V logu se objevilo:
level=error msg="HTTP request failed" error="Get \"http://localhost:8000/api/tags/\":
dial tcp [::1]:8000: connect: connection refused"
Příčina je zmatení Dockeru: localhost uvnitř kontejneru neznamená totéž co localhost na hostitelském stroji. Kontejner běží ve vlastním izolovaném síťovém prostoru — localhost:8000 tam znamená port 8000 na samotném kontejneru, ne na VM, kde běží Paperless-ngx. Řešením je speciální alias host.docker.internal, který Docker poskytuje pro přístup z kontejneru zpátky na hostitele. V
.env:
PAPERLESS_BASE_URL=http://host.docker.internal:8000
Na Linuxu ale tenhle alias není dostupný automaticky — je potřeba ho výslovně povolit v docker-compose.yml:
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
env_file:
- .env
extra_hosts:
- "host.docker.internal:host-gateway"
Po přidání extra_hosts a restartu (docker compose down && docker compose up -d) se chyba connection refused změnila na jinou — no such host — což ukázalo, že se úprava docker-compose.yml poprvé vůbec nepropsala (chybějící blok se nepřidal správně). Po opravě YAML struktury a druhém pokusu se spojení konečně podařilo — log ukázal Successfully refreshed custom fields cache with 9 fields, což potvrdilo funkční komunikaci s Paperless-ngx API. Poučení: pokud host.docker.internal i s extra_hosts nefunguje, spolehlivější záložní varianta je použít přímo IP adresu VM:
PAPERLESS_BASE_URL=http://192.168.x.x:8000
Chyba ve výchozím promptu pro datum
Po zprovoznění spojení jsem konečně rozjel Paperless-GPT, trochu jsem se rozkoukal a zkusil, co to dělá. Následně jsem začal opravovat zjevné chyby, ještě před nějakým hlubším testováním. První, čeho jsem si všimnul, bylo špatné datum, že Paperless-GPT vytěžuje špatně datum. Na testovací faktuře bylo datum vystavení 10.07.2026. AI ale opakovaně navrhovala 2026-07-01. Příčina se skrývala přímo ve výchozím promptu pro určení data (prompts/created_date_prompt.tmpl):
I will provide you with the content of a document. Your task is to find
the date when the document was created.
Respond only with the date in YYYY-MM-DD format, without any additional
information.
If no day was found, use the first day of the month. If no month was
found, use January. If no date was found at all, answer with today’s
date.
Řádek „If no day was found, use the first day of the month“ není chyba modelu — je to úmyslné fallback pravidlo zabudované přímo v promptu autorem nástroje. Jakmile si model nebyl jistý přesným dnem (pravděpodobně kvůli způsobu, jakým OCR vrátilo text), spadl do téhle větve a vrátil 1. den v měsíci — bez ohledu na to, že skutečné datum bylo v textu čitelné. Oprava spočívala v odstranění celého fallback mechanismu a nahrazení instrukcí, které model nutí buď zkopírovat datum přesně, nebo raději nevrátit nic:
I will provide you with the content of a document. Your task is to find
the date when the document was created (issue date, signing date, or
the date in the document header).
Respond only with the date in YYYY-MM-DD format, without any additional
information.
Copy the day, month, and year EXACTLY as they appear in the document —
do not guess, round, or substitute any missing part.
If the exact date cannot be confidently found in the document, respond
with an empty string instead of guessing.

Důležitý technický detail: úprava default_prompts/ na hostiteli nemá žádný efekt na běžící kontejner — tenhle adresář se zapéká do image při docker build a mění se jen při přebuildování. Aplikace při startu navíc kopíruje šablony do runtime adresáře prompts/, který ve výchozím docker-compose.yml vůbec není mountovaný jako volume — takže i úpravy přes web UI by se ztratily při každém down/up cyklu.
Řešení je přidat mount pro prompts/ adresář:
volumes: - ./prompts:/app/prompts
Po přidání mountu se při startu vytvoří lokální prompts/ se zkopírovanými výchozími šablonami, a teprve úpravy TOHOTO adresáře (ne default_prompts/) se skutečně použijí za běhu a přežijí restart. Soubory navíc vlastní uživatel s ID nastaveným přes PUID/PGID (v mém případě 10001), takže editace na hostiteli vyžaduje sudo.
Špatná částka
Po opravě data přišla třetí chyba, tentokrát to rovnou spadlo u zápisu (chyba 500) s tímto tracebackem z Paperless-ngx:
File "/usr/local/lib/python3.12/site-packages/django/db/models/fields/__init__.py",
line 2034, in get_prep_value
raise e.__class__(
ValueError: Field 'value_float' expected a number but got 'CZK3530.00'.
Příčina: custom field „Částka k úhradě“ je v Paperless-ngx definovaný jako číselný typ (float), který čeká čisté číslo. Výchozí prompt pro custom fields (prompts/custom_field_prompt.tmpl) ale instruoval model, aby u polí typu měna vracel hodnotu s třípísmenným kódem měny na začátku, v mém případě to bylo CZK3530.00. Databáze takový řetězec do float sloupce odmítla zapsat.
Relevantní část výchozího promptu:
3. For fields of type `monetary`, the value must be a number with two
decimal places and a period as the decimal separator. You must also
identify the currency from the document and place its three-letter
code (e.g., EUR, USD) at the beginning of the value. For example, if
the document shows ‚1.664,58 €‘, the correct format would be
`EUR1664.58`.
Oprava — instrukce pro číselné typy pole musí vracet čisté číslo bez prefixu měny:
3. For fields of type `float` or `integer` representing monetary
amounts, the value must be a plain number with a period as the
decimal separator, WITHOUT any currency code or symbol (e.g.,
`3530.00`, not `CZK3530.00`). For fields explicitly of type
`monetary`, use the format described by that field’s type instead.
Ponaučení: výchozí prompty počítají s tím, že custom field má v Paperless-ngx typ monetary (kde by prefix měny dával smysl a systém by si ho sám naparsoval). Pokud pole založíte jako obyčejné číslo, prompt a datový typ si přestanou rozumět a selže to až při zápisu, ne při generování návrhu — takže chyba se neprojeví v review obrazovce, ale až po kliknutí na Apply.
Výsledek po opravě obou promptů
Po úpravě created_date_prompt.tmpl a custom_field_prompt.tmpl proběhl zápis bez chyby. Modification History v Paperless-ngx potvrdila korektní zápis všech polí:
created_date: 2026-07-10 02:00 (správně, beze změny)
custom_fields: [{Variabilní symbol: 2026070315}
{Datum splatnosti: 2026-07-24}
{Částka k úhradě: 3530}]
title: Faktura č. 2026070315 – Tomáš Kučera
document_type: Faktura přijatá
tags: paperless-gpt tag odebrán po zpracování
Všechny tři custom fields (variabilní symbol, datum splatnosti, částka) i standardní metadata odpovídala přesně obsahu faktury. Po typické open-sourcové instalaci a opravě chyb viditelných na první pohled jsem se mohl pustit do testování.
Co Paperless-GPT umí kromě vytěžování metadat
Vytěžování faktur bylo mým hlavním testovacím scénářem, ale nástroj má víc funkcí, které stojí za zmínku pro každého, kdo zvažuje vyzkoušet toto rozšíření pro Paperless-ngx.
- Ad-hoc Document Analysis — jednorázová analýza vybrané sady dokumentů s vlastním promptem, bez nutnosti cokoliv tagovat. Hodí se na rychlé shrnutí obsahu více dokumentů najednou, nebo na dotaz typu „najdi mi ve vybraných smlouvách výpovědní lhůtu“. Funguje i mimo kontext faktur, defaultní šablona je sice zaměřená na faktury, ale prompt lze upravit pro jakýkoliv typ dokumentu.
- Více poskytovatelů OCR — kromě vision modelů (OpenAI, Ollama) lze napojit i Azure Document Intelligence, Google Document AI nebo self-hosted Docling server. Pro někoho, kdo už má enterprise OCR licenci, to znamená možnost použít existující infrastrukturu místo placení za nové API.
- Manual Review vs. Auto Processing — dva režimy práce. Manual Review vyžaduje schválení každého návrhu (bezpečnější, pomalejší). Auto Processing zapisuje návrhy rovnou, takže se dá soustředit jen na výjimky, které nástroj sám označí jako nejisté.
- Automatické přiřazení typu dokumentu — nedávno přidaná funkce, která nechá AI kategorizovat dokumenty (faktura, smlouva, účtenka…) bez ručního zásahu, se zapínacím přepínačem v nastavení.
- Historie a log aktivity — přehled zpracovaných dokumentů a jejich výsledků, užitečné pro zpětnou kontrolu, které dokumenty prošly automaticky a které potřebovaly zásah.
- Konfigurační Playground — možnost vyzkoušet nastavení OCR a promptů na jednom konkrétním dokumentu předtím, než se pustí na celou frontu, a read-only přehled aktuální živé konfigurace celého nástroje.
- Podpora více LLM providerů — OpenAI, Ollama, a nově i Anthropic Claude jako alternativní poskytovatel jazykového modelu, což rozšiřuje možnosti výběru mezi cenou, rychlostí a kvalitou podle konkrétního nasazení.
Uživatelský dojem
Princip: dvě oddělené obrazovky
- Paperless-GPT funguje na principu dvou navazujících obrazovek, podobně jako třeba i komerční vytěžovací programy.
V jedné obrazovce (té vytěžovací) vidíte jen to, co je v Paperless-ngx otagované sledovaným štítkem (ve výchozím nastavení paperless-gpt). Dokud dokument tenhle tag nemá, Paperless-GPT o něm vůbec neví — i kdyby ležel v Paperless-ngx už dávno. - V druhé vidíte výsledek vytěžení, to se pohybujete v samotném Paperless-ngx.
Možná se ptáte: Sakra, a jak to tam dostanu? Je to vlastně hrozně jednoduché. Než cokoliv uvidíte na Review obrazovce, musíte vždy projít přes štítkování v Paperless-ngx. Bez správně nastaveného tagu se fronta na Home obrazovce nikdy nenaplní, ani kdyby AI a OpenAI klíč fungovaly bezchybně.
Štítkování — jak dokument vůbec dostat do fronty
Paperless-GPT při prvním startu sám v Paperless-ngx vytvoří sadu řídicích štítků, které interně používá:
- paperless-gpt — ruční nebo automatické zařazení dokumentu do fronty ke zpracování.
- paperless-gpt-auto — obdoba předchozího, ale pro plně automatický režim (Auto Processing) bez manuálního review.
- paperless-gpt-failed — tag, který se přidá, pokud zpracování dokumentu selže, aby šlo neúspěšné případy snadno dohledat.
- paperless-gpt-auto-complete — potvrzení, že dokument prošel automatickým zpracováním úspěšně.
V praxi jsem měl už dřív nastavené, že se dokumentům podle obsahu automaticky přiřadí štítek „Faktura“ (přes matching algoritmus u tagu v Paperless-ngx). Stejným způsobem stačí u tagu paperless-gpt nastavit Algoritmus shody a zaškrtnout Automaticky přiřadit, a docílíte toho, že se nově příchozí faktury otagují oběma štítky najednou a rovnou spadnou do fronty, bez jediného kliknutí navíc.
Praktický postup
- V Paperless-ngx otevřete dokument (nebo více dokumentů) a přidejte mu tag paperless-gpt — buď ručně, nebo přes automatické přiřazení podle bodu výše.
- Přepněte se do Paperless-GPT, záložka Home. Dokument by se měl objevit do pár sekund (stránka kontroluje frontu automaticky), případně pomůže tlačítko Check now.
- Nad seznamem dokumentů zaškrtněte, která pole se mají navrhovat — Title, Tags, Correspondent, Document type, Created date, Custom fields. Ve výchozím stavu bývá zaškrtnuté všechno.
- Klikněte na Generate suggestions. Podle počtu vybraných dokumentů a délky textu to trvá řádově jednotky sekund.
- Otevře se Review suggestions. U každého pole vidíte přeškrtnutou původní hodnotu a novou navrženou hodnotu, s vlastním checkboxem Apply — takže jde přijmout jen část návrhů a zbytek nechat beze změny nebo doplnit ručně.
- Kliknete na Apply. Pokud vše projde, dokument zmizí z fronty na Home (tag paperless-gpt se automaticky odebere) a hodnoty jsou zapsané přímo v Paperless-ngx.
Tohle funguje na první dobrou, ale jak už to tak bývá, nějakému tomu ladění se nevyhnete. Jestliže máte vlastní custom fields, což hádám, že asi máte, jestliže chcete vytěžit české faktury, tak Custom fields se v návrhu objeví, jen pokud jsou předem povolené v Settings → Custom Fields Editor, kde se zvolí i konkrétní pole a způsob zápisu (přidat/přepsat). Bez tohohle jednorázového nastavení zůstane checkbox Custom fields v přehledu sice zaškrtnutý, ale žádný návrh se nevygeneruje — na první pohled to vypadá jako chyba, ve skutečnosti jde jen o chybějící krok v konfiguraci.
Kde review skutečně pomáhá
Strukturovaná čísla s jednoznačným kontextem — variabilní symbol, částka, datum splatnosti, ta fungovala spolehlivě prakticky od první opravy promptu. Kontextová interpretace, jako rozlišení dodavatel/odběratel nebo volba přesného data vytvoření, byla náchylnější k chybám a chtěla si prompt doladit na míru konkrétnímu typu dokumentů, který zpracovávám. Náklady a celkový dojem Nejvíc mě překvapily náklady — sada několika desítek kompletně vytěžených faktur (název, korespondent, typ, tři custom fields) vyšla na jednotky centů. I při přepočtu na stovky dokumentů měsíčně je cena API volání položka, která se prakticky ztratí v rozpočtu.
Shrnutí
Paperless-GPT zvládá výrazně víc, než jen „přečti fakturu a vyplň pole“, a nabízí volbu mezi několika OCR enginy, dávkovou analýzu dokumentů vlastním promptem, dva režimy zpracování podle míry důvěry v automatiku a rostoucí sadu funkcí kolem kategorizace a kontroly. Cena provozu je při rozumném nastavení zanedbatelná. Reálná práce spočívá v doladění promptů na vlastní strukturu dokumentů a typy custom fields. Jakmile je tahle fáze za vámi, dostanete nástroj, který dokáže nahradit hodiny ručního přepisování. Dejte tomuto rozšíření šanci, Paperless-GPT si to zaslouží.
- +Je zdarma
- +Spousty funkcí
- +AI
- −Chybí Workflow
- −Dokumentace
- −Skromný dashboard