Docs: PDF-ANALYSIS.md Purpose: Dokumentation der 70+ Schritt KI-Pipeline fur PDF-Analyse
Die PDF-Analyse-Pipeline ist ein autonomes Multi-Agenten-System fur die vollstandige Analyse beliebiger PDF-Dokumente -- von einzelnen Arbeitspapieren bis hin zu Korpora aus Hunderten von Dokumenten mit hunderttausenden Seiten.
Das Modul umfasst 18 Server-Module unter server/utils/pdfAnalysis/ und
durchlauft 70+ Entwicklungsschritte vom reinen Text-Layer uber OCR, Vision,
Deep Scan, Fact-Extraktion, deterministische Verifikation, Cross-Check gegen
externe Quellen bis hin zur Korpus-Synthese mit Konsens-/Konfliktanalyse.
Schlusselprinzipien:
- Streaming, nicht Laden -- Range-basiertes PDF-Lesen mit konstantem Speicherverbrauch unabhangig von Dateigroße
- Adaptive Parallelitat -- AIMD-Regelung (wie TCP) findet automatisch den maximalen stabilen Durchsatz des LLM-Providers
- Citations or die -- Jede Aussage im Report muss mit Seitenverweis belegt sein; unbesttigte Absatze werden markiert
- Deterministische Verifikation -- Fakten werden ohne LLM gegen den echten Seitentext gepruft (Substring-Match nach Normalisierung)
- Crash-Sicherheit -- Job-Snapshots und Chunk-Checkpoints werden atomar persistiert; unterbrochene Jobs werden beim Serverstart automatisch fortgesetzt
Upload ─► PdfReader ─► Chunk-Plan ─► AgentPool (AIMD) ─► Synthese ─► Report
│ │ │
├─ Text-Layer ├─ Chunks ├─ AnalysisAgent (LLM)
├─ OCR-Fallback ├─ Overlap ├─ CriticAgent (2-Stage)
├─ Vision-Triage├─ Checkpoint ├─ FactExtraction
├─ Deep Scan │ ├─ FactVerifier (deterministisch)
└─ Media-Desc │ └─ FactStore (SQLite+FTS5)
│
├─ CrossCheck-Pipeline (URL/PDF/YouTube/Web)
└─ Corpus-Pipeline (Multi-PDF Vergleich)
| Phase | Beschreibung | Modul |
|---|---|---|
| 1. Reading | PDF offnen, Seitenzahl ermitteln, Chunk-Plan generieren | pdfReader.js |
| 2. Analyzing | Parallele Multi-Agenten-Analyse mit adaptiver Parallelitat | agentPool.js, analysisAgent.js, criticAgent.js |
| 3. Synthesizing | Hierarchisches Map-Reduce uber alle Chunk-Ergebnisse | synthesizer.js |
| 4. Verifying-Facts | Deterministische Fact-Verifikation gegen echten Seitentext | factVerifier.js |
| 5. Storing-Facts | Fakten in SQLite+FTS5 speichern | factStore.js |
| 6. Done | Report schreiben, Checkpoint loschen, Job abschließen | index.js |
Seite laden
│
├─ Deep Scan aktiv? ──► MiniCPM-V liest Seite komplett visuell
│ (Fallback auf Text/OCR)
│
├─ Text-Layer vorhanden? ──► Programmatische Extraktion (schnell, exakt)
│ │
│ └─ Wenig Text? ──► OCR (Tesseract.js)
│
└─ Signifikante Bilder? ──► Vision-Agent beschreibt Bildinhalt
(MiniCPM-V lokal ODER Cloud-LLM)
Verwendet pdfjs-dist mit PDFDataRangeTransport statt die gesamte Datei in
den RAM zu laden. Nur ein kleiner Initial-Chunk (Header + Trailer-Nahe, Default
2 MB) wird vorab gelesen; weitere Byte-Ranges werden on-demand vom File
Descriptor angefordert.
- Konstanter Speicherverbrauch -- getestet fur Dateien im GB-Bereich und Dokumente mit hunderttausenden Seiten
- Seiten-Triage -- Text-Layer vs. OCR vs. Vision vs. Deep Scan
buildChunkPlan()-- partitioniert das Dokument in uberlappende Seiten-Chunks (Default: 8 Seiten/Chunk, 1 Seite Uberlappung)
- Sprachen: Deutsch + Englisch (
deu+eng, per ENV konfigurierbar) - Fallback-Kaskade:
deu+eng->deu->eng->osd(Orientation/Script) - Lazy Worker: ein einzelner Tesseract-Worker wird einmalig initialisiert und wiederverwendet (Worker-Spawn ist teuer)
- Serialisiert: OCR-Aufrufe werden serialisiert (Worker nicht thread-safe), aber nur Scan-Seiten landen in der OCR-Queue -- parallele Analyse-Agenten werden nicht blockiert
- Intelligent Routing:
needsOcr()pruft, ob die programmatische Extraktion zu wenig Text liefert (< 16 Zeichen konfigurierbar)
Backend-Auswahl (PDF_ANALYSIS_VISION_BACKEND):
"ollama"-- lokales MiniCPM-V 4.6 uber Ollama (Mac, kostenlos, privat)"cloud"-- multimodaler LLM-Provider des Forks"auto"(Default) -- lokal wenn verfugbar, sonst Cloud
localVision.js steuert Ollama an (/api/generate mit base64-Images):
- High-Resolution-Tiling: volle Render-Auflosung wird ubergeben (kein Downscale)
- OCR-Spezialist: liest Tabellen, Diagramme und komplexe Layouts aus dem Bild
- Multi-Image: Video-Keyframes konnen als Sequenz ubergeben werden
- Circuit-Breaker: nach 5 aufeinanderfolgenden Fehlern wird der Health-Check fur 5 Minuten pausiert (verhindert Dauerfeuer gegen totes Ollama)
Jede Seite wird hochauflösend gerastert (Scale 2.0) und komplett visuell gelesen -- MiniCPM-V interpretiert Layout, Tabellen und Diagramme im Zusammenhang.
- Aktivierung pro Job uber
deepScan=true - Fallback auf Text/OCR, falls lokales Modell nicht verfugbar
- Einsatz: komplexe Layouts (Rechnungen, Formulare, Prasentationen, gescannte Akten), bei denen reine Text-Parser Struktur verlieren
Wellen-Modell: Chunks in Seitenreihenfolge, Wellen laufen parallel, nach jeder Welle deterministisches Merge + atomarer Checkpoint.
AIMD-Regelung (Additive Increase / Multiplicative Decrease, wie TCP):
- Rate-Limit-/Uberlastfehler in einer Welle => Parallelitat halbieren (min. 1)
- kurze Abkuhlphase (5s)
- N fehlerfreie Wellen in Folge => Parallelitat +1 (bis Maximum)
- Retry-Cap: pro Chunk max. 10 Rate-Limit-Retries, dann finaler Fehler (verhindert infinite Loop bei dauerhaftem API-Ausfall)
Das System findet selbststandig den maximalen stabilen Durchsatz des jeweiligen LLM-Providers, ohne manuelles Tuning.
Pruft LLM-extrahierte Fakten ohne LLM gegen den echten Seitentext (kein Halluzinationsrisiko).
- Zitat und Seitentext normalisieren (Whitespace, Anfuhrungszeichen, Ligaturen, Silbentrennung am Zeilenende)
- Exakte Substring-Suche auf der angegebenen Seite
- Bei Fehlschlag: Nachbarseiten ±1 prufen (Chunk-Grenzen)
- Bei Treffer auf Nachbarseite: automatische Seitenkorrektur
STRICT-Modus (PDF_ANALYSIS_VERIFY_STRICT=true): unverifizierte Fakten
werden verworfen statt mit verified:false gespeichert.
CrossCheckPipeline (crossCheck/index.js) orchestriert die Kreuz-
Verifikation von Behauptungen gegen externe Quellen.
Quelltypen (sourceAdapters.js):
url-- Webseite (fetch + HTML→Text, SSRF-geschatzt)pdf-- lokale PDF (uber PdfReader, gestreamt)youtube-- YouTube-Video (Transkript via timedtext-API)text-- Roh-Text (z.B. aus Zwischenablage)image/video-- Bild/Video-URL (Vision-Agent)
SSRF-Schutz: nur http/https, keine privaten/lokalen IP-Bereiche, Redirect- Ziele werden erneut gepruft, Antwortgroße ist begrenzt.
Research-Agenten (researchAgent.js):
compareAgainstSource-- pruft Behauptungen gegen eine konkrete Quelle, liefert Urteil:supports | contradicts | inconclusivemit Beleg-ZitatdeepWebResearch-- autonome Web-Recherche: generiert Suchanfragen (LLM), holt Top-Treffer (Serper/SearchApi), ladet Seiten, pruft Behauptungen
Urteile werden an die betroffenen Fakten im FactStore zuruckgeschrieben.
CorpusPipeline (corpus/index.js) analysiert mehrere PDFs in einem Job:
- Pro Dokument: regularen
PdfAnalysisPipeline-Job starten (gestaffelt nachCORPUS_CONCURRENCY) - Auf Abschluss aller Einzel-Jobs warten (Polling, abbruchfahig)
compareCorpus()-- Vergleichs-Synthese + konsolidierter Report
Comparator (comparator.js) -- zwei Stufen:
- Konfliktanalyse: paarweise/gruppenweise -- welche Aussagen stutzen sich, welche widersprechen sich, welche sind Alleinstellungen?
- Korpus-Report: Executive Summary uber gesamten Bestand, Gemeinsamkeiten, Widerspruche (mit "Dokument A S. 12 vs. Dokument B S. 340"-Belegen)
Batching bei großen Korpora: COMPARE_BATCH_SIZE (Default 10) Dokumente pro
Batch, COMPARE_BATCH_CONCURRENCY (Default 1) parallele Batch-Calls.
Persistente Speicherung extrahierter Fakten mit vollem Quellenbezug (Dokument, Seite, wortliches Zitat, Job-ID).
- SQLite (
better-sqlite3) mit FTS5-Volltextindex -- Suche in Millisekunden statt Linear-Scan, auch bei Millionen Fakten (Prefix-Matching, Ranking nach bm25) - WAL-Modus -- transaktionale, crash-sichere Writes
- Incremental VACUUM -- automatische Defragmentierung alle 500 Inserts
- Einmal-Migration einer vorhandenen
facts.json(wird danach umbenannt) - JSON-Fallback: wenn
better-sqlite3nicht verfugbar ist, degradiert der Store transparent zu einem reinen JS-JSON-Store mit identischer API
Eine separate Prufinstanz (Multi-Agent-Reflexion, nicht derselbe Agent) pruft die Chunk-Ergebnisse:
Stufe 1 (deterministisch, kostenlos):
- Leere Summary trotz nicht-leerem Seitentext
- Findings ohne Seitenangaben
- Auffallig kurze Summary bei langem Input
Stufe 2 (LLM-Critic, nur fur Verdachtsfalle):
- Qualitatsurteil; bei
insufficient=> genau EIN Repair-Versuch durch erneute Analyse des Chunks mit Critic-Feedback im Prompt
Hierarchisches Map-Reduce uber alle Chunk-Ergebnisse:
- Stufe 1..n: Gruppen von Chunk-Zusammenfassungen werden zu Zwischen- zusammenfassungen verdichtet (rekursiv, bis eine ubrig ist)
- Finale Stufe: Best-Practices-Report (Markdown) mit Executive Summary, Kernerkenntnissen, Empfehlungen und durchgangigen Seitenverweisen
Citation Grounding: Absatze ohne (S. N)-Verweis werden deterministisch
erkannt. Der Report erhalt einen Grounding-Hinweis mit Deckungsgrad in %.
Automatische Speicher-Hygiene fur das PDF-Analyse-Modul:
- Uploads: loschen wenn alter als
UPLOAD_TTL_DAYS(Default 7) UND von keinem aktiven Job referenziert - Checkpoints: loschen wenn Job abgeschlossen/gescheitert oder verwaist
- Reports: loschen wenn alter als
REPORT_TTL_DAYS(Default 0 = nie) - Job-Snapshots: abgeschlossene Jobs alter als
JOB_TTL_DAYS(Default 30) - Stuck-Job-Detection:
running> 30 min =>failed - Orphan-Detection: PDF-Quelldatei verschwunden => warn
- FactStore: wird NIE automatisch bereinigt (dauerhaftes Gedachtnis)
Lauft beim Serverstart und danach im Intervall (Default: alle 1 h).
validatePdfPath() erlaubt als Analyse-Eingabe NUR Dateien innerhalb explizit
freigegebener Wurzelverzeichnisse (Default: modul-eigenes Upload-Verzeichnis +
Dokument-Storage). Schutzt gegen:
- Path Traversal (
../../etc/passwd) - Symlink-Ausbruch (realpath-Auflosung VOR dem Prefix-Check)
- Analyse beliebiger Server-Dateien durch API-Key-Inhaber
Zusatzliche Freigaben per PDF_ANALYSIS_ALLOWED_DIRS (kommasepariert).
Persistiert Job-Metadaten auf Disk (atomar via temp+rename), damit laufende Analysen einen Server-Neustart uberleben und automatisch fortgesetzt werden.
persistJob()-- atomarer JSON-Snapshot mitlastUpdated-TimestamploadAllJobs()-- lauft alle persistierten Jobs beim StartcleanupStaleJobs()-- terminale Jobs alter als N Stunden entfernenmarkStuckJobsAsFailed()--runningseit > N Stunden =>failedgetOrphanedJobs()-- Jobs deren PDF-Quelldatei verschwunden ist
Alle Endpoints unter /api/pdf-analysis/*, geschutzt uber validApiKey.
POST /api/pdf-analysis/upload
Content-Type: multipart/form-data
file=@dokument.pdfResponse:
{
"pdfPath": "/storage/pdf-analysis/uploads/1718900000000-dokument.pdf",
"documentName": "dokument.pdf",
"sizeBytes": 5242880
}POST /api/pdf-analysis/start
Content-Type: application/json
{
"pdfPath": "/storage/pdf-analysis/uploads/1718900000000-dokument.pdf",
"task": "Analysiere die Haushaltsplanung 2026",
"reportType": "umfassender Best-Practices-Bericht",
"factCriteria": "Budgetzahlen, Zusagen, Zeitpläne",
"deepScan": false
}Response:
{ "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }GET /api/pdf-analysis/:idResponse:
{
"id": "a1b2c3d4-...",
"documentName": "dokument.pdf",
"task": "Analysiere die Haushaltsplanung 2026",
"status": "running",
"progress": {
"phase": "analyzing",
"chunksDone": 12,
"chunksTotal": 30,
"totalPages": 240,
"concurrency": 6,
"etaSeconds": 180,
"pagesPerMinute": 48
},
"error": null,
"createdAt": "2026-06-22T10:00:00.000Z"
}GET /api/pdf-analysis/:id/resultResponse:
{
"status": "completed",
"report": "# Analysebericht: dokument.pdf\n\n## Executive Summary\n...",
"reportFile": "/storage/pdf-analysis/reports/a1b2c3d4-....md",
"pdfReport": null,
"masterSummary": "Das Dokument behandelt...",
"totalPages": 240,
"chunks": 30,
"factsStored": 87,
"factsVerified": 72,
"factsUnverified": 15,
"chunkErrors": 0,
"chunksRepaired": 3,
"ocrPages": 12,
"visionPages": 5,
"deepScannedPages": 0,
"groundingRatio": 94
}GET /api/pdf-analysis/:id/report/downloadLiefert den Report als Markdown-Datei (Content-Type: text/markdown).
GET /api/pdf-analysis/listDELETE /api/pdf-analysis/:idGET /api/pdf-analysis/facts?q=Haushalt&document=druck&tag=Budget&limit=50GET /api/pdf-analysis/facts/statsResponse:
{
"total": 1247,
"verified": 983,
"byDocument": {
"dokument.pdf": 87,
"bericht.pdf": 340
}
}GET /api/pdf-analysis/facts/:factId
DELETE /api/pdf-analysis/facts/:factIdPOST /api/pdf-analysis/crosscheck
Content-Type: application/json
{
"claims": ["Der Haushalt 2026 betraegt 500 Mrd. EUR"],
"factIds": ["abc123def456"],
"sources": [
{ "type": "url", "url": "https://example.com/haushalt" },
{ "type": "pdf", "path": "/storage/pdf-analysis/uploads/vergleich.pdf" }
],
"deepWeb": true
}GET /api/pdf-analysis/crosscheck/list
GET /api/pdf-analysis/crosscheck/:id
GET /api/pdf-analysis/crosscheck/:id/result
GET /api/pdf-analysis/crosscheck/:id/report/download
DELETE /api/pdf-analysis/crosscheck/:idPOST /api/pdf-analysis/corpus
Content-Type: application/json
{
"pdfPaths": [
"/storage/pdf-analysis/uploads/dokument-a.pdf",
"/storage/pdf-analysis/uploads/dokument-b.pdf"
],
"task": "Vergleiche die Haushaltsplane 2025 und 2026",
"reportType": "corpus",
"deepScan": false
}GET /api/pdf-analysis/corpus/list
GET /api/pdf-analysis/corpus/:id
GET /api/pdf-analysis/corpus/:id/result
DELETE /api/pdf-analysis/corpus/:idAlle Werte sind per ENV ubersteuerbar. Default-Werte in Klammern.
| ENV-Variable | Default | Beschreibung |
|---|---|---|
PDF_ANALYSIS_CONCURRENCY |
6 |
Initiale Agenten-Parallelitat (1-64) |
PDF_ANALYSIS_PAGES_PER_CHUNK |
8 |
Seiten pro Chunk (1-100) |
PDF_ANALYSIS_OVERLAP_PAGES |
1 |
Chunk-Uberlappung in Seiten (0-50) |
PDF_ANALYSIS_MAX_ACTIVE_JOBS |
2 |
Max. parallele Analyse-Jobs (1-32) |
PDF_ANALYSIS_MAX_PAGES |
0 |
Seiten-Limit (0 = unbegrenzt, max 100000) |
PDF_ANALYSIS_CORPUS_CONCURRENCY |
4 |
Max. parallele Einzel-Analysen im Korpus-Job (1-16) |
PDF_ANALYSIS_REDUCE_GROUP_SIZE |
20 |
Gruppengroße beim hierarchischen Reduce (1-200) |
PDF_ANALYSIS_TEMPERATURE |
0 |
LLM-Temperatur |
PDF_ANALYSIS_MAX_CHARS_PER_CHUNK |
24000 |
Max. Zeichen pro Chunk an LLM (1000-1000000) |
PDF_ANALYSIS_FACT_MIN_CONF |
0.7 |
Mindest-Confidence fur Fakten-Speicherung |
PDF_ANALYSIS_OCR |
true |
OCR-Fallback aktiviert |
PDF_ANALYSIS_OCR_LANGS |
deu+eng |
Tesseract-Sprachen |
PDF_ANALYSIS_OCR_SCALE |
2.0 |
Render-Skalierung fur OCR |
PDF_ANALYSIS_OCR_MIN_CHARS |
16 |
Mindest-Zeichen fur Text-Layer-Erkennung |
PDF_ANALYSIS_VISION |
true |
Vision-Agent aktiviert |
PDF_ANALYSIS_VISION_BACKEND |
auto |
Backend: ollama/cloud/auto |
PDF_ANALYSIS_VISION_MIN_AREA |
0.08 |
Mindest-Bildflachenanteil fur Vision (8%) |
PDF_ANALYSIS_VISION_MAX_PER_CHUNK |
3 |
Max. Vision-Seiten pro Chunk (Kostendeckel) |
PDF_ANALYSIS_CRITIC |
true |
Critic-Agent aktiviert |
Zusatzliche ENVs fur AIMD, Ollama, Security, Retention und Cross-Check:
| ENV-Variable | Default | Beschreibung |
|---|---|---|
PDF_ANALYSIS_AIMD_INCREASE_AFTER |
3 |
Saubere Wellen vor +1 Parallelitat |
PDF_ANALYSIS_AIMD_COOLDOWN_MS |
5000 |
Abkuhlphase nach Rate-Limit |
PDF_ANALYSIS_MAX_CHUNK_RETRIES |
10 |
Max. Rate-Limit-Retries pro Chunk |
PDF_ANALYSIS_OLLAMA_URL |
http://localhost:11434 |
Ollama-Endpoint |
PDF_ANALYSIS_OLLAMA_VISION_MODEL |
minicpm-v |
Ollama-Vision-Modell |
PDF_ANALYSIS_OLLAMA_TIMEOUT_MS |
180000 |
Ollama-Timeout (3 min) |
PDF_ANALYSIS_OLLAMA_NUM_CTX |
8192 |
Ollama Context-Window |
PDF_ANALYSIS_LLM_RETRIES |
4 |
Max. LLM-Retries bei Vision |
PDF_ANALYSIS_LLM_BACKOFF_MS |
2000 |
Basis-Backoff fur LLM-Retries |
PDF_ANALYSIS_DEEPSCAN_SCALE |
2.0 |
Render-Skalierung fur Deep Scan |
PDF_ANALYSIS_INITIAL_CHUNK_BYTES |
2097152 |
Initial-Chunk fur Range-Streaming (2 MB) |
PDF_ANALYSIS_VERIFY_WINDOW |
1 |
Nachbarseiten-Toleranz bei Fact-Verifikation |
PDF_ANALYSIS_VERIFY_STRICT |
false |
Unverifizierte Fakten verwerfen |
PDF_ANALYSIS_ALLOWED_DIRS |
(leer) | Zusatzliche freigegebene Verzeichnisse |
PDF_ANALYSIS_UPLOAD_TTL_DAYS |
7 |
Upload-Aufbewahrung in Tagen |
PDF_ANALYSIS_REPORT_TTL_DAYS |
0 |
Report-Aufbewahrung (0 = nie) |
PDF_ANALYSIS_JOB_TTL_DAYS |
30 |
Job-Snapshot-Aufbewahrung in Tagen |
PDF_ANALYSIS_CLEANUP_INTERVAL_MS |
3600000 |
Cleanup-Intervall (1 h) |
PDF_ANALYSIS_JOB_TIMEOUT_MINUTES |
30 |
Stuck-Job-Timeout in Minuten |
PDF_ANALYSIS_MAX_COMPLETED_JOBS |
500 |
Hard Cap fur terminale Jobs im RAM |
PDF_ANALYSIS_FACT_VACUUM_THRESHOLD |
500 |
Inserts bis Incremental VACUUM |
PDF_ANALYSIS_XCHECK_CONCURRENCY |
4 |
Cross-Check Parallelitat |
PDF_ANALYSIS_XCHECK_RESULTS |
4 |
Suchergebnisse pro Query |
PDF_ANALYSIS_XCHECK_QUERIES |
2 |
Suchanfragen pro Behauptung |
PDF_ANALYSIS_XCHECK_MAX_FETCH_BYTES |
5242880 |
Max. Fetch-Bytes (5 MB) |
PDF_ANALYSIS_XCHECK_FETCH_TIMEOUT_MS |
20000 |
Fetch-Timeout |
PDF_ANALYSIS_XCHECK_MAX_SOURCE_CHARS |
60000 |
Max. Zeichen pro Quelle |
SERPER_DEV_API_KEY |
(leer) | Serper-API-Key fur Web-Suche |
SEARCHAPI_API_KEY |
(leer) | SearchApi-Key fur Web-Suche (Fallback) |
Die Frontend-Komponente (frontend/src/pages/PdfAnalysis/) bietet 4 Tabs:
- Upload-Formular mit Aufgaben-Text, Berichtstyp, Fact-Kriterien, Deep-Scan-Option
- Live-Job-Liste mit Fortschrittsbalken, Phase, ETA, Pages/Minute, aktive Agenten
- Report-Modal mit Markdown-Rendering, Inhaltsverzeichnis (TOC), Download als
.md/.docx/.pdf, "Als Quelle hinzufugen" (Kopie in Zwischenablage)
- Volltext-Suche (
q) + Filter nach Dokument (document) - Fakten-Liste mit Quellenbezug (Dokument, Seite), Verifikations-Status (verifiziert/nicht verifiziert), Tags, Cross-Check-Ergebnis-Badge
- Aktionen pro Fakt: "Quellen prufen" (springt zum Cross-Check-Tab), Loschen
- Formular: Behauptungen (zeilenweise), Fact-IDs, Vergleichsquellen (URL/PDF/YouTube/Text/Bild/Video), Deep-Web-Option
- Job-Liste mit Fortschritt, Status, Urteilen
- Report-Modal mit Verifikationsmatrix (Behauptung x Quelle x Urteil) + Markdown-Report + Download-Link
- Multi-Datei-Upload (mindestens 2 PDFs)
- Aufgaben-Text, Fact-Kriterien, Deep-Scan-Option
- Job-Liste mit Phasen-Fortschritt (
analyzing-documents->comparing) - Report-Modal mit Konflikt-Liste (Topic + Position je Dokument mit Beleg), analysierte/fehlgeschlagene Dokumente, Markdown-Report
| ENV | Klein-Instanz | Standard | Gross-Instanz |
|---|---|---|---|
PDF_ANALYSIS_CONCURRENCY |
2 | 6 | 12-16 |
PDF_ANALYSIS_MAX_ACTIVE_JOBS |
1 | 2 | 4 |
PDF_ANALYSIS_CORPUS_CONCURRENCY |
1 | 4 | 5 |
PDF_ANALYSIS_XCHECK_CONCURRENCY |
2 | 4 | 8 |
AIMD regelt die Parallelitat automatisch nach unten bei 429/503. Die obere
Grenze sollte so gewahlt werden, dass MAX_ACTIVE_JOBS * CONCURRENCY die
Rate-Limits des LLM-Providers nicht dauerhaft uberschreitet.
- PdfReader: Range-Streaming, konstanter RAM unabhangig von Dateigroße
- Multer Upload:
fileSize: 1 GB,fieldSize: 1 GB-- streamt multipart direkt auf Disk, RAM bleibt konstant - OCR: Serialisierter Worker, keine parallelen Raster-Buffer
- FactStore: Incremental VACUUM alle 500 Inserts verhindert Fragmentierung
- Stuck-Job-Detection:
PDF_ANALYSIS_JOB_TIMEOUT_MINUTES(Default 30) markiertrunning-Jobs alsfailedmit Sentinel-Errororphaned-stuck - In-Memory Pruning: terminale Jobs alter als 24h werden aus dem RAM
entfernt; harter Cap bei
PDF_ANALYSIS_MAX_COMPLETED_JOBS(Default 500) - Disk Cleanup:
cleanupStaleJobs(24)entfernt terminale Disk-Snapshots alter als 24h
deu+eng ──► deu ──► eng ──► osd
(zwei Sprachen) (nur Deutsch) (nur Englisch) (Orientation/Script)
Jede Stufe wird nur versucht, wenn die vorherige fehlschlagt. Ein fehlendes
Sprachpaket bricht die Kaskade nicht -- osd ist in Tesseract immer verfugbar.
Beim Serverstart ruft PdfAnalysisPipeline.resumeInterrupted():
- Alle persistierten Jobs laden (
loadAllJobs()) - Unterbrochene (
pending/running) automatisch fortsetzen - Falls PDF-Datei nicht mehr vorhanden: Job als
failedmarkieren - Chunk-Checkpoints sorgen dafur, dass bereits analysierte Chunks NICHT erneut berechnet werden