Drei LLMs, ein Mahnlauf
Ein Python-Skript liest 40 offene Posten, beurteilt jeden Fall, erzwingt sauberes JSON und entwirft die Mahn-E-Mail — mit dem jeweils passenden Modell pro Schritt.
Die Frage, die fast jeder stellt
«Chatten können wir jetzt. Aber wie nutzen wir LLMs im Tagesgeschäft — ohne dass jemand den ganzen Tag Prompts tippt?»
Die Antwort ist unspektakulär: Man nimmt das Chat-Fenster weg. Ein LLM wird zum Arbeitswerkzeug, wenn es in einer Schleife steckt — ein Skript füttert ihm eine Datenzeile nach der anderen, prüft jede Antwort maschinell und schreibt das Ergebnis dorthin, wo die Arbeit weitergeht.
Genau das zeigt dieses Showcase an einem Prozess, den jedes KMU kennt: dem Mahnlauf.
Das Szenario
Die fiktive Muster Handwerk AG hat 40 offene Posten. Die Debitorenliste sieht aus, wie solche Listen wirklich aussehen: Notizen der Buchhaltung in Deutsch, Französisch und Englisch, Halbsätze, Grossbuchstaben, Widersprüche. Vier Beispiele:
| Rechnung | Kunde | Betrag | Tage überfällig | Notiz der Buchhaltung |
|---|---|---|---|---|
| 2026-1002 | Café Bellevue GmbH | CHF 1'350.00 | 34 | hat angerufen, zahlt Ende Monat. tel. bestätigt 08.07. |
| 2026-1003 | Menuiserie Perrin Sàrl | CHF 8'920.00 | 41 | attend le décompte final avant de payer – à clarifier avec chef de projet |
| 2026-1009 | Elektro Wyss AG | CHF 6'540.00 | 88 | Gerücht vom Lieferanten: Zahlungsschwierigkeiten?? nichts konkretes. VORSICHT mit Ton |
| 2026-1031 | Tech Solutions Ltd | CHF 21'430.00 | 42 | invoice stuck in their approval workflow, says 'processed next run' – 2nd time they say this |
Alle 40 Zeilen sind synthetisch — Firmen, Beträge und Notizen sind erfunden. Der komplette Datensatz liegt im Download.
Die Pipeline: drei Schritte, drei Modelle
Jede Zeile durchläuft drei Schritte. Für jeden Schritt ist ein anderes Modell zuständig — jeweils das, das für die Aufgabe reicht:
Claude Opus 4.8
Anthropic · $5 / $25 pro 1M TokensLiest die chaotische Notiz und wägt ab: Zahlungszusage? Streitfall? Teilzahlung? Das stärkste Modell sitzt dort, wo Urteilsvermögen gefragt ist.
GPT-5 mini
OpenAI · $0.25 / $2 pro 1M TokensPresst die Beurteilung in ein striktes JSON, das Software weiterverarbeiten kann. Formatarbeit — dafür reicht ein kleines, günstiges Modell.
Gemini 2.5 Flash
Google · $0.30 / $2.50 pro 1M TokensFormuliert den E-Mail-Entwurf in der Sprache des Kunden — Deutsch, Französisch oder Englisch, im Ton, den Schritt 1 vorgegeben hat.
Warum nicht einfach ein Modell für alles?
Ginge auch — die Aufteilung ist bewusst didaktisch. Sie zeigt zwei Dinge: Anbieter lassen sich pro Schritt austauschen (kein Lock-in), und das teure Modell arbeitet nur dort, wo es gebraucht wird. Bei 40 Posten ist das egal, bei 40'000 nicht mehr.
Der Code
Das komplette Skript hat gut 200 Zeilen, läuft mit drei API-Schlüsseln als Umgebungsvariablen und versendet nichts — E-Mails landen als Entwürfe auf der Festplatte. Die drei entscheidenden Stellen:
Der Prompt für Schritt 1 — Geschäftsregeln in Klartext
Die «Regeln des Betriebs» stehen direkt im Prompt. Das ist der Teil, den Sie ohne Programmierkenntnisse selbst anpassen können:
PROMPT_BEURTEILEN = """Du bist erfahrene:r Debitoren-Sachbearbeiter:in der Muster Handwerk AG,
einem Schweizer KMU. Beurteile diesen offenen Posten (Stichtag {stichtag}):
Rechnung {rechnung_nr} an {kunde_pseudo} über CHF {betrag_chf},
fällig seit {tage_ueberfaellig} Tagen, bisherige Mahnungen: {mahnstufe}.
Notiz aus der Buchhaltung (Originalton, evtl. DE/FR/EN): «{notizen}»
Empfiehl in 3–5 Sätzen das weitere Vorgehen. Regeln des Betriebs:
- Zahlungszusagen, Ratenvereinbarungen, Streitfälle und Teilzahlungen NICHT stur weitermahnen.
- Unklare Fälle (Insolvenzgerüchte, unzustellbare Post, Rechts-/Verrechnungsfragen) gehören zum Chef.
- Bei wichtigen Kunden bestimmt, aber beziehungsschonend formulieren.
- Erkenne die Sprache des Kunden (de/fr/en) aus Notiz und Firmenname."""Drei Anbieter, drei offizielle SDKs
Jeder Schritt ist ein kurzer Funktionsaufruf nach der offiziellen SDK-Dokumentation des jeweiligen Anbieters — inklusive Token-Zählung für den Kostenzähler:
def beurteilen(client, prompt: str):
"""Schritt 1 — Claude: liest den Fall und empfiehlt das Vorgehen."""
msg = client.messages.create(
model=MODELL_BEURTEILEN,
max_tokens=600,
thinking={"type": "adaptive"},
messages=[{"role": "user", "content": prompt}],
)
text = "".join(b.text for b in msg.content if b.type == "text")
return text, msg.usage.input_tokens, msg.usage.output_tokens
def strukturieren(client, prompt: str):
"""Schritt 2 — GPT-5 mini: erzwingt sauberes JSON."""
r = client.chat.completions.create(
model=MODELL_STRUKTUR,
response_format={"type": "json_object"},
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content, r.usage.prompt_tokens, r.usage.completion_tokens
def schreiben(client, prompt: str):
"""Schritt 3 — Gemini: formuliert den E-Mail-Entwurf."""
r = client.models.generate_content(model=MODELL_TEXT, contents=prompt)
u = r.usage_metadata
return r.text, u.prompt_token_count, u.candidates_token_countDie Schleife — prüfen, wiederholen, protokollieren
Das JSON aus Schritt 2 wird maschinell validiert (erlaubte Aktionen, Fristen, Sprachen). Ist es ungültig, bekommt das Modell genau einen zweiten Versuch mit der Fehlermeldung — danach bricht der Lauf ab, statt Müll zu produzieren:
for p in posten:
# Schritt 1: beurteilen
beurteilung, t_in, t_out = beurteilen(c_claude, PROMPT_BEURTEILEN.format(**p))
kosten_usd += (t_in * PREISE[MODELL_BEURTEILEN][0] + t_out * PREISE[MODELL_BEURTEILEN][1]) / 1e6
# Schritt 2: strukturieren (1 Wiederholung bei ungültigem JSON)
prompt2 = PROMPT_STRUKTUR.format(beurteilung=beurteilung, **p)
for versuch in (1, 2):
roh, t_in, t_out = strukturieren(c_openai, prompt2)
kosten_usd += (t_in * PREISE[MODELL_STRUKTUR][0] + t_out * PREISE[MODELL_STRUKTUR][1]) / 1e6
try:
entscheid = json_pruefen(roh)
break
except (ValueError, json.JSONDecodeError) as e:
if versuch == 2:
raise SystemExit(f"{p['rechnung_nr']}: JSON nach 2 Versuchen ungültig — {e}")
prompt2 += f"\n\nDeine letzte Antwort war ungültig ({e}). Korrigiere sie."
# Schritt 3: schreiben — nur wenn wirklich eine E-Mail raus soll
if entscheid["email_senden"] and entscheid["aktion"] not in ("zurueckstellen", "chef_fragen"):
entwurf, t_in, t_out = schreiben(c_gemini, PROMPT_TEXT.format(**entscheid, **p))
kosten_usd += (t_in * PREISE[MODELL_TEXT][0] + t_out * PREISE[MODELL_TEXT][1]) / 1e6
# Pseudonym erst jetzt, lokal, wieder durch den Klarnamen ersetzen
entwurf = entwurf.replace(p["kunde_pseudo"], p["kunde"])
with open(f"entwuerfe/{p['rechnung_nr']}.txt", "w", encoding="utf-8") as f:
f.write(entwurf)
ergebnisse.append({"rechnung_nr": p["rechnung_nr"], "kunde": p["kunde"],
"betrag_chf": p["betrag_chf"], "tage": p["tage_ueberfaellig"],
**{k: entscheid[k] for k in ("aktion", "sprache", "ton",
"frist_tage", "risiko", "grund")}})
print(f"{p['rechnung_nr']} {entscheid['aktion']:<16} {entscheid['risiko']:<7} {entscheid['grund']}")Der Testlauf: echte Ausgaben
Wir haben die Pipeline über alle 40 Posten laufen lassen. Ergebnis: 18× freundliche Erinnerung, 3× Mahnung, 1× letzte Mahnung — und 18 Fälle, die das System bewusst NICHT gemahnt hat, sondern zurückgestellt oder dem Chef vorgelegt. Genau diese Fälle sind der Punkt: Ein stures Mahnprogramm hätte der Kundin mit schriftlicher Ratenvereinbarung eine Mahnung geschickt.
| Fall | Notiz (Original) | Entscheid | Begründung des Modells |
|---|---|---|---|
| 2026-1002 CHF 1'350.00 · 34 Tage überfällig | hat angerufen, zahlt Ende Monat. tel. bestätigt 08.07. | Zurückstellen Risiko: mittel | Telefonische Zahlungszusage vom 08.07. liegt vor; Zusage abwarten, Wiedervorlage Anfang August. |
| 2026-1004 CHF 23'760.00 · 67 Tage überfällig | 2. Mahnung per Post 12.06. KEINE Reaktion. grosser Folgeauftrag offen!! | Zum Chef Risiko: hoch | 67 Tage überfällig, zwei Mahnungen unbeantwortet, grosser Folgeauftrag blockiert. Direkte telefonische Klärung nötig, bei Misserfolg Chef übergeben. |
| 2026-1007 CHF 4'870.00 · 51 Tage überfällig | Teilzahlung CHF 2'000 am 30.06. eingegangen, Rest offen. war ok für ihn per mail | Freundliche Erinnerung Risiko: tief | Teilzahlung und Zahlungszusage liegen vor; freundliche Erinnerung an Restbetrag von CHF 2'870.00. |
| 2026-1009 CHF 6'540.00 · 88 Tage überfällig | Gerücht vom Lieferanten: Zahlungsschwierigkeiten?? nichts konkretes. VORSICHT mit Ton | Zum Chef Risiko: hoch | Rechnung CHF 6'540.00 seit 88 Tagen überfällig (2. Mahnung). Gerücht über Zahlungsschwierigkeiten – Fall zur Klärung an GL weiterleiten. |
| 2026-1006 CHF 15'300.00 · 28 Tage überfällig | says PO number missing on invoice – resend with PO 4711, then payment 'immediately' | Zurückstellen Risiko: tief | Kunde beanstandet fehlende Bestellnummer, bittet um korrigierte Rechnung mit PO 4711 und sagt sofortige Zahlung zu. |
| 2026-1023 CHF 430.00 · 63 Tage überfällig | Kleinbetrag. 2 Mahnungen raus. lohnt sich Betreibung überhaupt? CHEF FRAGEN | Zum Chef Risiko: mittel | Kleinbetrag von CHF 430, zwei Mahnungen erfolglos. Chef entscheiden, ob Betreibung lohnt. |
22 E-Mail-Entwürfe, drei Sprachen
Zwei Beispiele aus dem Lauf — unverändert, wie das Modell sie abgelegt hat:
Betreff: 2. Mahnung – Rechnung 2026-1017 über CHF 640.00 Guten Tag Bereits am [Datum der ersten Mahnung] haben wir Sie an die offene Rechnung 2026-1017 über CHF 640.00 erinnert. Leider haben wir bis heute keinen Zahlungseingang verbuchen können. Die Rechnung ist nun seit 58 Tagen überfällig. Wir bitten Sie, den offenen Betrag von CHF 640.00 innert 10 Tagen auf unser Konto [IBAN] zu überweisen. Sollte die Zahlung bereits erfolgt sein, betrachten Sie diese Erinnerung als gegenstandslos. Besten Dank für Ihre geschätzte Bemühung. Freundliche Grüsse Muster Handwerk AG
Objet: Dernier rappel – Facture 2026-1026, CHF 27'890.00 Madame, Monsieur, Nous faisons suite à notre précédent rappel et à votre promesse de paiement faite il y a deux semaines, malheureusement non tenue à ce jour. La facture 2026-1026 d’un montant de CHF 27'890.00 est impayée depuis 47 jours. Nous vous accordons un ultime délai de 10 jours à compter de la réception de ce courrier. Passé ce délai, le dossier sera transmis à notre direction pour les suites nécessaires. Veuillez verser le montant sur notre compte [IBAN] en mentionnant la référence de la facture. Nous comptons sur votre règlement immédiat. Meilleures salutations, Muster Handwerk AG Musterstrasse 1, 2540 Grenchen
So wurden diese Ausgaben erzeugt
In unserer Entwicklungsumgebung liegen keine Schlüssel von Anthropic, OpenAI oder Google. Der Testlauf lief deshalb mit exakt denselben Prompts und derselben Validierung über einen einzigen OpenAI-kompatiblen Endpunkt (DeepSeek — dasselbe Modell, das den Chat auf dieser Website antreibt). Der veröffentlichte Code ruft die drei Anbieter gemäss deren offiziellen SDKs auf; die gezeigten Entscheide und Entwürfe sind echte, unbearbeitete Modell-Ausgaben aus diesem Testlauf.
Was kostet so ein Lauf?
Hochrechnung für die 40 Posten: gemessene Textmengen aus dem Testlauf, umgerechnet in Tokens (~3.6 Zeichen/Token) und multipliziert mit den Listenpreisen der drei Anbieter (Stand Juli 2026, USD pro 1 Mio. Tokens):
| Schritt | Preis in / out | Tokens in / out (40 Posten) | Kosten |
|---|---|---|---|
| Beurteilen Claude Opus 4.8 | 5.00 / 25.00 | ~8'342 / ~7'661 | ~$0.23 |
| Strukturieren GPT-5 mini | 0.25 / 2.00 | ~14'061 / ~2'702 | ~$0.01 |
| Schreiben Gemini 2.5 Flash | 0.30 / 2.50 | ~4'098 / ~3'206 | ~$0.01 |
Total pro Lauf: ~$0.25 — bei einem wöchentlichen Mahnlauf rund $1.09 pro Monat.
Das ist eine Projektion, keine Abrechnung: Tokenzahlen sind aus Zeichenmengen geschätzt, echte Provider-Rechnungen können abweichen. Die Grössenordnung ist der Punkt — der teuerste Posten im Mahnwesen ist nicht die KI, sondern die Stunde Handarbeit.
Sicherheit, Datenschutz, Grenzen
Klarnamen bleiben im Haus
Vor jedem API-Aufruf werden Kundennamen durch Pseudonyme ersetzt (KUNDE_07) und erst im fertigen Entwurf lokal wieder eingesetzt. Die Anbieter sehen Beträge und Notizen, aber keine Namen.
Nichts geht automatisch raus
Das Skript versendet keine einzige E-Mail — es schreibt Entwürfe. Und 18 von 40 Fällen hat es selbst als Chefsache oder Zurückstellung markiert, statt zu mahnen.
Schlüssel gehören nicht in den Code
API-Schlüssel kommen aus Umgebungsvariablen. Im veröffentlichten Code steht kein einziger Schlüssel — so gehört das.
Grenzen
Ein LLM kann eine Notiz falsch deuten. Deshalb: Entwürfe statt Versand, strikte JSON-Validierung, Sonderfälle zum Menschen. Automatisierung heisst hier: 80% Routine maschinell vorbereiten, 20% Urteil beim Menschen lassen.
Transparenz
- Kein Kundenprojekt — ein Lehrstück, gebaut für diese Website.
- Alle Daten synthetisch: Firmen, Beträge und Notizen sind erfunden.
- Der Code ist lauffähig, sofern Sie eigene API-Schlüssel der drei Anbieter einsetzen.
- Die gezeigten Ausgaben stammen aus einem Testlauf über einen einzelnen DeepSeek-Endpunkt (siehe oben) — nicht aus Läufen bei Anthropic, OpenAI oder Google.
- Die Kostentabelle ist eine Hochrechnung aus geschätzten Tokenzahlen × Listenpreisen.
Zum Selber-Ausprobieren
Skript, Datensatz und Anleitung als ZIP — MIT-Lizenz, frei verwendbar. Wenn Sie es auf Ihre echte Debitorenliste loslassen wollen: Genau dabei helfen wir.
Welcher Ihrer Prozesse hat eine «Notizen-Spalte»?
Mahnwesen, Offertwesen, Bestellabwicklung, Support-Postfach — wo Text anfällt, funktioniert dieses Muster. Im kostenlosen Erstgespräch finden wir den Prozess mit dem besten Verhältnis von Aufwand zu Wirkung.
Kostenloses Strategiegespräch