Startseite / Artikel

Agent Frameworks Reason, Temporal Survives: Langlebige Ausführung für Agenten

LangGraph, CrewAI und AutoGen koordinieren Aufrufe von LLMs, verhindern aber keine Abstürze oder wochenlange Wartezeiten. Wie Temporals langlebige Ausführung diese Lücke schließt – und wann sie überflüssig ist.

Verlag: REACTAPP.TOP11 Min. Lesezeit2460 Wörter

Quellen: Agentic AI Frameworks in 2026: The 3 Everyone’s Using and the 1 Nobody Talks About

Umfragen unter Ingenieuren, die 2026 agierende Systeme entwickeln, zu ihren Frameworks zeigen, dass fast alle LangGraph, CrewAI, AutoGen oder eine Kombination daraus nennen. Keines dieser Tools ist schlecht. Das Problem ist, dass die gesamte Diskussion auf der falschen Ebene des Stack stattfindet.

Produktionsagentensysteme weisen ein wiederkehrendes Muster auf. Was den Nachtdienst-Ingenieur um 3 Uhr morgens weckt, ist selten das Framework, das bestimmt, was der Agent denkt. Es handelt sich fast immer um eine bestimmte Art von Fehlern: Ein Prozess stirbt mitten in einem Workflow ab, ein Schritt wird zweimal ausgeführt, weil seine Wiederholung nicht idempotent war, eine Pod-Entfernung tötet eine lang laufende Agentenaufgabe nach 40 Minuten ab, ohne dass es möglich ist, sie wieder aufzunehmen, oder der Workflow-Zustand befindet sich in einer Python-Variable, die verschwindet, wenn der Container recycelt wird.

Niemand von LangGraph, CrewAI oder AutoGen bewältigt diese Aufgabenfamilie vollständig von Anfang bis Ende – und das ist beabsichtigt, da sie dazu dienen, Aufrufe an große Sprachmodelle sowie den Einsatz von Tools zu koordinieren. Produktionsagentensysteme scheitern tatsächlich an mangelnder Zuverlässigkeit bei der Ausführung, einem weitaus älteren und weniger interessanten Bereich, der das Konzept des „Agents“ um etwa ein Jahrzehnt vorausging. Temporal, das System, das diesen Ansatz verfolgt, taucht kaum in Debatten über Agentenframeworks auf, hat sich jedoch stillschweigend zur Grundlage für immer mehr ernsthafte Agentenimplementierungen entwickelt. Im Folgenden wird erläutert, was die drei beliebten Frameworks gut können, an welchen Stellen jede Abstraktion versagt und warum die unmodische vierte Option eine wirtschaftliche Investition wert ist.

LangGraph

Der Beitrag von LangGraph besteht darin, aus einem Agenten ein explizites Graph über typisierte Zustände zu machen – anstelle einer impliziten Kette von Anfragen. Das hat tatsächlichen Wert, denn der Kontrollfluss wird überprüfbar und testbar. Ein Triage-Arbeitsablauf könnte beispielsweise so aussehen:

from typing import TypedDict, Literal
from langgraph.graph import StateGraph, END

class TriageState(TypedDict):
    ticket_id: str
    category: Literal["billing", "technical", "abuse", "unknown"]
    priority: int
    assigned_queue: str | None

def classify_node(state: TriageState) -> TriageState:
    category = llm_classify(state["ticket_id"])  # the only probabilistic step
    return {**state, "category": category}

def route_node(state: TriageState) -> str:
    # deterministic, testable, no LLM involved
    return {
        "billing": "billing_queue",
        "technical": "tech_queue",
        "abuse": "trust_and_safety_queue",
        "unknown": "human_review",
    }[state["category"]]

graph = StateGraph(TriageState)
graph.add_node("classify", classify_node)
graph.set_entry_point("classify")
graph.add_conditional_edges("classify", route_node)
app = graph.compile()

