Multi-turn với tools — Sequential tool calls

5 — Tool UseTrung cấp20 phút

User hỏi: "What day is 103 days from today?"

Bạn sẽ học được
  • 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 text

After (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.

Nội dung này có hữu ích không?