Runbook: Paperless v3 in Betrieb nehmen (E7)¶
Schritt-für-Schritt-Anleitung für Robin. Sie setzt die Teile C bis F des Umsetzungsplans in die Reihenfolge um, in der du sie ausführst. Jeder Schritt hat einen Befehl, eine erwartete Ausgabe und ein Stopp-Kriterium. Nach jedem Block (◆) meldest du Claude die Ausgabe, bevor es weitergeht. Claude führt hier nichts selbst aus.
Voraussetzungen: homeserver#243, #244 und homelab-documentation#60 sind gemergt. Du hast Zugang zur
Infisical-UI (EU), zum Embedded Web Server des HP 4302 (PIN), zu Proton Mail und zu Uptime Kuma auf dem VPS.
Rollen auf dem NUC: ssh nuc = Benutzer robin (Docker, Lesen). ssh nuc-root = root (alles unter /etc,
/opt, Systembenutzer, git pull). Der Deploy-Pfad ist /opt/rpi-docker-compose/.
Was diese Inbetriebnahme löscht: den Inhalt der alten Paperless-Verzeichnisse (genau ein Testdokument) in
Phase 4. /mnt/scanner wird nie angefasst.
Phase 0 · Vorbereitung am Laptop (10 min)¶
0.1 Secrets erzeugen. Zwei Werte, die du gleich in Infisical einträgst. Nicht in den Chat kopieren.
uv run python3 -c "import secrets; print('SECRET_KEY', secrets.token_urlsafe(64))"
openssl rand -hex 32
0.2 Korrespondenten eintragen. In ~/repos/homeserver/paperless-ngx/seed/schema.yaml unter correspondents:
deine regelmäßigen Absender, Name wie im Briefkopf:
Branch, PR, Merge (Claude kann den PR für dich anlegen, wenn du die Liste schickst). Das kann auch später vor Phase 6 passieren.
Phase 1 · Infisical (15 min)¶
1.1 UI: Projekt wie ollama/speedtest-tracker, Env dev, neuer Pfad /paperless-ngx, drei Secrets:
| Key | Wert |
|---|---|
PAPERLESS_SECRET_KEY |
SECRET_KEY aus 0.1 |
POSTGRES_PASSWORD |
Hex-Wert aus 0.1 |
PAPERLESS_DBPASS |
derselbe Hex-Wert |
Dann Access Control → Identities → nuc-agent: Lesezugriff auf /paperless-ngx ergänzen.
1.2 Agent-Template [nuc-root]: Den Block aus dem Repo in die lokale Agent-Config übernehmen.
Falls FEHLT: den paperless-ngx-Block aus
/opt/rpi-docker-compose/infrastructure/infisical-agent/agent.yaml.example (nach dem Merge von #243 dort
enthalten) an die templates:-Liste in /etc/infisical/agent.yaml anhängen, gleiche Einrückung wie die
Nachbarblöcke. Dann:
Erwartet: drei Zeilen PAPERLESS_SECRET_KEY, POSTGRES_PASSWORD, PAPERLESS_DBPASS.
Stopp, wenn die Datei fehlt oder leer ist: journalctl -u infisical-agent.service -n 30.
◆ Melde die drei Key-Namen (nur Namen, cut -d= -f1 zeigt keine Werte).
Phase 2 · Systembenutzer und Verzeichnisse [nuc-root] (5 min)¶
2.1 IDs frei?
Erwartet: nur leer = frei. Stopp, wenn eine ID belegt ist.
2.2 Benutzer anlegen.
groupadd --gid 988 paperless
useradd --system --uid 977 --gid 988 --no-create-home --home-dir /nonexistent --shell /usr/sbin/nologin paperless
id paperless
Erwartet: uid=977(paperless) gid=988(paperless) groups=988(paperless)
2.3 Verzeichnisse.
install -d -o paperless -g paperless -m 0770 /mnt/ssd/container-data/paperless/consume/robin
install -d -o 1000 -g 1000 -m 0700 /mnt/ssd/container-data/paperless/protonmail-bridge
ls -ln /mnt/ssd/container-data/paperless/
Erwartet: consume mit 977 988, protonmail-bridge mit 1000 1000, daneben die alten data, db, redis.
◆ Melde die ls -ln-Ausgabe.
Phase 3 · Samba [nuc-root] (15 min)¶
3.1 Spricht noch jemand SMB1?
Erwartet: keine NT1-Sitzungen. Der 4302 und macOS können SMB2/3.
3.2 smb.conf anpassen. In /etc/samba/smb.conf im Abschnitt [global]:
server min protocol = NT1→server min protocol = SMB2map to guest = Bad Password→map to guest = Never- als letzte Zeile von
[global](direkt vor[homes]) ergänzen:include = /opt/rpi-docker-compose/paperless-ngx/host-config/smb-paperless.conf
Warum letzte Zeile: Samba fügt die Datei wörtlich ein, ihr [paperless]-Header wechselt den Abschnitt. Alles,
was in smb.conf nach dem Include stünde, landete still in der Freigabe.
3.3 Prüfen und laden.
testparm -s 2>&1 | grep -A12 '^\[paperless\]'
testparm -s 2>&1 | grep -E 'server min protocol|map to guest'
testparm -s 2>&1 | grep -i 'global parameter'
systemctl reload smbd
Erwartet: Freigabe paperless mit force user = paperless; server min protocol = SMB2;
map to guest = Never; die dritte Zeile bleibt leer (sonst steht das Include zu früh, Stopp).
3.4 Schreibtest vom Laptop (Passwort = das des Samba-Benutzers scanner, wie im Quick Set „Ablage"):
echo test > /tmp/smbtest.txt
smbclient //10.10.10.3/paperless -U scanner -c 'put /tmp/smbtest.txt smbtest.txt; ls'
Dann [nuc-root]:
ls -ln /mnt/ssd/container-data/paperless/consume/robin/
rm /mnt/ssd/container-data/paperless/consume/robin/smbtest.txt
Erwartet: smbtest.txt gehört 977 988, Rechte -rw-rw----. Die Datei danach löschen, falls sie noch da ist.
Meist ist sie schon weg: Der alte Stack läuft noch und liest denselben Consume-Ordner rekursiv mit, die Datei
wird sofort zum Dokument. Das ist der bessere Beweis, denn der Ordner ist 0770 paperless, und scanner hätte
ohne force user nicht hineinschreiben können. Die alte Instanz hat dann zwei Dokumente statt einem (siehe 4.3).
◆ Melde die testparm-Zeilen und die ls -ln-Zeile.
Phase 4 · Alten Stack ersetzen [nuc-root] (20 min)¶
4.1 Repo holen, Rechte prüfen.
cd /opt/rpi-docker-compose && git pull --ff-only
find /opt/rpi-docker-compose -type f ! -perm -o=r
ls paperless-ngx/
Erwartet: nur traefik/.env und ts3/.env; in paperless-ngx/ liegen docker-compose.yaml, docs.md,
host-config/, scripts/, seed/, keine paperless.env mehr.
4.2 Alten Stack stoppen.
docker compose -f /opt/rpi-docker-compose/paperless-ngx/docker-compose.yaml down
docker ps -a --filter name=paperless --filter name=gotenberg --filter name=tika
Erwartet: keine Container in der Liste.
4.3 Aufräumen: Sicherheitsblick, dann leeren. Das ist der destruktive Schritt. Erst schauen, was drin ist:
find /mnt/paperless/media -type f | wc -l
find /mnt/paperless/media -type f | head -5
du -sh /mnt/ssd/container-data/paperless/db /mnt/ssd/container-data/paperless/data
Erwartet: die Dateien des Testdokuments und der smbtest.txt aus 3.4 (je Original, Archiv, Thumbnail),
kleine db/data. Stopp, wenn hier mehr liegt, als du erwartest. Erst dann:
for d in /mnt/ssd/container-data/paperless/db /mnt/ssd/container-data/paperless/redis \
/mnt/ssd/container-data/paperless/data /mnt/paperless/media /mnt/paperless/export /mnt/paperless/trash; do
find "$d" -mindepth 1 -delete
done
find /mnt/paperless /mnt/ssd/container-data/paperless -mindepth 2 | wc -l
Erwartet: letzte Zeile 1 (nur consume/robin). Die Verzeichnisse selbst bleiben stehen, consume/ und
protonmail-bridge/ unberührt, /mnt/scanner nicht im Spiel.
4.4 Starten.
docker compose -f /opt/rpi-docker-compose/paperless-ngx/docker-compose.yaml config --quiet && echo OK
docker compose -f /opt/rpi-docker-compose/paperless-ngx/docker-compose.yaml up -d
sleep 90
docker ps --filter name=paperless --filter name=gotenberg --filter name=tika --filter name=protonmail --format '{{.Names}}\t{{.Status}}'
Erwartet: paperless_ngx, paperless_db, paperless_redis mit (healthy); gotenberg, tika Up;
protonmail-bridge Up (healthy) (noch ohne Konto). Kein protonmail-bridge-init.
Stopp, wenn paperless_ngx nicht healthy wird: docker logs paperless_ngx --tail 50.
4.5 Startlog auf v3-Warnungen.
Erwartet: keine Zeile zu entfernten oder veralteten Einstellungen.
◆ Melde die docker ps-Tabelle und die Warnungszeilen (falls welche).
Phase 5 · Erster Login (10 min)¶
5.1 Superuser [nuc], Passwort in den Passwortmanager:
Benutzername robin.
5.2 Login unter https://paperless.home.robinwerner.net, ohne 403. Einstellungen → Sprache Deutsch.
5.3 API-Token: Mein Profil → API-Auth-Token erzeugen, am Laptop in ~/.paperless-token ablegen
(chmod 600). Der Token bleibt bei dir.
5.4 Anwendungskonfiguration: Einstellungen → Anwendungskonfiguration → Abschnitt KI: alle Felder leer lassen. Ein Wert dort schlägt die Compose-Datei.
◆ Melde: Login ohne 403 ja/nein.
Phase 6 · Startschema einspielen (Laptop, 10 min)¶
Voraussetzung: 0.2 ist gemergt und ~/repos/homeserver auf main aktuell.
cd ~/repos/homeserver && git pull --ff-only
PAPERLESS_URL=https://paperless.home.robinwerner.net PAPERLESS_TOKEN="$(cat ~/.paperless-token)" \
uv run paperless-ngx/seed/seed.py --dry-run
Erwartet: je Abschnitt „0 vorhanden, N fehlen" mit den Namen aus dem Schema. Dann ohne --dry-run ausführen,
und ein zweites Mal:
Erwartet: erster Lauf legt 7 + 14 + 9 + deine Korrespondenten an; zweiter Lauf meldet überall „0 fehlen" und
0 Objekte angelegt. Ein HTTP 400 bei storage_paths wäre neu (die Vorlagen nutzen asn | int): Server-Body aus
der Skriptausgabe an Claude.
Sichtprüfung in der UI: Tags (Inbox mit Inbox-Häkchen), Dokumenttypen, Speicherpfade. Bei einem Speicherpfad
die Vorschau öffnen: sie muss robin/Arbeit/…/… ASN12345 zeigen.
◆ Melde die Ausgabe des zweiten Laufs und den Vorschau-Pfad.
Phase 7 · Workflows und Ansichten (UI, 15 min)¶
7.1 Einstellungen → Workflows:
| Nr. | Name | Auslöser | Aktion |
|---|---|---|---|
| 1 | Scan von Robin | Verarbeitung gestartet, Quelle Consume-Ordner, Pfadfilter */robin/* |
Zuweisung: Eigentümer robin, Tag Inbox |
| 2 | KI-Vorschläge anwenden | Dokument hinzugefügt, kein Filter | KI-Vorschläge anwenden: Titel ✔ Tags ✔ Korrespondent ✔ Dokumenttyp ✔ Speicherpfad ✔ Erstelldatum ✔ · Fehlende anlegen: nur Korrespondent · Vorhandene überschreiben: Titel und Erstelldatum |
Reihenfolge 1 vor 2, beide aktiv.
Die Schalter „Fehlende anlegen" und „Vorhandene überschreiben" gelten je Aktion, nicht je Feld. Workflow 2 besteht deshalb aus drei Aktionen „KI-Vorschläge anwenden": (a) Korrespondent mit „Fehlende anlegen", (b) Titel und Erstelldatum mit „Vorhandene überschreiben", (c) Tags, Dokumenttyp, Speicherpfad ohne Schalter. Im Quelltext von 3.1.3 fragt jede Aktion die KI eigenständig, es gibt keinen Zwischenspeicher: drei Anfragen je Dokument, im Hintergrund, der Import wartet nicht darauf. Ob das trägt, misst Phase 12; bei zu hoher Last auf zwei Aktionen zusammenziehen (Korrespondent getrennt, Rest mit „Überschreiben", dann überschreibt die KI auch Typ und Pfad aus dem klassischen Matching).
7.2 Dokumente → Filter → Ansicht speichern:
| Name | Filter | Anzeige |
|---|---|---|
| Inbox | Tag Inbox |
Seitenleiste, Dashboard |
| Handeln | Tag Handeln |
Seitenleiste, Dashboard |
| Steuer laufendes Jahr | Tag Steuer und Erstellt ab 01.01.<Jahr> |
Seitenleiste |
| Unsortiert | Speicherpfad ohne | Seitenleiste |
Phase 8 · Quick Set am HP 4302 (20 min)¶
8.1 EWS (IP des Druckers im Browser, PIN) → Settings → Control Panel Customization → Quick Sets → + → Typ Scan to Network Folder, Name Paperless.
8.2 Ziel: Netzwerkpfad \\10.10.10.3\paperless, Benutzer scanner + Passwort. Apply and Test muss grün
sein. Stopp bei rot: Passwort, dann [nuc-root] journalctl -u smbd -n 20.
8.3 Datei-Einstellungen: Dateityp PDF, Farbe Farbe, Auflösung 300 dpi, Original beidseitig,
Dateinamen-Präfix scan.
8.4 Erster Scan: ein einseitiges Dokument mit ASN-Etikett. Direkt danach [nuc]:
ls -ln /mnt/ssd/container-data/paperless/consume/robin/
sleep 60; ls -ln /mnt/ssd/container-data/paperless/consume/robin/
docker logs paperless_ngx --since 5m 2>&1 | grep -iE 'consum|asn|barcode|error' | tail -20
Erwartet: Datei taucht auf und verschwindet nach dem Import; in Paperless steht das Dokument in der Inbox mit ASN, Eigentümer Robin, nach Workflow 2 mit Titel und Korrespondent. Typ und Speicherpfad bleiben am ersten Tag meist leer, weil die KI nur Kandidaten aus ähnlichen Dokumenten kennt und der Index noch leer ist (siehe 8.7). Auf der Platte:
Erwartet: robin/<Lebensbereich oder _Unsortiert>/<Korrespondent>/2026/… ASN00042.pdf. Stopp, wenn dort
eine 0000001.pdf-artige Datei im Wurzelverzeichnis liegt (Vorlage rendert nicht).
8.5 Stapeltest: drei Dokumente mit je einem Etikett auf Seite 1 in einem Durchgang. Erwartet: drei Dokumente, drei ASN, richtige Seiten.
8.6 Nur falls kein ASN erkannt wird: Claude ergänzt per PR PAPERLESS_CONSUMER_BARCODE_DPI: "600" und
PAPERLESS_CONSUMER_BARCODE_UPSCALE: "1.5", du machst git pull und up -d --force-recreate paperless_ngx.
8.7 Bootstrap der KI. Das Modell sieht nie deine Liste der Speicherpfade, Typen und Tags, nur Kandidaten aus
ähnlichen Dokumenten im Embedding-Index. Der ist am ersten Tag leer, deshalb landet alles in _Unsortiert. Ordne die
ersten zehn bis zwanzig Dokumente in der Inbox von Hand ein (Speicherpfad, Typ, Tags, Inbox-Tag entfernen).
Kandidaten liest Paperless live aus der Datenbank der ähnlichen Dokumente, ein Dokument muss dafür nur im Index
sein. Der Index wird nachts um 03:30 ergänzt; Dokumente, die seither dazugekommen sind, holst du sofort hinein:
Voraussetzung auf dem aiserver: embed-llamacpp läuft mit --ubatch-size 2048 (aiserver#18). Vorher scheiterte
der Lauf mit input (959 tokens) is too large to process … batch size: 512, weil Paperless 1024-Token-Abschnitte
schickt und llama-server jede Eingabe in einem physischen Batch einbettet.
(rebuild statt update baut alles neu und bettet jedes Dokument erneut ein, nur nötig, wenn der Index kaputt ist.)
Ab dann bekommen ähnliche neue Dokumente deine Pfade, Typen und Tags als Kandidaten. Die Abnahme in Phase 12 zählt
deshalb den zweiten Stapel.
◆ Melde die Log-Zeilen und den ls -R-Baum.
Phase 9 · Mail über Proton Bridge (30 min)¶
9.1 Proton vorbereiten: Label Paperless anlegen (Seitenleiste → Labels → +). Für regelmäßige Post:
Einstellungen → Filter → Filter hinzufügen, Bedingung Absender enthält <Domain>, Aktion Label Paperless,
kein Verschieben. Postfachgröße notieren (Konto → Speicher), Bridge cached alles lokal auf der SSD.
9.2 Bridge anmelden [nuc]. Erst den Run-Container stoppen (Single-Instance-Lock), dann interaktiv, 2FA
bereithalten:
docker compose -f /opt/rpi-docker-compose/paperless-ngx/docker-compose.yaml stop protonmail-bridge
docker compose -f /opt/rpi-docker-compose/paperless-ngx/docker-compose.yaml --profile init run --rm protonmail-bridge-init
Im Bridge-CLI: login → Adresse, Passwort, 2FA → nach „Account … was added" info → Bridge-Passwort in den
Passwortmanager → exit. Dann:
docker compose -f /opt/rpi-docker-compose/paperless-ngx/docker-compose.yaml up -d protonmail-bridge
sleep 60
docker ps --filter name=protonmail-bridge --format '{{.Names}}\t{{.Status}}'
docker logs protonmail-bridge --since 2m 2>&1 | grep -iE 'sync|error|logged' | tail -10
docker exec paperless_ngx python3 -c "import socket; s=socket.create_connection(('protonmail-bridge',1243),5); print(s.recv(200)); s.close()"
Erwartet: Up … (healthy), Synchronisation läuft, keine Fehler; die letzte Zeile beginnt mit b'* OK und
enthält Proton Mail Bridge.
9.3 Paperless, Einstellungen → E-Mail:
- Konto „Proton (Bridge)": IMAP-Server
protonmail-bridge, Port1243. Nicht die Werte ausinfoübernehmen:127.0.0.1und1143gelten nur innerhalb des Bridge-Containers; aus dem Paperless-Container heißt der Serverprotonmail-bridge, und nur die socat-Weiterleitung auf1243antwortet, alles andere endet inConnection refused. Sicherheit Keine, Benutzername = Proton-Adresse, Passwort = Bridge-Passwort. Verbindung testen → grün. - Regel „Label Paperless → Anhänge": Ordner
Labels/Paperless, Maximales Alter0, Anhangstyp Nur Anhänge, Dateinamensfilter*.pdf, Umfang Nur Anhänge; Titel aus Betreff, Korrespondent aus Absender (Name), TagsInbox+E-Mail, Eigentümerrobin; Aktion nach Verarbeitung „In angegebenen Ordner verschieben" mit ZielLabels/Paperless-erledigt(das Label vorher in Proton anlegen). Bei Bridge ist ein „Ordner" unterLabels/ein Label: Das Verschieben tauscht nur das Label „Paperless" gegen „Paperless-erledigt", die Mail bleibt im Posteingang. Eine Aktion „Nichts" gibt es nicht, und die Aktion legt fest, welche Mails Paperless holt: „Als gelesen markieren" (Standard) nur ungelesene, „Markieren" nur Mails ohne Stern (Robin nutzt Sterne), „Verschieben" alle. Maximales Alter0heißt unbegrenzt; der Standard 30 Tage ließe ältere Mails liegen. Mails ohne passenden Anhang bleiben unter „Paperless" stehen und werden alle 10 Minuten erneut geprüft.
9.4 Test: einer Mail mit PDF-Anhang das Label geben, Jetzt abrufen oder 10 min warten. Erwartet: Dokument
in der Inbox mit Tag E-Mail, Eintrag unter E-Mail → Verarbeitete E-Mails, Mail liegt weiter im Posteingang.
◆ Melde docker ps-Zeile, IMAP-Banner und ob die Testmail ankam.
Phase 10 · Timer, Alarme, Kuma [nuc-root] (15 min)¶
10.1 Units installieren.
cp /opt/rpi-docker-compose/paperless-ngx/host-config/paperless-*.service /opt/rpi-docker-compose/paperless-ngx/host-config/paperless-*.timer /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now paperless-export.timer paperless-consume-collector.timer
systemctl list-timers 'paperless-*'
Erwartet: beide Timer mit nächstem Lauf (Export 01:15, Collector in ≤ 15 min).
10.2 Collector einmal von Hand, Wirkung prüfen.
systemctl start paperless-consume-collector.service
cat /mnt/ssd/container-data/monitoring-stack/textfile/paperless.prom
docker exec prometheus wget -qO- 'http://localhost:9090/api/v1/query?query=paperless_consume_collector_timestamp_seconds' | head -c 300
Erwartet: drei Gauges in der Datei; Prometheus antwortet mit "result":[{ und einem Wert. Stopp, wenn die
Datei fehlt: journalctl -u paperless-consume-collector -n 20 (dann fehlt meist das Gruppenrecht).
10.3 Export einmal von Hand.
systemctl start paperless-export.service
ls /mnt/paperless/export
ls /mnt/paperless/export/json | head -3
Erwartet: archive json manifest.json originals thumbnails, je Dokument eine JSON-Datei.
10.4 Alarmregeln laden (sind mit dem git pull aus 4.1 schon da):
docker exec prometheus wget -qO- --post-data='' http://localhost:9090/-/reload; echo "exit=$?"
docker exec prometheus wget -qO- http://localhost:9090/api/v1/rules | grep -o 'PaperlessConsume[A-Za-z]*' | sort -u
Erwartet: exit=0, PaperlessConsumeCollectorStale und PaperlessConsumeStuck.
10.5 Gegenprobe der Metrik (kein Dotfile, die zählt der Collector nicht):
touch -d '-45 minutes' /mnt/ssd/container-data/paperless/consume/robin/alerttest.txt
systemctl start paperless-consume-collector.service
docker exec prometheus wget -qO- 'http://localhost:9090/api/v1/query?query=paperless_consume_stale_files' | grep -o '"value":\[[^]]*\]'
rm /mnt/ssd/container-data/paperless/consume/robin/alerttest.txt
systemctl start paperless-consume-collector.service
Erwartet: erst "1", nach dem Löschen "0". Zügig löschen, sonst versucht Paperless die Datei zu konsumieren.
10.6 Kuma (VPS-UI): Neuer Monitor → HTTP(s), Name „Paperless", URL https://paperless.home.robinwerner.net/,
Intervall 60 s, Status 200.
◆ Melde list-timers, den Inhalt von paperless.prom und die Regelnamen.
Phase 11 · Nachtlauf-Nachweis (am Folgetag) [nuc-root]¶
/root/.local/bin/borgmatic list --archive latest --path mnt/paperless/export | head -5
/root/.local/bin/borgmatic list --archive latest --path var/lib/db-dumps | grep -i paperless
journalctl -u paperless-export.service --since yesterday | tail -3
Erwartet: Export-Pfade im Archiv, postgres-paperless_db.sql.gz im Dump-Verzeichnis, Export-Lauf 01:15
„Finished". (--path, nicht --find: das liefert falsche Nullen.)
Phase 12 · Modell-Turnier und Abnahme (eine Session mit Claude)¶
12.0 Last der drei KI-Aktionen (Robins Auftrag 22.09.; je Aktion zwei Anfragen, Klassifikation plus Übersetzung
nach de-de, also sechs je Dokument): Beim Stapel aus 12.2 die Zeit vom Import bis zum
fertig getaggten Dokument messen und die Länge der Warteschlange beobachten. Das rechnet
paperless-ngx/scripts/ai_load_report.py aus der Aufgaben-API aus (je Dokument Import→letzte KI-Aktion, Zahl
der KI-Aufrufe, Fehlschläge, offene; dazu Median, Maximum, Sekunden je Aufruf):
PAPERLESS_URL=https://paperless.home.robinwerner.net PAPERLESS_TOKEN="$(cat ~/.paperless-token)" \
uv run paperless-ngx/scripts/ai_load_report.py --hours 6
``` Auf dem aiserver parallel
`docker logs llama-swap --since 30m | grep -c brain` für die Zahl der Anfragen und die Zeit je Anfrage. Läuft die
Inbox dem Stapel um mehr als etwa eine Stunde hinterher oder blockiert brain die anderen Rollen dauerhaft, wird
Workflow 2 auf zwei Aktionen zusammengezogen (siehe 7.1) oder das Modell gewechselt.
**12.1 Turnier:** fünf **von Hand eingeordnete** Dokumente (Rechnung, Schreiben, Bescheid, Vertrag, Zeugnis oder
Befund), die Dokument-IDs stehen in der URL (`/documents/<id>/`). `paperless-ngx/scripts/ai_duel.py` setzt je Modell
`llm_model` über die Konfigurations-API, holt die Vorschläge, zählt Treffer bei Korrespondent, Typ und Speicherpfad
gegen die eingeordneten Werte, misst die Zeit, druckt die Titel nebeneinander und setzt das Feld am Ende zurück:
```bash
PAPERLESS_URL=https://paperless.home.robinwerner.net PAPERLESS_TOKEN="$(cat ~/.paperless-token)" \
uv run paperless-ngx/scripts/ai_duel.py --docs <id1>,<id2>,<id3>,<id4>,<id5>
Titel bewertet Robin von Hand aus der Ausgabe. Verliert brain, tauscht Claude das Modell per PR.
Erledigt 27.09.: brain 3/4/5 Treffer bei 60 s, coder 2/4/4 bei 14 s, translator 4/4/5 bei 56 s; die 60 s waren
Denktext. Neue Rolle paperless-llamacpp (brain ohne Denken, aiserver#29): 3/4/4 bei 9 s → Paperless nutzt sie
(homeserver#252). 12.0 damit gelöst: rund 40 s je Dokument für sechs Anfragen, drei Aktionen bleiben. Bei
handeingeordneten Dokumenten zählt der Titel-Vergleich nicht, weil der Dateiname den Titel trägt. Herausforderer (Stand 24.09.): coder-llamacpp (qwen3-coder, Tool-Calls erprobt, 93 t/s) und
translator-llamacpp (gemma4-26B, stark im Deutschen). Nicht json-llamacpp: seit Testrunde 3 ist das NuExtract3,
ein Bild-zu-Felder-Modell ohne Chat. Paperless fragt über openai-like per Tool-Call ab (tool_required),
ein Kandidat muss also Tool-Calls über --jinja sauber beantworten, sonst kommt gar kein Vorschlag.
Zwischenspeicher: Paperless hält Vorschläge je Dokument 50 Minuten vor, aber getrennt je Modell (Schlüssel aus
Modell, Endpunkt, Sprache, Benutzer). Ein Modellwechsel liefert also frische Antworten, ein zweiter Klick beim
selben Modell dagegen die gespeicherte in Nullzeit. Je Dokument und Modell zählt nur der erste Klick.
Zeitfenster: 12.0 und 12.1 nur messen, wenn auf dem aiserver keine großen Testrunde-3-Stichproben laufen
(Qwen3.8-Flash-Next, Ling); die lassen neben sich weder die docs-Gruppe noch brain zu, und Paperless' Timeout
von 300 s würde die Messung verfälschen. Wechselt Testrunde 3 später das Brain-Modell, braucht Paperless keine
Änderung (Alias brain-llamacpp), nur 12.1 wird wiederholt.
12.2 Abnahme (Spec Abschnitt 11):
- 20 echte Dokumente als Stapel: Trennung richtig, jede ASN gesetzt, ≥ 15 ohne Handkorrektur richtig in Korrespondent, Typ, Speicherpfad. Das ist der zweite Stapel nach dem Bootstrap aus 8.7
-
ls -R /mnt/paperless/media/documents/archive/robin/ | head -40zeigt den Ordnerbaum, nichts liegt im Wurzelverzeichnis vonarchive/ - OCR-Schwelle: je Dokument einen Betrag oder ein Aktenzeichen per Volltextsuche finden; ≥ 2 von 20 unbrauchbar → Vision-OCR nach E7b, sonst bleibt Tesseract
- eine Mail mit PDF-Anhang kam automatisch an
- Chat beantwortet eine Frage zu einem Dokument mit Quellenlink (Index nach 03:30 vorhanden)
-
paperless_consume_stale_fileshat Serien, Kuma grün, Export im Archiv (Phase 11)
Danach: Doku-Seite, Issue #190, #87 schließen, E7b-Issue anlegen, Log-Eintrag (Claude).
Neustart mit leerem Bestand (Robins Entscheid 28.09.)¶
Nach dem Experimentieren alle Dokumente löschen und frisch anfangen, ohne den Stack anzufassen:
- Dokumente → alle markieren → Löschen. Sie landen im Papierkorb und behalten dort ihre ASN.
- Papierkorb → alle endgültig löschen. Sonst wird ein neuer Scan mit derselben Etiketten-Nummer abgewiesen („ASN existiert bereits", sichtbar auf der Aufgaben-Seite).
- Mail-Dokumente kommen nicht von allein zurück: unter E-Mail → Verarbeitete E-Mails den Eintrag löschen und der Mail in Proton wieder das Label „Paperless" geben (statt „Paperless-erledigt").
- Aufgaben-Seite auf gescheiterte Importe prüfen; deren Datei liegt noch in
consume/robin/und muss von Hand weg, sonst feuert nach 30 MinutenPaperlessConsumeStuck. - Tags, Typen, Pfade und Korrespondenten bleiben. Nach den ersten zwanzig Einordnungen wieder
docker exec paperless_ngx document_llmindex update(8.7).
Der Export-Timer räumt gelöschte Dokumente beim nächsten Lauf aus /mnt/paperless/export (--delete).
Wenn etwas schiefgeht¶
- Phase 4 bricht ab: Es gibt nichts zu verlieren, der alte Bestand war ein Testdokument.
docker compose down, Ursache mit Claude klären, Phase 4.4 wiederholen. - Postgres meldet Passwortfehler:
POSTGRES_PASSWORDgreift nur bei leerem Datadir. Prüfen, dass 4.3 dasdb-Verzeichnis wirklich geleert hat. - Login 403:
PAPERLESS_TRUSTED_PROXIESsteht auf10.100.;docker network inspect proxy_networkzeigt, ob Traefik wirklich aus diesem Netz kommt. - Scan bleibt im Consume-Ordner liegen: Eigentümer der Datei prüfen (
ls -ln, muss977 988sein), danndocker logs paperless_ngx. Ab Phase 10 meldet das die Alarmregel nach 30 Minuten. - Index-Lauf meldet
input (N tokens) is too large to process: Das ist llama-server auf dem aiserver, nicht Paperless.embed-llamacppbraucht--ubatch-sizemindestens so groß wie der längste Abschnitt (2048 = Kontext, aiserver#18); nachgit pullauf dem aiserverdocker compose -f /opt/aiserver/llama-swap/docker-compose.yaml restart llama-swap, dann den Index-Lauf wiederholen. - Mail: „Verbindung zum Mailserver nicht möglich": meist läuft der Bridge-Run-Container nicht (nach einem
info-Abstecher über das Init-Profil vergessen).docker ps --filter name=protonmail-bridge, dannup -d protonmail-bridge; danach der IMAP-Banner-Test aus 9.2.