LB

Die Essenz von KI-Agenten

Wie aus Text Completion, System Instructions, Tools und einem einfachen Loop ein AI Agent entsteht.

02. SEP. 2026 · 12 MIN LESEZEIT

KI-Agenten sind überall.

Sie durchsuchen das Web, schreiben Code, analysieren Dateien, führen Tests aus, erinnern sich an frühere Gespräche und arbeiten teilweise über sehr lange Sessions hinweg an einer einzigen Aufgabe.

Weil sich all das wie ein zusammenhängendes intelligentes System anfühlt, liegt eine Annahme nahe: Das Modell selbst müsse all diese Fähigkeiten besitzen.

Doch genau das ist nicht der Fall.

Das Sprachmodell durchsucht nicht das Web. Es führt keinen Python-Code aus. Es öffnet keine Dateien und speichert auch keine Erinnerungen. Es kann nur eine einzige Sache: den nächsten Token eines Textes vorhersagen.

Die Websuche wird von einer Suchmaschine ausgeführt. Code läuft in einem separaten Prozess oder einer Sandbox. Erinnerungen liegen in einer Datenbank. Bilder werden häufig von einem anderen Modell generiert.

Was all diese Komponenten zu einem scheinbar einheitlichen System verbindet, ist der Agent Harness – die Software rund um das Modell.

Der Harness bereitet den Kontext vor, ruft das Modell auf, interpretiert dessen Ausgabe, führt angeforderte Aktionen aus, schreibt die Ergebnisse zurück in den Kontext und startet den nächsten Modellaufruf.

Das Faszinierende an AI Agents ist deshalb nicht nur, wie leistungsfähig sie inzwischen sind. Noch spannender ist, wie primitiv ihre grundlegenden Bausteine geblieben sind.

In dieser Serie werden wir einen solchen Harness Stück für Stück selbst bauen, indem wir die wenigen Mechanismen zusammensetzen, aus denen moderne agentische Systeme entstehen.

Dazu fangen wir ganz am Anfang an:


Ganz unten liegt Text Completion

Betrachte den folgenden Satz:

The capital of France is

Was ist das nächste Wort?

Vermutlich hast du an „Paris“ gedacht. Damit hast du im Wesentlichen genau die Aufgabe gelöst, auf die autoregressive Sprachmodelle während des Pre-Trainings optimiert werden.

Genauer gesagt sagt das Modell nicht direkt ein Wort voraus, sondern den nächsten Token. Ein Token kann ein vollständiges Wort sein, nur ein Teil davon, ein Satzzeichen oder sogar ein Leerzeichen zusammen mit einem Wort.

Formal modelliert das LLM eine Verteilung der Form

P(tn+1∣t1,t2,…,tn)P(t_{n+1} \mid t_1, t_2, \ldots, t_n)

Es berechnet also, welcher Token unter Berücksichtigung aller bisherigen Tokens als Nächstes wahrscheinlich ist.

Zur Veranschaulichung nehmen wir folgende Verteilung an:

" Paris"        97,8 %
" Lyon"          0,7 %
" Marseille"     0,3 %
" located"       0,2 %
" a"             0,1 %
alles andere     0,9 %

Streng genommen erzeugt das Modell zunächst Scores über sein gesamtes Vokabular. Der Decoding-Algorithmus verwandelt diese Scores in eine Wahrscheinlichkeitsverteilung und wählt daraus den nächsten Token aus.

Dabei muss nicht immer der wahrscheinlichste Token gewählt werden. Parameter wie Temperature oder Top-p beeinflussen, wie stark alternative Kandidaten berücksichtigt werden.

Der ausgewählte Token wird an den bisherigen Text angehängt. Anschließend wird erneut der nächste Token bestimmt.

Aus

The capital of France is

wird

The capital of France is Paris

Danach berechnet das Modell wieder eine neue Verteilung:

The capital of France is Paris,

Dann die nächste:

The capital of France is Paris, which

Dieser Prozess wiederholt sich, bis ein Abbruchkriterium erreicht ist.

Das ist Text Completion.

Dabei gibt es kein inhärentes Konzept von Chats, Assistenten, Tools oder Benutzerrollen. Das Modell vervollständigt lediglich eine vorhandene Tokensequenz.

Geben wir einem klassischen Base Model einfach diesen Text

Hi, wie geht es dir?

wird das Modell nicht wie ein AI-Assistant antworten. Der Text könnte ebenso gut Teil eines Romans sein:

„Hi, wie geht es dir?“, fragte Anna, als sie die Tür öffnete.
 
