- Python 81%
- Shell 19%
- Voucher-zentrisches Modell statt postings/transactions (Lexware ist dokumentenzentriert; categoryId + taxRatePercentage statt SKR-Konto) - LexClient: Bearer Token, base https://api.lexware.io/v1, 2 req/s rate-limit - 17 MCP-Tools: list_open_vouchers, get_voucher, propose_voucher, commit_voucher, attach_file_to_voucher, search_similar (RAG) etc. - Schema: historical_vouchers, category_catalog, contact_category_mapping - Erhalten: tmux-Launcher (Sonnet), Controller-Session (Opus), Watchdog, Cron-Setup, dry_run-Hook, RAG mit 3 Backends, AGPL-3.0, Logging |
||
|---|---|---|
| .claude | ||
| knowledge | ||
| lex_mcp | ||
| scripts | ||
| .env.example | ||
| .gitignore | ||
| .mcp.json | ||
| CLAUDE.md | ||
| config.yml | ||
| CONTROLLER.md | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
Lex-Agent — Lexware Office Auto-Kontierung
Ein lokal laufender Agent, der offene Bankumsätze und Belege in Lexware Office (Lex) autonom kontiert und verbucht. Mitarbeiter pflegen Belege/Umsätze weiterhin manuell ein — der Agent übernimmt nur die Buchungslogik.
Sicherheitsmodell: Vollautonom mit Schwellenwerten — sichere Fälle direkt verbuchen, unklare in eine lokale Review-Queue, niedrige Konfidenz wird eskaliert. Erste 1-2 Wochen läuft alles im DRY_RUN-Modus (keine Lex-Schreibvorgänge), damit du die Vorschläge vor der Aktivierung kalibrieren kannst.
Wie es funktioniert
Cron (alle 15min) ─→ Trigger-Datei ──┐
▼
tmux-Session mit Claude Code (im /loop)
│
┌──────────────┴──────────────┐
▼ ▼
lex_mcp Tools Wissensbasis
├─ list/get/search ├─ categoryIds
├─ propose (review-queue) ├─ USt / Bewirtungen
└─ commit (Lex-API) └─ Eigene Regeln
│
▼
Lexware Office API
(HTTPS + Bearer Token)
Pro Tick (Verarbeitungsrunde):
- Offene Vouchers + unverbuchte Files holen
- Contact-Historie und Wissensbasis konsultieren
- categoryId + taxRatePercentage vorschlagen (statt SKR-Konto)
- Konfidenz schätzen + validieren (categoryId existiert, Periode offen, Idempotenz, Caps)
- Verzweigung: ≥0.90 commit · 0.60–0.90 review-queue · <0.60 escalate
- Audit-Log + Heartbeat
Mental Model: Lexware Office ist dokumentenzentriert. Die API hat keine
/postings- oder/transactions-Endpoints — stattdessen weist du Vouchers (Belegobjekten) einecategoryIdundtaxRatePercentagezu. DiecategoryIdersetzt die SKR-Kontonummer.
Voraussetzungen
| OS | macOS (Apple Silicon getestet); Linux mit minimalen Anpassungen |
| Tools | Homebrew, uv, tmux, Claude Code |
| Account | Claude.ai-Subscription mit Claude Code |
| Lex-Zugang | Lexware Office ab aktivierter Public-API (API verfügbar). API-Credentials: API-Token — generieren unter https://app.lexware.de/addons/public-api |
Installation
# 1) Voraussetzungen (macOS)
brew install uv tmux
# 2) Repo klonen
git clone https://git.mngd.io/patrick/lex-agent.git ~/projects/lex-agent
cd ~/projects/lex-agent
# 3) Abhängigkeiten
uv sync
Konfiguration
.env mit Lexware-Token
cp .env.example .env
# .env in einem Editor öffnen und ausfüllen:
# LEX_API_TOKEN=<aus https://app.lexware.de/addons/public-api>
# MANDANT_NAME=meine_gmbh # nur fürs Logging
.env ist in .gitignore — wird nie committet.
config.yml: Schwellenwerte und Sicherheitsgeländer
thresholds:
auto_post_confidence: 0.90 # ≥ → direkt verbuchen
review_confidence: 0.60 # < → eskalieren
guards:
max_amount_auto_post: 5000 # Cap deckelt Konfidenz auf 0.85
blocked_accounts: ["1200","1210","1800"] # Bank/Kasse — nie hier buchen
require_receipt_for_expenses: true # keine Aufwandsbuchung ohne Beleg
hard_max_postings_per_hour: 50 # Notbremse
max_posting_age_days: 90 # ältere Buchungen → abgelehnt
dry_run: true # solange true: keine Lex-Writes
Konfidenz-Bänder
| Konfidenz | Verhalten |
|---|---|
≥ auto_post_confidence |
direkt verbuchen (nur wenn dry_run: false) |
| zwischen den Schwellen | Vorschlag in review_queue — manuell via /lex-review |
< review_confidence |
Eskalation in errors, kein Vorschlag |
Konfidenz-Berechnung
LLM-Selbsteinschätzung + Validator-Anpassungen:
| Bedingung | Δ |
|---|---|
| Contact bekannt (count > 5) und Betrag in ±2σ | +0.10 |
| Contact unbekannt | −0.20 |
| Betrag > 2σ vom Contact-Mittel | −0.20 |
| Beschreibung widerspricht historischer Kategorie | −0.30 |
Betrag > max_amount_auto_post |
Cap auf 0.85 (erzwingt Review) |
Tuning-Hinweise
| Setting | Default | Wann anders setzen |
|---|---|---|
auto_post_confidence |
0.90 |
Pilot mit 0.95 starten, später Richtung 0.85 wenn High-Confidence-Vorschläge sauber sind |
review_confidence |
0.60 |
auf 0.50 wenn viele Vorschläge bei 0.50–0.65 hängen |
max_amount_auto_post |
5000 |
im Pilot bei ~500 starten, schrittweise hochziehen |
blocked_categories |
leer | nach erstem /lex-sync ggf. interne Verrechnungs-Kategorien sperren |
hard_max_postings_per_hour |
50 |
bei sehr hohem Voucher-Volumen hochziehen |
max_voucher_age_days |
90 |
730 bei aktiven Vorjahres-Korrekturen, 35 bei strenger Monats-Schließung |
dry_run und Aktivierungspfad
dry_run: true ist der sichere Default — alle Vorschläge gehen in review_queue, keine Lex-Writes. Der PreToolUse-Hook in Claude Code blockt lex_commit_voucher-Aufrufe direkt.
Pfad zur Aktivierung:
- Pilot mit
dry_run: true, Vorschläge täglich via/lex-reviewdurchsehen - Konfidenzen aus
audit_logkalibrieren max_amount_auto_postklein setzen (z.B.500)dry_run: false- Cap schrittweise hochziehen, sobald committete Buchungen seit ≥1 Woche fehlerfrei laufen
Erster Lauf
Empfohlen: ein einziger Befehl macht alles — Voraussetzungen prüfen, beim Erst-Run Voucher-Historie syncen + Kategorie-Katalog ableiten + (falls konfiguriert) Embeddings indexieren, tmux-Session aufmachen, Claude Code starten:
bash scripts/start.sh
Optionen:
| Flag | Wirkung |
|---|---|
--bypass-permissions |
Claude Code mit --dangerously-skip-permissions — Tools ohne Approval-Prompts. Alternativ via LEX_BYPASS_PERMISSIONS=true in .env persistieren. |
--attach |
Nur an bestehende tmux-Session attachen (kein Neustart). |
--session NAME |
tmux-Session-Name überschreiben (default lex-agent). |
--skip-first-run |
Initialisierungsschritte überspringen, falls die DB neu angelegt wurde aber bewusst leer bleiben soll. |
Manuell (wenn du nicht das Start-Skript nutzen willst):
uv run python -m lex_mcp.skr_detect # SKR-Profil + Kontenrahmen-Cache
uv run python scripts/sync_history.py --full # Historie (24 Monate)
tmux new -s lex-agent
claude --model claude-sonnet-4-6 # Worker im Projektverzeichnis
Lex-Controller — optionaler Supervisor
Zwei-Modell-Setup: der Worker (scripts/start.sh) macht die Kontierungs-Arbeit mit Sonnet (schnell, günstig, ausreichend für Routinefälle). Der Controller (scripts/controller.sh) ist ein paralleler Supervisor mit Opus 4.7, der den Worker beobachtet, Vorschläge auf Plausibilität prüft und bei Bedarf den Worker wiederbelebt oder steuert.
bash scripts/controller.sh # eigene tmux-Session "lex-controller"
bash scripts/controller.sh --bypass-permissions # für autonome Kontroll-Loops
Der Controller liest beim Start CONTROLLER.md und wartet dann auf Anweisungen oder /lex-control.
Was /lex-control macht (eine Kontroll-Runde):
- Health-Check: tmux-Session
lex-agentaktiv?data/status.jsonaktuell?claude-Prozess am Leben? - Plausibilität: Top-10 pending Vorschläge gegen
historical_vouchersundcategory_catalogcross-checken — passt das Konto zum Vendor/Belegtext? Halluzinierte Konten? - Diagnose: Muster-Erkennung (immer derselbe Fehler?
knowledge/regeln.mderweitern?) - Empfehlung (max. 6 Zeilen) + Vorschlag für eine nächste Aktion. Aktionen werden erst nach User-OK ausgeführt:
- Worker-Restart:
tmux kill-session -t lex-agent && bash scripts/start.sh - Tick erzwingen:
tmux send-keys -t lex-agent '/lex-tick' Enter - Klärfrage in den Worker tippen
- oder: nichts tun
- Worker-Restart:
Hard rules für den Controller (in CONTROLLER.md festgeschrieben):
- bucht nicht selbst (keine
lex_commit_voucher) - ändert keine Daten in
lex_state.db(nur SELECT) - führt Aktionen nur mit User-Bestätigung aus
Modell-Override:
# Worker mit anderem Modell
LEX_MODEL=claude-haiku-4-5-20251001 bash scripts/start.sh
# Controller mit anderem Modell
LEX_CONTROLLER_MODEL=claude-sonnet-4-6 bash scripts/controller.sh
In Claude Code:
/lex-tick # einen Tick manuell auslösen
/lex-status # Heartbeat + Queue-Status
/lex-review # Vorschläge interaktiv durchsehen + freigeben
/lex-sync # Historie aktualisieren
Im DRY_RUN-Modus landen alle Vorschläge in data/lex_state.db → Tabelle review_queue. Nichts wird in Lexware geschrieben.
Cron einrichten (Automatik)
bash scripts/install_cron.sh
Installiert drei Einträge in deiner User-Crontab:
*/15 * * * *→ Tick-Trigger0 */6 * * *→ History-Sync* * * * *→ Watchdog (lokales Monitoring + macOS-Notification bei Stillstand)
Die tmux-Session muss laufen, damit der Agent die Tick-Dateien abarbeitet.
Logging und Audit
Der Agent loggt auf drei Ebenen — alles lokal, alles in data/:
1. data/logs/agent.log — Python-Anwendungs-Log
Was du hier findest: jeder Lex-API-Call (Pfad, Status, Dauer; niemals Bodies/Secrets), jedes lex_propose_voucher/lex_commit_voucher/lex_escalate mit Konto, Betrag und Konfidenz. Rotierend (5 × 5 MB).
tail -f data/logs/agent.log # live mitlesen
grep ERROR data/logs/agent.log # nur Fehler
grep COMMIT data/logs/agent.log # nur echte Verbuchungen
Log-Level steuern: LEX_LOG_LEVEL=DEBUG uv run python … für mehr Details.
2. data/logs/{tick,sync,watchdog}.log — Cron-Logs
Stdout/stderr der Cron-Jobs:
tick.log— Trigger-Generierung (jede 15min eine Zeile)sync.log— Historie-Sync (alle 6h)watchdog.log— Watchdog-Output
3. SQLite — data/lex_state.db
Strukturierte Aufzeichnung der Agent-Entscheidungen:
# Alle Aktionen heute
sqlite3 data/lex_state.db "SELECT ts, action, ref_type, ref_id, category_id, amount, confidence
FROM audit_log WHERE ts > datetime('now','start of day') ORDER BY ts DESC"
# Eskalierte Fälle der letzten 7 Tage
sqlite3 data/lex_state.db "SELECT ts, ref_id, message FROM errors
WHERE ts > datetime('now','-7 days')"
# Pending Review-Queue mit Konfidenz absteigend
sqlite3 data/lex_state.db "SELECT id, ref_type, ref_id, confidence, reason
FROM review_queue WHERE status='pending' ORDER BY confidence DESC"
Schneller Status-Überblick:
uv run python scripts/status_cli.py
Was wird nicht geloggt
.env-Werte, API-Keys, HTTP-Bodies- Belegrohdaten (die bleiben in Lexware)
- Inhalte der Markdown-Wissensbasis
Pilot-Phase und Aktivierung
- Woche 1-2:
dry_run: true, alle Vorschläge täglich via/lex-reviewdurchsehen. Konfidenzen tunen. - Aktivierung:
dry_run: falsesetzen,max_amount_auto_post: 500als Anfangs-Cap. - Schrittweise hochziehen: Cap erhöhen, sobald die committeten Buchungen seit 1 Woche fehlerfrei laufen.
Sicherheit
- Belege bleiben in Lexware — werden nie ins lokale Repo gespiegelt.
- GoBD-Trail: jede Aktion trägt eine
justification(1-3 Sätze) im lokalenaudit_log. Lexware Office hat keinen offenen Kommentar-Endpoint pro Voucher — dieremarkim Voucher selbst kann optional erweitert werden. - Idempotenz: SHA-256-Hash aus
(voucher_id, category_id, amount, date)verhindert Doppel-Verbuchungen. - Notbremse:
hard_max_postings_per_hourstoppt den Agenten, falls er amok läuft. - PreToolUse-Hook: blockt schreibende Tools im DRY_RUN-Modus auf Claude-Code-Ebene.
- Tool-Scope: Agent darf nur
lex_mcp__*-Tools nutzen — andere globale MCP-Server (n8n, Gmail, Drive etc.) sind in.claude/settings.jsondeny-gelistet.
Semantische Suche (RAG, optional)
Die statistische Contact-Kategorie-Map (contact_category_mapping) reicht für wiederkehrende Lieferanten/Kunden. Für Schreibvarianten, neue Contacts mit ähnlichem Pattern und ungewöhnliche Kategorien-Mappings macht ein semantisches Retrieval mehr Sinn — der Agent ruft dafür das Tool lex_search_similar(query, k=5) auf, das die Top-k ähnlichsten historischen Vouchers via Cosine-Similarity über Embeddings liefert.
Drei Backends, alle optional, alle in .env umschaltbar:
| Backend | Setup | Modell (Default) | Dim | Setup-Größe | Pro Anfrage | Privatsphäre |
|---|---|---|---|---|---|---|
voyage |
API-Key | voyage-3-lite |
512 | — | API-Roundtrip ~150 ms | Texte verlassen den Host |
openai |
API-Key | text-embedding-3-small |
1536 | — | API-Roundtrip ~200 ms | Texte verlassen den Host |
local |
uv sync --group local |
intfloat/multilingual-e5-small |
384 | ~470 MB (einmalig) | ~30 ms (CPU/M1) | komplett offline |
Anthropic hat keine eigene Embeddings-API. Die offiziell empfohlene Option für die "Anthropic-Welt" ist Voyage AI (Anthropic-Partner) → Backend voyage.
Aktivierung
# In .env eines wählen:
LEX_EMBEDDING_BACKEND=local # oder: voyage / openai
# bei voyage: VOYAGE_API_KEY=...
# bei openai: OPENAI_API_KEY=...
# Modell-Override optional via LEX_EMBEDDING_MODEL=...
# Bei local einmalig:
uv sync --group local
# Index aufbauen:
uv run python scripts/reindex_embeddings.py # nur neue/geänderte
uv run python scripts/reindex_embeddings.py --force # alles neu (z.B. nach Backend-Wechsel)
Beim regulären scripts/sync_history.py werden neue Vouchers automatisch mit-indexiert.
Was wird embedded? Pro Voucher der Text "{contact_name} | {voucher_type} | cat:{category_id} | <voucher-Felder wie remark/title>". Anpassbar in lex_mcp/embeddings_index.py:voucher_embed_text.
Wann hilft RAG? Erst ab ~100+ historischen Vouchers mit unterschiedlichen Pattern.
Wissensbasis pflegen
knowledge/categories.md— Pflicht zu pflegen: Klartext-Mapping von Lexware-categoryIds zu Bedeutung. Nach erstem/lex-syncfüllt der Agent IDs incategory_catalog; die Klartext-Namen ergänzt du hier.knowledge/regeln.md— eigene Buchungsregeln, z.B.:
- Notion, Linear, GitHub → categoryId <UUID> "Bürobedarf 19%"
- Bewirtung: separate Vouchers für 70%/30%-Anteile (siehe knowledge/bewirtungen.md)
Je besser dein Agent die Konventionen kennt, desto präziser werden seine Vorschläge.
Troubleshooting
| Symptom | Ursache | Fix |
|---|---|---|
tmux: command not found |
brew shellenv nicht im PATH | source ~/.zprofile oder neues Terminal |
400 Bad Request von Lexware |
API-Endpoint braucht zusätzliche Pflichtfelder | lex_mcp/server.py und scripts/sync_history.py prüfen |
dry_run is true — refusing to commit |
richtig so | erst nach Pilot dry_run: false setzen |
Lizenz
Lizenziert unter der GNU Affero General Public License v3.0 (AGPL-3.0) — siehe LICENSE.
Kurz: du darfst den Code frei nutzen, modifizieren und weitergeben. Wenn du modifizierte Versionen über ein Netzwerk anbietest (z.B. als gehosteten Service), musst du den Quellcode deiner Modifikationen unter derselben Lizenz veröffentlichen.
Beitragen
Pull Requests willkommen — das Repo lebt unter https://git.mngd.io/patrick/lex-agent.