User hỏi: "What day is 103 days from today?"
- Hiểu pattern multi-turn tool: Claude có thể call nhiều tool liên tiếp
- Thấy vì sao single-turn không đủ cho task phức tạp
- Plan refactoring helper functions cho multi-turn
- Sketch agent conversation loop
Multi-turn flow
3 turns, 2 tool calls. Claude tự orchestrate sequence.
User: "What day is 103 days from today?"
│
▼
Claude (Turn 1): "I need to know current date first"
+ ToolUse(get_current_datetime)
│
▼
You run: get_current_datetime() → "2026-04-20"
│
▼
Send tool_result to Claude
│
▼
Claude (Turn 2): "Now I add 103 days"
+ ToolUse(add_duration_to_datetime,
input={"datetime_str": "2026-04-20", "duration": 103, "unit": "days"})
│
▼
You run: add_duration(...) → "2026-08-01"
│
▼
Send tool_result
│
▼
Claude (Turn 3): "103 days from today is August 1, 2026 (Saturday)."
→ stop_reason: "end_turn"
│
▼
Done.Conversation loop pattern
Loop continues cho đến khi Claude end_turn (không cần tool nữa).
def run_conversation(initial_message: str, tools: list) -> str:
"""Loop until Claude done with tools."""
messages = []
add_user_message(messages, initial_message)
while True:
# Ask Claude
response = chat(messages, tools=tools)
add_assistant_message(messages, response)
# Check stop reason
if response.stop_reason == "end_turn":
# Claude done, return final text
return text_from_message(response)
if response.stop_reason == "tool_use":
# Execute tools, send results back
tool_calls = [b for b in response.content if b.type == "tool_use"]
tool_results = run_tools(tool_calls)
add_user_message(messages, tool_results)
continue # Loop again
if response.stop_reason == "max_tokens":
raise ValueError("Response truncated, increase max_tokens")
raise ValueError(f"Unexpected stop_reason: {response.stop_reason}")Helpers cần refactor
Before (single-turn)
After (multi-turn ready)
def chat(messages):
msg = client.messages.create(
model=model,
max_tokens=1000,
messages=messages
)
return msg.content[0].text # ← just textAfter (multi-turn ready)
Return full Message để caller check stop_reason, inspect blocks.
def chat(messages, system=None, tools=None, temperature=1.0, stop_sequences=None):
params = {
"model": model,
"max_tokens": 1000,
"messages": messages,
"temperature": temperature,
}
if system:
params["system"] = system
if tools:
params["tools"] = tools
if stop_sequences:
params["stop_sequences"] = stop_sequences
return client.messages.create(**params) # ← full Message
def text_from_message(message):
"""Extract combined text from multi-block message."""
return "\n".join(
b.text for b in message.content if b.type == "text"
)Ví dụ đầy đủ
Output dự kiến
# Setup tools (schemas + functions từ bài 6.31-6.32)
tools_schemas = [
get_current_datetime_schema,
add_duration_to_datetime_schema,
]
TOOL_REGISTRY = {
"get_current_datetime": get_current_datetime,
"add_duration_to_datetime": add_duration_to_datetime,
}
def run_tools(tool_uses):
results = []
for tu in tool_uses:
try:
output = TOOL_REGISTRY[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": str(e),
"is_error": True
})
return results
def run_conversation(user_msg, tools):
messages = []
add_user_message(messages, user_msg)
max_turns = 10 # safety limit
for turn in range(max_turns):
response = chat(messages, tools=tools)
add_assistant_message(messages, response)
print(f"\n--- Turn {turn + 1} ---")
print(f"Stop: {response.stop_reason}")
if response.stop_reason == "end_turn":
return text_from_message(response)
if response.stop_reason == "tool_use":
tool_uses = [b for b in response.content if b.type == "tool_use"]
for tu in tool_uses:
print(f"Calling: {tu.name}({tu.input})")
results = run_tools(tool_uses)
add_user_message(messages, results)
for r in results:
print(f"Result: {r['content']}")
continue
raise ValueError(f"Unexpected: {response.stop_reason}")
raise RuntimeError(f"Exceeded {max_turns} turns")
# Run
final = run_conversation(
"What day is 103 days from today? Include day of week.",
tools_schemas
)
print(f"\n=== Final ===\n{final}")Output dự kiến
--- Turn 1 ---
Stop: tool_use
Calling: get_current_datetime({'date_format': '%Y-%m-%d'})
Result: 2026-04-20
--- Turn 2 ---
Stop: tool_use
Calling: add_duration_to_datetime({'datetime_str': '2026-04-20T00:00:00', 'duration': 103, 'unit': 'days'})
Result: 2026-08-01T00:00:00
--- Turn 3 ---
Stop: end_turn
=== Final ===
103 days from today (2026-04-20) is 2026-08-01, which falls on a Saturday.Safety: max_turns
Always có limit. Nếu Claude stuck in loop (bug) → app crash controlled, không burn tokens.
Typical budget:
Nếu vượt thường do:
- Simple task: 2-5 turns
- Medium (research, multi-step): 5-15
- Complex (agentic coding, multi-tool): 20-50
- Tool fail, Claude retry vô hạn
- Circular reasoning
- Task quá phức tạp — cần break down
MAX_TURNS = 10 # adjust based on expected task complexity
for turn in range(MAX_TURNS):
...
raise RuntimeError(f"Agent exceeded {MAX_TURNS} turns — possible loop")Agent = multi-turn tool use
"Agent" đơn giản là multi-turn tool use với nhiều tools.
Claude Code:
Computer Use:
Bạn xây agent = viết tools + conversation loop. Đó là cốt lõi.
Chi tiết architecture agent ở Module 9.
- Tools: Read, Edit, Bash, Grep, WebFetch, ...
- Multi-turn loop: plan → read → analyze → edit → test → commit
- Max turns: rất cao (>100 cho complex task)
- Tools: screenshot, click, type
- Multi-turn loop: see → plan → act → see → plan → act
- Each "act" là 1 tool call
Optimization: Context trimming
Conversation dài → token cost scale. Sau N turn, trim:
Hoặc summarize old turns (ở Module 6 prompt caching có cách tốt hơn).
def trim_conversation(messages, max_history=20):
"""Keep last N messages + initial user msg."""
if len(messages) <= max_history:
return messages
return [messages[0]] + messages[-(max_history - 1):]Anti-patterns
❌ Không có max_turns
Infinite loop → burn $100 mà không xong task.
Fix: Always have safety limit.
❌ Quên pass tools ở mọi call
Turn 2 không pass tools → Claude không thấy schema → confused.
Fix: tools= parameter ở mọi chat() trong loop.
❌ Xử lý tool result đồng bộ khi có thể async
Nếu có 5 tool calls parallel, asyncio.gather speed up 5x.
Fix: Async execution khi tool calls independent.
❌ Hardcoded tool dispatcher
Adds 1 tool = modify 2 chỗ.
Fix: Dict registry (như pattern ở trên).
if name == "get_weather":
...
elif name == "get_time":
...Áp dụng ngay
Bài tập 1: Implement run_conversation (30 phút)
Copy code ở trên, test với:
Quan sát turn sequence.
Bài tập 2: Add safety + logging (20 phút)
Mở rộng:
run_conversation("What's 30 days from tomorrow?", tools_schemas)
# Claude có thể:
# - get_current_datetime → today
# - add_duration (+1 day) → tomorrow
# - add_duration (+30 days from tomorrow)Bài tập 2: Add safety + logging (20 phút)
Production-grade conversation loop.
def run_conversation(user_msg, tools, max_turns=10, verbose=True):
"""
- Log mỗi turn (tool name, latency, result)
- Limit max_turns
- Handle timeout per tool (5 seconds)
- Track total tokens
"""
...Tóm tắt
🎯 Multi-turn tool use = Claude chain tool calls cho task phức tạp.
🎯 Loop pattern: chat → check stop_reason → if tool_use, execute, continue.
🎯 Always có max_turns safety limit.
🎯 Refactor helpers — chat return full Message, text extraction tách riêng.
🎯 Agent = multi-turn tool use scaled up — đây là mô hình cho Module 9.