Tom zuckte mit den Schultern. „Ganz gut. Ich bin nur ziemlich müde.“
 
„Langer Tag?“
 
„Du glaubst gar nicht, wie lang.“

Aus Sicht des Modells ist das eine völlig plausible Fortsetzung.

Ein Base Model besitzt keine fest eingebaute Assistenten-Persona. Es versucht lediglich, den Text so fortzusetzen, wie es anhand seiner Trainingsdaten wahrscheinlich erscheint.

Wie wird daraus also ein Chatbot?


Ein Chat ist eine gelernte Form von Text Completion

In den Trainingsdaten eines Sprachmodells befinden sich zahlreiche Dialoge wie der zwischen Anna und Tom. Um sie fortzusetzen, muss das Modell lernen, unterschiedliche Sprecher, Perspektiven und Gesprächsmuster zu simulieren.

Diese Fähigkeit können wir für unseren Chat nutzen.

Wir könnten unseren Chat zunächst als gewöhnlichen Text serialisieren:

# AI & Human Interactions
 
The following interaction takes place on 22 August 2026 at 11:44.
 
AI language models have become helpful assistants to humans.
 
The following is a text-based interaction between a human and such an AI model.
Human responses are wrapped in <user> tags, AI responses in <model> tags.
 
<chat>
<user>
Hi, wie geht es dir?
</user>
<model>

Geben wir diesen Prefix an unser Completion-Modell, muss es weiterhin nur eine einzige Aufgabe erfüllen: den vorhandenen Text fortsetzen.

Der gesamte bisherige Text etabliert jedoch ein klares Muster. Nach <user> folgt eine Nachricht des Menschen. Nach <model> folgt die Antwort eines AI-Modells.

Eine plausible Completion wäre deshalb:

Mir geht es gut, danke! Wie kann ich dir helfen?
</model>

Das Modell hat damit keine neue Fähigkeit erhalten. Wir haben lediglich den Text vor der eigentlichen Generierung so konstruiert, dass die wahrscheinlichste Fortsetzung wie die Antwort eines Assistenten aussieht.

Ohne weitere Maßnahmen würde das Modell möglicherweise direkt den restlichen Chat weiterschreiben:

Mir geht es gut, danke! Wie kann ich dir helfen?
</model>
<user>
Erkläre mir, wie neuronale Netze funktionieren.
</user>
<model>
Neuronale Netze sind ...

Auch das wäre schließlich eine plausible Fortsetzung des Dokuments.

Unser Harness kann das verhindern, indem er </model> als Stop Sequence verwendet.

Sobald das Modell diese Sequenz erzeugen würde, wird die Generierung beendet. Der Harness verwendet nur den Inhalt nach <model> bis zu dieser Stop-Grenze und zeigt ihn dem User als Antwort an.

Damit haben wir aus einer allgemeinen Text-Completion-Maschine einen einfachen Chatbot gebaut.

In modernen Modellen werden dafür normalerweise keine selbst erfundenen XML-Tags verwendet. Stattdessen existieren spezielle Tokens und Chat Templates für System-, User-, Assistant- und Tool-Nachrichten.

Das fundamentale Prinzip bleibt jedoch dasselbe.

Der Chat wird in eine Sequenz serialisiert. Das Modell erhält diese Sequenz und vervollständigt den Teil, der zur Assistant-Rolle gehört.

Durch entsprechendes Post-Training lernt es, dieses Format zuverlässig zu verwenden, Anweisungen zu befolgen und sich wie ein hilfreicher Assistent zu verhalten.

Entscheidend ist nur, dass selbst der Chat am Ende auf derselben primitiven Operation basiert wie zuvor.

Text hinein. Nächsten Token bestimmen. Wiederholen.

System Instructions sind Teil desselben Prefixes

Vielleicht hast du schon von System Instructions oder System Messages gehört.

Sie wirken zunächst wie eine besondere Schnittstelle direkt in das Innere des Modells. Als gäbe es dort neben dem normalen Gespräch einen eigenen Bereich, in dem sich Identität, Regeln und Verhalten des Modells programmieren lassen.

Für unser mentales Modell müssen wir aber nur den bisherigen String erweitern:

# AI & Human Interactions
 
[Previous instructions]
...
 
The AI model embraces the following role and instructions:
 
<system>
You are a precise and helpful AI assistant.
 
Follow the user's instructions carefully.
Explain technical concepts from first principles.
Do not claim to have performed actions you have not performed.
</system>
 
<chat>

Das Modell sieht nun nicht nur den bisherigen Gesprächsverlauf, sondern auch eine Beschreibung der Rolle, die es innerhalb dieses Gesprächs einnehmen soll.

