# Der Prompt als Vertrag

> Vorbedingung, testbare Nachbedingung und ausdrückliche Nicht-Pflicht: einen Prompt so schreiben, dass Aufrufer und Konsument wissen, worauf sie sich verlassen dürfen — und was als Version gilt, wenn er sich ändert.

Track: [Prompt Engineering](https://physar.tech/learn/prompt-engineering)  
Kanonische Fassung: https://physar.tech/learn/prompt-engineering/prompt-contract-design  
Stand: 2026-09-04  
Interaktiver Teil: 9 Checks (nur im Browser)

## Der Prompt als Vertrag

### Warum Vertrag und nicht Anweisung

Eine Anweisung hat einen Adressaten: das Modell. Ein **Vertrag** hat drei Parteien — den **Aufrufer**, der Eingaben liefert, das **Modell**, das arbeitet, und den **Konsumenten**, der die Ausgabe weiterverarbeitet. Sobald ein Prompt in einer Pipeline steht, ist er faktisch ein Vertrag zwischen diesen drei, und die interessante Frage ist nicht, wie höflich er formuliert ist, sondern **worauf sich wer verlassen darf**.

Der Begriffsapparat dafür ist alt und passt genau. Bertrand Meyer hat mit Design by Contract die Idee eingeführt, jede Routine mit **Vorbedingung**, **Nachbedingung** und Invarianten zu versehen: Was muss gelten, damit der Aufruf zulässig ist; was gilt hinterher, wenn er zulässig war. Der Zugewinn hier ist derselbe wie damals — die Verantwortung wird zuweisbar, statt bei jedem Fehler neu verhandelt zu werden.

_[Abbildung: Ein Prompt-Vertrag benennt drei Grenzen: was der Aufrufer garantiert, was die Ausgabe garantiert, und was ausdrücklich niemand garantiert.]_

> **Merksatz:** Ohne Vertrag ist bei jedem Produktionsfehler zuerst zu klären, wer schuld ist. Mit Vertrag ist die Frage schon beantwortet: Wer seine Seite nicht eingehalten hat.

### Vorbedingung: was die Eingabe erfüllen muss

**Vorbedingung:** Eine Bedingung, die vor dem Aufruf gelten muss, damit die Zusicherung über die Ausgabe überhaupt gilt. Ist sie verletzt, ist der Aufruf unzulässig — und das Ergebnis undefiniert, nicht falsch.

Der praktische Wert liegt in dieser Unterscheidung zwischen **undefiniert** und **falsch**. Ein Zusammenfassungs-Prompt, dessen Vorbedingung „Eingabetext ist nicht leer und in einer der unterstützten Sprachen“ lautet, ist nicht defekt, wenn er auf eine leere Eingabe Unsinn liefert — der Aufruf hätte nicht passieren dürfen. Fehlt die Vorbedingung, wird derselbe Vorfall zum Modellfehler erklärt und im Prompt reparaturversucht.

Deshalb gehört zu jeder Vorbedingung eine Entscheidung, **wer sie prüft**. Bei einem Sprachmodell ist die Antwort fast immer: der Aufrufer, im Code, vor dem Aufruf. Ein Modell kann eine Vorbedingung nicht durchsetzen, weil es die Verletzung nur erkennen und dann trotzdem arbeiten kann — und genau das tut es, wenn du ihm keinen anderen Ausgang gibst.

- **Formulieren** — was muss von der Eingabe gelten? Nicht leer, Mindestlänge, Sprache, Typ, Vollständigkeit der Felder.
- **Prüfen** — im Code des Aufrufers, vor dem Aufruf. Billig, deterministisch, nachvollziehbar.
- **Verhalten bei Verletzung festlegen** — abweisen mit Grund, oder ein definierter Rückgabewert. Nie: bester Versuch.

> **Merksatz:** Eine Vorbedingung, die niemand prüft, ist eine Notiz. Ein Modell, das eine verletzte Vorbedingung bemerkt, hat ohne definierten Ausgang nur die Wahl, trotzdem zu antworten.

### Nachbedingung: was die Ausgabe garantiert

**Nachbedingung:** Eine Aussage über die Ausgabe, die gilt, sofern die Vorbedingung erfüllt war. Sie ist die Seite des Vertrags, auf die sich der Konsument stützt — und sie ist nur dann eine Zusicherung, wenn ihre Verletzung feststellbar ist.

Die Nachbedingung ist die Seite, die der Konsument liest — und sie ist der Teil, der in der Praxis fast immer als Wunsch geschrieben wird. „Eine gute, hilfreiche Zusammenfassung“ ist keine Nachbedingung, weil niemand nachprüfen kann, ob sie eingehalten wurde. Eine Nachbedingung, die nicht **testbar** ist, ist keine.

|  |  |
| --- | --- |
| Wunsch (nicht prüfbar) | Nachbedingung (prüfbar) |
| Die Antwort ist hilfreich und gut lesbar | Die Antwort umfasst 3 bis 5 Sätze und enthält keine Aufzählung |
| Die Ausgabe ist strukturiert | Die Ausgabe ist ein JSON-Objekt mit genau den Feldern `betrag`, `datum`, `waehrung` |
| Fehlende Werte werden sinnvoll behandelt | Nicht belegte Felder haben den Wert `null`, niemals einen geschätzten Wert |
| Die Antwort bleibt beim Thema | Jede Tatsachenaussage nennt eine Belegstelle aus dem mitgegebenen Dokument |

Der Test dafür ist mechanisch: Kannst du eine Funktion schreiben, die `wahr` oder `falsch` zurückgibt? Wenn ja, ist es eine Nachbedingung, und du kannst sie im Eval-Set als Assertion verwenden. Wenn nein, hast du eine Absichtserklärung — nützlich für den Ton, unbrauchbar für einen Vertrag.

> **Merksatz:** Eine Nachbedingung, aus der sich keine Assertion schreiben lässt, kann nicht verletzt werden — und damit auch nicht eingehalten. Sie steht im Prompt und trägt nichts.

### Zusicherung ist nicht Durchsetzung

Hier liegt der Bruch zwischen Design by Contract in gewöhnlichem Code und dem Prompt-Fall, und er muss ausgesprochen werden. In einer Programmiersprache **erzwingt** der Compiler oder die Laufzeit die Typen; eine verletzte Nachbedingung wirft. Ein Sprachmodell erzwingt nichts: Die Nachbedingung im Prompt ist eine **Verschiebung von Wahrscheinlichkeit**, keine Garantie.

Daraus folgt keine Resignation, sondern eine Arbeitsteilung. Der Prompt **erklärt** den Vertrag — er macht das Erwünschte wahrscheinlich und das Unerwünschte unwahrscheinlich. Die **Durchsetzung** liegt außerhalb: Schema-Zwang beim Decoding, Validierung im Code, Ablehnung ungültiger Antworten. Beides ist nötig, und keines ersetzt das andere.

|  |  |
| --- | --- |
| Ebene | Was sie leistet |
| Prompt-Text | Macht die Nachbedingung wahrscheinlich. Kostet nichts, garantiert nichts. |
| Schema-Zwang beim Decoding | Erzwingt die Form. Sagt nichts über die Richtigkeit der Werte. |
| Validierung im Code | Erzwingt Form und prüfbare Semantik. Kann nur ablehnen, nicht reparieren. |
| Menschliche Prüfung | Erfasst, was keine Assertion erfasst. Skaliert nicht. |

Die Tabelle liest man am besten von unten nach oben, wenn eine Zusicherung wirklich tragen muss: Frage zuerst, welche Ebene die Verletzung **feststellen** kann, und schreibe die Nachbedingung dann so, dass diese Ebene sie prüfen kann. Eine Zusicherung, für die keine Ebene zuständig ist, gehört nicht in den Vertrag — sie gehört in die Liste der ausdrücklichen Nicht-Pflichten.

> **Merksatz:** Der Prompt trägt die **Erklärung** des Vertrags, der Code trägt seine **Durchsetzung**. Wer die Durchsetzung im Prompt sucht, hat einen Vertrag, den nur eine Partei kennt.

### Die Zuständigkeitsgrenze

Ein Vertrag benennt nicht nur Pflichten, sondern auch **Nicht**-Pflichten — und das ist der Teil, der am meisten Streit vermeidet. Ein Zusammenfassungs-Prompt ist nicht dafür zuständig, dass die Eingabe vollständig war. Ein Extraktions-Prompt ist nicht dafür zuständig, dass die extrahierten Werte im Quelldokument korrekt waren. Wer diese Grenzen nicht schreibt, bekommt sie im Vorfall zugewiesen.

### Wem gehört der Fehler?

Ein Prompt fasst Support-Verläufe zusammen; die Zusammenfassung geht in ein Kundenticket. Ein Vorfall: Die Zusammenfassung nennt einen Lösungsschritt, der nie stattgefunden hat. Die Untersuchung zeigt: Der übergebene Verlauf war abgeschnitten — die letzten Nachrichten fehlten, weil der Aufrufer eine Paginierung falsch behandelt. Die Zusammenfassung des abgeschnittenen Verlaufs war korrekt.

**Anforderungen**

- Welche Vertragsteile hätten diesen Vorfall zuweisbar gemacht?
- Benenne auch, was der Vertrag nicht verhindert hätte.

**Schritte**

- Vorbedingung: Der übergebene Verlauf ist vollständig — alle Nachrichten von der ersten bis zur letzten. Damit liegt der Defekt eindeutig beim Aufrufer, nicht beim Prompt.
- Prüfung dieser Vorbedingung im Code des Aufrufers: Nachrichtenzahl gegen die Gesamtzahl abgleichen, bei Abweichung abweisen statt aufrufen.
- Nicht-Pflicht ausdrücklich schreiben: Der Prompt gibt keine Zusicherung über Vollständigkeit oder Wahrheitsgehalt der Eingabe. Das beendet die Diskussion, ob das Modell den fehlenden Teil hätte erkennen müssen.
- Nachbedingung, die hier zusätzlich getragen hätte: Jede Tatsachenaussage muss auf eine Nachricht im übergebenen Verlauf zurückführbar sein. Sie war es — der Vertrag hätte den Vorfall nicht verhindert, aber sofort korrekt zugeordnet.

**Merksatz:** Ein Vertrag verhindert keine Vorfälle; er macht sie zuweisbar — und verhindert damit die zweite, teurere Runde, in der im Prompt nach einem Fehler gesucht wird, der nicht dort liegt. **+** Die Ursache wird beim ersten Blick sichtbar, und die Behebung landet an der richtigen Stelle. **−** Die explizite Nicht-Pflicht ist unbequem zu vertreten: Sie liest sich wie eine Ausrede, bis der erste Vorfall zeigt, dass sie eine Zuständigkeitsklärung war.

### Tolerant lesen, streng schreiben

Für die Konsumentenseite gibt es eine bewährte Haltung, und sie hat eine ältere und präzisere Quelle als jede Prompt-Sammlung. RFC 1122 formuliert sie in Abschnitt 1.2.2 als **Robustness Principle**: „Be liberal in what you accept, and conservative in what you send.“ Der begleitende Rat ist noch schärfer — Software solle sich auf jeden vorstellbaren Fehler vorbereiten, „no matter how unlikely“, und bei Werten, die eine Spezifikation aufzählt, nicht scheitern, wenn ein undefinierter Wert auftritt: protokollieren, nicht abbrechen.

Übertragen heißt das: Dein Konsument sollte ein unbekanntes zusätzliches Feld überleben und ein fehlendes Pflichtfeld klar ablehnen. Dein Produzent — der Prompt samt Validierung — sollte genau das Vereinbarte liefern und nichts darüber hinaus.

> **Der Preis dieser Regel:** Toleranz hat eine bekannte Kehrseite: Ein nachsichtiger Leser lässt fehlerhafte Produzenten überleben, und irgendwann hängt die Praxis am tolerierten Verhalten statt am Vertrag. Toleranz gehört deshalb mit **Protokollierung** gepaart — was du duldest, musst du sehen, sonst wird die Duldung zur unausgesprochenen Spezifikation.

### Der Prompt ist ein Produktions-Artefakt

Ein Vertrag, den man zur Laufzeit unbemerkt ändern kann, ist keiner. Damit ist die organisatorische Konsequenz benannt: Der Prompt-Text gehört wie Code behandelt — in die Versionskontrolle, durch Review, mit Tests, mit nachvollziehbarer Änderungshistorie. Nicht in ein Freitextfeld einer Admin-Oberfläche, in dem jemand um 23 Uhr einen Satz ergänzt.

Die Verwechslung, die hier zuverlässig passiert: **editierbar** ist nicht **versioniert**. Eine Konfigurationsdatenbank, in der der aktuelle Prompt steht, macht ihn schnell änderbar — sie sagt hinterher nicht, welche Fassung eine bestimmte Ausgabe erzeugt hat. Ohne diese Zuordnung ist keine Fehleranalyse möglich und kein Rückweg.

Der zweite Grund für dieselbe Regel ist der Vertrag selbst. Eine Nachbedingung, die im Repository steht, kann jemand im Review lesen und bestreiten; eine, die im Prompt-Feld eines Admin-Werkzeugs steht, ändert sich ohne Gegenüber. Damit verschwindet nicht nur die Rückverfolgbarkeit, sondern der Prüfschritt: Der Konsument erfährt von der geänderten Zusicherung erst, wenn sein Parser bricht.

- **In der Versionskontrolle** — der Prompt-Text als Datei, nicht als Datenbankfeld.
- **Mit Review** — eine Vertragsänderung braucht ein zweites Paar Augen, so wie eine Signaturänderung.
- **Mit Tests** — die Nachbedingungen als Assertions in einer Fallmenge, die im Repository mitliegt.
- **Mit Historie** — nachlesbar, wann welche Fassung galt und warum sie geändert wurde.

> **Merksatz:** Wenn niemand beantworten kann, welche Prompt-Fassung eine bestimmte Produktions-Ausgabe erzeugt hat, existiert keine Version — nur ein aktueller Stand.

### Was eine Prompt-Version eigentlich umfasst

Die naheliegende Antwort — „der Prompt-Text“ — ist zu klein. Die Ausgabe hängt an einer **Kombination**, und wenn nur ein Teil davon versioniert ist, beschreibt die Version nicht, was tatsächlich lief. Ein Modellwechsel oder ein geändertes Decoding verschiebt das Verhalten, ohne dass sich die Prompt-Version rührt.

- **Der Prompt-Text** samt Vorlage und Slot-Struktur.
- **Der Modell-Pin** — eine feste, benannte Modellversion, kein gleitender Alias.
- **Die Decoding-Parameter** — `temperature`, `top_p`, `max_tokens` und was sonst gesetzt wird.
- **Die Beispiele** — Few-shot-Block inklusive Reihenfolge, denn die ist Teil des Prompts.
- **Das Ausgabeschema** — die Nachbedingung, gegen die validiert wird.

Diese Bündelung ist die Vertragsseite der Sache. Wie so ein Bündel dann stufenweise ausgerollt, an Live-Metriken gemessen und zurückgerollt wird, ist Betriebsarbeit und Thema des Tracks **LLM Production Ops** — hier zählt nur: Ohne gemeinsame Version gibt es nichts, was man ausrollen oder zurückrollen könnte.

> **Merksatz:** Eine Version muss genau die Faktoren umfassen, die die Ausgabe bestimmen. Verstreute Konstanten in mehreren Dateien und Diensten sind keine Version, sondern ein Zustand.

### Ein Formatwechsel ist ein Major-Release

Die Nachbedingung ist eine öffentliche Schnittstelle, auch wenn sie nur in eurem Monorepo existiert. Semantic Versioning 2.0.0 formuliert die Regel unmissverständlich: MAJOR wird erhöht, „when you make incompatible API changes“ — und weiter: MAJOR **MUSS** erhöht werden, wenn irgendeine rückwärtsinkompatible Änderung an der öffentlichen Schnittstelle eingeführt wird.

Für einen Prompt-Vertrag heißt das konkret: Ein Feld umbenennen, ein Feld entfernen, den Typ eines Feldes ändern, den Wertebereich eines Enums einschränken — all das bricht Konsumenten und ist ein Major-Schritt, unabhängig davon, wie klein die Prompt-Änderung war. Ein Feld **hinzufügen**, das niemand lesen muss, ist ein Minor-Schritt, sofern die Konsumenten tolerant lesen.

|  |  |
| --- | --- |
| Änderung an der Nachbedingung | Einordnung |
| Feld `betrag` in `summe` umbenennen | Bricht jeden Leser — Major. |
| `betrag` von String auf Zahl umstellen | Bricht jeden Leser mit Typprüfung — Major. |
| Neues optionales Feld `waehrung` ergänzen | Minor, sofern Leser unbekannte Felder dulden. |
| Zusammenfassung von 5 auf 3 Sätze verkürzen | Formal kompatibel — und trotzdem riskant (siehe nächster Abschnitt). |

> **Merksatz:** Die Größe der Prompt-Änderung sagt nichts über die Größe der Vertragsänderung. Ein gestrichenes Wort im Prompt kann ein Major-Release sein, ein umgeschriebener Absatz keines.

### Kontrakt-Drift: der Vertrag, den niemand geschrieben hat

Die letzte Zeile der Tabelle oben ist der interessante Fall. Formal ist „3 statt 5 Sätze“ kompatibel — der Typ stimmt, das Feld existiert. Praktisch kann es eine Oberfläche zerlegen, die auf fünf Sätze ausgelegt war, oder eine nachgelagerte Klassifikation verschlechtern, die aus der Länge Signal zog.

Dafür gibt es eine treffende Formulierung, Hyrum's Law: „With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.“ Bei Modellausgaben ist die Menge beobachtbaren Verhaltens ungewöhnlich groß — Länge, Ton, Reihenfolge, typische Formulierungen, sogar die Häufigkeit bestimmter Wörter.

Zwei Konsequenzen. Erstens: **Schreibe die Nachbedingung eng genug**, dass Konsumenten wissen, worauf sie sich verlassen dürfen — und ausdrücklich, worauf nicht („die Reihenfolge der Felder ist nicht Teil des Vertrags“). Zweitens: Erwarte trotzdem, dass jemand sich auf mehr verlässt, und behandle jede Verhaltensänderung als Änderung mit Publikum, nicht als Detail.

> **Merksatz:** Was beobachtbar ist, wird benutzt. Eine Nachbedingung, die schweigt, wird durch das ersetzt, was das Modell zufällig getan hat — und diese stille Fassung verteidigt später jemand als Anforderung.

### Die Vorlage ist Teil des Vertrags

Zum Schluss die konkrete Bauform. Ein Produktions-Prompt ist eine Vorlage mit **Slots**, und jeder Slot ist eine Vertragsposition: Wer füllt ihn, mit welchem Typ, was gilt, wenn er leer ist. Ein Slot ohne diese drei Angaben ist die häufigste Stelle, an der ein Vertrag stillschweigend endet.

**Kurzcheck:** Euer Extraktions-Prompt hat einen Slot `{{ dokumenttyp }}`, der aus einer Vorklassifikation kommt. Bei etwa 4 % der Dokumente ist die Vorklassifikation unsicher und liefert einen leeren String. Was ist die Vertragslücke?

- [x] Für den Slot ist kein Verhalten bei leerem Wert festgelegt
- [ ] Die Vorklassifikation ist zu unzuverlässig für einen Produktionseinsatz
- [ ] Der Slot gehört nicht in den Prompt, sondern in die Vorbedingung
- [ ] Die Nachbedingung müsste den unsicheren Fall im Ausgabeschema abbilden

> Genau. Der Slot hat einen Befüller und einen Typ, aber keine Regel für den leeren Fall — also entsteht ein gerenderter Prompt, in dem eine Angabe fehlt, ohne dass irgendwo entschieden wurde, was dann gilt.

### Zusammenfassung — gleich im Check

Du kennst jetzt die Bestandteile: **Vorbedingung** mit benanntem Prüfer, **testbare Nachbedingung**, die klare Trennung von **Zusicherung und Durchsetzung**, die **Zuständigkeitsgrenze** samt ausgesprochener Nicht-Pflicht, **tolerantes Lesen bei strengem Schreiben** samt seiner Kehrseite, den Prompt als **versioniertes Artefakt** mit allem, was die Ausgabe bestimmt, den **Formatwechsel als Major-Release** und die **Drift** hin zu dem, was nur beobachtbar war.

> **Gleich im Check:** In den nächsten Aufgaben ziehst du diese Grenzen an konkreten Fällen — und die schwierige Frage ist regelmäßig nicht, was der Vertrag zusichern soll, sondern was er ausdrücklich **nicht** zusichert.

## Quellen

- Meyer, Applying Design by Contract, IEEE Computer 25(10), 1992 — https://doi.org/10.1109/2.161279
- RFC 1122, Requirements for Internet Hosts, Abschnitt 1.2.2 Robustness Principle — https://www.rfc-editor.org/rfc/rfc1122
- Semantic Versioning 2.0.0 — https://semver.org/
- Hyrum's Law — https://www.hyrumslaw.com/
- RFC 8259, The JavaScript Object Notation (JSON) Data Interchange Format — https://www.rfc-editor.org/rfc/rfc8259
- Anthropic — Prompting best practices: Control the format of responses — https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices
- Google SRE Workbook, Configuration Design — https://sre.google/workbook/configuration-design/