Das ist eine gute Idee – sauber formuliert. Probleme entstehen, wenn etwas länger bestehen muss als ein einziger invoke()-Aufruf sowie dessen Checkpoint. Man denke an die Koordination zwischen verschiedenen Diensten, Workflows, die über mehrere Tage andauern, oder Schritte, bei denen LangGraph keine Kontrolle hat: ein Spark-Job, eine Freigabewarteschlange, die über Slack läuft und erst Tage später antwortet, oder ein Dienst, den ein anderes Team nach eigenem Rhythmus bereitstellt. Das Checkpointing funktioniert zwar, deckt aber nur LangGraphs eigenes Ausführungsmodell ab. Nach einer Übertragung an Infrastruktur, die keine Verbindung zu LLMs hat, muss die Langzeitverfügbarkeit manuell gewährleistet werden.

CrewAI

Eine Mehr-Agenten-Gruppe kann in wenigen Zeilen deklariert werden:

from crewai import Agent, Task, Crew

researcher = Agent(role="Research Analyst", goal="Find relevant market data", tools=[search_tool])
writer = Agent(role="Report Writer", goal="Turn research into a client-ready summary")

crew = Crew(agents=[researcher, writer], tasks=[
    Task(description="Research {company}'s Q2 earnings", agent=researcher),
    Task(description="Write a 3-paragraph summary for the client", agent=writer),
])
crew.kickoff(inputs={"company": "Acme Corp"})

Kein anderes Tool bringt eine lockere Idee für einen Mehr-Agenten-Arbeitsablauf schneller als in zwanzig Minuten zu einer laufenden Demo – und zwar mit CrewAI. Die Kosten treten danach auf. kickoff() blockiert den Prozess, es gibt keine Möglichkeit, ihn für Stunden oder Tage pausieren zu lassen, kein Wiederholungsverhalten außer dem, was den einzelnen Tools zugeordnet ist, und keinen dauerhaften Zustand außer dem, was der Entwickler selbst speichert. Es orchestriert das Denken von LLMs, ist aber kein Workflow-Engine – und wurde auch nie so beworben, obwohl viele Teams es versehentlich so nutzen.

AutoGen

Eine minimale AutoGen-Einrichtung kombiniert einen Assistenten mit einem Benutzer-Proxy:

import autogen

assistant = autogen.AssistantAgent(name="assistant", llm_config={"config_list": config_list})
user_proxy = autogen.UserProxyAgent(name="user_proxy", human_input_mode="NEVER")
user_proxy.initiate_chat(assistant, message="Summarize this quarter's incident reports.")

AutoGen’s konversationeller, emergenter Stil eignet sich gut für exploratives Arbeiten. Seine Version von 2026, mit einer strukturierteren Arbeitsumgebung sowie ereignisgesteuerten Mustern, neigt sogar dazu, Orchestrierung von Konversationen zu trennen. Dennoch basiert das System weiterhin auf Transkripten von Nachrichten statt auf einer dauerhaften, strukturierten Aufzeichnung der Ausführung. Wenn der Prozess, der eine Konversation hostet, abstirbt, bleibt nur ein unvollständiger Chat-Log übrig – keine Berechnung, die fortgesetzt werden könnte.

Die gemeinsame Lücke

Sie alle leisten gute Arbeit bei dem wirklich Neuen: Dem LLM dabei zu helfen, zu reasoningen, Tools aufzurufen und mit anderen von LLMs gesteuerten Agenten zusammenzuarbeiten. Die ältere, einfachere Herausforderung – sicherzustellen, dass ein mehrschrittiger Prozess auch bei Abstürzen, Neustarts, Zeitüberschreitungen oder anderen Störungen ordnungsgemäß abgeschlossen wird – versuchen sie nicht zu bewältigen. Das ist kein Fehler ihrerseits. Es wäre ein Kategorienfehler, das von ihnen zu erwarten.

