Applied AI School
v0 · 規劃中
Anthropic

Claude tool use 教學:四步流程

從 API 角度看 tool use 的四步流程——initial、tool_use、tool_result、final,中間是兩個 request、不是 callback。

TL;DR

  • Tool use = 你給 Claude 一張「能做什麼」的菜單,它告訴你「我要呼叫哪個」,你執行完再餵回去
  • 整個來回是兩個 API request——中間是你的 server 在跑東西,不是 Claude 自己連你的 DB
  • 不是 callback、不是 webhook——是分段對話,每段都要把完整歷史重傳

想看 agent 角度的 tool use 心智模型?看 Claude Code 系列。本章是 API hands-on——JSON 結構、content blocks、agent loop 怎麼寫都會走過。

一個情境:使用者問「SF 現在的天氣」

你在做 chatbot,使用者敲:「舊金山現在的天氣?」

直接餵給 Claude 不開 tool use,會拿到很客氣的拒答:

抱歉,我沒辦法存取即時天氣資訊。我的訓練資料有 cutoff date,無法查詢當下情況。

問題很直白:Claude 沒有網路、沒有 DB、沒有檔案系統。它只會吃 text、吐 text。真的去打 weather API 的人是你的 server。Tool use 就是把「Claude 想要什麼資料」跟「你的 server 怎麼拿」串起來的協定。

四步流程

一輪 tool use 的訊息流——中間是兩個獨立的 messages.create 呼叫

關鍵:圖中第 2 步跟第 6 步是兩個獨立的 messages.create 呼叫。中間沒有 long-polling、webhook 或 streaming callback。就是你正常 send 完一個 request 看 response,發現它要 tool,自己跑完,再 send 下一個。

像櫃檯的便利貼,不是廚房的快遞

比喻一下:tool use 像餐廳櫃檯。

  • 客人(user)跟櫃檯(你的 server)下單
  • 櫃檯把單子(tool_use 請求)寫在便利貼遞給廚房(外部 API / DB)
  • 廚房做完菜端回櫃檯(tool_result)
  • 櫃檯再把菜送到客人桌上(final response)

廚房不會直接跟客人講話。Claude 也不會自己連你的 DB。每一段都是櫃檯(你的 code)在轉手。

一個 happy path 的 JSON

第一個 request——附上 tools 參數:

import anthropic

client = anthropic.Anthropic()

tools = [{
    "name": "get_weather",
    "description": "查詢指定城市的即時天氣",
    "input_schema": {
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "城市名"}
        },
        "required": ["city"],
    },
}]

messages = [{"role": "user", "content": "舊金山現在的天氣?"}]

res = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

Claude 回的 response 會是這個結構:

{
  "stop_reason": "tool_use",
  "content": [
    { "type": "text", "text": "我來查一下舊金山的天氣。" },
    {
      "type": "tool_use",
      "id": "toolu_01A09q90qw90lq917835lq9",
      "name": "get_weather",
      "input": { "city": "San Francisco" }
    }
  ]
}

兩個 block:一段給 user 看的 text、一個 tool_use block 告訴你它要哪個 tool、傳什麼 input。stop_reason: "tool_use" 是「我要 tool、不要忘了餵我結果」的訊號。

第二個 request——把 tool_result 塞回去

你的 server 拿 input 真的去打 weather API(或假資料),然後再呼叫一次 API:

weather_data = "18°C, partly cloudy"  # 假裝你打完 weather API

messages.append({"role": "assistant", "content": res.content})  # 整段塞回去
messages.append({
    "role": "user",
    "content": [{
        "type": "tool_result",
        "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
        "content": weather_data,
    }],
})

final = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,           # 後續 request 也要帶
    messages=messages,
)
print(final.content[0].text)
# "舊金山現在 18°C,多雲。"

兩個重點:

  1. tool_use_id 必須對應前一個 response 的 tool_use.id,不然 API 直接報錯
  2. 第二個 request 也要帶 tools 參數——Claude 需要 schema 才能解讀對話歷史裡的 tool block

為什麼不直接讓 Claude 連 DB?

幾個務實的原因:

  • 權限管理:你的 DB 連線、API key、business logic 在你 server 才管得住
  • 安全:model 不該直接拿到 production credentials
  • 可觀測:tool 怎麼跑、跑多久、傳什麼參數,全部走你的 code,log / audit / rate limit 都好做
  • stateless:Anthropic 的 API 設計成可以負載到任意機器;幫 user 連他們的 DB 不可能 scale

「分段對話」聽起來繞,但這個繞是必要的。

接下來

下一篇進到第一步的關鍵——tool schema 怎麼寫name 動詞化、description 是 tool 的 system prompt、input_schema 怎麼讓 Claude 不亂傳參數,schema 寫得好決定了它選不選對 tool。