Den Agenten steuern: Wie man mit AI wirklich Produktionscode ausliefert
- ai-agents
- developer-tools
- workflow
- field-notes
Der AI-Agent ist nicht der Operator. Das bist du. Hier sind das mentale Modell, die Speicherarchitektur und die Verifikationsdisziplin, die eine chaotische LLM-Sitzung in eine zuverlässige Shipping-Schleife verwandeln.
Alle wollen darüber reden, was das Modell kann. Niemand will über den Teil reden, der tatsächlich darüber entscheidet, ob Code ausgeliefert wird: den Operator. Das bist du. Der Agent ist ein sehr fähiger Ausführer ohne Langzeitgedächtnis, ohne Situationsbewusstsein und ohne Interesse daran, ob der Pull Request funktioniert. Du hast alle drei. In dem Moment, in dem du das vergisst, produzierst du Schrott in großem Maßstab statt Software.
Ich liefere schon lange genug Produktionscode mit AI-Coding-Agenten aus, um ein wiederholbares System zu haben. Es ist keine Magie. Es ist Disziplin, die auf ein neues Werkzeug angewendet wird.

Spezifikation, nicht Wunsch
Die Qualität deines Outputs ist durch die Qualität deines Inputs begrenzt. Das ist keine Metapher, es ist eine mechanische Tatsache darüber, wie Sprachmodelle funktionieren. Vage Prompts erzeugen vagen Code. Ein Wunsch klingt so: "Füge Authentifizierung zur App hinzu." Eine Spezifikation klingt so: "Implementiere JWT-basierte Authentifizierung mit dem vorhandenen User-Modell in src/models/user.ts, schütze alle Routen unter /api/v1/ außer /api/v1/auth/login und /api/v1/auth/refresh, speichere das Refresh-Token in einem HTTP-only-Cookie und schreibe Unit-Tests für das Middleware in src/middleware/__tests__/auth.test.ts."
Der Unterschied ist kein Stil. Es ist Signaldichte. Jede Einschränkung, die du auslässt, ist eine Entscheidung, die der Agent für dich trifft, und der Agent hat keine Ahnung, wie deine Architektur aussieht, worüber sich dein Team letzten Dienstag geeinigt hat oder welche Drittanbieter-Bibliothek du vor sechs Monaten verbannt hast. Du schon. Schreib es in den Prompt.
In überprüfbare Schritte zerlegen
Lange agentische Aufgaben scheitern an der Grenze zwischen Teilaufgaben. Der Agent verliert den Faden, halluziniert einen Import oder ändert still und heimlich die Schnittstelle, die er hätte bewahren sollen. Die Lösung ist Zerlegung: Teile die Arbeit in Schritte auf, bei denen jeder Schritt eine klare, überprüfbare Ausgangsbedingung hat.
Nicht "baue das Feature." Stattdessen: (1) schreibe den Data Layer mit Typen und Tests, stop, (2) schreibe den Service Layer gegen diese Typen, stop, (3) verbinde den HTTP-Handler, stop, (4) führe die vollständige Test-Suite aus und melde Fehler. Jeder Stop ist ein Checkpoint, an dem du den Output prüfst, bevor du fortfährst. Du bist der Orchestrator. Du entscheidest, wann Schritt N gut genug ist, um Schritt N+1 zu beginnen.
- 01Definiere zuerst den Datenvertrag (Typen, Schema, Fixtures).
- 02Implementiere die Kernlogik gegen diesen Vertrag.
- 03Verbinde die Integrationsschicht (HTTP, Event Bus, CLI).
- 04Verifiziere in einem separaten Durchgang mit einem frischen Kontextfenster.
Kleine Schritte halten auch das Kontextfenster sauber. Eine Änderung mit 200 Zeilen ist überprüfbar. Eine mit 2000 Zeilen ist ein Risiko.
Die Architektur lebt in deinem Kopf (und in Markdown)
Hier ist die unverblümte Wahrheit über AI-Coding-Agenten: Sie haben kein Gedächtnis zwischen den Sitzungen. Der Agent, der gestern dein Authentifizierungs-Middleware geschrieben hat, weiß heute nicht, dass es existiert. Er wird fröhlich ein anderes Muster erfinden, eine konkurrierende Bibliothek importieren oder die von dir etablierte Konvention verletzen, nicht weil er nachlässig ist, sondern weil du es ihm nie gesagt hast.
Hier werden Markdown-Gedächtnisdateien zum wichtigsten Teil deines Stacks. Keine cleveren Prompts. Keine System-Nachrichten. Persistente, versionskontrollierte Markdown-Dateien, die im Repository leben und zu Beginn jeder Sitzung in den Kontext geladen werden. Der Agent liest sie und hat plötzlich das institutionelle Wissen, das ihm sonst fehlen würde.
Die Markdown-Datei ist keine Dokumentation für Menschen. Sie ist das Arbeitsgedächtnis für den Agenten. Schreibe sie entsprechend.
Das Muster ist einfach: eine AGENTS.md im Projektstamm, mit Abschnitten für Architekturentscheidungen, Namenskonventionen, auszuführende Befehle, verbotene Muster und bekannte Tücken. Halte sie aktuell, während sich die Codebasis entwickelt. Behandle sie mit der gleichen Disziplin wie eine README.md, die das Laufzeitverhalten tatsächlich beeinflusst, denn in deinem AI-Workflow tut sie das.
# AGENTS.md — Project Memory for AI Coding Agents
## Architecture
- Monorepo: apps/web (Next.js), apps/api (Hono on Bun), packages/shared (types + utils)
- Database: Postgres via Drizzle ORM. Schema lives in packages/db/schema.ts.
- Auth: JWT in Authorization header for API, HTTP-only cookie for web client. Use the helpers in packages/shared/src/auth.ts — do NOT roll your own.
- State: Zustand stores in apps/web/src/stores/. No Redux, no Context API for app state.
## Conventions
- File naming: kebab-case for files, PascalCase for components/classes.
- All API routes return { data, error, meta } — use the ResponseEnvelope type from packages/shared.
- Services live in src/services/, handlers in src/handlers/. Services contain business logic, handlers do HTTP concerns only.
- Tests co-located: src/services/__tests__/auth-service.test.ts next to src/services/auth-service.ts.
## Commands
```bash
bun run dev # start all apps in parallel
bun run test # run full test suite
bun run db:migrate # apply pending migrations
bun run typecheck # tsc --noEmit across all packages
```
## Forbidden Patterns
- Do NOT use `any` in TypeScript. Use `unknown` and narrow.
- Do NOT import from `apps/api` in `apps/web` or vice versa. Use packages/shared.
- Do NOT use `console.log` in production code. Use the logger in packages/shared/src/logger.ts.
## Known Gotchas
- Drizzle's `db.query.*` API requires the relational schema to be passed at client init time. See packages/db/client.ts.
- Hono middleware runs in declaration order. Auth middleware must be registered before route handlers.
- The web app uses the App Router. Do not use getServerSideProps or pages/api — those are dead.
Diese Datei wird zu Beginn jeder Agentensitzung geladen. Der Agent kennt jetzt die verbotenen Muster, die Helper-Speicherorte, die Namenskonvention für Testdateien und die auszuführenden Befehle. Er handelt zuverlässig nach diesem Wissen, weil er es liest und nicht aus der Codebase-Struktur ableitet.
In einem separaten Durchgang verifizieren
Das ist die Regel, die die meisten überspringen, und es ist die, die sie am teuersten zu stehen kommt. Bitte nicht denselben Agenten-Kontext, der den Code geschrieben hat, ihn auch zu verifizieren. Der Kontext ist befangen. Der Agent hat ein Modell davon, was er schreiben wollte, und dieses Modell lässt ihn übersehen, was er tatsächlich geschrieben hat.
Starte eine neue Sitzung. Lade die AGENTS.md. Lade den Diff oder die geänderten Dateien. Gib dem Agenten einen Verifikations-Prompt: "Überprüfe die folgenden Änderungen gegen die Konventionen in AGENTS.md. Melde alle Verstöße, fehlende Tests, Typfehler und Logikfehler. Schlage keine Verbesserungen vor, melde nur Fehler." Der frische Kontext hat keine Bindung an die vorherige Arbeit. Er liest, was tatsächlich da ist.

