Zum Inhalt springen

Human-in-the-Loop Patterns

Wissen

Nicht jede Entscheidung sollte ein Agent allein treffen. Ein Agent, der automatisch Code in Produktion deployt, Kunden-E-Mails versendet oder Datenbankmigrationen ausführt, braucht menschliche Kontrolle an kritischen Punkten. Human-in-the-Loop (HITL) Patterns definieren, wann und wie ein Mensch eingreift.

Wann muss ein Mensch entscheiden?

Die Entscheidung, ob ein Mensch eingreifen muss, hängt von zwei Faktoren ab: Risiko und Reversibilität.

Reversibel (Undo möglich)Irreversibel (kein Undo)
Niedriges RisikoAgent entscheidet autonomAgent entscheidet, Mensch wird informiert
Hohes RisikoMensch gibt FreigabeMensch muss aktiv bestätigen (Approval-Gate)

Beispiele:

  • Autonom: Code-Formatierung, Lint-Fixes, Test-Ausführung
  • Informieren: Dependency-Updates, Dokumentations-Änderungen
  • Freigabe: Code-Deployment, API-Schema-Änderungen
  • Aktive Bestätigung: Datenbankmigration, Löschung von Produktionsdaten, Kunden-Kommunikation

Die drei HITL-Patterns

1. Approval-Gate: Der Agent hält an einem definierten Punkt an und wartet auf menschliche Freigabe, bevor er weitermacht.

2. Escalation: Der Agent erkennt, dass er an seine Grenzen stößt, und übergibt an einen Menschen -- mit vollständigem Kontext.

3. Feedback-Loop: Der Mensch gibt dem Agent nach Abschluss einer Aufgabe Feedback, das in zukünftige Entscheidungen einfließt.

Verstehen

Approval-Gate implementieren

import json
from enum import Enum
from dataclasses import dataclass, field
from datetime import datetime

class ApprovalStatus(Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"
    TIMEOUT = "timeout"

@dataclass
class ApprovalRequest:
    task_id: str
    agent_name: str
    action: str
    context: dict
    risk_level: str
    status: ApprovalStatus = ApprovalStatus.PENDING
    reviewer: str = ""
    feedback: str = ""
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())

class ApprovalGate:
    def __init__(self, notification_handler=None):
        self.pending_requests: list[ApprovalRequest] = []
        self.notify = notification_handler or self._default_notify

    def request_approval(
        self, task_id: str, agent_name: str, action: str,
        context: dict, risk_level: str = "high"
    ) -> ApprovalRequest:
        """Erstellt eine Freigabe-Anfrage und benachrichtigt den Reviewer."""
        request = ApprovalRequest(
            task_id=task_id,
            agent_name=agent_name,
            action=action,
            context=context,
            risk_level=risk_level
        )
        self.pending_requests.append(request)
        self.notify(request)
        return request

    def approve(self, task_id: str, reviewer: str, feedback: str = "") -> bool:
        """Gibt eine ausstehende Anfrage frei."""
        for req in self.pending_requests:
            if req.task_id == task_id and req.status == ApprovalStatus.PENDING:
                req.status = ApprovalStatus.APPROVED
                req.reviewer = reviewer
                req.feedback = feedback
                return True
        return False

    def reject(self, task_id: str, reviewer: str, feedback: str) -> bool:
        """Lehnt eine Anfrage ab."""
        for req in self.pending_requests:
            if req.task_id == task_id and req.status == ApprovalStatus.PENDING:
                req.status = ApprovalStatus.REJECTED
                req.reviewer = reviewer
                req.feedback = feedback
                return True
        return False

    def _default_notify(self, request: ApprovalRequest):
        print(f"[APPROVAL NEEDED] {request.agent_name}: {request.action}")
        print(f"  Risk: {request.risk_level}")
        print(f"  Context: {json.dumps(request.context, indent=2)}")

Escalation-Pattern

@dataclass
class EscalationRule:
    condition: str       # z.B. "confidence < 0.7"
    threshold: float
    target: str          # z.B. "senior-developer", "team-lead"
    message_template: str

ESCALATION_RULES = [
    EscalationRule(
        condition="confidence",
        threshold=0.7,
        target="senior-developer",
        message_template=(
            "Agent {agent} ist unsicher bei Aufgabe {task}. "
            "Confidence: {confidence:.0%}. Bitte übernehmen."
        )
    ),
    EscalationRule(
        condition="retries",
        threshold=3,
        target="team-lead",
        message_template=(
            "Agent {agent} hat Aufgabe {task} nach {retries} Versuchen "
            "nicht lösen können. Menschliches Eingreifen nötig."
        )
    ),
    EscalationRule(
        condition="cost",
        threshold=5.0,
        target="engineering-manager",
        message_template=(
            "Agent {agent} hat für Aufgabe {task} bereits ${cost:.2f} "
            "verbraucht. Budget-Grenze erreicht."
        )
    ),
]

Der HITL-Workflow visualisiert

Das folgende Diagramm zeigt den Workflow eines Production Agents mit Approval-Gates. Klicke auf die Checkpoints, um zu sehen, wann der Agent autonom handelt und wann menschliche Freigabe nötig ist:

Human-in-the-Loop Flow

Automatisch:4 Schritte
Manuell:2 Gates
Zeitersparnis:~85%

Anwenden

Dein Agent soll automatisch Kunden-E-Mails beantworten. Welches HITL-Pattern passt am besten?

Reflektieren

Human-in-the-Loop ist kein Zeichen von Schwäche eines Agent-Systems -- es ist ein Zeichen von Reife. Die besten Production Agents sind nicht die, die alles allein machen, sondern die, die wissen, wann sie einen Menschen einbeziehen müssen. Die Kunst liegt darin, die richtige Balance zu finden: Zu viel menschliche Kontrolle macht den Agent nutzlos, zu wenig macht ihn gefährlich. Die Risiko-Reversibilitäts-Matrix aus diesem Abschnitt ist dein Werkzeug für diese Entscheidung.