Bạn đã: - Viết tool function (6.32) - Viết schema (6.31) - Nhận response multi-block (6.33)
- Execute tool function với Claude's input params
- Build tool_result block đúng format
- Match tool_use_id giữa request và result
- Gửi multiple tool results trong 1 message
- Handle tool error (is_error=True)
Execute tool function
Từ ToolUseBlock, extract và call:
**input_data unpack dict thành keyword arguments.
tool_call = response.content[-1] # giả định block cuối là ToolUse
# Extract
name = tool_call.name # "get_current_datetime"
input_data = tool_call.input # {"date_format": "%H:%M:%S"}
# Call function
result = get_current_datetime(**input_data)
# "14:30:25"Tool result block structure
Sau khi có result, build tool_result block:
Critical: tool_use_id phải match. Nếu sai → Claude không biết result cho request nào.
tool_result_block = {
"type": "tool_result",
"tool_use_id": tool_call.id, # ← Match với ToolUseBlock.id
"content": str(result), # ← Convert to string
"is_error": False # ← True nếu function raise
}Wrap trong user message
Tool result đi trong user message (dù không phải user gửi):
Tại sao là user? Vì Anthropic API alternating user/assistant. Assistant đã request → user (bạn) provide data.
messages.append({
"role": "user",
"content": [tool_result_block]
})Flow đầy đủ (1 tool)
Lưu ý: Pass tools= cả ở final call — Claude cần schema để hiểu tool_result trong history.
# 1. User hỏi
messages = []
add_user_message(messages, "What time is it in HH:MM:SS?")
# 2. Claude request tool
response = chat(messages, tools=[get_current_datetime_schema])
# 3. Append response với ToolUseBlock
add_assistant_message(messages, response)
# 4. Execute tool
tool_call = next(b for b in response.content if b.type == "tool_use")
result = get_current_datetime(**tool_call.input) # "14:30:25"
# 5. Build tool_result + append
add_user_message(messages, [{
"type": "tool_result",
"tool_use_id": tool_call.id,
"content": result,
"is_error": False
}])
# 6. Final call với full history
final = chat(messages, tools=[get_current_datetime_schema])
print(final.content[0].text)
# "The current time is 14:30:25."Handle tool error
Nếu tool raise exception:
Khi is_error=True:
- Claude "thấy" có lỗi
- Có thể retry với params khác
- Hoặc acknowledge trong response tới user
try:
result = run_tool(tool_call.name, tool_call.input)
is_error = False
except Exception as e:
result = str(e)
is_error = True
tool_result = {
"type": "tool_result",
"tool_use_id": tool_call.id,
"content": result,
"is_error": is_error
}Multiple tool calls trong 1 response
Claude có thể request nhiều tool cùng lúc:
Loop qua, build list tool_results:
# User: "What's 10+10 and 30+30?"
# response.content:
[
TextBlock(text="Let me calculate both."),
ToolUseBlock(id="t1", name="add", input={"a": 10, "b": 10}),
ToolUseBlock(id="t2", name="add", input={"a": 30, "b": 30})
]Multiple tool calls trong 1 response (tiếp)
Claude xử lý tất cả results → tạo final response.
tool_calls = [b for b in response.content if b.type == "tool_use"]
tool_results = []
for tc in tool_calls:
try:
result = run_tool(tc.name, tc.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": tc.id,
"content": str(result),
"is_error": False
})
except Exception as e:
tool_results.append({
"type": "tool_result",
"tool_use_id": tc.id,
"content": str(e),
"is_error": True
})
# Gửi tất cả results trong 1 user message
add_user_message(messages, tool_results)Dispatcher pattern
Reusable cho mọi turn tool use.
TOOL_REGISTRY = {
"get_weather": get_weather,
"get_current_datetime": get_current_datetime,
"add_duration_to_datetime": add_duration_to_datetime,
}
def run_tool(name: str, input_data: dict) -> str:
"""Dispatch to registered function."""
if name not in TOOL_REGISTRY:
raise ValueError(f"Unknown tool: {name}")
func = TOOL_REGISTRY[name]
return func(**input_data)
def run_tools(tool_uses: list) -> list:
"""Run multiple tools, return tool_result blocks."""
results = []
for tu in tool_uses:
try:
output = run_tool(tu.name, tu.input)
results.append({
"type": "tool_result",
"tool_use_id": tu.id,
"content": str(output),
"is_error": False
})
except Exception as e:
results.append({
"type": "tool_result",
"tool_use_id": tu.id,
"content": f"Error: {str(e)}",
"is_error": True
})
return resultsContent types cho tool_result
content có thể là:
String (simple)
Multi-part content (advanced)
{"content": "72°F, sunny"}Multi-part content (advanced)
Ví dụ: tool render_chart return cả text description + chart image.
JSON string
{"content": [
{"type": "text", "text": "Weather data:"},
{"type": "image", "source": {...}} # Image in result!
]}JSON string
Claude parse được JSON, nhưng formatted string thường clearer.
import json
result_dict = {"temp": 72, "condition": "sunny", "humidity": 65}
{"content": json.dumps(result_dict)}Ví dụ đầy đủ từ scratch
Chạy → thấy full flow.
import json
from datetime import datetime
from anthropic import Anthropic
from anthropic.types import Message
client = Anthropic()
model = "claude-sonnet-5-20260205"
# === Tool definition ===
def get_current_datetime(date_format="%Y-%m-%d %H:%M:%S"):
if not date_format:
raise ValueError("date_format cannot be empty")
return datetime.now().strftime(date_format)
get_current_datetime_schema = {
"name": "get_current_datetime",
"description": "Get current date/time in specified strftime format. Use when user asks for current time.",
"input_schema": {
"type": "object",
"properties": {
"date_format": {
"type": "string",
"description": "Python strftime format",
"default": "%Y-%m-%d %H:%M:%S"
}
},
"required": []
}
}
# === Helpers ===
def add_user_message(messages, content):
if isinstance(content, Message):
content = content.content
messages.append({"role": "user", "content": content})
def add_assistant_message(messages, content):
if isinstance(content, Message):
content = content.content
messages.append({"role": "assistant", "content": content})
def chat(messages, tools=None):
params = {
"model": model, "max_tokens": 1000, "messages": messages
}
if tools:
params["tools"] = tools
return client.messages.create(**params)
# === Main flow ===
messages = []
add_user_message(messages, "What's the current time in HH:MM?")
# Turn 1
response = chat(messages, tools=[get_current_datetime_schema])
print(f"Stop: {response.stop_reason}") # "tool_use"
add_assistant_message(messages, response)
# Execute
tool_call = next(b for b in response.content if b.type == "tool_use")
result = get_current_datetime(**tool_call.input)
print(f"Tool result: {result}") # "14:30"
# Turn 2 — send result
add_user_message(messages, [{
"type": "tool_result",
"tool_use_id": tool_call.id,
"content": result,
"is_error": False
}])
final = chat(messages, tools=[get_current_datetime_schema])
print(f"Final: {final.content[0].text}")
# "The current time is 14:30."Anti-patterns
❌ Quên tool_use_id
Fix: tool_use_id bắt buộc.
❌ Sai ID (mismatch)
{"type": "tool_result", "content": "..."} # thiếu id❌ Sai ID (mismatch)
Fix: Copy chính xác từ tool_call.id.
❌ Content không phải string
tool_call.id = "toolu_ABC"
tool_result.tool_use_id = "toolu_XYZ" # khác!❌ Content không phải string
Fix: str(result) hoặc json.dumps(result).
❌ Quên pass tools= ở final call
Claude sẽ confused, không biết tool schema cho context.
Fix: Pass tools ở mọi call trong conversation.
❌ Handle single, không multi
Code assume 1 tool call per response.
Fix: Loop qua all ToolUseBlocks.
"content": {"result": "..."} # dict, not stringÁp dụng ngay
Bài tập 1: Complete flow 1 tool (20 phút)
Ship full example ở trên. Verify:
Bài tập 2: Handle multi-tool (20 phút)
User: "What's time now + add 100 days"
Claude có thể request 2 tool:
Viết code handle. Có thể Claude request sequential (Module 6) hoặc parallel. Code phải robust.
- Claude request tool đúng
- Bạn execute đúng
- Final response có dùng tool result
- get_current_datetime
- add_duration_to_datetime
Tóm tắt
🎯 Tool result block: {type, tool_use_id, content, is_error}.
🎯 Wrap trong user message, append vào history.
🎯 tool_use_id match với ToolUseBlock.id — critical cho tracking.
🎯 Multi-tool: loop, build list of results, 1 user message với tất cả.
🎯 is_error=True khi function raise → Claude có thể retry.