Lộ trình học›Claude API›Building with the Claude API Mở bản đồ

6.33 · Handling message blocks — Multi-block responses

Bài viếtTrung cấp15 phút đọc

Ở bài 6.6 bạn học: message.content[0].text — response là 1 text block. Khi dùng tool, response có thể có nhiều blocks:

Sau bài này, bạn sẽ
  1. Hiểu multi-block response khi Claude dùng tool
  2. Phân biệt TextBlock và ToolUseBlock
  3. Append multi-block message vào history đúng cách
  4. Update helper functions để handle multi-block

Mở đầu: Response không còn là 1 text block

response.content = [
    TextBlock(text="I'll check the weather for you..."),  # ← block 1
    ToolUseBlock(name="get_weather", input={"location": "SF"})  # ← block 2
]

Bạn cần handle cả 2.

Gọi API với tools

messages = [{"role": "user", "content": "What's the exact time in HH:MM:SS?"}]

response = client.messages.create(
    model=model,
    max_tokens=1000,
    messages=messages,
    tools=[get_current_datetime_schema],  # ← thêm tools param
)

tools = list of schemas (bài 6.31).

Anatomy multi-block response

response.content = [
    TextBlock(
        type="text",
        text="I'll get the current time for you."
    ),
    ToolUseBlock(
        type="tool_use",
        id="toolu_01ABC...",
        name="get_current_datetime",
        input={"date_format": "%H:%M:%S"}
    )
]

response.stop_reason = "tool_use"  # ← Claude dừng vì muốn tool

3 loại block thường thấy:

  • TextBlock — text Claude muốn nói
  • ToolUseBlock — request tool call
  • ThinkingBlock — (Module 6) reasoning process

ToolUseBlock có gì?

ToolUseBlock(
    type="tool_use",
    id="toolu_01XYZ...",         # Unique ID cho tool call
    name="get_current_datetime",  # Function name
    input={"date_format": "%H:%M:%S"}  # Arguments
)
  • id — match với tool_result sau (critical nếu có multi-tool)
  • name — function nào
  • input — dict của arguments

Check stop_reason

if response.stop_reason == "tool_use":
    # Claude muốn call tool → bạn execute
    ...
elif response.stop_reason == "end_turn":
    # Claude done, không cần tool nữa
    text = response.content[0].text
elif response.stop_reason == "max_tokens":
    # Bị cắt
    ...

stop_reason="tool_use" là signal bạn cần process tool call.

Append multi-block vào messages

phải append toàn bộ** response.content (không chỉ text):

messages.append({
    "role": "assistant",
    "content": response.content  # ← Entire list of blocks
})

messages.append({
    "role": "assistant",
    "content": response.content[0].text  # chỉ text
})

Lý do: Claude cần thấy full history. Nếu quên ToolUseBlock → turn tiếp theo Claude không biết mình đã request tool.

Extract specific block

tool_calls = [b for b in response.content if b.type == "tool_use"]

text_blocks = [b for b in response.content if b.type == "text"]

full_text = "\n".join(b.text for b in text_blocks)

Helper function:

def get_tool_calls(response):
    return [b for b in response.content if b.type == "tool_use"]

def text_from_message(response):
    return "\n".join(b.text for b in response.content if b.type == "text")

Update helper functions

add_user_message cũ chỉ nhận string. Update để flexible:

from anthropic.types import Message

def add_user_message(messages, content):
    """Accept string, list of blocks, or Message object."""
    if isinstance(content, str):
        messages.append({"role": "user", "content": content})
    elif isinstance(content, Message):
        messages.append({"role": "user", "content": content.content})
    else:
        # Assume list of content blocks
        messages.append({"role": "user", "content": content})


def add_assistant_message(messages, content):
    if isinstance(content, str):
        messages.append({"role": "assistant", "content": content})
    elif isinstance(content, Message):
        messages.append({"role": "assistant", "content": content.content})
    else:
        messages.append({"role": "assistant", "content": content})

Giờ có thể pass full response hoặc plain text.

Update chat function

def chat(messages, system=None, temperature=1.0, stop_sequences=None, tools=None):
    params = {
        "model": model,
        "max_tokens": 1000,
        "messages": messages,
        "temperature": temperature,
    }
    if system:
        params["system"] = system
    if stop_sequences:
        params["stop_sequences"] = stop_sequences
    if tools:
        params["tools"] = tools
    
    return client.messages.create(**params)  # Return full Message

Thay vì return .content[0].text, return full message. Gọi side extract text nếu cần.

Ví dụ đầy đủ — 1 tool call

messages = []
add_user_message(messages, "What is the exact time, formatted HH:MM:SS?")

response = chat(messages, tools=[get_current_datetime_schema])
print(f"Stop reason: {response.stop_reason}")  # "tool_use"

for block in response.content:
    print(f"Block type: {block.type}")
    if block.type == "text":
        print(f"Text: {block.text}")
    elif block.type == "tool_use":
        print(f"Tool: {block.name}, Input: {block.input}")

add_assistant_message(messages, response)

Ví dụ: Multiple tool calls trong 1 response

Claude có thể request nhiều tool cùng lúc:

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})
]

Loop qua tất cả:

tool_calls = [b for b in response.content if b.type == "tool_use"]
for tc in tool_calls:
    result = run_tool(tc.name, tc.input)
    # tracked với tc.id

Chi tiết xử lý ở bài 6.34.

Anti-patterns

Assume response.content[0] luôn là text

Với tool use, content[0] có thể là TextBlock HOẶC ToolUseBlock.

Fix: Dùng block.type check.

Quên append ToolUseBlock vào history

Turn tiếp Claude không thấy đã call tool → re-request.

Fix: messages.append({"role": "assistant", "content": response.content}) — FULL list.

Modify response.content

Không thêm/xóa block từ response.

Fix: Xem response là immutable.

Hardcode block index

tool_call = response.content[1]  # assume always index 1

Fix: Filter by type.

Áp dụng ngay

Bài tập 1: Inspect tool response (15 phút)

Gọi Claude với tool, print chi tiết content:

response = chat([{"role": "user", "content": "Time now?"}], 
                tools=[get_current_datetime_schema])

for i, block in enumerate(response.content):
    print(f"Block {i}: type={block.type}")
    if block.type == "tool_use":
        print(f"  name: {block.name}")
        print(f"  input: {block.input}")
        print(f"  id: {block.id}")
    elif block.type == "text":
        print(f"  text: {block.text}")

Bài tập 2: Update helpers (15 phút)

Update add_user_message, add_assistant_message, chat theo code ở trên. Test với:

  • Plain string input
  • Message object input
  • List of blocks input

Tóm tắt bài học

Những điều cốt lõi
  • Tool use → multi-block response. TextBlock + ToolUseBlock.
  • Check stop_reason="tool_use" để biết Claude muốn tool.
  • Append FULL response.content vào history — không chỉ text.
  • Update helpers flexible với string/Message/list.
  • Filter block by type — [b for b in content if b.type == "tool_use"].
Kiểm tra nhanh3 câu hỏi · chọn đáp án để xem giải thích
    Bạn đã hoàn thành bài này chưa?Tiến độ lưu trên trình duyệt của bạn.