KI-Agenten 01

Mehr als nur Automatisierung: Entwurf kognitiver Architekturen für KI-Agenten

Michael Schöffel

13. Januar 202612 Min. Lesezeit

KI-Agenten - Mehr als nur Automatisierung: Entwurf kognitiver Architekturen für KI-Agenten

Zusammenfassung

Der aktuelle Wandel im Software-Engineering markiert den Übergang von deterministischen Regelwerken zu probabilistischen kognitiven Architekturen [1], in denen Large Language Models (LLMs) mittels des ReAct-Patterns (Schlussfolgern & Handeln) Aufgaben autonom durch iterative Analyse- und Handlungsschritte lösen. Während klassische Zustandsmaschinen (Finite State Machines - FSM) zwar verifizierbare Kontrolle bieten, ermöglichen Agenten durch dynamische Pfadfindung die Bewältigung unstrukturierter und unvorhergesehener Szenarien, erfordern jedoch im Gegenzug Strategien zur Handhabung von Latenz und logischen Fehlern.

Für die Entwicklung robuster Langzeit-Agenten reicht ein flüchtiger Skript-Ansatz nicht aus; notwendig ist eine Architektur, die den Zustand persistent speichert und das System als dauerhaften Hintergrunddienst (Daemon) betreibt, um asynchrone Ereignisse ohne Gedächtnisverlust zu verarbeiten. Um bei komplexen Aufgabenstellungen Kontextverlust ("Context Drift") zu vermeiden, muss zudem eine hierarchische Trennung von Planung und Ausführung implementiert werden, bei der ein übergeordneter Planer Ziele in atomare Teilaufgaben zerlegt, die sequenziell abgearbeitet werden.

Einleitung

Die Geschichte der Informatik ist eine Geschichte der Abstraktion. Von Maschinencode zu Assembler, von prozeduralen zu objektorientierten Sprachen - und nun stehen wir vor einem neuen, radikalen Abstraktionssprung: dem Wechsel hin zu Agenten (Agentic Shift).

Die erste Welle der generativen KI (ChatGPT & Co.) war durch das "Prompt-Antwort"-Paradigma (Prompt-Response) geprägt. In diesem Modus ist der Mensch der Prozessor. Wir geben einen Befehl, erhalten ein Ergebnis, bewerten es und geben den nächsten Befehl. Das Modell selbst ist zustandslos und zeitlich ungebunden; es existiert nur im Moment der Inferenz.

Der Wechsel hin zu Agenten markiert die Übertragung der prozeduralen Verantwortung und der zeitlichen Kontinuität vom Menschen auf das System. Ein KI-Agent wird nicht durch die Größe seines Modells definiert, sondern durch seine Architektur, die es ihm erlaubt, autonome Entscheidungen zu treffen, um abstrakte Ziele zu erreichen.

In diesem ersten Beitrag meiner Serie dekonstruieren wir das Fundament moderner KI-Systeme. Wir analysieren den Übergang von deterministischen Skripten zu probabilistischen "Schlussfolgerungsschleifen" (Reasoning-Loops), untersuchen das CoALA-Framework als theoretischen Bauplan und implementieren schließlich einen persistenten Agenten in Python.

Vom OODA-Loop zum ReAct-Pattern

Agenten sind keine neue Erfindung. Strategen nutzen seit Jahrzehnten Entscheidungsmodelle für unsichere Umgebungen. Das bekannteste ist die OODA-Schleife (OODA-Loop), entwickelt von Militärstratege John Boyd: Observe, Orient, Decide, Act (Beobachten, Orientieren, Entscheiden, Handeln).

In der Softwareentwicklung war dieser Loop lange Zeit schwer umzusetzen, da der Schritt "Orient" (das Verstehen des Kontextes) und "Decide" (die Entscheidung unter Unsicherheit) deterministisch kaum abzubilden waren. Wir hatten nur if-else.

