Eigene Sub-Agenten bauen — Designmuster für KI-gestützte Workflows
Designmuster für spezialisierte Sub-Agenten mit engem Scope, expliziten Kontrakten und komposierbare Pipelines für KI-gestützte Entwicklung.
Die Philosophie des engen Scopes
Ein häufiger Fehler beim Bauen von KI-Agenten ist es, sie zu allgemein zu machen. Ein einzelner Agent, der Code-Generierung, Testing, Dokumentation, Deployment und Code-Review übernimmt, wird bei all diesen Aufgaben schlechter abschneiden. Er sammelt zu viel Kontext an, sein System-Prompt wird zu einem Widerspruchs-Wall, und sein Verhalten wird unvorhersehbar.
Die Alternative: enge, fokussierte Sub-Agenten bauen, die je eines gut können. Das ist keine neue Idee — es ist die Unix-Philosophie angewandt auf KI. Kleine, komposierbare Werkzeuge, die sich verketten lassen.
Ein Sub-Agent sollte haben:
- Eine klare Verantwortung (“Unit Tests für Python-Funktionen generieren”)
- Definierte Eingaben (Quellcode der Funktion, bevorzugtes Test-Framework)
- Definierte Ausgaben (Testdatei-Inhalt, Coverage-Schätzung)
- Keine Seiteneffekte außerhalb seines Scopes (er verändert nicht den Quellcode, den er testet)
Explizite I/O-Kontrakte
Jeder Sub-Agent braucht einen Kontrakt — eine klare Spezifikation, was er akzeptiert und was er zurückgibt. Ohne das bricht die Komposition zusammen. Du kannst den Output eines Agenten nicht in einen anderen pipen, wenn du die Form des Outputs nicht kennst.
Kontrakte definieren
# agents/test-generator.yaml
name: test-generator
description: "Generiert Unit Tests für Python-Funktionen"
model: claude-sonnet-4-20250514
input:
format: json
schema:
source_code:
type: string
required: true
description: "Die zu testende(n) Python-Funktion(en)"
framework:
type: enum
values: [pytest, unittest]
default: pytest
coverage_target:
type: number
default: 0.8
description: "Ziel-Branch-Coverage (0.0–1.0)"
output:
format: json
schema:
test_code:
type: string
description: "Inhalt der generierten Testdatei"
test_count:
type: number
description: "Anzahl generierter Test-Cases"
estimated_coverage:
type: number
description: "Geschätzte Branch-Coverage"
Dieser Kontrakt erfüllt drei Aufgaben gleichzeitig: Er dokumentiert den Agenten für Menschen, validiert Eingaben zur Laufzeit und ermöglicht es Tooling, Pipeline-Konfigurationen automatisch zu generieren.
Shell-Scripting als Orchestrierung
Für das Verketten von Sub-Agenten braucht es kein komplexes Orchestrierungs-Framework. Shell-Skripte funktionieren überraschend gut für lineare Pipelines und haben den Vorteil, allgemein verständlich zu sein.
#!/bin/bash
# pipeline: analyse → tests generieren → tests reviewen
set -euo pipefail
SOURCE_FILE="$1"
SOURCE_CODE=$(cat "$SOURCE_FILE")
# Schritt 1: Code-Struktur analysieren
ANALYSIS=$(agent-run code-analyzer \
--input "{\"source_code\": $(echo "$SOURCE_CODE" | jq -Rs .)}" \
--output json)
echo "Analyse abgeschlossen: $(echo "$ANALYSIS" | jq -r '.summary')"
# Schritt 2: Tests basierend auf Analyse generieren
FUNCTIONS=$(echo "$ANALYSIS" | jq -r '.functions')
TESTS=$(agent-run test-generator \
--input "{\"source_code\": $(echo "$SOURCE_CODE" | jq -Rs .), \"framework\": \"pytest\"}" \
--output json)
echo "$(echo "$TESTS" | jq -r '.test_count') Test-Cases generiert"
# Schritt 3: Generierte Tests auf Qualität reviewen
REVIEW=$(agent-run code-reviewer \
--input "{\"code\": $(echo "$TESTS" | jq -r '.test_code' | jq -Rs .), \"review_type\": \"test_quality\"}" \
--output json)
# Schritt 4: Output schreiben, wenn Review bestanden
SCORE=$(echo "$REVIEW" | jq -r '.quality_score')
if (( $(echo "$SCORE > 0.7" | bc -l) )); then
echo "$TESTS" | jq -r '.test_code' > "tests/test_$(basename "$SOURCE_FILE")"
echo "Tests geschrieben. Qualitäts-Score: $SCORE"
else
echo "Tests unter Qualitätsschwelle ($SCORE). Review: $(echo "$REVIEW" | jq -r '.issues')"
exit 1
fi
Diese Pipeline ist lesbar, debugbar (mit set -x für Tracing) und einfach zu modifizieren. Der Output jedes Schritts wird in einer Variable gespeichert und kann bei Problemen geprüft werden.
Komposierbare Pipelines
Die eigentliche Stärke von Sub-Agenten zeigt sich, wenn du sie zu wiederverwendbaren Pipelines zusammensetzt. Eine Pipeline ist eine Abfolge von Sub-Agenten mit definierten Übergabepunkten.
Pipeline-Definition
# pipelines/pr-review.yaml
name: pr-review
description: "Vollständige Pull-Request-Review-Pipeline"
steps:
- agent: diff-analyzer
input:
diff: "${pipeline.input.diff}"
output_as: analysis
- agent: security-scanner
input:
code_changes: "${analysis.changed_files}"
output_as: security
- agent: test-coverage-checker
input:
source_files: "${analysis.changed_files}"
test_files: "${analysis.test_files}"
output_as: coverage
- agent: review-summarizer
input:
analysis: "${analysis}"
security: "${security}"
coverage: "${coverage}"
output_as: summary
output:
review: "${summary.review_text}"
approve: "${summary.should_approve}"
issues: "${summary.blocking_issues}"
Jeder Schritt referenziert Outputs vorheriger Schritte über eine einfache Variablen-Syntax. Der Pipeline-Runner löst diese Referenzen auf, validiert die Datenstrukturen gegen die Agenten-Kontrakte und behandelt Fehler an jedem Übergabepunkt.
Validierung zwischen Übergaben
Der Raum zwischen zwei Agenten ist der, wo Dinge kaputtgehen. Agent A gibt etwas Unerwartetes zurück, Agent B empfängt Müll, die Pipeline produziert Unsinn. Validierung an den Übergabepunkten fängt diese Fehler frühzeitig ab.
interface HandoffValidator {
validate(
output: unknown,
expectedSchema: Schema
): ValidationResult;
}
// Zwischen jedem Pipeline-Schritt:
const result = await agent.run(input);
const validation = validator.validate(result, nextAgent.inputSchema);
if (!validation.valid) {
// Option 1: Mit Fehlerkontext wiederholen
const retryResult = await agent.run(input, {
additionalContext: `Vorheriger Output war ungültig: ${validation.errors.join(", ")}. Bitte korrigieren.`,
});
// Option 2: Standardwert verwenden
// Option 3: Pipeline mit klarem Fehler stoppen
}
Drei Validierungsstrategien in der Reihenfolge ihrer Bevorzugung:
- Mit Feedback wiederholen. Dem Agenten seine Validierungsfehler mitgeben und ihn bitten, sie zu beheben. Funktioniert gut bei Formatierungsproblemen.
- Standardwerte. Wenn ein nicht-kritisches Feld fehlt, sinnvollen Standardwert verwenden und weitermachen.
- Mit Diagnose stoppen. Wenn der Output grundlegend falsch ist, Pipeline stoppen und exakt angeben, welcher Schritt warum scheiterte.
Mit Agent Forge bauen
Agent Forge liefert das Gerüst für dieses Muster. Es generiert die Agenten-Konfigurationsdateien, richtet die Kontrakt-Validierung ein und erstellt den Pipeline-Runner — damit du dich auf das Schreiben der System-Prompts und das Testen des Agentenverhaltens konzentrieren kannst statt auf die Infrastruktur.
# Neuen Sub-Agenten scaffolden
agent-forge create test-generator --model claude-sonnet --category testing
# Kontrakt interaktiv definieren
agent-forge contract test-generator
# Zur Pipeline hinzufügen
agent-forge pipeline add pr-review --step test-generator --after diff-analyzer
# Pipeline lokal ausführen
agent-forge pipeline run pr-review --input '{"diff": "..."}'
Design-Richtlinien
Prompts unter 500 Wörter halten. Wenn dein System-Prompt länger ist, ist der Scope des Agenten wahrscheinlich zu breit. Aufteilen.
Mit adversarialen Inputs testen. Den Agenten mit fehlerhaftem JSON, leeren Strings und absurd langen Inputs konfrontieren. Die Kontrakt-Validierung sollte das abfangen — aber verifizieren.
Agenten versionieren. Wenn ein System-Prompt aktualisiert wird, ändert sich das Verhalten des Agenten. Versionen taggen, damit Pipelines auf bekannte funktionierende Konfigurationen pinnen können.
Alles protokollieren. Jeder Sub-Agenten-Aufruf sollte Input, Output, verwendetes Modell, Token-Anzahl und Latenz loggen. Diese Daten sind unverzichtbar zum Debuggen von Pipelines und zur Kostenoptimierung.
Mit zwei Agenten beginnen. Keine Zwölf-Agenten-Pipeline auf dem Papier entwerfen. Zwei Agenten bauen, verbinden, die Übergabe verifizieren, dann den dritten hinzufügen. Schrittweise Komposition findet Integrationsprobleme früh.
Das Ziel ist nicht das ausgefeilteste Multi-Agenten-System zu bauen. Das Ziel ist zuverlässige, vorhersehbare Automatisierung aus kleinen, testbaren Teilen.