Der Tool-Runner übernimmt die agentische Schleife, das Einpacken von Fehlern und die Typsicherheit, damit du es nicht tun musst. Wenn du eine Human-in-the-Loop-Genehmigung, benutzerdefiniertes Logging oder bedingte Ausführung benötigst, verwende stattdessen die manuelle Schleife.
Anstatt Tool-Aufrufe, Tool-Ergebnisse und die Konversationsverwaltung manuell zu handhaben, erledigt der Tool-Runner automatisch Folgendes:
Definiere Tools mit den SDK-Helfern und verwende dann den Tool-Runner, um sie auszuführen.
Abhängig von der Tool-Signatur des SDK gibt ein Tool sein Ergebnis als String oder als Content-Blöcke (Text-, Bild- oder Dokument-Blöcke) zurück, sodass ein Tool multimodale Ergebnisse zurückgeben kann. Ein zurückgegebener String wird zu einem einzelnen Text-Content-Block. Um strukturierte Daten zurückzugeben, wie ein JSON-Objekt oder eine Zahl, kodiere sie zuerst als String.
Verwende den @beta_tool-Decorator, um Tools mit Type Hints und Docstrings zu definieren.
import json
from anthropic import Anthropic, beta_tool
client = Anthropic()
@beta_tool
def get_weather(location: str, unit: str = "fahrenheit") -> str:
"""Get the current weather in a given location.
Args:
location: The city and state, e.g. San Francisco, CA
unit: Temperature unit, either 'celsius' or 'fahrenheit'
"""
return json.dumps({"temperature": "20°C", "condition": "Sunny"})
@beta_tool
def calculate_sum(a: int, b: int) -> str:
"""Add two numbers together.
Args:
a: First number
b: Second number
"""
return str(a + b)
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[get_weather, calculate_sum],
messages=[
{
"role": "user",
"content": "What's the weather like in Paris? Also, what's 15 + 27?",
}
],
)
for message in runner:
print(message)Der @beta_tool-Decorator untersucht die Funktionsargumente und den Docstring, um das JSON-Schema für dich abzuleiten.
Der Tool-Runner ist ein Iterable, das Nachrichten von Claude liefert. Bei jeder Iteration prüft der Runner, ob Claude eine Tool-Nutzung angefordert hat. Falls ja, führt er das Tool aus und sendet das Ergebnis automatisch an Claude zurück, dann liefert er die nächste Nachricht von Claude, um deine Schleife fortzusetzen.
Du kannst die Schleife bei jeder Iteration mit einer break-Anweisung beenden. Der Runner läuft in einer Schleife, bis Claude eine Nachricht ohne Tool-Nutzung zurückgibt oder bis er max_iterations erreicht, falls du diesen Wert gesetzt hast.
Wenn du keine Zwischennachrichten benötigst, kannst du die finale Nachricht direkt abrufen:
Verwende runner.until_done(), um die finale Nachricht zu erhalten.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[get_weather, calculate_sum],
messages=[
{
"role": "user",
"content": "What's the weather like in Paris? Also, what's 15 + 27?",
}
],
)
final_message = runner.until_done()
for block in final_message.content:
if block.type == "text":
print(block.text)Innerhalb der Schleife kannst du jede Antwortnachricht lesen und den Zustand des Runners vor dem nächsten API-Aufruf ändern. Jede Iteration folgt diesem Lebenszyklus:
Standardmäßig verwaltet der Runner den Konversationszustand für dich: Nach jedem Tool-Aufruf-Turn hängt er die Assistant-Nachricht und alle Tool-Ergebnisse an seinen eigenen Nachrichtenverlauf an. Du übernimmst den Nachrichtenverlauf, wenn du einen Turn wiederholen möchtest (die Antwort verwerfen und erneut senden), eine Folgenachricht einfügen oder das Tool-Ergebnis selbst erstellen möchtest.
Du übernimmst, indem du die Nachrichten des Runners aus dem Schleifenkörper heraus änderst. Die genaue Methode hängt vom SDK ab. Siehe die folgenden sprachspezifischen Tabs.
Wenn du für eine Iteration übernimmst, hängt der Runner die Assistant-Nachricht oder die Tool-Ergebnisse dieses Turns nicht an. Du bist dafür verantwortlich, die Konversation gültig zu halten: Hänge die Assistant-Nachricht und ein Tool-Ergebnis selbst an (wenn der Turn zählen soll), ändere den Zustand bedingt, damit die Schleife weiterhin beendet werden kann, wenn es keine Tool-Aufrufe gibt, und übergib max_iterations, um die Schleife zu begrenzen. Alle sieben SDKs unterstützen max_iterations.
Verwende generate_tool_call_response(), um das Tool-Ergebnis zu inspizieren oder zu berechnen. Der Aufruf von append_messages() innerhalb der Schleife teilt dem Runner mit, dass du den Verlauf selbst verwaltest, also füge die Assistant-Nachricht und das Tool-Ergebnis in das ein, was du anhängst.
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
max_iterations=10,
tools=[get_weather],
messages=[{"role": "user", "content": "What's the weather in San Francisco?"}],
)
for message in runner:
tool_response = runner.generate_tool_call_response()
if tool_response is not None:
# append_messages() markiert den Zustand als geändert, daher überspringt der Runner
# sein automatisches Anhängen für diese Iteration. Hänge die Assistant-Nachricht und
# das Tool-Ergebnis selbst an, plus etwaige Folgenachrichten.
runner.append_messages(
message,
tool_response,
{"role": "user", "content": "Please be concise."},
)
# Wenn kein Tool-Aufruf vorliegt, lass den Zustand unverändert, damit die Schleife endet.Um Anfrageparameter wie max_tokens zu ändern, ohne den Nachrichtenverlauf zu übernehmen, verwende set_messages_params(). Der Runner hängt die Assistant-Nachricht und das Tool-Ergebnis weiterhin automatisch an.
for message in runner:
runner.set_messages_params(lambda params: {**params, "max_tokens": 2048})Für langlaufende agentische Aufgaben unterstützen die Tool-Runner für Python, TypeScript und Ruby automatische Kompaktierung, die Zusammenfassungen generiert, wenn die Token-Nutzung einen Schwellenwert überschreitet, damit die Konversation über die Grenzen des Kontextfensters hinaus fortgesetzt werden kann. Alle drei SDKs haben diese clientseitige Option zugunsten der serverseitigen Kontextbearbeitung als veraltet markiert, die in jedem SDK verfügbar ist. Die Tool-Runner für Go, Java, C# und PHP enthalten keine clientseitige Kompaktierung.
Wenn ein Tool eine Exception wirft, fängt der Tool-Runner sie ab und gibt den Fehler als Tool-Ergebnis mit is_error: true an Claude zurück. Das Tool-Ergebnis enthält die Nachricht der Exception (in Python deren Typ und Nachricht), nicht den vollständigen Stack-Trace.
Was das SDK protokolliert, ist sprachspezifisch. Das Python SDK protokolliert die vollständige Exception, einschließlich ihres Stack-Trace, über das Standard-logging-Modul, wann immer ein Tool eine unbehandelte Exception auslöst. Die Python-, TypeScript- und Java-SDKs lesen die Umgebungsvariable ANTHROPIC_LOG, um das Logging des SDK zu aktivieren, das Details zu Anfragen und Antworten enthält:
# Logging auf Info-Level
export ANTHROPIC_LOG=info
# Logging auf Debug-Level für ausführlichere Ausgaben
export ANTHROPIC_LOG=debugDie Go-, Ruby-, C#- und PHP-SDKs lesen ANTHROPIC_LOG nicht. Außerhalb von Python protokolliert kein SDK ein fehlgeschlagenes Tool: Um zu sehen, warum ein Tool fehlgeschlagen ist, fange die Exception innerhalb der Tool-Funktion ab und protokolliere sie, bevor du zurückkehrst oder sie erneut wirfst.
Standardmäßig werden Tool-Fehler an Claude zurückgegeben, das dann angemessen reagieren kann. Du möchtest jedoch möglicherweise Fehler erkennen und anders behandeln, zum Beispiel um die Ausführung frühzeitig zu stoppen oder eine benutzerdefinierte Fehlerbehandlung zu implementieren.
In den Python- und TypeScript-SDKs verwendest du die Tool-Response-Methode (generate_tool_call_response() in Python, generateToolResponse() in TypeScript), um Tool-Ergebnisse abzufangen und auf Fehler zu prüfen, bevor sie an Claude gesendet werden. Die anderen SDKs stellen diesen Hook nicht bereit. Ihre Tabs beschreiben die nächstliegende Alternative:
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[my_tool],
messages=[{"role": "user", "content": "Run my_tool with the query 'hello'."}],
)
for message in runner:
tool_response = runner.generate_tool_call_response()
if tool_response is not None:
# tool_response ist ein Dict: {"role": "user", "content": [...]}
# Prüfe, ob ein Tool-Ergebnis einen Fehler enthält
for block in tool_response["content"]:
if block.get("is_error"):
# Option 1: Löse eine Exception aus, um die Schleife zu stoppen
raise RuntimeError(f"Tool failed: {json.dumps(block['content'])}")
# Option 2: Logge und fahre fort (lass Claude das behandeln)
# logger.error(f"Tool error: {json.dumps(block['content'])}")
# Verarbeite die Nachricht normal
print(message.content)Du kannst Tool-Ergebnisse ändern, bevor sie an Claude zurückgesendet werden. Dies ist nützlich, um Metadaten wie cache_control hinzuzufügen, um Prompt-Caching für Tool-Ergebnisse zu aktivieren, oder um die Tool-Ausgabe zu transformieren.
In den Python- und TypeScript-SDKs verwendest du die Tool-Response-Methode, um das Tool-Ergebnis zu erhalten, und änderst es dann, bevor der Runner fortfährt. Ob du das geänderte Ergebnis explizit anhängst oder es direkt mutierst, hängt vom SDK ab. Siehe die Code-Kommentare in jedem Tab.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[search_documents],
messages=[
{
"role": "user",
"content": "Search for information about the climate of San Francisco",
}
],
)
for message in runner:
tool_response = runner.generate_tool_call_response()
if tool_response is not None:
# tool_response ist ein Dict: {"role": "user", "content": [...]}
# Ändere das Tool-Ergebnis, um Cache-Control hinzuzufügen
for block in tool_response["content"]:
if block["type"] == "tool_result":
# Füge cache_control hinzu, um dieses Tool-Ergebnis zu cachen
block["cache_control"] = {"type": "ephemeral"}
# Hänge die geänderte Antwort an (das verhindert das automatische Anhängen des Originals)
runner.append_messages(message, tool_response)
print(message.content)Aktiviere Streaming, um die Antwort jedes Turns inkrementell zu verarbeiten. Jede Iteration liefert ein Stream-Objekt, über das du für Events iterieren kannst.
Setze stream=True und verwende get_final_message(), um die akkumulierte Nachricht zu erhalten.
client = anthropic.Anthropic()
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
tools=[calculate_sum],
messages=[{"role": "user", "content": "What is 15 + 27?"}],
stream=True,
)
# Beim Streaming gibt der Runner BetaMessageStream zurück
for message_stream in runner:
for event in message_stream:
print("event:", event)
print("message:", message_stream.get_final_message())
print(runner.until_done())Erzwinge JSON-Schema-Konformität für Claudes Tool-Eingaben mit grammatikbeschränktem Sampling.
Parse tool_use-Blöcke, formatiere tool_result-Antworten und behandle Fehler mit is_error.
Aktiviere, formatiere und deaktiviere parallele Tool-Aufrufe, mit Anleitung zum Nachrichtenverlauf und Fehlerbehebung.
Spezifiziere Tool-Schemata, schreibe effektive Beschreibungen und steuere, wann Claude deine Tools aufruft.
Was this page helpful?