Large Language Models (LLMs) fungieren heute als die Schlussfolgerungsmaschine (Reasoning Engine), die diese Schleife ermöglicht. Das moderne Äquivalent zur OODA-Schleife in der KI-Forschung ist das ReAct (Reason + Act)Pattern (Yao et al., 2022) [2].

Das ReAct-Muster im Detail

Das von Yao et al. (2022) vorgestellte ReAct-Muster ist der De-facto-Standard für einfache Agenten. ReAct bricht das "Black-Box"-Verhalten von LLMs auf und zwingt das Modell, "laut zu denken", bevor es handelt.

  1. Observation (Wahrnehmung): Der Agent erhält Input (z.B. Fehlermeldung: "Connection refused").
  2. Thought (Kognition): Das LLM analysiert die Situation [3]. Es greift auf sein internes Wissen zu und plant. ("Der Port könnte geblockt sein. Ich sollte den Port-Status prüfen.")
  3. Action (Interaktion): Der Agent wählt ein deterministisches Werkzeug [4] (Werkzeugaufruf: check_firewall_status).
  4. Execution (Ausführung): Die Umgebung führt den Code aus und liefert ein Ergebnis zurück.
  5. Loop (Schleife): Das Ergebnis wird zur neuen Observation.

Dieser Zyklus erlaubt Fehlerresilienz. Wenn das Werkzeug einen Fehler wirft, bricht das Skript nicht ab. Der Agent "liest" den Fehler, passt seinen Plan an und versucht einen alternativen Weg.

FSM vs. Agent: Die Architektur der Entscheidung

In der Praxis wird der Begriff "Agent" inflationär gebraucht. Oft reicht ein Endlicher Automat (Finite State Machine - FSM) völlig aus. Die Entscheidung zwischen FSM und Agent ist eine Entscheidung zwischen Kontrolle und Flexibilität.

Endlicher Automat (Deterministischer Graph)

Eine FSM besteht aus einer endlichen Anzahl von Zuständen. Übergänge (Kanten) sind fest codiert.

  • Logik: Zustand A + Eingabe X → Zustand B.
  • Vorteil: Verifizierbar korrekt. Keine Halluzinationen. Latenz im Millisekundenbereich.
  • Grenze: Die "Explosion des Zustandsraums". Man kann nicht jeden möglichen Fehlerfall in einem komplexen Prozess (z.B. Fehlersuche in IT-Netzwerken) vorhersehen und codieren.

Autonomer Agent (Probabilistischer Graph)

Ein Agent ist, abstrakt gesehen, eine Zustandsmaschine, bei der die Übergangsfunktion durch ein LLM ersetzt wurde.

  • Logik: Aktueller Kontext + Ziel → LLM → Nächste Aktion.
  • Vorteil: Kann mit unstrukturierten Daten und unvorhergesehenen Szenarien umgehen (Generalisierung).
  • Risiko: Probabilistische Natur bedeutet, dass 2+2 nicht immer 4 ist. Es besteht die Gefahr von Endlosschleifen oder logischen Fehlern.

Entscheidungsmatrix: Wann nutze ich was?

KriteriumZustandsmaschine (FSM)Autonomer Agent
EingabedatenStrukturiert (JSON, Zahlen, Aufzählungen)Unstrukturiert (Natürliche Sprache, Protokolle, Bilder)
LösungsraumEndlich & BekanntOffen & Unbekannt
FehlertoleranzNiedrig (Ausnahme führt zum Abbruch)Hoch (Selbstkorrektur möglich)
Kosten pro SchrittVernachlässigbar (CPU-Zyklen)Hoch (LLM-Token + Latenz)

Grafische Visualisierung

Zustandsmaschine (FSM):

Autonomer Agent:

Topologie von KI-Systemen: Kette, Graph, Agent

Wenn wir Systeme bauen, müssen wir die Interaktion zwischen den LLM-Aufrufen strukturieren. Wir unterscheiden vier Evolutionsstufen der Komplexität.

Level 1: Die Kette (Pipeline)

