載入中

串流工具呼叫:不再出現凍結的載入圖示

第 4 課8 分鐘
載入中

串流工具呼叫:不再出現凍結的載入圖示

摘要逐字稿

串流工具呼叫會先送出工具名稱,接著分段送出其輸入內容,讓畫面得以顯示正在發生的事。影片並排展示了 Meridian(一家虛構的工程顧問公司)畫面的兩個版本:一個沒有串流,另一個有串流。此示範為模擬畫面,並以三分之一速度播放。

此影片錄製於 2026 年 9 月,其程式碼使用 Claude Sonnet 5(claude-sonnet-5)。在較新的模型上,您的結果可能有所不同。

假設您的使用者盯著載入圖示看了八秒鐘。這是因為您的工具迴圈會送出每個請求、等待,然後取得一個完整的回應。

在這段時間裡,Claude 會選擇一個工具並寫出其輸入內容,您的程式碼會執行該工具,然後 Claude 會寫出答案。您的程式碼只有在回應完成後才能看到它,因此在 Claude 寫作期間,您的使用者介面沒有任何東西可以顯示。

Meridian 目前就有這個問題。其支援代理人以 Python 撰寫,負責回答員工關於專案的問題。當員工詢問「抽水站工程的狀態如何?」時,畫面會顯示「思考中...」,直到答案出現為止。

串流會改變那位員工看到的畫面。有了串流,您的程式碼可以在回應完成之前,逐段取得回應。因此,一旦 Claude 選定工具,Meridian 的畫面就可以顯示「正在查詢專案...」。

串流工具呼叫的樣貌

在您的工具迴圈傳送請求的地方,將 client.messages.create(...) 替換成 client.messages.stream(...)。接著,回應會以一系列的伺服器發送事件到達,這些是每個都有 type 的小型 JSON 訊息。

一個回應是由內容區塊(簡稱區塊)所組成,Claude 每一段文字會對應一個區塊,每次工具呼叫也會對應一個區塊。工具呼叫的區塊類型為 tool_use。目前的模型也可以加入思考區塊,但本課程的程式碼會忽略它們。

想像一位朋友在電話中唸一組電話號碼給您聽。首先他們會告訴您這是誰的號碼:「這是水電工的號碼。」接著他們會一次唸幾個數字,想停就停。最後他們會說「就是這樣。」

工具呼叫的區塊到達的方式,就跟那組電話號碼一樣。一個事件開啟它,一個或多個事件填入它,再由一個事件關閉它。以下是 Meridian 工具呼叫的事件。

text
{"type": "content_block_start", "index": 0,
 "content_block": {"type": "tool_use", "id": "toolu_01",
                   "name": "lookup_project_status", "input": {}}}
{"type": "content_block_delta", "index": 0,
 "delta": {"type": "input_json_delta", "partial_json": "{\"project_"}}
{"type": "content_block_delta", "index": 0,
 "delta": {"type": "input_json_delta", "partial_json": "id\": \"MEG-2"}}
{"type": "content_block_delta", "index": 0,
 "delta": {"type": "input_json_delta", "partial_json": "026-0142\"}"}}
{"type": "content_block_stop", "index": 0}

在區塊開啟的當下,立即顯示所選擇的工具

工具的名稱位於 content_block_start 中,這是該區塊的第一個事件。當此事件到達時,您的程式碼就能更新狀態列,也就是使用者等待期間,您的使用者介面所顯示的那一行文字。

Meridian 維護一個小型字典 STATUS,將每個工具的名稱對應到給使用者看的文字,因此 lookup_project_status 在畫面上會變成「正在查詢專案...」。

組合串流請求

當串流結束時,呼叫 stream.get_final_message() 即可取得完整的回應,這與 client.messages.create(...) 回傳的物件類型相同。因此,您工具迴圈的其餘部分都能維持原樣。

以下是 Meridian 的串流請求。on_status 是您自行撰寫的函式,用於將字串顯示為狀態列。

載入中

重點整理

  • 串流工具呼叫會先送出工具名稱,接著分段送出其輸入內容。
  • 一旦 content_block_start 到達,就以使用者能理解的文字顯示所選擇的工具。
  • stream.get_final_message() 會回傳完整的回應,因此您工具迴圈的其餘部分不需要改變。