Startseite / Artikel

Warum Langzeit-Agenten eine Zustandsmaschine benötigen: Die Rollen von Leader, Worker und Verifier

Vier Möglichkeiten, wie ein einzelner Agentenzyklus bei langen Aufgaben fehlschlägt, und wie ein aus Leader, Worker und Verifier bestehendes Team, das von einem deterministischen Engine angetrieben wird, diese Probleme behebt – sowie wann man darauf verzichten sollte.

Verlag: REACTAPP.TOP11 Min. Lesezeit2547 Wörter

Quellen: MiniMax Agent Team Best Practice: An AI Agent Is a While Loop. A Reliable One Is a State Machine.

Der Übergang von Chatbot zu Agent erfordert eine while-Schleife – keine bahnbrechende Forschung. Das stimmt, und genau hier enden die meisten Erklärungen und beginnt das größte Problem in der Produktion. Die nützlichere Frage ist, warum diese Schleife bei Aufgaben, die länger als zehn Minuten dauern, stillschweigend versagt, und welche Struktur sie wiederherstellt. MiniMax hat zwei ehrliche Referenzen zu genau diesem Thema veröffentlicht: das Open-Source-Tutorial Mini-Agent, das die Grundschleife zeigt, sowie einen technischen Bericht ihres Agent-Teams, der beschreibt, was in großem Maßstab schiefgelaufen ist, und den daraufhin entwickelten Laufzeitmechanismus. Beide dienen hier als Grundlage, und dieses Muster lässt sich auf jede Technologieplattform übertragen.

Eine Schleife – von Anfang bis Ende

Wenn man einen Agenten vereinfacht, bleiben vier Komponenten übrig: ein Modell, eine laufende Liste von Nachrichten, ein Register der Tools sowie eine Schleife, die diese Komponenten verarbeitet. Die Mini-Agent-Referenz funktioniert wie folgt: Bei jedem Durchlauf wird zunächst überprüft, ob der Historieinhalt nahe am Kontextlimit liegt; falls ja, wird er zusammengefasst. Anschließend werden Nachrichten sowie Definitionen der Tools an das Modell gesendet. Wenn das Modell keine Tools anfordert, kehrt die Schleife zurück. Andernfalls werden die angeforderten Tools ausgeführt, ihre Ausgaben den Nachrichten hinzugefügt und der Vorgang wiederholt. Hier ist eine vollständige Version für jeden OpenAI-kompatiblen Endpunkt, mit zwei echten Tools sowie dem Zusammenfassungsfunktion:

# naive_loop.py -- a single-agent tool loop,
# Mini-Agent style: summarize if needed ->
# llm.generate(messages, tools) -> execute
# tool_calls -> append results -> repeat.
import json
import subprocess
from openai import OpenAI

client = OpenAI()
MODEL = "gpt-4o-mini"
MAX_HISTORY = 40  # crude context budget


def read_file(path: str) -> str:
    with open(path) as f:
        return f.read()[:4000]


def run_cmd(cmd: str) -> str:
    p = subprocess.run(
        cmd, shell=True, timeout=30,
        capture_output=True, text=True)
    out = p.stdout + p.stderr
    return f"exit={p.returncode}\n{out[-2000:]}"


TOOLS = {"read_file": read_file,
         "run_cmd": run_cmd}


def schema(name, desc, arg):
    return {
        "type": "function",
        "function": {
            "name": name,
            "description": desc,
            "parameters": {
                "type": "object",
                "properties": {
                    arg: {"type": "string"},
                },
                "required": [arg],
            },
        },
    }


SCHEMAS = [
    schema("read_file",
           "Read a local text file.", "path"),
    schema("run_cmd",
           "Run a shell command.", "cmd"),
]



def summarize(msgs):
    # Mini-Agent compresses old turns with an
    # LLM summary call when the context nears
    # its limit; hard truncation is the v0.
    if len(msgs) <= MAX_HISTORY:
        return msgs
    return [msgs[0]] + msgs[-MAX_HISTORY:]