Eine lineare Abfolge. Prompt A → Ausgabe A → Prompt B → Ausgabe B.

  • Beispiel: Ein Blogpost-Generator. (Thema generieren → Gliederung erstellen → Text schreiben).
  • Problem: Fehlende Redundanz. Wenn die Gliederung schlecht ist, wird der Text schlecht ("Fehlerfortpflanzung").

Level 2: Der Router (Weichensteller)

Das LLM entscheidet am Anfang, welchen Pfad es nimmt, folgt dann aber einer linearen Kette.

  • Beispiel: Kundensupport Klassifizierung.
    • Ist es eine Rückerstattung? → FühreRückerstattungs_Kette aus.
    • Ist es eine technische Frage? → FühreTechnischer_Support_Kette aus.

Level 3: Der Graph (Zustandsbehaftete Zyklen)

Hier führen wir Schleifen ein. Dies ist der Ansatz moderner Rahmenwerke wie LangGraph. Die Ausführung ist nicht mehr ein DAG (gerichteter azyklischer Graph), sondern zyklisch.

  • Beispiel: Code-Generierung mit Unit-Tests.
    1. LLM schreibt Code.
    2. System führt Tests aus.
    3. Wenn Tests fehlschlagen → Rücksprung zu Schritt 1 mit Fehlermeldung als Kontext.

Level 4: Der Agent (Dynamische Navigation)

Es gibt keinen vorgezeichneten Pfad. Das LLM entscheidet nach jedem Schritt neu ("ReAct"). Es wählt aus einem Werkzeugkasten (Werkzeuge) die passende Aktion.

  • Vorteil: Maximale Flexibilität.
  • Nachteil: Schwer zu steuern ("Steuerbarkeit"). Ohne klare System-Prompts neigen Agenten dazu, vom Weg abzukommen.

Der theoretische Bauplan: Das CoALA Framework

Bevor wir gleich den ersten Code schreiben, lohnt sich ein Blick ins Theorie. Während Entwickler oft pragmatisch "drauflos hacken", haben Forscher von Princeton und DeepMind mit dem CoALA Framework (Cognitive Architectures for Language Agents) [5] eine standardisierte Terminologie geschaffen, um Agenten zu beschreiben.

Wenn wir von einem "Cognitive Archetype" sprechen, meinen wir meist die Interaktion von drei Kernmodulen, die CoALA definiert:

  1. Memory (Gedächtnis):
    • Working Memory: Der aktuelle Kontext (Chat History), den das LLM gerade "sieht".
    • Episodic Memory: Vergangene Erfahrungen (Vektordatenbanken / Logs).
    • Procedural Memory: Das Wissen über Werkzeuge und wie man sie benutzt (System-Prompts & Werkzeug-Definitionen).
  2. Action Space (Handlungsraum):
    • Die Menge aller möglichen Aktionen (API Calls, Python REPL, Search).
  3. Decision Making (Entscheidung):
    • Der Prozess, der Memory und Action Space verbindet. In unserem Fall ist das die "Schlussfolgerungsschleife" (ReAct).

Warum ist das wichtig für uns? Viele einfache "Agenten" scheitern, weil sie alles in das Working Memory (Kontext-Fenster) stopfen. Ein guter Architekt trennt diese Speicherbereiche sauber.

Wenn wir gleich unseren Python-Agenten bauen, werden wir diese Trennung implizit vornehmen:

  • self.messages = Arbeitsgedächtnis (Working Memory)
  • tools_registry = Prozedurales Gedächtnis (Procedural Memory)
  • system_prompt = Semantisches Gedächtnis (Semantic Memory/Instruktionen)

Hier ist die CoALA-Architektur visuell vereinfacht:

Reine Python Logik: Die "Unendliche Gedankenschleife"

Frameworks wie LangChain, CrewAI oder AutoGen sind hilfreich, abstrahieren aber oft zu viel. Um kognitive Architekturen wirklich zu beherrschen, muss man sie einmal "von Grund auf" gebaut haben.

