Handbücher · Agent-Harnesses
Build: Der Loop
Lernziele
- Du baust einen Agenten-Loop mit zwei Tools gegen die Messages-API und startest ihn im eigenen Terminal.
- Du erklärst, welche Rolle
stop_reason,tool_useundtool_resultim Loop spielen. - Du nennst die zwei Lücken, die diesen Loop zur Vorführung machen.
Die Lektion Was ein Harness ist behauptet, dass im Kern jedes HarnessAlles, was festlegt, was der Agent erreichen darf: Tools, Berechtigungen, Sandbox, Zeitlimits und die Prüfung, bevor eine Aktion wirkt. Englisch für Geschirr oder Gestell.Im Glossar eine while-Schleife steckt. Diese Lektion belegt es. In rund 80 Zeilen Python entsteht ein AgentEin System, in dem ein Sprachmodell seinen nächsten Schritt selbst wählt: Es ruft Werkzeuge auf, wertet das Ergebnis aus und macht weiter, bis das Ziel erreicht ist.Im Glossar, der Dateien liest und Befehle ausführt, gegen die echte Claude-API. Die nächste Lektion ergänzt die Teile, die daraus einen Harness machen. Für alle drei Build-Lektionen zusammen planst du einen Abend ein.
Du brauchst Python 3.10 oder neuer, pip install anthropic (getestet mit 2.8.0) und die Umgebungsvariable ANTHROPIC_API_KEY.
Zwei Tools
Ein Tool · engl. toolEin Werkzeug, das das Modell aufrufen kann: eine API, eine Datenbankabfrage, ein Shell-Befehl, eine Suche. Über Tools wirkt das Modell auf die Welt.Im Glossar besteht aus einem JSON-Schema und einer Funktion. Das Schema liest das Modell · engl. model, LLMDas Sprachmodell (LLM, Large Language Model). Es liest Text, schreibt Text und entscheidet in einem Agenten, was als Nächstes passiert.Im Glossar, die Funktion führt dein Code aus. Die Beschreibungen zählen so viel wie der Code, denn das Modell wählt ein Tool nach seiner Beschreibung. Die Lektion Tools und MCP zeigt das ausführlich.
Der Agent bekommt genau zwei Tools:
read_fileliest eine Textdatei und gibt den Inhalt zurück.run_commandführt einen Shell-Befehl aus und gibt Exit-Code, Standardausgabe und Fehlerausgabe zurück. Nach 30 Sekunden bricht der Befehl ab.
Über run_command listet der Agent Dateien, durchsucht sie mit grep und startet Tests.
Der Code
Die Datei heißt agent.py. Der Loop · engl. agent loopDie Schleife eines Agenten: denken, handeln, beobachten, wiederholen. Zwischen den Durchläufen wird der Kontext des Modells jedes Mal neu gebaut.Im Glossar steckt in der Funktion agent, alles darüber ist Vorbereitung.
# agent.py – der nackte Loop: zwei Tools, eine while-Schleife
import json
import subprocess
import sys
import anthropic # pip install anthropic
MODEL = "claude-opus-5-5"
SYSTEM = "You are a coding agent working in the current directory. Investigate before you conclude."
TOOLS = [
{
"name": "read_file",
"description": "Read a text file and return its full contents. Use for source files, configs and logs. Paths are relative to the working directory.",
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string", "description": "File path, e.g. src/main.py"}},
"required": ["path"],
},
},
{
"name": "run_command",
"description": "Run a shell command and return its exit code, stdout and stderr. Use for listing files, searching and running tests. Times out after 30 seconds.",
"input_schema": {
"type": "object",
"properties": {"command": {"type": "string", "description": "The command, e.g. ls -la"}},
"required": ["command"],
},
},
]
def read_file(path):
with open(path, "r", encoding="utf-8") as f:
return f.read()
def run_command(command):
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30)
return f"exit code {result.returncode}\n{result.stdout}{result.stderr}"
def execute(name, tool_input):
if name == "read_file":
return read_file(tool_input["path"])
if name == "run_command":
return run_command(tool_input["command"])
raise ValueError(f"unknown tool: {name}")
def agent(task):
client = anthropic.Anthropic() # liest ANTHROPIC_API_KEY
messages = [{"role": "user", "content": task}]
while True:
response = client.messages.create(
model=MODEL, max_tokens=16000, system=SYSTEM, tools=TOOLS, messages=messages,
output_config={"effort": "high"},
)
for block in response.content:
if block.type == "text":
print(block.text)
if response.stop_reason != "tool_use":
return
messages.append({"role": "assistant", "content": response.content})
results = []
for block in response.content:
if block.type != "tool_use":
continue
print(f"-> {block.name} {json.dumps(block.input)}")
try:
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": execute(block.name, block.input)})
except Exception as exc:
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": f"Error: {exc}", "is_error": True})
messages.append({"role": "user", "content": results})
if __name__ == "__main__":
agent(" ".join(sys.argv[1:]) or "List the files here and describe this project.")Zeilen 8-9
Der Code nennt Claude Opus 5.5, das aktuelle Standardmodell. Der Systemprompt ist ein einziger Satz: Erst untersuchen, dann urteilen.
Was die Teile tun
Die API zeigt über stop_reason, was das Modell vorhat. Steht dort tool_use, enthält die Antwort einen oder mehrere tool_use-Blöcke. Jeder hat eine id, einen name und eine bereits geparste Eingabe input.
Dein Code hängt die gesamte Antwort response.content unverändert an den Verlauf. Aktuelle Modelle liefern darin Thinking-Block · engl. thinking blockEin Block in der Antwort des Modells, der seine Überlegungen trägt, meist ohne sichtbaren Text und mit einer Signatur. Er geht unverändert mit dem Verlauf zurück und gilt nur, solange alles davor gleich bleibt.Im Glossar mit, die mit dem Verlauf zurückgehen müssen. In unseren Läufen mit Haiku 5.5 war der Text dieser Blöcke leer. Jeder trug nur eine Signatur mit 944 Zeichen. Das ist der Standard: Die Überlegungen selbst bleiben verborgen, die Signatur bindet sie an das Gespräch.
Dann führt dein Code jeden Tool-Aufruf aus. Alle Ergebnisse gehen als tool_result-Blöcke in einer Nachricht zurück, zugeordnet über tool_use_id. Auch Fehler gehen zurück, markiert mit is_error. Mit einer guten Fehlermeldung steuert das Modell in einer Runde um.
Speichere die Datei als agent.py und starte sie in einem Projektordner:
python agent.py "find the TODO comments in this repo and summarise them"
So sah ein Lauf in einem kleinen Testprojekt mit drei TODO-Kommentaren und einem FIXME aus:
- $python agent.py "find the TODO comments in this repo and summarise them"
Das Modell hat nach dem ersten grep von sich aus weitergesucht, auch nach FIXME. Den zweiten Befehl hat es selbst zusammengesetzt. Thorsten Ball baut in seinem Tutorial denselben Loop in Go, die Ressourcen verlinken es. Der Zweck dieser Lektion ist, dass du den Loop im eigenen Terminal laufen siehst.
Was macht der Loop mit dieser Antwort?
Ordne jede Situation dem Schritt zu, den agent.py als Nächstes ausführt.
Noch offen (5)
- stop_reason ist end_turn, die Antwort enthält nur Text.
- Die Antwort enthält zwei tool_use-Blöcke für read_file.
- read_file bekommt einen Pfad zu einer Datei, die es nicht gibt.
- Das Modell ruft ein Tool namens write_file auf.
- stop_reason ist max_tokens, weil das Limit erreicht ist.
Loop endet (0)
Tool ausführen, Ergebnis zurück (0)
Fehler als Ergebnis zurück (0)
Was es noch nicht ist
Der Agent ist eine Vorführung. Er führt jeden Befehl aus, den das Modell vorschlägt, auch rm. Bei einer langen Aufgabe wächst der Verlauf ohne Grenze, und jede Runde schickt ihn vollständig an die API. Einen Fehler 400 fängt der Code nicht ab, und nichts hält ein Modell auf, das sich im Kreis dreht.
Für das Rechte-Problem und das Kontext-Problem gibt es zwei Bausteine: ein Permission-Gate und ein Kontextbudget. Beide baut die nächste Lektion, Build: Berechtigungen und Kontext, zusammen mit einer Stoppregel · engl. stop ruleDie Bedingungen, unter denen ein Loop endet: Der Prüfer ist zufrieden, das Runden-Limit ist erreicht oder zwei Versuche hintereinander sind gleich.Im Glossar.
Merksätze
- Ein Agent ist ein Loop: API aufrufen, Tool-Aufrufe ausführen, Ergebnisse zurückgeben, bis
stop_reasonnicht mehrtool_useist. - Die Antwort geht unverändert in den Verlauf, samt Thinking-Blöcken mit ihrer Signatur.
- Alle Tool-Ergebnisse einer Runde gehen in einer Nachricht zurück, zugeordnet über
tool_use_id, Fehler mitis_error. - Bei Opus 5.5 setzt du
effortselbst, weil der Standardwertmediumist, und gibstmax_tokensPlatz für das Nachdenken. - Ohne Gate und Budget führt der Loop jeden Befehl aus und lässt den Verlauf unbegrenzt wachsen.
Prüf dich selbst
Denkfrage
Warum hängt der Code response.content an und nicht nur den Text der Antwort?
Die Antwort enthält neben Text auch die tool_use-Blöcke und Thinking-Blöcke. Die tool_result-Blöcke der nächsten Nachricht verweisen über ihre ID auf die tool_use-Blöcke. Ohne diese Blöcke passen sie zu nichts. Die Thinking-Blöcke tragen die Überlegungen des Modells in die nächste Runde. Der Text allein würde beides abschneiden.
Denkfrage
Ein Befehl scheitert. Warum geht der Fehler an das Modell, statt das Programm zu beenden?
Für das Modell ist ein Fehler eine Beobachtung wie jede andere. Mit einer klaren Meldung probiert es einen anderen Pfad oder einen anderen Befehl. Bricht das Programm ab, ist die ganze Arbeit der Session verloren.
Prüf dich selbst
Selbst nachweisen
0 von 3 erledigt
Quellen
- Tool use with Claude
Tool-Schemas, tool_use und tool_result.
- Parallel tool use
Mehrere Tool-Aufrufe in einer Antwort, alle Ergebnisse in einer Nachricht.
- Preserved thinking
Warum Thinking-Blöcke unverändert zurückgehen.
- How to build an agent
Derselbe Loop als Coding-Agent in Go.