def run(task: str, max_steps: int = 20):
    msgs = [{"role": "user", "content": task}]
    for _ in range(max_steps):
        msgs = summarize(msgs)
        r = client.chat.completions.create(
            model=MODEL,
            messages=msgs,
            tools=SCHEMAS,
        )
        msg = r.choices[0].message
        msgs.append(msg)
        if not msg.tool_calls:
            return msg.content  # model says done
        for call in msg.tool_calls:
            fn = TOOLS[call.function.name]
            args = json.loads(
                call.function.arguments)
            try:
                out = fn(**args)
            except Exception as e:
                out = f"tool error: {e}"
            msgs.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": out,
            })
    return "hit max_steps -- gave up"


if __name__ == "__main__":
    print(run("Count the .py files here, then "
              "summarize naive_loop.py."))

Der Schleifenprozess verfügt über zwei Ausgänge, und beide sind von Bedeutung. Erfolg wird durch das eigene Urteil des Modells bestimmt, das sich darin äußert, dass keine weiteren Toolaufrufe erfolgen. Sicherheit wird durch einen einfachen Zähler, nämlich die Schrittanzahl, gewährleistet. Diese Asymmetrie, bei der das Modell allein entscheidet, wann die Arbeit abgeschlossen ist, ist genau das, was zuerst versagt.

Wo eine einzelne Schleife bei langen Aufgaben versagt

Führen Sie dieselbe Schleife bei einer zweistündigen Aufgabe statt bei einer zweiminütigen durch – dann beginnen die Fehler. Das MiniMax-Team hat die Fehler aufgelistet, auf die sie immer wieder stießen. Keiner davon ist ein Bug. Es handelt sich dabei um Eigenschaften eines einzelnen Kontexts, weshalb weder ein größeres Zeitfenster noch eine bessere Anweisung diese Probleme behebt.

Zunächst tritt Kontextangst auf: Der Agent stoppt, obwohl niemand es verlangt hat. Nach sieben Unteraufgaben schafft er drei und gibt an, Aufgaben 1, 2 und 3 erledigt zu haben, und fragt, ob er fortfahren soll. Modelle haben unklare Vorstellungen davon, wann eine lange Aufgabe abgeschlossen sein könnte, und die direkte Schulung dieser Einschätzung ist sowohl finanziell als auch in Bezug auf algorithmische Anstrengungen äußerst kostspielig. Eine einfache Schleife überlässt die Entscheidung zum Stoppen ihrem am wenigsten zuverlässigen Bestandteil.

Dann folgt die Degradation: Der Agent wird mit zunehmendem Kontext dümmer. Nach fünf Minuten ist er noch präzise, nach sechzigfünf Minuten verhält er sich wie ein abgelenkter Praktikant. Ein einziger Fehler, wie beispielsweise, dass eine Forschungsaufgabe in Marketingtexte übergeht, beeinträchtigt alles danach, da jede nachfolgende Generation auf dieser „verunreinigten“ Historie aufbaut.

Drittens wird die Arbeit von niemandem überprüft, da der Richter gleichzeitig auch der Teilnehmer ist. Ein einzelner Agent, der das Endergebnis erzeugt, bewertet sich selbst im selben Kontext, in dem er Fehler gemacht hat. Das ist keine Unaufrichtigkeit – es handelt sich dabei um die Überprüfung einer Szene, die das Modell selbst gerade erst erstellt hat.

Viertens ist da die Stille. Chat-basierte Interfaces lassen die Nutzer erwarten, innerhalb weniger Sekunden eine Bestätigung zu erhalten – selbst bei einer stundenlangen Aufgabe – doch eine Schleife, die innerhalb eines Modellaufrufs verborgen ist, kann nicht mitteilen, dass sie die Anfrage erhalten hat und zurückmelden wird. Laut der MiniMax-Studie löste die Frage „Warum antwortet mein Agent nicht?“ mehr Nutzerfeedback aus als alles andere.