Wir bauen nun eine synchronisierte ReAct-Schleife in Python:

Kernkomponenten

  1. System-Prompt: Definiert die Persönlichkeit und das Ausgabeformat.
  2. Werkzeug-Verzeichnis (Registry): Ein Wörterbuch (Dictionary), das Funktionsnamen auf Python-Funktionen mappt.
  3. Kontext (Gedächtnis): Eine Liste, die den Gesprächsverlauf speichert (nur zum Anhängen / append-only).
  4. Die Schleife: Die while-Schleife, die abbricht, wenn das LLM ein FINAL ANSWER Signal sendet.

Implementierung

1import json
2import time
3from datetime import datetime
4
5# Simulierter OpenAI Client (in Produktion: import openai)
6class MockLLM:
7    def chat_completion(self, messages):
8        # Hier würde der echte API Call stehen.
9        # Um das Beispiel lauffähig zu halten, simulieren wir Antworten basierend auf der letzten Benutzereingabe.
10        last_msg = messages[-1]['content']
11        
12        if "Objective" in last_msg:
13            return """Thought: Der Nutzer möchte die IP scannen. Ich sollte zuerst das 'nmap' Tool verwenden.
14Action: {"tool": "nmap", "args": {"target": "192.168.1.5"}}"""
15        
16        if "Port 80 is OPEN" in last_msg:
17            return """Thought: Port 80 ist offen. Das bedeutet ein Webserver. Ich sollte den HTTP Header abrufen.
18Action: {"tool": "http_get", "args": {"url": "http://192.168.1.5"}}"""
19        
20        if "Server: Apache" in last_msg:
21            return """Thought: Ich habe alle Informationen. Es ist ein Apache Server auf Port 80.
22FINAL ANSWER: Das Zielsystem 192.168.1.5 betreibt einen Apache Webserver auf Port 80."""
23        
24        return "FINAL ANSWER: Ich kann das nicht lösen."
25
26class AgentEngine:
27    def __init__(self, tools, system_prompt="", max_steps=10):
28        self.tools = tools
29        self.system_prompt = system_prompt
30        self.max_steps = max_steps
31        self.memory = []
32        self.llm = MockLLM() # Platzhalter für echten Client
33
34    def _update_memory(self, role, content):
35        self.memory.append({"role": role, "content": content, "timestamp": datetime.now().isoformat()})
36
37    def _parse_response(self, response_text):
38        """
39        Extrahiert Gedanke (Thought) und Aktion (Action) aus dem LLM Output.
40        Erwartetes Format:
41        Thought: ...
42        Action: {"tool": "...", "args": {...}}
43        """
44        result = {"thought": None, "action": None, "final_answer": None}
45        
46        if "FINAL ANSWER:" in response_text:
47            result["final_answer"] = response_text.split("FINAL ANSWER:")[1].strip()
48            return result
49
50        if "Action:" in response_text:
51            parts = response_text.split("Action:")
52            result["thought"] = parts[0].replace("Thought:", "").strip()
53            try:
54                result["action"] = json.loads(parts[1].strip())
55            except json.JSONDecodeError:
56                print("!! Fehler beim Parsen des JSON !!")
57        
58        return result
59
60    def run(self, objective):
61        # CoALA: Initialisierung des Arbeitsgedächtnisses mit semantischem Gedächtnis (System-Prompt)
62        self._update_memory("system", self.system_prompt)
63        self._update_memory("user", f"Objective: {objective}")
64
65        step = 0
66        while step < self.max_steps:
67            step += 1
68            print(f"\n--- SCHRITT {step} ---")
69            
70            # 1. SCHLUSSFOLGERN (Reasoning)
71            response = self.llm.chat_completion(self.memory)
72            print(f"AGENT: {response}")
73            
74            parsed = self._parse_response(response)
75            
76            # Prüfung auf Abschluss
77            if parsed["final_answer"]:
78                return parsed["final_answer"]
79            
80            # 2. HANDELN (Acting)
81            if parsed["action"]:
82                tool_name = parsed["action"]["tool"]
83                tool_args = parsed["action"]["args"]
84                
85                if tool_name in self.tools:
86                    print(f"WERKZEUG AUSFÜHRUNG: {tool_name} mit {tool_args}")
87                    try:
88                        # Dynamischer Funktionsaufruf
89                        observation = self.tools[tool_name](**tool_args)
90                    except Exception as e:
91                        observation = f"Fehler bei Werkzeugausführung: {str(e)}"
92                else:
93                    observation = f"Fehler: Werkzeug '{tool_name}' nicht gefunden."
94                
95                print(f"BEOBACHTUNG: {observation}")
96                
97                # 3. FEEDBACK-SCHLEIFE
98                # Das Ergebnis wird zurück in den Speicher geschrieben
99                self._update_memory("user", f"Beobachtung von {tool_name}: {observation}")
100            else:
101                self._update_memory("user", "Systembenachrichtigung: Bitte definiere ein valides Action-JSON oder FINAL ANSWER.")
102
103        return "Abgebrochen: Maximale Schrittzahl erreicht."
104
105# --- TOOL DEFINITIONS ---
106def nmap_tool(target):
107    # Simuliert einen Portscan
108    return f"Scan Ergebnisse für {target}: Port 80 ist OFFEN, Port 22 ist GESCHLOSSEN."
109
110def http_tool(url):
111    # Simuliert eine HTTP-Anfrage
112    return "HTTP 200 OK. Headers: {Server: Apache/2.4.41 (Ubuntu)}"
113
114# --- EXECUTION ---
115tools_registry = {
116    "nmap": nmap_tool,
117    "http_get": http_tool
118}
119
120prompt = """
121Du bist ein autonomer Netzwerksicherheits-Auditor.
122Nutze die bereitgestellten Werkzeuge, um Informationen zu sammeln.
123Denkprozess: Gib immer deinen 'Gedanken' (Thought) vor deiner 'Aktion' (Action) an.
124Format:
125Thought: [Überlegung]
126Action: {"tool": "tool_name", "args": {"arg_name": "value"}}
127Wenn fertig, gib 'FINAL ANSWER: [Ergebnis]' aus.
128"""
129
130agent = AgentEngine(tools=tools_registry, system_prompt=prompt)
131result = agent.run("Analysiere den Host 192.168.1.5")
132print(f"\nERGEBNIS: {result}")

