Der Aufbau eines Konversationssystems mit Tools erfordert die Implementierung einer Schleife, die Claude so lange aufruft, bis es keine Tool-Nutzung mehr anfordert. Wenn Claude keine Tools mehr anfordert, signalisiert dies, dass eine endgültige Antwort für den Nutzer bereitsteht.
Erkennen von Tool-Anfragen
Der Schlüssel, um zu erkennen, ob Claude ein Tool nutzen möchte, liegt im Feld stop_reason der Antwortnachricht. Wenn Claude entscheidet, dass es ein Tool aufrufen muss, wird dieses Feld auf "tool_use" gesetzt. Dies gibt uns eine saubere Möglichkeit zu prüfen, ob wir die Konversationsschleife fortsetzen müssen:
if response.stop_reason != "tool_use":
break # Claude is done, no more tools neededDie Konversationsschleife
Die Hauptfunktion der Konversation folgt einem einfachen Muster:
def run_conversation(messages):
while True:
response = chat(messages, tools=[get_current_datetime_schema])
add_assistant_message(messages, response)
print(text_from_message(response))
if response.stop_reason != "tool_use":
break
tool_results = run_tools(response)
add_user_message(messages, tool_results)
return messagesDiese Schleife läuft weiter, bis Claude eine endgültige Antwort liefert, ohne weitere Tools anzufordern.
Umgang mit mehreren Tool-Aufrufen
Claude kann in einer einzigen Antwort mehrere Tools anfordern. Der Nachrichteninhalt enthält eine Liste von Blöcken, und wir müssen jeden Tool-Use-Block separat verarbeiten:

Die Funktion run_tools übernimmt dies, indem sie nach Tool-Use-Blöcken filtert und jeden einzeln verarbeitet:
def run_tools(message):
tool_requests = [
block for block in message.content if block.type == "tool_use"
]
tool_result_blocks = []
for tool_request in tool_requests:
# Verarbeite jede Tool-Anfrage...Tool-Result-Blöcke
Für jeden Tool-Use-Block müssen wir einen entsprechenden Tool-Result-Block erstellen. Diese Blöcke haben bestimmte erforderliche Felder:

Der Tool-Result-Block muss dieselbe ID wie der ursprüngliche Tool-Use-Block enthalten, jedoch im Feld tool_use_id:
tool_result_block = {
"type": "tool_result",
"tool_use_id": tool_request.id,
"content": json.dumps(tool_output),
"is_error": False
}Fehlerbehandlung
Eine robuste Tool-Ausführung erfordert die Behandlung potenzieller Fehler. Wenn ein Tool fehlschlägt, müssen wir dennoch einen Tool-Result-Block zurückgeben, jedoch mit Fehlerinformationen:
try:
tool_output = run_tool(tool_request.name, tool_request.input)
tool_result_block = {
"type": "tool_result",
"tool_use_id": tool_request.id,
"content": json.dumps(tool_output),
"is_error": False
}
except Exception as e:
tool_result_block = {
"type": "tool_result",
"tool_use_id": tool_request.id,
"content": f"Error: {e}",
"is_error": True
}Skalierbares Tool-Routing
Um mehrere Tools zu unterstützen, erstelle eine separate Routing-Funktion, anstatt Tool-Namen fest zu codieren:
def run_tool(tool_name, tool_input):
if tool_name == "get_current_datetime":
return get_current_datetime(**tool_input)
elif tool_name == "other_tool":
return other_tool_function(**tool_input)
# Füge bei Bedarf weitere Tools hinzuDieser Ansatz macht es einfach, neue Tools hinzuzufügen, ohne die Kernlogik der Konversation zu verändern.
Vollständiger Ablauf
Die vollständige Konversation über mehrere Turns funktioniert folgendermaßen:
- Sende die Nutzernachricht zusammen mit den verfügbaren Tools an Claude
- Claude antwortet mit Text und/oder Tool-Use-Blöcken
- Führe alle angeforderten Tools aus und erstelle Tool-Result-Blöcke
- Sende die Tool-Ergebnisse als Nutzernachricht zurück an Claude
- Wiederhole dies, bis Claude eine endgültige Antwort ohne Tool-Anfragen liefert
Dies schafft ein nahtloses Erlebnis, bei dem Claude über mehrere Konversations-Turns hinweg mehrere Tool-Aufrufe tätigen kann, um alle benötigten Informationen zu sammeln, bevor es dem Nutzer eine umfassende endgültige Antwort liefert.