Document AI and OCR skill for Codex
Route each page, keep confidences, review the doubtful. Codex classifies every page before choosing an engine, so born-digital text is never re-OCRed and photos get cropped and deskewed first. Layout-aware output with page numbers, bounding boxes and confidences feeds the LLM extraction step, numeric fields are checked against OCR evidence, and low-confidence values go to a review queue whose corrections become new gold labels.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- document-ai-ocr Installs to ~/.agents/skills/document-ai-ocr/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install document-ai-ocr The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project document-ai-ocr Installs to .agents/skills/document-ai-ocr/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/document-ai-ocr.zipunzip -o document-ai-ocr.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) document-ai-ocr PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $document-ai-ocr, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Builds document understanding pipelines that combine OCR and layout analysis with LLMs - Tesseract and OCRmyPDF, Docling, PaddleOCR, AWS Textract, Google Document AI, Azure Document Intelligence and vision-capable LLMs - covering page classification, image preprocessing, table and form extraction, reading order, confidence-based human review and accuracy measurement. Use when processing scanned PDFs, photos of receipts, forms, invoices, IDs or handwritten pages, choosing an OCR engine or cloud document API, or when OCR output is too noisy for downstream LLM extraction.
Use it when
- Documents are scans, photos or handwriting without a usable text layer
- You must choose between local OCR and cloud document APIs
- Tables or forms need structure preserved for an LLM
- Extraction needs confidence-based human review
Use something else when
- The task is a one-off OCR of a few PDFs into searchable files: use ocr-documents
- Text is already clean and you only need fields extracted: use structured-extraction
How to check the result
Measure character error rate and field accuracy on labelled pages per source type, and confirm low-confidence or failed-validation fields land in the review queue.
The mistake it prevents
Running OCR over pages that already have a good text layer, or trusting digits a vision model read from a blurry photo without checking them.
Done well means: each page is routed by what it is (born-digital, scanned, photo, form), text comes out with reading order, tables and coordinates preserved, low-confidence regions are flagged instead of silently trusted, the LLM step works on clean structured text (or the page image when layout matters), and accuracy is measured on labelled pages.
Classify pages first
pdffonts in.pdf | head # no fonts listed => likely image-only
pdftotext -f 1 -l 3 -layout in.pdf - | head -50
pdfimages -list in.pdf | head # page-sized images => scans
pdftoppm -r 150 -f 1 -l 1 -png in.pdf page # look at it
| Page type | Route |
|---|---|
| Born-digital PDF with good text layer | Skip OCR; extract text/tables ($pdf-processing) or layout-aware parse (Docling) |
| Scanned PDF, clean print | OCRmyPDF (Tesseract) or Docling with OCR |
| Phone photos, skewed, shadows | Preprocess (deskew, dewarp, crop) then OCR; or a vision LLM |
| Tables-heavy (statements, lab reports) | Layout model with table structure (Docling, Textract TABLES, Azure prebuilt-layout) |
| Fixed forms (tax forms, applications) | Key-value extraction (Textract FORMS, Azure prebuilt or custom model) or template coordinates |
| Receipts, invoices, IDs | Prebuilt specialised models (Textract AnalyzeExpense/AnalyzeID, Azure prebuilt-invoice/receipt, Google processors) |
| Handwriting | Cloud APIs or vision LLMs; Tesseract is weak here |
| Mixed PDFs (some pages scanned) | Per-page routing; never OCR pages that already have good text |
Engine choice
| Need | Default | Notes |
|---|---|---|
| Local, offline, printed text | OCRmyPDF + Tesseract | Adds a searchable text layer; mature CLI |
| Local, layout + tables to Markdown/JSON | Docling | Reading order, tables, figures; uses OCR when needed |
| Local, multilingual/CJK, better on photos | PaddleOCR | Heavier install, GPU helps |
| Managed, forms/tables/receipts at scale | Textract, Azure Document Intelligence, Google Document AI | Per-page pricing; data leaves your network |
| Messy layouts, semantic questions about the page | Vision-capable LLM on page images | Costly per page; can hallucinate unreadable text |
Data residency and confidentiality decide between local and cloud before accuracy does; ask
the user if documents may be sent to a cloud API. Installing engines, downloading models and
calling cloud APIs need network; in a Codex sandbox with network off, request approval or use
what is already installed (tesseract --version, ocrmypdf --version).
Workflow
- [ ] 1. Sample 30-50 pages across sources; label ground truth for the fields that matter
- [ ] 2. Classify pages (table above) and route per page
- [ ] 3. Preprocess images where needed (300 DPI, deskew, contrast)
- [ ] 4. OCR/layout -> structured intermediate (Markdown or JSON with page, bbox, confidence)
- [ ] 5. LLM extraction over the intermediate (or page image) with page references
- [ ] 6. Validate; route low-confidence and failed checks to human review
- [ ] 7. Measure field accuracy and character error rate on the labelled sample; iterate
Local OCR
# Searchable PDF; keeps existing text pages, OCRs image pages
ocrmypdf --skip-text --rotate-pages --deskew -l eng+deu --output-type pdf in.pdf out.pdf
pdftotext -layout out.pdf out.txt
# Single image, with per-word confidences as TSV
tesseract receipt.png receipt -l eng --psm 6 tsv
--skip-textleaves pages with text untouched;--redo-ocrreplaces an existing bad OCR layer;--force-ocrrasterises everything (loses vector text quality, use sparingly).--psm 6(uniform block) suits receipts;--psm 4single column of varying sizes; default3for full pages. Wrong page segmentation mode is a common cause of garbage.- Install language packs for every language present (
tesseract --list-langs).
from docling.document_converter import DocumentConverter
result = DocumentConverter().convert("statement.pdf")
markdown = result.document.export_to_markdown() # headings, reading order, tables as Markdown
doc_json = result.document.export_to_dict() # elements with page numbers and bounding boxes
Feed Markdown tables to the LLM rather than flattened text; table structure survives.
Image preprocessing (photos and poor scans)
- Render PDFs at 300 DPI for OCR (
pdftoppm -r 300); 150 DPI loses small print. - Deskew and rotate (OCRmyPDF does this; OpenCV
minAreaRecton text pixels for custom code). - Convert to grayscale; apply adaptive thresholding only if it measurably helps; it can erase faint text.
- Crop to the document (contour detection) for phone photos; perspective-correct before OCR.
- Keep the original image; store preprocessing parameters with the result for debugging.
Cloud document APIs
- Textract:
AnalyzeDocumentwithFeatureTypes=["TABLES","FORMS"](addQUERIESfor targeted questions),AnalyzeExpensefor receipts/invoices; multi-page PDFs use the asyncStart.../Get...APIs via S3. - Azure Document Intelligence:
prebuilt-layoutfor text/tables/structure,prebuilt-invoice,prebuilt-receipt,prebuilt-idDocument, or a custom model trained on your forms. - Google Document AI: create a processor (OCR, Form Parser, Invoice, custom extractor) in the project, then call it by processor ID.
- All return confidences per field or word: keep them in your intermediate format; they drive the review queue.
- Respect quotas with backoff; record page counts for cost tracking.
LLM step
Two ways to combine OCR with an LLM:
| Approach | Use when |
|---|---|
| OCR/layout text (Markdown) -> text LLM with a schema | Clean print, tables, many pages; cheaper and auditable |
| Page image (+ optional OCR text) -> vision LLM | Complex layouts, checkboxes, stamps, handwriting, charts |
Rules:
- Include page numbers in the input (
<page n="3">...</page>) and ask forpageper value. - Ask for verbatim evidence for key fields and check it against the OCR text in code.
- For vision LLMs, tell the model to return null for unreadable regions; verify numbers that matter against OCR output or a second pass, because vision models can produce plausible-looking digits.
- Schema design, validation and accuracy scoring:
$structured-extraction.
Human review
Route to review when any holds: OCR confidence on a critical field below threshold (start at 0.85 and tune), validation failure (totals, checksums, dates), evidence quote not found, page classified as handwriting or low quality. Show the reviewer the cropped region (bbox), the OCR text and the proposed value side by side, and record corrections as new gold labels.
Gotchas
- OCRing born-digital pages lowers quality; always check for a text layer first.
- Multi-column pages: plain OCR text interleaves columns; use layout-aware output (Docling,
prebuilt-layout, Tesseract--psmtuned) and verify reading order on samples. - Tables spanning pages lose headers on continuation pages; carry the header forward in code.
- Numbers: OCR confuses
0/O,1/l/I,5/S,,/.; validate numeric fields with regex and checksums (IBAN, VAT IDs, totals). - IDs and medical or financial documents are sensitive: keep processing in the approved region, avoid logging page text, and delete intermediates per the retention rule agreed with the user.
- Very large batches: process page by page, persist per-page results, make reruns resumable.
Verification
- Character error rate (CER) on 20+ labelled pages per source type, compared across engines before choosing one.
- Field accuracy on the labelled sample meets the threshold (
$structured-extractionscript). - Review-queue rate and reviewer correction rate tracked weekly; corrections become gold data.
- Spot-check 5 pages per new source visually: overlay bounding boxes on the image.
Report template
**Corpus:** 1,840 PDFs (62% born-digital, 31% scanned, 7% phone photos)
**Routing:** text layer -> Docling; scans -> OCRmyPDF --skip-text --deskew; photos -> crop + Textract AnalyzeExpense
**Accuracy (60 labelled pages):** CER 1.8% scans, 4.9% photos; total field acc 0.98, date 0.95
**Review:** 9% of documents (low confidence 6%, sum mismatch 3%)
**Data handling:** processed in eu-west-1, no page text in logs, intermediates deleted after 30 days What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Document AI and OCR"
short_description: "Route each page, keep confidences, review the doubtful"
default_prompt: "$document-ai-ocr Build a pipeline that pulls totals and dates out of scanned receipts and phone photos" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.