Analyse des Codes

Dieser Code demonstriert das "Thought-Action-Observation" Tripel.

  1. Das LLM generiert einen Gedanken und ein JSON.
  2. Die Python-Logik (AgentEngine) stoppt das LLM, parst das JSON und führt die echte Python-Funktion aus.
  3. Der Rückgabewert der Funktion wird als neuer "User"-Prompt an das LLM geschickt. Das ist entscheidend: Das LLM hat kein Gedächtnis. Der gesamte "State" wird in der Listeself.memory bei jedem Aufruf erneut übergeben (Kontextfenster).

Vom Skript zum System: Der Langzeitagent

Der im vorigen Abschnitt gezeigte ReAct-Code hat eine fundamentale Einschränkung: Er ist flüchtig (transient). Er existiert nur für die Dauer der Ausführung im Arbeitsspeicher (RAM). Wenn das Skript abstürzt oder die Aufgabe erledigt ist, stirbt der "Geist" des Agenten. Alles Gelernte ist verloren.

Ein Langzeit-Agentensystem hingegen verhält sich wie ein Hintergrunddienst (Daemon) oder Service [6]. Es ist nicht nur eine Schleife, sondern ein Zustandsautomat mit Persistenz. Es wartet auf Events (E-Mails, Zeitpläne) und muss seinen Zustand über Tage hinweg behalten. In der Enterprise-Architektur nennen wir dieses Konzept Dauerhafte Ausführung (Durable Execution).

