Skip to content
BotServBotServ
DokumentenbotRAGPaperless-ngxOllamaFirmen-ChatbotWissensmanagementSelf-HostingPraxisprojekt

Projekt: Firmen-Dokumentenbot bauen: Paperless-ngx + Ollama + RAG im eigenen Chat

Praxis-Projekt: Ein interner Bot beantwortet Mitarbeiter-Fragen aus Firmen-Dokumenten: Paperless-ngx scannt, Ollama antwortet, RAG liefert Kontext. Komplett lokal.

S

schutzgeist

3 min read
Dokumentenbot-Architektur: Paperless, RAG, Chat

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)

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?

Weitgehend, der Prompt erzwingt „nur aus Kontext antworten” plus Quellenangabe. Kein Treffer → Bot sagt „nicht gefunden” statt zu raten. Reranking verbessert die Trefferqualität weiter.

Wie viele Dokumente verträgt das?

Zehntausende problemlos: Qdrant skaliert gut. Der Engpass ist eher die Chunking-Strategie: sehr lange Dokumente in Abschnitte teilen für bessere Treffer.

Welche Hardware braucht es?

32 GB RAM reichen für 7B-14B-Antwortmodelle + Embeddings. Für bessere Qualität 14B-30B-Modelle, dann 64 GB oder ein 128-GB-Mini-PC (MS-S1 Max). Siehe Homelab-Guide.

Berechtigungen pro Abteilung?

Ja, separate Qdrant-Collections pro Abteilung, Bot prüft User-Rolle aus dem Chat (Mattermost/Nextcloud liefern das) und sucht nur in erlaubten Collections.

Geht das ohne Python?

Ja, als n8n-Workflow: Webhook → Paperless-Suche → Ollama → Antwort an Chat. Siehe n8n RAG-Workflows-Artikel. Etwas weniger flexibel, dafür ohne Code.

Quellen und weiterführende Literatur

Zurück zum KI Blog
Share:

Ähnliche Beiträge