Projekt: Firmen-Dokumentenbot bauen: Paperless-ngx + Ollama + RAG im eigenen Chat
Was dieses Projekt macht
Mitarbeiter fragen im Chat: „Wie ist die Kündigungsfrist im Vertrag mit Kunde X?” oder „Wo ist die Betriebsanleitung für die Maschine?”, der Bot durchsucht das Firmen-Archiv und antwortet mit Quellenangabe. Komplett lokal: keine Dokumente verlassen die Firma.
Der Stack:
Papier/E-Mail/PDF
└── Paperless-ngx (Scan + OCR + Archiv)
└── RAG-Pipeline (Embeddings + Qdrant)
└── Ollama (Antwort-Modell)
└── Chat-Frontend (Mattermost / Nextcloud Talk / Web)
Voraussetzungen: Ein Mini-PC/Server (~32 GB RAM reicht), die Tools aus dem Software-Stack-Artikel, ~2-3 Stunden Setup.
Warum dieser Aufbau
- Paperless-ngx ist das Archiv-Rückgrat: OCR macht Scans durchsuchbar, Tags/Correspondents strukturieren. Siehe Paperless-ngx.
- RAG statt Training: Dokumente werden nicht ins Modell gebrannt, sondern per Embeddings durchsucht, neue Dokumente sind sofort drin, kein Re-Training.
- Chat als Frontend: Mitarbeiter fragen dort, wo sie eh sind: Mattermost oder Nextcloud Talk, kein neues Tool.
- DSGVO-sauber: Alles on-premise: Mandanten-/Vertragsdaten bleiben intern.
Schritt 1: Paperless-ngx als Archiv
# docker-compose.yml (Kern)
services:
paperless:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
ports: ["127.0.0.1:8000:8000"]
environment:
- PAPERLESS_OCR_LANGUAGE=deu
- PAPERLESS_TIME_ZONE=Europe/Berlin
volumes:
- paperless_data:/usr/src/paperless/data
- ./consume:/usr/src/paperless/consume # Ordner rein → OCR + Archiv
volumes:
paperless_data:
Dokumente in ./consume legen (oder Scanner direkt dorthin): Paperless macht OCR und archiviert. Siehe Paperless-ngx-Integration.
Schritt 2: RAG-Pipeline: Dokumente durchsuchbar machen
# ingest.py — Paperless-Dokumente in Qdrant indexieren
import requests, uuid
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct
qdrant = QdrantClient("localhost", port=6333)
def embed(text):
r = requests.post("http://localhost:11434/api/embeddings",
json={"model": "nomic-embed-text", "prompt": text})
return r.json()["embedding"]
# Paperless-API: Dokumente holen
docs = requests.get("http://localhost:8000/api/documents/",
headers={"Authorization": "Token DEIN_TOKEN"}).json()["results"]
for doc in docs:
text = doc.get("content", "")[:8000] # OCR-Text
vec = embed(text)
qdrant.upsert("firma", [PointStruct(
id=str(uuid.uuid4()), vector=vec,
payload={"title": doc["title"], "id": doc["id"], "text": text[:2000]}
)])
nomic-embed-text als Embedding-Modell (ollama pull nomic-embed-text), Qdrant als Vektor-DB. Siehe Lokales RAG.
Schritt 3: Der Bot: Frage → Retrieval → Antwort
# bot.py — Kern-Loop
import requests
def answer(question):
# 1. Ähnliche Dokumente finden
qvec = requests.post("http://localhost:11434/api/embeddings",
json={"model": "nomic-embed-text", "prompt": question}
).json()["embedding"]
hits = qdrant.search("firma", query_vector=qvec, limit=5)
# 2. Kontext bauen
context = "\n\n".join(h.payload["text"] for h in hits)
sources = [h.payload["title"] for h in hits]
# 3. Antwort generieren — NUR aus Kontext
prompt = f"""Beantworte die Frage NUR aus diesen Dokumenten.
Dokumente:
{context}
Frage: {question}
Wenn die Antwort nicht in den Dokumenten steht: sage es ehrlich."""
r = requests.post("http://localhost:11434/api/generate",
json={"model": "qwen2.5:14b", "prompt": prompt, "stream": False})
return r.json()["response"], sources
Der Kniff: NUR aus diesen Dokumenten im Prompt verhindert Halluzinationen, der Bot zitiert das Archiv statt zu raten.
Schritt 4: Chat-Frontend anbinden
Der Bot antwortet dort, wo die Firma chattet: Beispiele in den Plattform-Artikeln:
- Mattermost (Artikel): WebSocket-Bot,
/wissen frage..., self-hosted, DSGVO-sauber. - Nextcloud Talk (Artikel): Webhook-Bot, direkt in der bestehenden Nextcloud.
- Einfaches Web-Frontend: Open WebUI mit RAG-Funktion, schnellster Weg, kein eigener Bot-Code nötig.
- Telegram (Artikel): Wenn’s auch mobil sein soll.
Schritt 5: Betrieb, automatische Indexierung
Ein Cron-Job oder Paperless-Webhook triggert ingest.py bei jedem neuen Dokument, das Archiv wächst, der Bot weiß automatisch mehr:
# Paperless: post-consume-script oder einfacher Cron
*/10 * * * * docker exec paperless python3 /scripts/ingest.py --new-only
Erweiterungen
- Berechtigungen: Pro-Abteilung Kollektionen in Qdrant: Vertrieb sieht nicht die HR-Dokumente.
- n8n-Variante: Derselbe Flow als n8n-Workflow, visuell, ohne Python.
- Antwort-Qualität: Reranking-Modell nach dem Embedding-Retrieval, siehe Reranking-Modelle.
- Quellen-Links: Bot postet direkte Paperless-Links zum Dokument, nicht nur den Titel.
- Mehrsprachig: Fremdsprachige Verträge: Embedding-Modell mehrsprachig wählen.
Was Du dabei lernst
- RAG-Architektur im echten Einsatz (nicht nur Theorie)
- OCR + Archiv + Suche als Pipeline
- Halluzinations-Schutz durch „nur Kontext”-Prompts
- Chat-Bots in Firmen-Kontext (Berechtigungen, DSGVO)
Weiterführende Links
- IRC-Coding.de: Programmier-Tutorials: RAG, Bot-Code.
- Lokales RAG: Die Technik im Detail.
- Paperless-ngx: Archiv-Setup.
- Mattermost-Bots: Chat-Frontend.
- Interner Wissensbot: Verwandtes Szenario.
- Homelab: Die Hardware.
Key Takeaways:
- Firmen-Dokumentenbot = Paperless (Archiv/OCR) + Qdrant (Vektorsuche) + Ollama (Antworten) + Chat-Frontend.
- „Nur aus Dokumenten antworten”-Prompt verhindert Halluzinationen: Kern des Designs.
- Komplett lokal = DSGVO-sauber für Verträge/Mandanten-Daten.
- Indexierung per Cron/Webhook: Archiv wächst, Bot lernt mit.
- ~2-3h Setup auf vorhandener Hardware.
FAQ
Verhindert das Halluzinationen?
Wie viele Dokumente verträgt das?
Welche Hardware braucht es?
Berechtigungen pro Abteilung?
Geht das ohne Python?
Quellen und weiterführende Literatur
- Paperless-ngx: GitHub.
- Qdrant: Vektordatenbank.
- IRC-Coding.de: Programmier-Tutorials.