Durable Execution (Dauerhafte Ausführung)

Das Ziel ist es, den Zustand des Programms (Variablen, Stack Trace) so zu persistieren, dass es nach einem Absturz oder Neustart exakt dort weitermachen kann, wo es aufgehört hat. Rahmenwerke wie Temporal.io sind hier führend, aber das Prinzip lässt sich simpel erklären.

Wir benötigen eine Persistenz-Schicht (Datenbank), die das Working Memory speichert.

Der architektonische Unterschied

Während der einfache ReAct-Agent durch einen direkten User-Input gestart wird ("Tu X"), agiert ein Langzeit-System oft ereignisbasiert. Es "schläft" (Ruhezustand), bis ein Auslöser (z.B. eine eingehende E-Mail, ein Timer, ein Webhook) es aufweckt.

Entscheidend ist hier die Zustandsverwaltung: Der Kontext darf nicht im RAM liegen, sondern muss in einer Datenbank (SQLite, Redis, JSON) persistiert werden, damit der Agent nach einem Neustart dort weitermachen kann, wo er aufgehört hat.

Hier ist der Lebenszyklus eines Langzeit-Systems im Vergleich:

Code Implementierung: Der persistente "Daemon"

Wir erweitern unsere Logik nun um eine StateEngine, die den Zustand auf der Festplatte speichert. Der Agent läuft nun in einer Endlosschleife, prüft auf Arbeit, erledigt sie und legt sich wieder schlafen.

1import time
2import json
3import os
4
5class PersistentState:
6    def __init__(self, filename="agent_state.json"):
7        self.filename = filename
8        self.state = self._load()
9
10    def _load(self):
11        if os.path.exists(self.filename):
12            with open(self.filename, 'r') as f:
13                return json.load(f)
14        return {"status": "IDLE", "memory": [], "current_task": None}
15
16    def save(self):
17        with open(self.filename, 'w') as f:
18            json.dump(self.state, f, indent=2)
19
20    def update_memory(self, entry):
21        self.state["memory"].append(entry)
22        self.save()
23
24class LongRunningDaemon:
25    def __init__(self):
26        self.db = PersistentState()
27        # Wir nutzen die AgentEngine
28        # (Hier als Platzhalter, in Realität würde man sie importieren)
29        self.engine = AgentEngine(tools=tools_registry) 
30
31    def check_triggers(self):
32        """Simuliert einen Ereignis-Listener (z.B. Cronjob oder Message Queue)"""
33        # Beispiel: Prüfe, ob eine Datei 'task.txt' existiert
34        if os.path.exists("task.txt") and self.db.state["status"] == "IDLE":
35            with open("task.txt", "r") as f:
36                task = f.read().strip()
37            os.remove("task.txt") # Ereignis konsumieren
38            return task
39        return None
40
41    def run_service(self):
42        print("Agenten-Dienst gestartet. Warte auf Ereignisse...")
43        while True:
44            # 1. Zustandswiederherstellung: Wo waren wir?
45            if self.db.state["status"] == "BUSY":
46                print("Systemneustart erkannt. Nehme vorherige Aufgabe wieder auf...")
47                
48                # Wiederaufnahmelogik: Laden des existierenden Gedächtnisses
49                current_task = self.db.state.get("current_task")
50                if current_task:
51                    self.engine.memory = self.db.state["memory"]
52                    # Fortsetzen der Arbeit
53                    print(f"Nehme Aufgabe wieder auf: {current_task}")
54                    result = self.engine.run(current_task)
55                    
56                    # Aufgabenabschluss
57                    print(f"AUFGABE ERLEDIGT (Wiederaufgenommen): {result}")
58                    self.db.update_memory({"role": "system", "content": f"Erledigt: {current_task}. Ergebnis: {result}"})
59                    self.db.state["status"] = "IDLE"
60                    self.db.state["current_task"] = None
61                    self.db.save()
62            
63            # 2. Ereignisabfrage
64            new_task = self.check_triggers()
65            
66            if new_task:
67                print(f"EREIGNIS AUSGELÖST: {new_task}")
68                self.db.state["status"] = "BUSY"
69                self.db.state["current_task"] = new_task
70                self.db.save()
71
72                # 3. Ausführen der ReAct-Schleife
73                # Wir übergeben das persistente Gedächtnis
74                self.engine.memory = self.db.state["memory"] 
75                result = self.engine.run(new_task)
76                
77                # 4. Aufgabenabschluss & Persistierung
78                print(f"AUFGABE ERLEDIGT: {result}")
79                self.db.update_memory({"role": "system", "content": f"Erledigt: {new_task}. Ergebnis: {result}"})
80                self.db.state["status"] = "IDLE"
81                self.db.state["current_task"] = None
82                self.db.save()
83            
84            else:
85                # Energiesparmodus / Abfragerate
86                time.sleep(2) 
87
88# Starten des Daemons
89if __name__ == "__main__":
90    daemon = LongRunningDaemon()
91    daemon.run_service()