Die Lösung: drei Rollen und ein deterministischer Motor

MiniMax antwortet strukturiert. Drei Arten von Rollen werden durch gewöhnlichen, deterministischen Code koordiniert – nicht durch einen cleveren Prompt – und dieser Code wird als Team Engine bezeichnet. Ein Aufgabenzyklus entspricht einer Sitzung, wobei die Engine jede Sitzung durch drei Zustände führt: Erstellung, Überprüfung und Abschluss. Im Falle eines Überprüfungserfolgs wird der Ersteller erneut aktiviert.

  • Der Leiter zerlegt die Anfrage des Benutzers in einen strukturierten Plan, entscheidet, welche Teilaufgaben gleichzeitig ausgeführt werden, antwortet dem Benutzer innerhalb von Sekunden, während der Rest im Hintergrund abläuft, und fügt schließlich die Ergebnisse zusammen.
  • Die Arbeiter übernehmen Unteraufgaben, wobei jede in einem eigenen Kontext mit eigenen Werkzeugen, Speicher und dem erforderlichen Ausgabeformat arbeitet. Diese Trennung ist wichtig, denn ein Arbeiter sieht nur eine Unteraufgabe – niemals mehrere Stunden an angesammelten Daten.
  • Die Überprüfer wandeln „Es ist fertig“ in „Es kann ausgeliefert werden“ um. Die Beziehung zwischen ihnen ist absichtlich konfrontativ gestaltet. Ein Arbeiter, der eine Aufgabe abschließt, startet den Überprüfer, und ein Überprüfer, der ablehnt, startet den Arbeiter erneut – ähnlich wie bei R&D und QA, wobei die Qualität aus den Iterationsrunden entsteht und nicht durch menschliche Mikroverwaltung.
  • Der Kern des Engines ist klein genug, um vollständig ausgeschrieben zu werden. Diese Version benötigt keine API-Schlüssel, da Stumpfrollen die LLM-Aufrufe ersetzen und ohne Berühren der Zustandsmaschine ausgetauscht werden können:

    # team_engine.py -- the loop promoted to a
    # state machine. One task lifecycle is one
    # Session; deterministic code owns every
    # transition: producing -> verifying -> done.
    from dataclasses import dataclass, field
    
    PRODUCING = "producing"
    VERIFYING = "verifying"
    DONE = "done"
    FAILED = "failed"
    
    
    @dataclass
    class Session:
        goal: str
        state: str = PRODUCING
        attempts: int = 0
        artifact: str = ""
        reviews: list = field(default_factory=list)
    
    
    def run_session(s, worker, verifier,
                    max_retries: int = 3):
        while s.state not in (DONE, FAILED):
            if s.state == PRODUCING:
                s.attempts += 1
                s.artifact = worker(
                    s.goal, s.reviews)
                s.state = VERIFYING
            elif s.state == VERIFYING:
                ok, report = verifier(
                    s.goal, s.artifact)
                s.reviews.append(report)
                if ok:
                    s.state = DONE
                elif s.attempts >= max_retries:
                    s.state = FAILED
                else:
                    # Reject: wake the worker with
                    # the review log in context.
                    s.state = PRODUCING
        return s
    
    
    def run_team(goals, worker, verifier):
        # Leader's job: split the goal, run the
        # sessions (a thread pool in real use),
        # then merge N artifacts into 1 result.
        done, failed = [], []
        for g in goals:
            s = run_session(Session(g),
                            worker, verifier)
            if s.state == DONE:
                done.append(s.artifact)
            else:
                failed.append(s.goal)
        return done, failed
    
    
    if __name__ == "__main__":
        # Stub roles so the engine runs anywhere;
        # swap in LLM calls for the real thing.
        def worker(goal, reviews):
            v = len(reviews) + 1
            return f"draft v{v}: {goal}"
    
        def verifier(goal, artifact):
            ok = "v2" in artifact
            return ok, f"review {artifact!r}: {ok}"
    
        done, failed = run_team(
            ["intro", "benchmarks", "faq"],
            worker, verifier)
        print("delivered:", done)
        print("failed:", failed)
    

    Wenn man es ausführt, wird die erste Version jeder Sitzung abgelehnt, der Worker wird mit dem Überprüfungsprotokoll in seinem Kontext gestartet, und die zweite Version wird akzeptiert. Setzt man es neben die naive Schleife, fallen einige Unterschiede ins Auge.

    • Beendigung: Die einfache Schleife endet, wenn das Modell aufhört, Toolaufrufe auszusenden (fuzzy), oder wenn die Schrittanzahl erreicht ist, während der Engine explizite „done“- und „failed“-Zustände sowie ein in Code festgelegtes Wiederholungslimit verwendet werden.
    • Überprüfung: In der Schleife wird der gleiche Kontext, der die Arbeit erzeugt hat, zur Überprüfung genutzt, während das Team einen adversarialen Verifier in einem separaten Kontext unter Verwendung von Befehlen einsetzt.
    • Parallelität: Die Schleife arbeitet in einem sequenziellen Kontext, während das Team Worker in parallelen Sitzungen ausführt und der Leader mehrere davon zu einem zusammenführt.
    • Persistenz: Die Schleife speichert eine Nachrichtenliste bis zur Zusammenfassung, während das Team den Sitzungszustand, Überprüfungsprotokolle, Artefakte sowie die Speicherung pro Agent beibehält.

    Der Leader ist außerdem nicht auf einmalige Aufrufe beschränkt, die eine Unteraufgabe erstellen und Text zurückgeben. Im Design von MiniMax liest er den aktuellen Status des Engines und kann zusätzliche Anfragen in eine laufende Sitzung einspeisen, die gerade erstellt oder überprüft wird – es gibt somit sowohl proaktive Einspeisung als auch Anfragen auf Verlangen. Dadurch wird auch das Problem der Stille gelöst: Der Leader bestätigt innerhalb weniger Sekunden, und der Benutzer erhält anschließend Berichte an wichtigen Meilensteinen wie „gestartet“, „blockiert“, „Entscheidung erforderlich“ und „abgeschlossen“.

    Verankerung des Verifiers

    Der Fehler der Selbstbewertung verschwindet nur dann, wenn das Urteil von außerhalb jedes Modells stammt. Bei Code umgibt MiniMax die Ausführung mit einem Rahmen. Die Arbeit erfolgt in Branches, läuft in Sandboxes, Änderungen werden als Diffs dargestellt, Tests können wiederholt werden, Reviews hinterlassen Aufzeichnungen und Fehlschläge können erneut abgespielt werden. Der wichtigste Satz: Eine Abbruchbedingung muss an etwas Deterministisches gebunden sein, das von außen beobachtet werden kann – wie Tests, Lint-Tools und der Build-Prozess – und niemals an das Gefühl des Modells, mit der Arbeit fertig zu sein.

    # grounded_verifier.py -- evidence over vibes.
    # The verdict comes from exit codes and logs,
    # never from the model's self-assessment.
    import subprocess
    import sys
    
    PY = sys.executable
    
    # pytest exit codes: 0 = all passed,
    # 1 = failures, 5 = no tests collected
    # (acceptable for a docs-only change).
    PYTEST_OK = (0, 5)
    
    CHECKS = [
        ("diff", ["git", "diff", "--stat"], (0,)),
        ("build", [PY, "-m", "compileall",
                   "-q", "."], (0,)),
        ("tests", [PY, "-m", "pytest", "-q",
                   "--maxfail", "1"], PYTEST_OK),
    ]
    
    
    def run_check(name, cmd, ok_codes, cwd):
        p = subprocess.run(
            cmd, cwd=cwd,
            capture_output=True, text=True)
        tail = (p.stdout + p.stderr)[-2000:]
        return {
            "check": name,
            "cmd": " ".join(cmd),
            "exit_code": p.returncode,
            "ok": p.returncode in ok_codes,
            "output_tail": tail,
        }
    
    
    def verify(repo: str):
        evidence = []
        for name, cmd, ok_codes in CHECKS:
            e = run_check(name, cmd, ok_codes, repo)
            evidence.append(e)
            if not e["ok"]:
                # Reject with logs attached: the
                # worker retries against real
                # errors, not "please improve".
                return False, evidence
        return True, evidence
    
    
    
    if __name__ == "__main__":
        ok, ev = verify(".")
        for e in ev:
            print(f"{e['check']:>6} exit "
                  f"{e['exit_code']} ok={e['ok']}")
        print("verdict:", "pass" if ok else "fail")
    

    Die Liste der Beweise dient gleichzeitig als Überprüfungsprotokoll und ist Teil einer nachvollziehbaren, nachprüfbaren Spur, die zwischen etwas, das tatsächlich geliefert werden kann, und etwas, das lediglich plausibel erscheint, unterscheidet. MiniMax unterteilt die Überprüfungsfunktion weiter. Ein Tester erzeugt externe Beweise, indem er die Tests ausführt, Fehlerprotokolle komprimiert und eine minimale Wiederherstellung vornimmt. Ein Überprüfer stellt eine andere Frage – nicht, ob etwas bestanden hat, sondern ob sich der Code auf diese Weise ändern sollte – und betrachtet dabei Abstraktionsgrenzen, neue Abhängigkeiten, erweiterte Berechtigungen sowie Geheimnisse, die in die Protokolle gelangen. Überprüfer können parallel zu Spezialisten wie Allgemeinen, Sicherheits- oder Domänenexperten arbeiten.

    Was ein Team kostet und wann man darauf verzichten sollte

    Ein Team ist kein kostenloses Parallelverfahren. Sobald die Agenten zusammenarbeiten, treten drei Kosten auf, und ein größeres Kontextfenster beseitigt keine davon.

    Kosten des Übergabeprozesses: Dasselbe Information muss zwischen den Agenten neu organisiert werden, wodurch Forschungsseiten zu einem Schreibbrief werden, der wiederum als Eingabe für eine Formatprüfung dient. Die Lösung besteht darin, lesbare Dateien mit kurzen Zusammenfassungen über Pfade weiterzugeben, anstatt rohe Kontextdatenmengen.

    Kosten des Teilen: Jeder globale, gemeinsam genutzte Token wird in jeder Runde von jedem Mitarbeiter bezahlt. Ein praktikables Layout bewahrt die Speicherinhalte für jeden Agenten privat, ermöglicht es den Agenten, sich durch CLI-Unterbrechungen gegenseitig anzusteuern, und bietet eine gemeinsame Tafel, die die Agenten nur dann lesen, wenn sie sie benötigen.

    Die dritte Kostenart ist die Aggregierungskosten. Zehn teilweise Entwürfe zu sammeln ist einfach. Sie in ein einziges Dokument mit passenden Fakten, Zitaten und Tonfall zusammenzufassen, ist die schwierigste Aufgabe des Leiters und erfordert einen Zusammenführungsschritt – nicht zusätzliches Personal.

    Auch Konsens hat seinen Preis. Eine freie Debatte zwischen Agenten kann mehrere Male so viele Token verbrauchen wie die isolierte Selbstkorrektur, ohne die Genauigkeit zu verbessern. Der Wert liegt in der Struktur: begrenzte Sitzungen, explizite Artefakte sowie ein Prüfer, der Ablehnungen vornehmen darf.

    Überspringen Sie das Team, wenn die Aufgabe klein, lokal und kostengünstig zu überprüfen ist, denn ein einziger Schleifenlauf kostet weniger. Verwenden Sie ein Team, wenn die Arbeit klar aufgeteilt ist, Fehler teuer sind und eine unabhängige Überprüfung Beweise liefern kann, dass das Modell sich nicht selbst überlisten kann. Ein guter Test: Fügen Sie eine Rolle nur hinzu, wenn sie einen Fehlerfall abdeckt, den keine andere Rolle abdeckt; andernfalls bringt die Zusammenarbeit keinen Vorteil.

    Was zuerst entwickelt werden sollte

    Beginnen Sie mit der kleinsten Zustandsmaschine, der Sie vertrauen können. Jede Sitzung erhält ein Ziel, ein Artefakt, ein Überprüfungsprotokoll sowie einen Wiederholungsbudget, und jede Übergangssituation wird ausführlich beschrieben. Stellen Sie den Überprüfer hinter ein System, dessen Antworten aus Tests, Lint-Checks, Kompilierungen oder ähnlichen mechanischen Überprüfungen stammen. Parallel arbeitende Prozesse kommen später hinzu: Parallelismus ist eine Optimierung, während die Grenzen der Korrektheit zur Architektur gehören. Lassen Sie den Merge-Schritt des Leiters sichtbar, halten Sie die Übertragungsartefakte für Inspektionen offen und dokumentieren Sie, warum jede Sitzung beendet wurde.

    Was dabei herauskommt, ist kein autonomes Denkvermögen. Es handelt sich um einen kontrollierten Arbeitsablauf: Die Modelle geben Vorschläge, die Tools führen die Ausführung durch, und herkömmlicher Code entscheidet, ob die Aufgabe abgeschlossen ist. Bauen Sie zuerst manuell einen einfachen Schleifencode, bevor Sie ein Framework verwenden. Rechnen Sie damit, dass lange Aufgaben durch Angst, Abweichungen, Selbstbewertung und Schweigen die Struktur untergraben – und dass dies durch keine Menge Anweisungen behoben werden kann. Betrachten Sie Multi-Agenten als Laufzeitumgebung mit einer Steuerungsplattform für Zustände, Ereignisse, Berechtigungen, Überwachbarkeit sowie wiederherstellbare Artefakte – und nicht als Rollenspiel in einer Anweisung. Planen Sie außerdem Zeit für die Übergabe, den Austausch und die Aggregation ein, bevor Sie ein Team zusammenstellen, und gründen Sie niemals ein Team wegen eines Tippfehlers.

    Verwandte Literatur

  • Delegierungsverträge für KI-Agententeams: Umfang, Akzeptanz und Handhabung von Fehlern — Warum Agentenbezeichnungen fehlerhafte Übergaben nicht verhindern und wie ein Delegierungsvertrag mit durchgesetzten Richtlinien sowie unabhängigen Akzeptanz- und Fehlerrichtlinien getestet werden kann.
  • Lokale Programmieragenten auf einem Mac mit oMLX ausführen: Erklärung des auf der Festplatte basierenden KV-Caches — Warum lokale Agenten bei langen Sitzungen langsamer werden, wie oMLX Präfixe auf SSD speichert und wie man es installiert sowie Claude Code, Codex, Cursor und OpenCode auf Apple Silicon verbindet.
  • Sicheres Abfragen von Postgres mit KI: MCP-Server, Einrichtung und Best Practices — Wie ein PostgreSQL-MCP-Server es ermöglicht, dass Claude Code oder Cursor aktuelle Schemata lesen, sowie ein Vergleich von CrystalDBA, Supabase und dem archivierten Referenzserver sowie sichere Vorgehensweisen.
  • Ein Agent ist ein While-Loop: LangChain create_agent und LangGraph Fit — Ein in zwanzig Zeilen implementierter Agent zur Aufrufung von Tools, die vier Produktionsprobleme, mit denen Loops konfrontiert sind, und wann man von LangChain create_agent zu LangGraph State Graphs wechseln sollte.
  • Praktische Notizen: Agent Loop Engineering – Ein vollständiger Blog von der Entwicklung bis zur produktiven KI — Detaillierte Anleitung zu den Praktischen Notizen: Agent Loop Engineering – Ein vollständiger Blog von der Entwicklung bis zur produktiven KI: Verträge, Überprüfungen sowie Code-Blöcke, die Teams nutzen können, die dieses Muster implementieren.