# RAG Ingestion Tools Lokale Python-Tools zum Importieren von Dokumenten in pgvector-Vektordatenbanken. Vier eigenständige Scripts für unterschiedliche Zielplattformen und Anwendungsfälle, plus zwei gemeinsame Module für Extraktion und KI-Normalisierung. --- ## Übersicht & Zielplattformen | Tool | Wofür? | Zielplattform | Verbindung | .env-Datei | |---|---|---|---|---| | `ingest_V2.py` | Fremde Dokumente, Scans, Bilder — mit automatischer Strukturierung | apply4jobs.de · anythingllm DB | SSH-Tunnel | `.env.ingest` | | `ingest_anythingllm.py` | Eigene, bereits strukturierte Dokumente (Templates) | apply4jobs.de · anythingllm DB | SSH-Tunnel | `.env.ingest` | | `ingest_job_matching.py` | Stellenanzeigen & Profil-Dokumente für Job-Matching | NAS · job_matching DB | Direkt (Port 5433) | `.env.job_matching` | | `sync_missing_docs.py` | Dokumentenabgleich: fehlende/aktualisierte Profil-Dokumente von job_matching nach apply4jobs.de spiegeln | NAS → apply4jobs.de | Direkt + SSH-Tunnel | `.env.ingest` + `.env.job_matching` | **Wann welches Tool?** - Du hast ein **fremdes CV, einen Scan oder ein Bild** → `ingest_V2.py` (OCR + KI-Normalisierung) - Du hast ein **eigenes, fertiges Markdown-Dokument** (z.B. ausgefülltes Template) → `ingest_anythingllm.py` - Du willst **Stellenanzeigen oder Profil-Dokumente** für Job-Matching einpflegen → `ingest_job_matching.py` - Du willst, dass Dokumente aus der **Job-Matching-Plattform auch in der apply4jobs.de-RAG-DB** verfügbar sind (Profil-Kern, Skill-Matrix, Projektbeschreibungen etc.) → `sync_missing_docs.py` --- ## Dateistruktur ``` rag-ingestion/ ├── ingest_V2.py Vollpipeline v2: OCR + KI-Normalisierung (→ apply4jobs.de) ├── ingest_anythingllm.py Einfaches Script v1: nur MD/TXT/PDF (→ apply4jobs.de) ├── ingest_job_matching.py Job-Matching Script: direkt auf NAS (→ NAS Port 5433) ├── sync_missing_docs.py Abgleich job_matching → apply4jobs.de (fehlende/geänderte Dokumente spiegeln) │ ├── extractor.py Modul: Text-Extraktion + OCR (wird von ingest_V2 genutzt) ├── normalizer.py Modul: KI-Normalisierung via Claude (wird von ingest_V2 genutzt) │ ├── architektur/ │ └── ocr_rag_pipeline.svg Architektur-Diagramm der ingest_V2 Pipeline │ ├── templates/ │ ├── *_TEMPLATE.md Ziel-Strukturvorlagen (leer) │ └── ausgefuellt/ Ausgefüllte Referenzbeispiele (Few-Shot für KI, nicht in Git) │ ├── .env.ingest Secrets für ingest_V2.py + ingest_anythingllm.py (nicht in Git) ├── .env.job_matching Secrets für ingest_job_matching.py (nicht in Git) └── .env.example Vorlage für .env.ingest ``` --- ## Tool 1: `ingest_V2.py` — Vollpipeline mit OCR + KI-Normalisierung **Verwendungszweck:** Verarbeitung beliebiger Fremddokumente — gescannte PDFs, Bilder, DOCX-Dateien, fremde CVs — die zunächst per OCR extrahiert und dann via Claude KI automatisch in die einheitliche Template-Struktur transformiert werden. **Zielplattform:** pgvector-Datenbank (`anythingllm`) für `ai.apply4jobs.de` via SSH-Tunnel. > **Hinweis (Stand 2026-07):** Server + Domain wurden von Hetzner auf einen Digital-Ocean- > Droplet umgezogen. `SSH_HOST` in `.env.ingest` muss auf die aktuelle DO-IP zeigen — > die alte Hetzner-IP (`46.225.225.226`) ist nicht mehr gültig. **Benötigt:** `extractor.py` und `normalizer.py` im gleichen Verzeichnis. ### Architektur ![OCR → RAG Ingestion Pipeline](architektur/ocr_rag_pipeline.svg) ### Unterstützte Formate `.md` `.txt` `.pdf` `.docx` `.png` `.jpg` `.jpeg` `.tif` `.tiff` `.bmp` `.webp` ### Voraussetzungen ```bash # System-Tools (einmalig) brew install tesseract tesseract-lang poppler # Python-Pakete pip install -r requirements.txt # .env.ingest konfigurieren cp .env.example .env.ingest nano .env.ingest # ANTHROPIC_API_KEY eintragen ``` ### Verwendung ```bash # Einzelne Datei importieren (mit KI-Normalisierung) python ingest_V2.py file ~/Downloads/fremdes_cv.pdf # Dry-Run: erst testen, nichts in DB schreiben python ingest_V2.py file ~/Desktop/scan.png --dry-run # Dry-Run + normalisiertes Markdown automatisch speichern (_normalized.md) python ingest_V2.py file ~/Downloads/cv.pdf --dry-run # Dry-Run + normalisiertes Markdown in bestimmte Datei speichern python ingest_V2.py file ~/Downloads/cv.pdf --dry-run --output ~/Desktop/cv_normalisiert.md # Ohne KI-Normalisierung (für bereits strukturierte Dokumente) python ingest_V2.py file ~/templates/ausgefuellt/mein_cv.md --no-normalize # Ganzes Verzeichnis importieren python ingest_V2.py dir ~/Dokumente/bewerbungsunterlagen/ --force # Ordner live beobachten (neue Dateien automatisch verarbeiten) python ingest_V2.py watch ~/Desktop/scan-eingang/ ``` ### Datenbank verwalten ```bash # Alle importierten Dokumente anzeigen # Zeigt: Titel, Dokumenttyp, Qualitäts-Score, Format, Anzahl Chunks, Datum python ingest_V2.py list # Ausgabe-Beispiel: # Titel Typ Score Fmt Chunks Datum # ────────────────────────────────────────────────────────────────────────────────────────── # Arbeitszeugnis Sopra Financial Technology... arbeitszeugnis 95% .pdf 3 2026-04-28 # Einzelnes Dokument löschen (alle Chunks) # Hinweis: Titel = exakter Dateiname wie beim Import python ingest_V2.py delete "Arbeitszeugnis Sopra Financial Technology GmbH.pdf" ``` ### Flags | Flag | Beschreibung | |---|---| | `--force` | Bestehende Chunks überschreiben | | `--no-normalize` | KI-Normalisierung überspringen | | `--dry-run` | Nur extrahieren + normalisieren, nicht in DB speichern | | `--output ` | Normalisiertes Markdown als Datei speichern (nur mit `--dry-run`) | | `--quality-min 0.5` | Mindest-Qualitäts-Score (0.0–1.0, Standard: 0.0) | ### KI-Normalisierung (`normalizer.py`) - **Typ-Erkennung:** Claude Haiku (günstig + schnell) - **Transformation:** Claude Sonnet - **Fehlende Felder** werden mit `[FEHLT]` markiert, nie halluziniert - **Qualitäts-Score:** 0.0–1.0 (Anteil befüllter Pflichtfelder) Erkannte Typen: `arbeitszeugnis`, `cv`, `lebenslauf`, `anschreiben`, `projektbeschreibung`, `karriereziele`, `technologie`, `zertifikate`, `referenzen`, `elevatorpitch`, `rahmenbedingungen`, `zielstellen` ### OCR-Pipeline (`extractor.py`) 1. `pypdf` versucht Text zu extrahieren 2. Weniger als 150 Zeichen → OCR-Fallback via Tesseract (300 DPI) 3. Bilder (PNG, JPG etc.) → direkt OCR --- ## Tool 2: `ingest_anythingllm.py` — Einfaches Ingestion-Script (v1) **Verwendungszweck:** Schnelles Einpflegen von eigenen, bereits fertig strukturierten Dokumenten — z.B. ausgefüllte Templates, eigene Markdown-Dateien oder einfache PDFs die keiner OCR oder KI-Normalisierung bedürfen. **Zielplattform:** pgvector-Datenbank (`anythingllm`) für `ai.apply4jobs.de` via SSH-Tunnel. Gleiche Zieldatenbank wie `ingest_V2.py` — unterschiedlicher Verarbeitungsweg. ### Unterstützte Formate `.md` `.txt` `.pdf` ### Verwendung ```bash python ingest_anythingllm.py file ~/Dokumente/Lebenslauf.md python ingest_anythingllm.py file ~/Dokumente/Lebenslauf.md --force python ingest_anythingllm.py dir ~/Dokumente/bewerbung/ python ingest_anythingllm.py list ``` --- ## Tool 3: `ingest_job_matching.py` — Job-Matching-Datenbank (NAS) **Verwendungszweck:** Einpflegen von Stellenanzeigen und Profil-Dokumenten in die dedizierte Job-Matching-Datenbank. Diese Datenbank wird von einem separaten Job-Matching-Workflow genutzt um Stellenanzeigen mit dem eigenen Profil abzugleichen. **Zielplattform:** pgvector-Datenbank (`job_matching`) direkt auf dem NAS (Port 5433) — kein SSH-Tunnel erforderlich. > `JM_DB_HOST` steht auf die Tailscale-IP `100.81.64.115`, damit die Verbindung auch > außerhalb des Heimnetzes funktioniert. Im lokalen WLAN geht alternativ auch die > LAN-IP `192.168.178.128` (kann sich per DHCP ändern). Embeddings via OpenAI `text-embedding-3-small` (1536 Dimensionen, kostenpflichtig). ### Verwendung ```bash python ingest_job_matching.py file ~/Dokumente/stellenanzeige.pdf python ingest_job_matching.py dir ~/Dokumente/stellen/ python ingest_job_matching.py list python ingest_job_matching.py clear "stellenanzeige.pdf" ``` --- ## Tool 4: `sync_missing_docs.py` — Dokumentenabgleich job_matching → apply4jobs.de **Verwendungszweck:** Job-Matching-Plattform und `ai.apply4jobs.de`/`apply4jobs.de` nutzen zwei getrennte pgvector-Datenbanken (NAS vs. DO-Droplet). Dieses Script gleicht sie ab, damit Profil-Dokumente, die in der Job-Matching-Plattform erstellt/hochgeladen wurden (Skill-Matrix, Rahmenbedingungen, Zielstellen-Profil, Muster-Anschreiben, Elevator-Pitch, Projektbeschreibungen, CV, Arbeitszeugnisse etc.), auch für die Webseiten-RAG verfügbar sind. **Matching-Kriterium:** `filename` (job_matching.profile_documents) ↔ `metadata->>'title'` (anythingllm_vectors). **Embeddings:** werden für die Zielseite immer **neu berechnet** (lokal, `multilingual-e5-small`, 384 Dim) — die OpenAI-1536-Dim-Vektoren aus job_matching sind mit der 384-Dim-Spalte in `anythingllm_vectors` nicht kompatibel. Nur der Text wird übernommen. **Voraussetzung:** benötigt beide `.env`-Dateien (`.env.ingest` für die SSH-Tunnel-Verbindung zum Ziel, `.env.job_matching` für die Quelle) sowie `sentence-transformers` (aus `requirements.txt`). ### Verwendung ```bash # Nur anzeigen, welche Dokumente in apply4jobs.de fehlen (kein Schreibzugriff) python sync_missing_docs.py diff # Optional auf einen Workspace eingrenzen python sync_missing_docs.py diff --workspace-id 1 # Fehlende Dokumente übertragen python sync_missing_docs.py sync --dry-run # Simulation, nichts wird geschrieben python sync_missing_docs.py sync # tatsächlich schreiben # Auch bereits vorhandene Titel neu einlesen (Update/Re-Sync): # löscht die alten Chunks in anythingllm_vectors und ersetzt sie mit dem # aktuellen Stand aus job_matching python sync_missing_docs.py sync --force python sync_missing_docs.py sync --force --dry-run ``` ### Flags | Flag | Beschreibung | |---|---| | `--workspace-id ` | Nur Dokumente eines bestimmten Workspace aus job_matching berücksichtigen | | `--dry-run` | Nur anzeigen, nichts in die Zieldatenbank schreiben | | `--force` | Auch bereits vorhandene Titel neu einlesen (löscht + ersetzt statt nur zu ergänzen) | --- ## Gemeinsame Infrastruktur ### Embedding-Modelle | Tool | Modell | Dimensionen | Kosten | |---|---|---|---| | `ingest_V2.py` | multilingual-e5-small (lokal) | 384 | kostenlos | | `ingest_anythingllm.py` | multilingual-e5-small (lokal) | 384 | kostenlos | | `ingest_job_matching.py` | OpenAI text-embedding-3-small | 1536 | kostenpflichtig | | `sync_missing_docs.py` | multilingual-e5-small (lokal, für Ziel-DB neu berechnet) | 384 | kostenlos | ### Umgebungsvariablen | Datei | Verwendet von | Inhalt | |---|---|---| | `.env.ingest` | `ingest_V2.py`, `ingest_anythingllm.py`, `sync_missing_docs.py` | ANTHROPIC_API_KEY, SSH, DB | | `.env.job_matching` | `ingest_job_matching.py`, `sync_missing_docs.py` | OPENAI_API_KEY, NAS-DB | --- ## Setup (komplett, einmalig) ```bash cd ~/Projekte/rag-ingestion python3 -m venv venv source venv/bin/activate pip install -r requirements.txt brew install tesseract tesseract-lang poppler # nur für ingest_V2.py cp .env.example .env.ingest nano .env.ingest # ANTHROPIC_API_KEY + SSH + DB-Zugangsdaten eintragen ```