Der kritische Unterschied

MerkmalReAct SkriptLangzeit-System
LebensdauerProzessdauer (Sekunden/Minuten)Unendlich (Tage/Wochen)
GedächtnisRAM (Flüchtig)Datenbank/Datei (Persistent)
AuslöserMenschlicher StartbefehlAsynchrone Ereignisse (Zeit, API, Datei)
FehlerfallAbsturz = DatenverlustAbsturz = Neustart & Wiederaufnahme (durch Zustandsdatei)

Ein Langzeit-System ist die Voraussetzung für echte Autonomie. Nur so kann ein Agent beispielsweise "Jeden Morgen um 8:00 Uhr die Nachrichten prüfen" oder "Warten, bis der Server wieder erreichbar ist, und dann weitermachen".

Zielzerlegung: Die Kunst der Zerlegung

Der oben gezeigte ReAct-Loop funktioniert gut für kurze Aufgaben. Bei komplexen Zielen (z.B. "Erstelle eine Marktanalyse für Produkt X, identifiziere 5 Wettbewerber und schreibe einen LinkedIn-Post darüber") scheitert eine einfache Schleife oft.

Warum?

  1. Kontextdrift (Context Drift): Je länger der Chat-Verlauf, desto eher vergisst das Modell das ursprüngliche Ziel ("Lost-in-the-Middle"-Phänomen (in der Mitte verloren)[7]).
  2. Schlussfolgerungsermüdung: Das Modell versucht, Planung und Ausführung gleichzeitig zu machen.

Lösung: Hierarchische Planung (Plan-and-Solve) [8]

Wir trennen Planung (System 2) von Ausführung (System 1).

Architektur

  1. Der Planer: Ein LLM-Aufruf, der nur das Ziel analysiert und einen DAG (gerichteten azyklischen Graphen) von Teilaufgaben erstellt. Er hat keine Werkzeuge.
  2. Der Ausführende: Ein ReAct-Agent (wie oben im Code), der die Teilaufgaben nacheinander abarbeitet.
  3. Der Integrator: Fügt die Teilergebnisse zusammen.

Beispiel: "Infiltrieren des Subnetzes X"

Schritt 1: Planer-Prompt

Ziel: Infiltrieren von Subnetz X. Zerlege dies in atomare, sequentielle Schritte. Antworte nur als JSON Liste.

Ausgabe Planer:

1[
2  {"id": 1, "task": "Identifiziere aktive Hosts im Subnetz", "dependency": null},
3  {"id": 2, "task": "Identifiziere offene Ports auf gefundenen Hosts", "dependency": 1},
4  {"id": 3, "task": "Suche nach CVEs für identifizierte Services", "dependency": 2}
5]

Schritt 2: Steuerungsschleife (Controller Loop)