Moderne Modelle verwenden spezielle Rollenmarker, und ihr Post-Training bringt ihnen bei, System Instructions stärker zu gewichten als gewöhnliche User-Nachrichten. Technisch ist die System Instruction also nicht einfach nur beliebiger Fließtext.

Sie ist aber auch keine magische zweite Intelligenz im Inneren des Modells.

Sie ist ein besonders markierter Teil des Kontextes, dessen Bedeutung das Modell während seines Trainings gelernt hat.

Ein Tool Call ist zunächst ebenfalls nur Text

Bis hierhin kann unser Modell chatten. Für viele Aufgaben reicht das aber nicht. Aktuelle Informationen liegen im Web, Code muss irgendwo ausgeführt werden und Dateien müssen von echter Software gelesen werden.

Wir brauchen also eine Möglichkeit, mit der das Modell solche Aktionen anfordern kann.

Dafür erweitern wir einfach den bisherigen Prefix. Innerhalb von <model> darf das Modell jetzt sowohl normalen Text als auch eine Websuche erzeugen:

At the top level, <chat> contains <user>, <model>, and <webSearchResults> blocks.
<webSearchResults> blocks contain search results and are inserted only by the harness.
 
Inside <model>, every output part must be wrapped in one of these tags:
 
<text>
The response to the human
</text>
 
<webSearch>
The search query
</webSearch>

Unser bisheriges Format bleibt also bestehen. Eine Completion könnte nun so aussehen:

<text>
Ich schaue kurz nach.
</text>
<webSearch>
current CEO of Example Corporation
</webSearch>

Auch hier erzeugt das Modell weiterhin nur Text.

Sobald unser Harness den Abschluss </webSearch> erkennt, beendet er die Generierung, schließt den aktuellen <model>-Block und extrahiert die Suchanfrage zwischen den beiden Tags.

Anschließend führt nicht das Modell, sondern unser Programm die eigentliche Suche aus:

const results = await webSearch(
  "current CEO of Example Corporation",
);

Das Ergebnis könnte beispielsweise so aussehen:

[
  {
    "title": "Example Corporation Leadership",
    "url": "https://example.com/leadership",
    "snippet": "The company is led by ..."
  }
]

Dieses Resultat schreiben wir nun zurück in unseren großen Kontext-String:

<user>
Wer ist aktuell CEO von Example Corporation?
</user>
 
<model>
<text>
Ich schaue kurz nach.
</text>
 
<webSearch>
current CEO of Example Corporation
</webSearch>
</model>
 
<webSearchResults>
[
  {
    "title": "Example Corporation Leadership",
    "url": "https://example.com/leadership",
    "snippet": "The company is led by ..."
  }
]
</webSearchResults>
 
<model>

Anschließend geben wir den gesamten aktualisierten String erneut an unsere Text-Completion-Maschine.

Das Modell sieht jetzt die ursprüngliche Frage, seine eigene Suchanfrage und die vom Harness eingefügten Suchergebnisse.

Darauf aufbauend kann es entweder weitere <webSearch> ausführen oder dem Nutzer per

<text>
Laut der offiziellen Unternehmensseite wird Example Corporation derzeit von ... geführt.
</text>
</model>

antworten.

„Die Generierung fortsetzen“ bedeutet einen neuen Modellaufruf

Wenn wir sagen, dass die Generierung nach dem Tool Call fortgesetzt wird, bedeutet das nicht, dass das Modell während der Websuche pausiert und im Hintergrund weiterdenkt.

Der erste Modellaufruf endet.

Dann führt der Harness die angeforderte Aktion aus.

Danach nimmt er den bisherigen Kontext, hängt das Ergebnis an und ruft das Modell erneut auf:

Alter Kontext
+ Ausgabe des Modells
+ Ergebnis des Tools
= neuer Kontext

Für uns sieht das wie eine zusammenhängende Unterhaltung aus. Technisch handelt es sich jedoch um eine Folge einzelner Modellaufrufe, die durch den Harness miteinander verbunden werden.

Das Modell selbst ist zwischen zwei Calls zustandslos. Alles, was es beim nächsten Aufruf wissen soll, muss erneut im Kontext enthalten sein oder durch andere Mechanismen bereitgestellt werden.

Der Kontext kann jedoch nicht beliebig groß werden, denn ein Modell kann pro Aufruf nur eine begrenzte Anzahl von Tokens verarbeiten. Diese Grenze heißt Context Window. Da der Harness den Kontext nach jedem Schritt erweitert, muss er ihn bei langen Sessions irgendwann kürzen oder zusammenfassen. Wie das funktioniert, sehen wir später bei der Compaction.