Betrachten Sie einen tatsächlichen Arbeitsablauf, nicht eine Demo: Ein Agent, der Lieferantenrechnungen abgleicht. Er extrahiert Posten aus einer PDF-Datei, vergleicht sie mit einer Bestellbestätigung im ERP-System, markiert Abweichungen über einem bestimmten Schwellenwert zur Überprüfung durch einen Menschen und wartet anschließend möglicherweise Tage auf eine Antwort, bevor er die genehmigte Eintragung in der Buchhaltung einstellt. Ein fehlgeschlagener Schritt muss ohne doppelte Erfassung erneut versucht werden. Ein Neustart mitten im Arbeitsablauf darf weder zu einem Neuanfang von vorne noch zu einem stillen Verschwinden der Daten führen.

┌────────────┐     ┌───────────────┐     ┌─────────────────┐     ┌───────────────┐
│  Extract   │ --> │ Match against │ --> │  Human approval │ --> │  Post to ERP  │
│  line items│     │ purchase order│     │  (can take days)│     │  (idempotent) │
└────────────┘     └───────────────┘     └─────────────────┘     └───────────────┘
      ▲                                          │
      └───────────── retry on failure ───────────┘

Niemand hat LangGraph, CrewAI oder AutoGen so konzipiert, dass sie garantieren könnten, dieses Diagramm über einen Neustart der Pod-Instanz am dritten Tag zu erhalten, während jemand noch Überlegungen zur Position 14 anstellt. Es geht dabei nicht um die Orchestrierung von LLMs, sondern um zuverlässige Ausführung – und die Systeme, die dies gut bewältigen, wobei Temporal an der Spitze steht, haben seit Jahren unscheinbare Backend-Aufgaben wie Zahlungsverarbeitung, Bestellabwicklung sowie Microservices im Saga-Muster unterstützt, lange bevor das Wort „Agent“ solche Programme beschrieb.

Wie Temporal funktioniert

Temporals Modell erscheint auf den ersten Blick überraschend einfach. Der Workflow wird als Funktion in gewöhnlichem Code geschrieben, mit Schleifen, bedingten Anweisungen und Funktionsaufrufen, wobei Temporal die Ausführungshistorie dieser Funktion dauerhaft speichert. Falls der Prozess zu jedem Zeitpunkt abstürzt, wiedergibt Temporal die Funktion anhand der gespeicherten Ereignishistorie bis zum Absturzpunkt und setzt deterministisch fort, ohne Nebeneffekte erneut auszuführen. Die „stille Magie“ besteht darin, dass Nebeneffekte wie der Aufruf eines LLMs, die Anfrage an eine API oder das Schreiben in eine Datenbank in separaten Funktionen untergebracht sind, die als Aktivitäten bezeichnet werden. Temporal versucht diese unabhängig voneinander mit konfigurierbaren Wartezeiten erneut auszuführen, während die Workflow-Funktion lediglich deren Koordination übernimmt.

Der untenstehende Rechnungskonsolidierungsagent behandelt den Aufruf des LLMs als eine von mehreren Aktivitäten. Zunächst die Aktivitäten, also die Teile, die mit der Außenwelt interagieren:

# activities.py — the parts that touch the outside world (LLMs, APIs, DBs)
from temporalio import activity
from dataclasses import dataclass

@dataclass
class Invoice:
    invoice_id: str
    vendor: str
    line_items: list[dict]

@activity.defn
async def extract_line_items(pdf_url: str) -> list[dict]:
    # calls a vision-capable LLM, or a dedicated OCR model — doesn't matter to the workflow
    text = await download_and_ocr(pdf_url)
    return await llm_extract_structured(text, schema=LINE_ITEM_SCHEMA)

@activity.defn
async def match_purchase_order(invoice: Invoice) -> dict:
    po = await erp_client.get_purchase_order(invoice.vendor)
    discrepancies = diff_line_items(invoice.line_items, po["line_items"])
    return {"po_id": po["id"], "discrepancies": discrepancies}
@activity.defn
async def post_to_erp(invoice_id: str, po_id: str) -> dict:
    # idempotency is enforced with a deterministic key derived from workflow inputs,
    # not a random UUID generated per attempt — this is the same lesson as always
    return await erp_client.post_journal_entry(
        idempotency_key=f"invoice-post-{invoice_id}",
        invoice_id=invoice_id,
        po_id=po_id,
    )

