Cursor Agent Loops in der Praxis

Ein Cursor-Agent-Loop ist mehr als „Prompt, warten, committen“. Stabile Loops folgen dem Muster Plan → Act → Observe → Refine – mit definierten Exit-Kriterien und Abbruchbedingungen. Agenticode in Köln implementiert diese Loops für Kunden, die agentische Entwicklung skalieren wollen, ohne in Endlosschleifen aus Halluzinationen und Scope Creep zu geraten.

Plan-Act-Observe-Refine: Anatomie eines stabilen Loops

In der Plan-Phase zerlegt der Agent (oder ein Planungs-Sub-Agent) das Intent in atomare Tasks mit Abhängigkeiten. Jeder Task hat einen klaren Done-Zustand: „Unit-Tests für InvoiceCalculator grün“, nicht „Billing verbessern“. Vage Tasks sind die häufigste Ursache für instabile Loops.

Act bedeutet: Code schreiben, Tests ausführen, Diffs erzeugen – innerhalb der Policy-Grenzen. Observe sammelt Artefakte: Test-Output, Linter-Fehler, Diff-Statistik, Laufzeit. Refine entscheidet: Weiter im selben Task, Task abschließen, oder Escalation an den Menschen.

Der kritische Punkt ist Observe: Ohne strukturierte Beobachtung refiniert der Agent blind. Agenticode standardisiert Observe-Output als JSON, das maschinell auswertbar ist – Grundlage für agentische CI/CD und Multi-Agent-Handoffs.

Loop-State-Machine: Phasen, Iterationslimit und Escalation.
<?php

declare(strict_types=1);

namespace App\Agentic\Loop;

final class AgentLoopState
{
    public const PHASE_PLAN = 'plan';
    public const PHASE_ACT = 'act';
    public const PHASE_OBSERVE = 'observe';
    public const PHASE_REFINE = 'refine';
    public const PHASE_DONE = 'done';
    public const PHASE_ESCALATE = 'escalate';

    public function __construct(
        public string $phase = self::PHASE_PLAN,
        public int $iteration = 0,
        public int $maxIterations = 8,
        public array $tasks = [],
        public int $currentTaskIndex = 0,
        public array $observeLog = [],
    ) {}

    public function shouldContinue(): bool
    {
        if ($this->phase === self::PHASE_DONE || $this->phase === self::PHASE_ESCALATE) {
            return false;
        }
        return $this->iteration < $this->maxIterations;
    }

    public function advancePhase(): void
    {
        $cycle = [self::PHASE_PLAN, self::PHASE_ACT, self::PHASE_OBSERVE, self::PHASE_REFINE];
        $idx = array_search($this->phase, $cycle, true);
        $this->phase = $cycle[($idx + 1) % count($cycle)];
        if ($this->phase === self::PHASE_PLAN) {
            $this->iteration++;
        }
    }
}

Exit-Kriterien und Abbruchbedingungen

Jeder Loop braucht harte Exit-Kriterien. Softe Kriterien wie „Code sieht gut aus“ führen zu Regressionen. Agenticode definiert typischerweise: alle required_checks grün, Diff unter Limit, keine neuen PHPStan-Errors, Coverage-Schwelle eingehalten.

Abbruchbedingungen sind ebenso wichtig: Max-Iterationen erreicht, gleicher Fehler dreimal hintereinander, Policy-Verletzung, oder Scope-Eskalation (Agent will Dateien außerhalb des erlaubten Pfads ändern). In diesen Fällen stoppt der Loop und eskaliert – statt weiter zu raten.

In Cursor setzen wir das über Rules und Hooks um: Ein afterAgentResponse-Hook kann Test-Output parsen und den nächsten Schritt vorgeben. So wird der Loop deterministischer als reines Prompt-Chaining.

Strukturierter Observe-Output: maschinell auswertbare Loop-Metadaten.
{
  "loop_id": "billing-invoice-calc-2024-03",
  "intent": "InvoiceCalculator mit MwSt-Sätzen DE/AT implementieren",
  "exit_criteria": {
    "phpunit": { "status": "pass", "failures": 0 },
    "phpstan": { "level": 8, "errors": 0 },
    "diff_lines": { "max": 350, "actual": 287 },
    "forbidden_patterns": []
  },
  "abort_triggers": [
    { "type": "max_iterations", "value": 8, "current": 3 },
    { "type": "repeated_error", "signature": "TypeError: float + string", "count": 2 }
  ],
  "phase": "refine",
  "next_action": "complete_task_and_advance"
}

Praxis bei Agenticode: Loop-Templates für wiederholbare Runs

Wir pflegen Loop-Templates für wiederkehrende Aufgaben: Feature-Implementierung, Bugfix, Refactoring-Extraktion, Test-Nachziehen. Jedes Template definiert Plan-Prompt, Act-Constraints, Observe-Parser und Refine-Entscheidungsbaum.

Für komplexe Features nutzen wir verschachtelte Loops: Ein äußerer Plan-Loop zerlegt in Sub-Loops pro Modul. Der äußere Loop beobachtet nur Sub-Loop-Ergebnisse – nicht jeden einzelnen Diff. Das reduziert Kontext-Überladung und hält Cursor-Runs fokussiert.

Kunden in Köln profitieren davon, dass wir diese Templates nicht als Black Box liefern, sondern dokumentieren und anpassen. Ihr Team kann Loops für eigene Domains erweitern – mit denselben Stabilitäts-Garantien.

Observe-Script: Test- und Diff-Metriken nach jedem Act-Schritt erfassen.
#!/usr/bin/env bash
# cursor-loop-observe.sh – nach jedem Agent-Act-Schritt
set -euo pipefail

RESULT_FILE=".agentic/observe-$(date +%s).json"

PHPUNIT_OUT=$(vendor/bin/phpunit --log-junit /tmp/junit.xml 2>&1 || true)
PHPSTAN_OUT=$(composer phpstan 2>&1 || true)
DIFF_LINES=$(git diff --numstat | awk '{s+=$1+$2} END {print s+0}')

php -r "
echo json_encode([
  'timestamp' => time(),
  'phpunit_pass' => strpos('$PHPUNIT_OUT', 'OK') !== false,
  'phpstan_clean' => strpos('$PHPSTAN_OUT', '[OK]') !== false,
  'diff_lines' => (int)'$DIFF_LINES',
], JSON_PRETTY_PRINT);
" > "$RESULT_FILE"

echo "Observe written to $RESULT_FILE"