Agent oder Workflow?
Track KI · M4 Baustein 01 · ca. 45 Min.
Worum es geht
“Agent” ist ein Modewort geworden. Viele Aufgaben, die man einem Agenten geben will, löst ein fester Workflow (feste Schrittfolge, jeder Schritt eine Funktion oder ein Modellaufruf) besser: vorhersagbarer, günstiger, leichter zu testen. Ein Agent lohnt sich nur, wenn Anzahl und Reihenfolge der Schritte von der Aufgabe abhängen. Technisch ist ein Agent kein neues Konzept: Es ist das Tool Calling aus Lektion 06 in einer Schleife. Wer zuerst die Workflow-Muster beherrscht, erkennt schneller, wann die Schleife wirklich nötig ist.
In diesem ersten Teil lernst du die Entscheidung (Workflow oder Agent?) und die drei Muster, die oft schon reichen: Verkettung (prompt chaining), Routing und Parallelisierung (parallelization). Die Tool-Use-Schleife mit ihren Schutzgittern baust du in Teil 2.
Was im Browser läuft: Kein echtes Modell. Wo in echtem Code ein Modellaufruf stünde, steht eine kleine Funktion mit festen Regeln.
Zeitplan ehrlich: etwa 15 Minuten Lesen, 30 Minuten für die drei Übungen und die Transferaufgabe.
Von JS/TS her gedacht
Routing mit Fallback sieht in JavaScript fast gleich aus. Ausgeführt mit Node 20:
const handler = {
lieferung: () => "Weiter an Versand",
rechnung: () => "Weiter an Buchhaltung",
};
const klassifiziere = (f) => (f.toLowerCase().includes("paket") ? "lieferung" : "sonstiges");
function route(frage) {
const etikett = klassifiziere(frage).trim().toLowerCase();
return (handler[etikett] ?? (() => "Weiter an einen Menschen"))(frage);
}
console.log(route("Wo ist mein Paket?"));
console.log(route("Wie ist das Wetter?"));
console.log(handler["sonstiges"]);Ausgabe: Weiter an Versand, Weiter an einen Menschen, undefined. Der Unterschied zu Python: Ein unbekannter Schlüssel ist in JS ein stilles undefined, in Python ein lauter KeyError. Beides ist ohne Fallback ein Fehler, nur zeigt er sich an anderer Stelle.
| Idee | JavaScript / TypeScript | Python |
|---|---|---|
| Handler-Tabelle | Objekt { etikett: fn } |
dict {"etikett": fn} |
| Unbekanntes Etikett | undefined, Aufruf wirft TypeError |
KeyError beim Zugriff, dict.get gibt None |
| Standardwert | handler[e] ?? fallback |
handler.get(e, fallback) |
| Verkettung | await a(); await b(x) oder .then |
b(a(x)) oder eine Schleife über Schritte |
| Fehler eines Schritts | try / catch |
try / except |
Konzept
Schritt 1: Agent oder Workflow?
Die Quelle gibt eine Faustregel: Steht die Schrittfolge im Voraus fest (immer erst extrahieren, dann validieren, dann speichern), ist es ein Workflow. Hängen Anzahl und Reihenfolge der Schritte von der jeweiligen Aufgabe ab (mal reicht eine Websuche, mal braucht es drei Zwischenschritte), ist es ein Agent. Die Tool-Mechanik ist in beiden Fällen dieselbe. Der Unterschied liegt darin, wer die Schrittfolge kontrolliert: dein Code (Workflow) oder das Modell (Agent).
| Workflow | Agent | |
|---|---|---|
| Schrittfolge | steht im Code | entscheidet das Modell bei jedem Schritt |
| Anzahl Modellaufrufe | vorher bekannt | vorher unbekannt |
| Kosten | vorhersagbar | schwankt, Obergrenze nötig |
| Testen | jeder Schritt einzeln, reproduzierbar | Verhalten hängt vom Modell ab, schwer zu garantieren |
| Passt, wenn | die Schritte feststehen | die Schritte von Zwischenergebnissen abhängen |
Merksatz der Quelle: Ein Agent ist die teurere, unvorhersagbarere Lösung für ein Problem, das oft auch ein fester Workflow löst. Erst wenn die Schrittfolge nachweislich variiert, lohnt sich der Umstieg.
Drei Workflow-Muster begegnen dir ständig. Sie stehen so nicht in der Quelle (allgemeines Fachwissen, Namen bitte gegen die aktuelle Literatur prüfen), aber sie machen den Begriff “fester Workflow” konkret. Hier sind sie als einfache Funktionen, das “Modell” ist durch Regeln simuliert.
1. Prompt-Chaining (Verkettung): Schritt A liefert die Eingabe für Schritt B. Die Reihenfolge ist fest. Ein Fehler in einem Schritt bricht die Kette ab, und du weißt genau, wo. Das baust du in Übung 3.
2. Routing: Ein erster Schritt klassifiziert die Eingabe, danach geht sie an den passenden Handler. Das Modell entscheidet nur das Etikett, nicht den Ablauf. Die Handler sind feste Funktionen.
Die Falle steht in der letzten Zeile: Ein Etikett, für das es keinen Handler gibt, ist ein KeyError. Ein Modell liefert gelegentlich Etiketten außerhalb deiner Liste, mit Leerzeichen oder anderer Schreibweise. Ein robustes Routing hat darum einen Fallback (zum Beispiel “an einen Menschen”). Das baust du in Übung 2.
3. Parallelisierung: Unabhängige Prüfungen laufen nebeneinander, die Ergebnisse werden gesammelt. Weil keine Prüfung auf eine andere wartet, spart das Zeit (in echtem Code mit asyncio.gather aus Lektion 03, hier nur nacheinander als Dict-Comprehension):
In allen drei Mustern steht die Struktur im Code. Beim Agenten steht sie nicht im Code, und genau das macht ihn flexibel und riskant.
Falle
- Agent, wo ein Workflow reicht. Wenn du die Schrittfolge aufschreiben kannst, schreib sie als Code. Das ist günstiger, schneller und testbar.
- Routing ohne Fallback. Ein Etikett, für das es keinen Handler gibt, ist ein
KeyError(oder in JS einundefined, das erst beim Aufruf knallt). Säubere das Etikett und plane einen Ausgang für Unbekanntes. - Kette ohne Fehlerort. Wenn ein Schritt in der Mitte versagt und du nicht weißt, welcher, suchst du im Blindflug. Gib den Namen des gescheiterten Schritts mit zurück.
Übungen
Übung 1: Welcher Fall braucht einen Agenten? (leicht)
Vier Aufgaben stehen zur Wahl. Welche ist der Agenten-Fall, und warum sind die anderen drei keiner?
- a) Jede Rechnung: Felder lesen, gegen ein Schema prüfen, speichern. Immer dieselben drei Schritte in dieser Reihenfolge.
- b) Eingehende Mails in vier feste Kategorien einsortieren und je Kategorie an dieselbe Funktion übergeben.
- c) Zu einem Kundenbericht immer dieselben vier Abschnitte aus festen Datenquellen füllen lassen.
- d) Eine Störung eingrenzen: Welche Quelle als Nächstes dran ist, hängt vom letzten Fund ab.
Trage den Buchstaben als String ein.
Frage dich bei jeder Aufgabe: Kann ich die Schrittfolge vorab aufschreiben, ohne die Eingabe zu kennen? Wer kontrolliert dann den Ablauf?
antwort = "d"
antwortÜbung 2: Routing mit Fallback (leicht)
Schreibe route(text, klassifiziere, handler). Das ist das Routing-Muster aus Schritt 1, jetzt robust:
- Rufe
klassifiziere(text)genau einmal auf. Das Ergebnis ist das Etikett. - Ist das Etikett ein String, säubere es: Leerzeichen am Rand entfernen (
strip) und alles klein schreiben (lower). - Ist das gesäuberte Etikett ein Schlüssel von
handler(ein dict Etikett zu Funktion), rufe genau diesen Handler mittextauf und gib sein Ergebnis zurück. - In jedem anderen Fall (unbekanntes Etikett, leeres Etikett, kein String, zum Beispiel
None, eine Zahl oder eine Liste) rufst duhandler["mensch"]auf. Dieser Eintrag ist immer vorhanden.
Es wird immer genau ein Handler aufgerufen.
Was passiert bei label in handler, wenn label eine Liste ist? Prüfe zuerst den Typ, dann den Schlüssel. Und wie oft ruft dein Code klassifiziere auf?
def route(text, klassifiziere, handler):
label = klassifiziere(text)
if isinstance(label, str):
label = label.strip().lower()
if label in handler:
return handler[label](text)
return handler["mensch"](text)
routeÜbung 3: Verkettung mit Abbruch (mittel)
Schreibe kette(text, schritte), das Verkettungsmuster aus Schritt 1 mit Fehlerort. schritte ist eine Liste von Paaren (name, funktion). Die erste Funktion bekommt text, jede weitere das Ergebnis der vorigen. Die Rückgabe ist ein dict:
- Läuft alles durch:
{"ok": True, "ergebnis": <Ergebnis des letzten Schritts>, "erledigt": [alle Namen]}. Bei einer leeren Liste ist das Ergebnis der Text selbst underledigtleer. - Wirft ein Schritt eine
Exception:{"ok": False, "fehlgeschlagen": <Name>, "fehler": str(exception), "erledigt": [Namen der Schritte davor]}. Spätere Schritte laufen nicht mehr. - Liefert ein Schritt
None(ein Modell, das nichts geliefert hat), zählt das ebenfalls als Fehlschlag, mit"fehler": "kein Ergebnis". Andere “leere” Werte wie0,""oder[]sind gültige Ergebnisse.
Beispiel: kette("abc", [("gross", str.upper), ("laenge", len)]) ergibt {"ok": True, "ergebnis": 3, "erledigt": ["gross", "laenge"]}.
Was musst du dir merken, während die Schritte laufen, um beim Fehler den Ort melden zu können? Und welcher Vergleich unterscheidet None von 0 und ""?
def kette(text, schritte):
wert = text
erledigt = []
for name, f in schritte:
try:
wert = f(wert)
except Exception as e:
return {"ok": False, "fehlgeschlagen": name, "fehler": str(e), "erledigt": erledigt}
if wert is None:
return {"ok": False, "fehlgeschlagen": name, "fehler": "kein Ergebnis", "erledigt": erledigt}
erledigt.append(name)
return {"ok": True, "ergebnis": wert, "erledigt": erledigt}
ketteTransferaufgabe: Marktlokation (Baustein 01 der Quelle, ohne Check)
Szenario aus der Quelle: Eine neue Marktlokation soll angelegt werden. Dafür müssen die Stammdaten gegen den Data Contract (lernlabor/uebung/data_contract_marktlokation.yaml) geprüft, bei fehlenden Pflichtfeldern nachgefragt und am Ende gespeichert werden. Entscheide begründet: fester Workflow oder Agent? Skizziere die Schrittfolge für beide Varianten. Schreibe deine Antwort auf, bevor du die Musterlösung aufklappst.
Entscheidung: fester Workflow. Die Schrittfolge steht vorher fest und hängt nicht von Zwischenergebnissen ab: prüfen, bei Fehlern nachfragen, speichern. Das “Nachfragen” ist eine Verzweigung mit festem Ziel (zurück zur Prüfung), kein offener Plan. Anzahl der Modellaufrufe ist durch die Zahl fehlender Felder begrenzt, jeder Schritt lässt sich einzeln testen, und die Kosten bleiben vorhersagbar.
Workflow:
- Stammdaten annehmen.
- Mit dem Pydantic-Modell
Marktlokationvalidieren (Code, kein Modell nötig). - Gibt es Fehler: pro fehlendem Pflichtfeld eine feste Rückfrage an den Nutzer stellen, Antwort einsetzen, zurück zu Schritt 2. Maximal eine feste Zahl von Runden.
- Sind alle Pflichtfelder gültig: speichern und Bestätigung zurückgeben.
Agent (nur zum Vergleich): Das Modell bekommt die Tools pruefe_stammdaten, frage_nutzer und speichere, plus die Anweisung, die Marktlokation anzulegen. Es entscheidet selbst, wann es prüft, wen es was fragt und wann es speichert. Das funktioniert meist, kostet aber mehr Modellaufrufe, hat einen variablen Ablauf, braucht Schrittlimit und Budget und kann zum Beispiel speichern, bevor alles geprüft ist.
Wann würde die Entscheidung kippen? Wenn die Eingaben so unterschiedlich wären, dass die nötigen Prüfungen und Rückfragen von Fall zu Fall verschieden sind und sich nicht mehr als feste Regeln schreiben lassen. Erst dann wäre ein Agent nachweislich im Vorteil.
Selbstcheck:
Merksatz
Ein Agent ist die teurere, unvorhersagbarere Lösung für ein Problem, das oft auch ein fester Workflow löst: Wenn du die Schrittfolge aufschreiben kannst, schreib sie als Code (Verkettung, Routing, Parallelisierung) und teste jeden Schritt einzeln.
Prüfstein
Ein Kunde möchte “einen Agenten” für die Eingangspost seiner Fachabteilung. Welche drei Fragen stellst du, bevor du zustimmst, und woran würdest du erkennen, dass die Schrittfolge wirklich variiert?
Weiter mit Teil 2: Die Tool-Use-Schleife.
Quelle: quellen/kursbuch-lerninhalte.md, Modul M4, Baustein “01 Agent oder Workflow?” (Warum, Kernidee, Faustregel, Merksatz). Über die Quelle hinaus (allgemeines Fachwissen, Namen bitte gegen die aktuelle Literatur prüfen): die drei Workflow-Muster Prompt-Chaining, Routing und Parallelisierung, die Vergleichstabelle Workflow gegen Agent, die Übung zur Verkettung. Die Beispieldaten und die “Modellaufrufe” sind von Hand geschrieben und erfunden.