Der Controller nimmt Task 1, instanziiert einen neuenAgentEngine mit leerem Speicher (Speicherzurücksetzung!) und übergibt nur Task 1 als Ziel. Das Ergebnis von Task 1 wird als Kontext für Task 2 übergeben.

Diese "Kontextbereinigungs"-Strategie ist essenziell für Langzeit-Agenten, da sie Token spart und den Fokus des Modells schärft.

Fazit & Nächste Schritte

Wir haben gesehen, dass moderne KI-Agenten weit mehr sind als nur clevere Prompts. Sie sind Software-Architekturen, die probabilistische Logik (LLM) mit deterministischen Tools (Code) durch Kontrollfluss-Strukturen (Loops, Graphen) verbinden. Wir bewegen uns vom einfachen Skripting hin zur Durable Execution kognitiver Prozesse.

Die größte Schwachstelle unseres aktuellen AgentEngine Codes ist jedoch offensichtlich: Er vertraut sich selbst zu sehr. Was passiert, wenn das LLM halluziniert und nmap mit falschen Parametern aufruft? Der aktuelle Code würde einen Fehler werfen oder in eine Schleife geraten.

Im nächsten Beitrag über Selbstreflexion [9] werden wir dieses System härten. Wir implementieren einen "Inneren Kritiker" - einen sekundären Schlussfolgerungskreis, der die Ausgaben des Agenten prüft, Fehler korrigiert und sicherstellt, dass der Agent nicht in einer Sackgasse landet, ohne menschliches Eingreifen.

Quellen

[1]

Z. Xi et al., “The Rise and Potential of Large Language Model Based Agents: A Survey,” arXiv preprint arXiv:2309.07864, 2023.

[2]

S. Yao et al., “ReAct: Synergizing Reasoning and Acting in Language Models,” in International Conference on Learning Representations (ICLR), 2023. arXiv:2210.03629.

[3]

J. Wei et al., “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models,” in Advances in Neural Information Processing Systems (NeurIPS), vol. 35, 2022, pp. 24824-24837.

[4]

T. Schick et al., “Toolformer: Language Models Can Teach Themselves to Use Tools,” arXiv preprint arXiv:2302.04761, 2023.

[5]

T. Sumers et al., “Cognitive Architectures for Language Agents,” arXiv preprint arXiv:2309.02427, 2023.

[6]

J. S. Park et al., “Generative Agents: Interactive Simulacra of Human Behavior,” in Proceedings of the 36th Annual ACM Symposium on User Interface Software and Technology (UIST '23), 2023.

[7]

N. F. Liu et al., “Lost in the Middle: How Language Models Use Long Contexts,” Transactions of the Association for Computational Linguistics, vol. 12, pp. 157-173, 2024. arXiv:2307.03172.

[8]

L. Wang et al., “Plan-and-Solve Prompting: Improving Zero-Shot Chain-of-Thought Reasoning by Large Language Models,” in Proceedings of the 61st Annual Meeting of the Association for Computational Linguistics (ACL), 2023, pp. 2609-2634.

[9]

N. Shinn et al., “Reflexion: Language Agents with Verbal Reinforcement Learning,” in Advances in Neural Information Processing Systems (NeurIPS), vol. 36, 2023.

Kontaktiere mich

Kontaktiere mich

Haben Sie Fragen an mich oder möchten Sie mit mir in Kontakt treten?

Name
Michael Schöffel
Telefonnummer
Telefonnummer auf Anfrage
Wohnort
Deutschland, Wohnort auf Anfrage
Email
[email protected]

Senden Sie mir eine Nachricht

* Durch Betätigung der 'Senden' Schaltfläche stimmen Sie einer notwendigen Roboteranalyse mittels Google reCAPTCHA zu. Hierbei werden Cookies gesetzt und das Nutzungsverhalten ausgewertet. Andernfalls senden Sie mir bitte direkt eine E-Mail. Folgende Richtlinien von Google gelten: Datenschutz & Nutzungsbedingungen.

Max. 500 Zeichen