@activity.defn
async def notify_human_reviewer(invoice_id: str, discrepancies: list[dict]) -> None:
    await slack_client.post_message(
        channel="#ap-review",
        text=f"Invoice {invoice_id} has {len(discrepancies)} discrepancies needing review.",
    )

Danach kommt der Workflow, der die Rolle der Orchestrierung übernimmt, die normalerweise von LangGraph oder CrewAI ausgeübt wird – mit dem Unterschied, dass die Zuverlässigkeit bereits integriert ist und nicht von der Aktivierung eines Checkpointers abhängt:

# workflows.py
from temporalio import workflow
from datetime import timedelta

@workflow.defn
class InvoiceReconciliationWorkflow:
    def __init__(self) -> None:
        self._approval_received = False
        self._approved = False

    @workflow.signal
    async def submit_human_decision(self, approved: bool) -> None:
        # A signal is how the outside world talks to a running, possibly-days-old workflow
        self._approval_received = True
        self._approved = approved

    @workflow.run
    async def run(self, pdf_url: str, invoice_id: str, vendor: str) -> dict:
        line_items = await workflow.execute_activity(
            extract_line_items, pdf_url,
            start_to_close_timeout=timedelta(minutes=2),
            retry_policy=workflow.RetryPolicy(maximum_attempts=3),
        )

        invoice = Invoice(invoice_id=invoice_id, vendor=vendor, line_items=line_items)
        match_result = await workflow.execute_activity(
            match_purchase_order, invoice,
            start_to_close_timeout=timedelta(seconds=30),
            retry_policy=workflow.RetryPolicy(maximum_attempts=5),
        )

        if match_result["discrepancies"]:
            await workflow.execute_activity(
                notify_human_reviewer, invoice_id, match_result["discrepancies"],
                start_to_close_timeout=timedelta(seconds=10),
            )
            # This is the line that would be genuinely hard to write safely in the
            # other three frameworks: wait, durably, for up to 7 days, for a signal
            # that might arrive from a Slack button, a web form, anything —
            # while the workflow's entire state sits safely in Temporal's history,
            # costing zero compute while it waits.
            await workflow.wait_condition(
                lambda: self._approval_received,
                timeout=timedelta(days=7),
            )
            if not self._approved:
                return {"status": "rejected", "invoice_id": invoice_id}

        result = await workflow.execute_activity(
            post_to_erp, invoice_id, match_result["po_id"],
            start_to_close_timeout=timedelta(seconds=30),
            retry_policy=workflow.RetryPolicy(maximum_attempts=5),
        )
        return {"status": "posted", "invoice_id": invoice_id, "erp_result": result}

Mehrere Details verdienen Aufmerksamkeit, denn sie sind genau die Fehlermuster, die Agentensysteme instabil machen, und hier handelt es sich um Eigenschaften der Laufzeitumgebung und nicht um Funktionen, an die man sich erinnern muss, um sie hinzuzufügen.

Ein siebentägiges Warten ist kostenlos und überlebt alles. workflow.wait_condition hält weder einen Thread noch einen Prozess noch einen Container eine Woche lang offen. Temporal speichert den Zustand des Workflows in seinem eigenen langlebigen Speicher, sodass der Worker in der Zwischenzeit neu bereitgestellt, auf Null skaliert oder vollständig abstürzen kann. Wenn das Signal schließlich eintrifft, wiedergibt Temporal die Historie ab und setzt genau an dieser await-Stelle fort. Dasselbe bei einem LangGraph Checkpointer oder einer CrewAI Crew zu tun, würde im Grunde bedeuten, Temporals Ereignisquellungsmodell neu aufzubauen.

Die Wiederholungsversuche sind automatisch, auf enges Gebiet beschränkt und wiederholen niemals bereits abgeschlossene Arbeiten. Angenommen, post_to_erp fehlschlägt nachdem match_purchase_order erfolgreich war. Temporal wird match_purchase_order nicht erneut ausführen, da sein Ergebnis bereits in der dauerhaften Historie gespeichert ist. Setzen Sie dies neben die naive Herangehensweise an dieselbe Idee:

# BEFORE — naive retry loop, the kind you write at 11pm under deadline pressure
async def reconcile_invoice_naive(pdf_url, invoice_id, vendor):
    for attempt in range(3):
        try:
            line_items = await extract_line_items(pdf_url)
            match_result = await match_purchase_order(Invoice(invoice_id, vendor, line_items))
            if match_result["discrepancies"]:
                await notify_human_reviewer(invoice_id, match_result["discrepancies"])
                approved = await wait_for_approval_somehow(invoice_id)  # good luck
                if not approved:
                    return {"status": "rejected"}
            return await post_to_erp(invoice_id, match_result["po_id"])
        except Exception:
            continue  # retries EVERYTHING from the top, including re-extracting the PDF

Diese naive Version wiederholt die LLM-Extraktion bei jedem Neversuch, wodurch Tokens und Geld für einen Schritt ausgegeben werden, der bereits funktioniert hat. Zudem bietet wait_for_approval_somehow keine gute Lösung, falls er länger bestehen muss als ein Neustart. Temporals Version begegnet diesen Problemen nicht durch zusätzlichen Code – aufgrund der Struktur der Ausführung treten diese Probleme in dieser Form nie auf.

Einfaches Python bedeutet testbarkeit. Temporal enthält ein Testframework, das in einem Unit-Test ein Warten von timedelta(days=7) in Millisekunden überspringen kann, und Aktivitäten können ohne Netzwerkkontakt emuliert werden:

import pytest
from temporalio.testing import WorkflowEnvironment
from temporalio.worker import Worker

@pytest.mark.asyncio
async def test_rejected_invoice_does_not_post_to_erp():
    async with await WorkflowEnvironment.start_time_skipping() as env:
        async with Worker(
            env.client, task_queue="test-queue",
            workflows=[InvoiceReconciliationWorkflow],
            activities=[extract_line_items, match_purchase_order, notify_human_reviewer, post_to_erp],
        ):
            handle = await env.client.start_workflow(
                InvoiceReconciliationWorkflow.run,
                args=["fake.pdf", "INV-001", "Acme"],
                id="test-invoice-001",
                task_queue="test-queue",
            )
            await handle.signal(InvoiceReconciliationWorkflow.submit_human_decision, False)
            result = await handle.result()
            assert result["status"] == "rejected"

Achten Sie auf die Abwesenheiten: Kein echter siebentägiger Schlaf, keine Einmischung in die internen Abläufe eines Checkpointers, kein manuelles Abwägen bezüglich Neustarts. Belastbarkeit ist eine tragende Infrastruktur – etwas, das jeder Test nicht erneut nachweisen muss.

Nutzung beider Schichten

Die ausgewogene Lösung besteht nicht darin, LangGraph zugunsten von Temporal zu verwerfen. Beide arbeiten auf unterschiedlichen Ebenen, und die besten Produktionsumgebungen von 2026 kombinieren sie: LangGraph oder CrewAI für das Abwägen innerhalb eines Schritts, Temporal für die Workflow-Ebene, die den mehrtägigen, mehrdienstlichen Prozess verbindet. Eine Aktivität kann einen solchen begrenzten Abwägungsschritt wie folgt umfassen:

@activity.defn
async def run_agent_reasoning_step(ticket_text: str) -> dict:
    # LangGraph handles the bounded, single-turn reasoning —
    # classify, decide, draft — inside an activity that Temporal
    # retries and times out like any other side effect.
    result = triage_graph.invoke({"ticket_id": ticket_text, "audit_log": []})
    return {"category": result["category"], "priority": result["priority"]}

@workflow.defn
class SupportTicketWorkflow:
    @workflow.run
    async def run(self, ticket_id: str, ticket_text: str) -> dict:
        triage = await workflow.execute_activity(
            run_agent_reasoning_step, ticket_text,
            start_to_close_timeout=timedelta(seconds=30),
            retry_policy=workflow.RetryPolicy(maximum_attempts=3),
        )
        if triage["priority"] >= 8:
            await workflow.execute_activity(page_oncall_engineer, ticket_id, ...)
            await workflow.wait_condition(lambda: self._acknowledged, timeout=timedelta(minutes=15))
        # ... routing, SLA timers, escalation chains — all durable, all testable