Ergänze das durch einen automatisierten Verifikationsschritt, den der Agent selbst ausführen kann. Die folgende Schleife ist nicht clever, sie ist einfach diszipliniert:
// scripts/verify-loop.ts
// Run with: bun run scripts/verify-loop.ts
// Iterates: typecheck → test → lint. Prints first failure and exits.
import {execSync} from 'child_process';
const steps = [
{name: 'typecheck', cmd: 'bun run typecheck'},
{name: 'test', cmd: 'bun run test --bail'},
{name: 'lint', cmd: 'bun run lint'},
] as const;
for (const step of steps) {
console.log(`\n--- ${step.name} ---`);
try {
execSync(step.cmd, {stdio: 'inherit'});
console.log(`${step.name}: PASS`);
} catch {
console.error(`${step.name}: FAIL — stopping.`);
process.exit(1);
}
}
console.log('\nAll checks passed.');
Der Agent führt dieses Skript nach jeder nicht trivialen Änderung aus. Wenn es fehlschlägt, meldet der Agent den Fehler zurück an dich. Du entscheidest, ob du iterierst oder zurückrollst. Das Skript ist deterministisch. Der Agent macht keine Ermessensentscheidungen darüber, ob die Tests "größtenteils bestehen".
Die Architektur im Kopf behalten
Die Markdown-Datei entlastet bei Konventionen und Befehlen. Sie entlastet nicht beim architektonischen Urteilsvermögen. Du musst noch wissen, warum der Service Layer existiert, warum die Authentifizierung in einem gemeinsamen Paket ist, warum du Drizzle statt Prisma gewählt hast. Der Agent kann Muster befolgen. Er kann nicht darüber nachdenken, ob die Muster noch für das Problem geeignet sind, das du gerade löst.
Wenn eine Aufgabe eine architektonische Entscheidung erfordert, triff sie selbst, bevor du die Agentensitzung öffnest. Schreibe die Entscheidung in die AGENTS.md, bevor du den Agenten bittest, sie zu implementieren. Die Reihenfolge ist: du entscheidest, du dokumentierst, der Agent implementiert. Nicht: der Agent implementiert, du entdeckst die getroffene Entscheidung und akzeptierst oder rückgängig machst sie.
- Du besitzt den Abhängigkeitsgraphen. Der Agent weiß nicht, was das Hinzufügen eines neuen Pakets kostet.
- Du besitzt das Datenmodell. Schema-Änderungen sind schwer rückgängig zu machen. Delegiere sie niemals ohne eine schriftliche Spezifikation.
- Du besitzt die Sicherheitsgrenze. Der Agent implementiert jeden Authentifizierungsfluss, den du beschreibst, einschließlich fehlerhafter.
- Du besitzt das Release. Der Agent hat kein Interesse daran, ob die Produktion läuft.
Die Schleife in der Praxis
Eine echte Sitzung sieht so aus. Du identifizierst eine Aufgabe. Du schreibst eine Spezifikation mit genug Einschränkungen, damit der Agent keine Mehrdeutigkeit über Dateispeicherorte, Schnittstellengrenzen und Akzeptanzkriterien hat. Du lädst AGENTS.md und die relevanten Quelldateien. Du gibst dem Agenten Schritt eins der zerlegten Arbeit. Er produziert einen Output. Du liest ihn. Wenn er gut ist, gehst du zu Schritt zwei. Wenn alle Schritte abgeschlossen sind, startest du eine neue Sitzung für den Verifikationsdurchgang. Der Verifizierer meldet Fehler. Du gibst die Fehler zurück an den Implementierungskontext oder behebst sie selbst.
Die gesamte Schleife ist vielleicht dreißig Prozent langsamer als einfach drauflos zu prompten und zu hoffen. Sie produziert Code, der dich im Review nicht blamiert und dich nicht um 3 Uhr morgens aufweckt.
Die Verschiebung im mentalen Modell ist einfach: Höre auf zu fragen, was das Modell kann, und fange an zu fragen, was du als Operator bereitstellst. Qualität der Spezifikation, Qualität der Zerlegung, Qualität der Gedächtnisdatei, Verifikationsdisziplin. Das sind die Variablen, die du kontrollierst. Kontrolliere sie, und der Agent wird zu einem echten Kraftmultiplikator. Ignoriere sie, und du hast eine teure Autovervollständigung.