Von der Websuche zu beliebigen Tools

Die Websuche war nur ein einzelnes Beispiel. Wir können diese Technik auf beliebige Tools verallgemeinern.

Dafür erweitern wir unseren Prefix einfach um eine Liste der verfügbaren Funktionen:

The model has access to the following tools:
 
webSearch:
  description: Search the web for current or external information.
  args: { query: string }
 
executeCode:
  description: Execute code in an isolated environment.
  args: { code: string, language: "ts" | "py" }
 
setChatTitle:
  description: Set the title of the current chat.
  args: { title: string }
 
To request a tool execution, the model emits:
 
<tool_call>
{
  "tool": "name of the tool",
  "args": {}
}
</tool_call>

Das Modell könnte nun beispielsweise erzeugen:

<tool_call>
{
  "tool": "executeCode",
  "args": {
    "code": "print(2 + 2)",
    "language": "py"
  }
}
</tool_call>

Sobald der Harness </tool_call> erkennt, stoppt er die Generation, parst den Call, sucht die entsprechende Funktion in seiner Tool Registry und ruft sie auf:

const result = await tools.executeCode({
  language: "py",
  code: "print(2 + 2)",
});

Danach serialisiert er das Ergebnis und fügt es in den Kontext ein:

</model>
 
<tool_call_result>
{
  "output": "4\n",
  "exitCode": 0
}
</tool_call_result>
 
<model>

Das Modell wird erneut aufgerufen und kann mit diesem Ergebnis weiterarbeiten.

Aus Sicht des Modells unterscheiden sich Websuche, Code Execution, Dateizugriff oder Bildgenerierung dadurch zunächst kaum.

Es kennt für jedes Tool einen Namen, eine Beschreibung und ein Schema für die Argumente.

Unser Harness verbindet diesen symbolischen Namen mit einer echten Implementierung.

Wir können deshalb beliebig viele Fähigkeiten hinzufügen, ohne die grundlegende Architektur zu verändern.

Moderne APIs abstrahieren dieses Protokoll

Unser XML-Beispiel hat den Mechanismus in einem vereinfachten Format sichtbar gemacht. Moderne Model APIs stellen dafür eine strukturierte Schnittstelle bereit. Wir übergeben ihnen die System Instruction, den bisherigen Gesprächskontext und die verfügbaren Tools als getrennte Daten.

Der Provider übersetzt diese Daten in das eigene Format, auf das sein Modell trainiert wurde, mit speziellen Tokens statt unserer XML-Tags. Auch die Antwort erhalten wir strukturiert zurück, beispielsweise als Text oder Tool Call.

Unser Harness muss weiterhin festhalten, was bisher passiert ist, die angeforderten Tools ausführen und den Kontext für den nächsten Aufruf bereitstellen.

Jetzt entsteht der Agent Loop

Enthält die Modellausgabe Text, zeigen wir ihn dem Nutzer.

Wenn das Modell einen Tool Call erzeugt, führt der Harness die angeforderte Aktion aus, hängt das Ergebnis an den Gesprächskontext und ruft das Modell erneut auf.

Fordert das Modell daraufhin eine weitere Aktion an, wiederholt sich dieser Ablauf. Wir haben eine Continuation. Antwortet das Modell abschließend, ohne ein weiteres Tool anzufordern, ist der Turn terminal.

Genau dadurch kann aus mehreren einfachen Completions ein längerer Arbeitsprozess entstehen.

Ein Coding Agent liest eine Datei, verändert Code, startet die Tests, sieht den Fehler und versucht es erneut.

Ein Research Agent sucht im Web, liest die Ergebnisse, erkennt eine offene Frage und startet eine weitere Suche.

Der Harness folgt dabei diesem Ablauf:

Wir haben mit einer Maschine begonnen, die lediglich den nächsten Token vorhersagt.

Dann haben wir ihren Prefix so gestaltet, dass sie sich wie ein Chatbot verhält.

Anschließend haben wir ihr ein Format gegeben, mit dem sie externe Aktionen anfordern kann.

Und schließlich haben wir diese Aktionen in eine Schleife eingebaut, deren Ergebnisse wieder in die nächste Completion einfließen.

Aus diesen wenigen Bausteinen entsteht ein Agent.

Wie es weitergeht

Im nächsten Teil definieren wir den Kontext unseres Harnesses: die Nachrichten und Inhalte, die er von einem Modellaufruf zum nächsten weiterträgt.