Das ist die Architektur, die für ein Team empfohlen wird, dessen Agentensystem in der Produktion eingesetzt werden muss. Das auf LLMs ausgelegte Framework kümmert sich um das, wofür es geeignet ist – nämlich das Reasoning innerhalb begrenzter Schritte. Ein robuster Ausführungsmotor übernimmt die Aufgaben, für die er entwickelt wurde, und sorgt dafür, dass der gesamte Prozess, egal ob er Minuten oder Wochen dauert und ob er LLM-Aufrufe oder menschliche Genehmigungen umfasst, entweder korrekt abgeschlossen wird oder deutlich fehlschlägt. Keiner der Komponenten muss die Rolle des anderen übernehmen.

Wo zeitbezogene Kosten zu hoch sind

Temporal weist Nachteile auf, die es zu einer ungeeigneten Standardlösung für viele Aufgaben im Agentenbetrieb machen. Es handelt sich dabei um ein weiteres zu betreibendes verteiltes System – sei es ein selbst gehosteter Server oder eine Gebühr für Temporal Cloud – zusammen mit Arbeitern und Aufgabenschlangen; das erfordert weitaus mehr Infrastruktur als ein Python-Prozess, der LangGraph ausführt. Zudem unterliegt der Workflow-Code strengen Determinismusregeln. Die direkte Aufrufung von datetime.now(), der Einsatz unkontrollierter Zufälligkeiten oder die Ausführung von Roh-I/O direkt in der Workflow-Funktion sind alle verboten. Teams benötigen Zeit, um dies zu verstehen; das Verletzen der Regeln führt bei erneuter Ausführung zu rätselhaften Fehlern aufgrund von Nicht-Determinismus. Für kurze, einprozessige Aufgaben – wie das Beantworten einer Frage oder das Zusammenfassen eines Dokuments in einem einzigen LLM-Aufruf – ist eine dauerhafte Ausführung reine Overhead, da es keinen mehrschrittigen Prozess gibt, der durch einen Absturz beschädigt werden könnte.

Eine Entscheidungsregel

Wählen Sie eine zuverlässige Ausführung als Grundlage, wenn ein Workflow länger andauern könnte als ein einzelner Prozess, Koordination zwischen verschiedenen Diensten erfordert oder auf eine Person warten muss, oder wenn es um Daten geht, die nicht zweimal verarbeitet werden dürfen – wie Geld, Lagerbestände oder E-Mails, die nicht zweimal versandt werden sollten. Lassen Sie das gewünschte LLM-Framework als Komponente innerhalb eines seiner Schritte laufen. Wenn alles innerhalb eines einzigen Anfrage-Antwort-Zyklus beginnt und endet, überspringen Sie es, da dessen Hinzufügen ein noch nicht vorhandenes Problem lösen würde.

Viele Diskussionen zum Thema „Welches Agentenframework soll ich verwenden?“ übersehen eine wichtigere Frage: Welcher Schicht des Systems muss tatsächlich mehr Sicherheit verliehen werden? LangGraph, CrewAI und AutoGen führen eine wertvolle Debatte darüber, wie das Reasoning von LLMs sowie die Koordination mehrerer Agenten strukturiert werden kann. Doch die Frage, ob ein Agentensystem einer fehlerhaften Implementierung, einer Netzwerkteilung oder der Situation überlebt, in der eine Person drei Tage braucht, um „Genehmigen“ zu klicken, ist eine separate Frage. Workflow-Engine haben diese bereits vor langer Zeit stillschweigend gelöst, indem sie zuverlässige Ausführungen ermöglichten – noch bevor der Begriff agenteles KI aufkam. Die beliebten Frameworks optimieren das, was in einer Demo sichtbar ist. Das übersehene Element entscheidet jedoch darüber, ob nach einem Monat noch etwas übrig ist.