與 Claude Managed Agents 的通訊是以事件為基礎的。您向代理傳送使用者事件,並接收回傳的代理事件與工作階段事件以追蹤狀態。
事件以兩個方向流動。
user.* 事件會啟動工作階段並在其進行過程中加以引導;system.message 會附加系統層級的上下文,適用於隨附的回合以及所有後續回合。工作階段、span、代理、使用者與系統事件類型字串遵循 {domain}.{action} 命名慣例。僅限串流的增量預覽事件(event_start、event_delta)是例外。完整目錄請參閱參考文件中的事件類型。
每個持久化的事件都包含一個 processed_at 時間戳記,於事件處理完成時設定。對於您傳送的事件,當事件仍排在較早事件之後的佇列中時,processed_at 為 null。例外是 user.define_outcome、user.custom_tool_result 與 user.tool_result,它們在接收時即被處理,並在回傳時已填入 processed_at。
傳送 user.message 事件以啟動或繼續代理的工作:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Analyze the performance of the sort function in utils.py",
},
],
},
],
)傳送 user.interrupt 事件以在執行過程中停止代理,然後接著傳送 user.message 事件將其重新導向:
# Agent 目前正在分析檔案...
# 以新的指示中斷:
client.beta.sessions.events.send(
session.id,
events=[
{"type": "user.interrupt"},
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Instead, focus on fixing the bug in line 42.",
},
],
},
],
)代理會確認中斷並切換到新任務。被中斷的回合以 session.status_idle 事件結束,其 stop_reason 為 end_turn,與自行完成的回合相同;沒有專屬於中斷的停止原因。
預設情況下,代理的回應文字會以緩衝的 agent.message 事件形式抵達串流,每個事件僅在產生它的模型請求完成後才發出。「Event deltas」(事件增量)讓您能在模型仍在生成文字時,以即時預覽的方式漸進地呈現該文字。預覽並非回應本身:預覽是盡力而為的顯示輔助,而緩衝的 agent.message 永遠是權威記錄。忽略預覽的用戶端仍會收到完整且正確的串流。
預覽是依每個串流連線選擇加入的。將 event_deltas[] 查詢參數加入您正在讀取的串流,針對每個您想要預覽的事件類型重複一次。由於 [] 是 shell 的 glob 模式,每當您在 shell 中建構請求時請將 URL 加上引號;範例中將方括號百分比編碼為 %5B%5D,這同樣有效。兩個串流端點都接受此參數:位於 GET /v1/sessions/{session_id}/events/stream 的工作階段層級串流,以及每個工作階段執行緒各自位於 GET /v1/sessions/{session_id}/threads/{thread_id}/stream 的串流。可接受的值為 agent.message 與 agent.thinking;任何其他值都會回傳 400 錯誤,超過 100 個值的請求亦然。子代理的預覽會出現在該子代理自己的執行緒串流上。
當被預覽的事件開始時,串流會發出一個 event_start,攜帶即將到來之事件的類型與 id:
{
"type": "event_start",
"event": {
"type": "agent.message",
"id": "sevt_01abc..."
}
}對於 agent.message,開始事件之後會接著攜帶漸進文字的 event_delta 事件。每個增量在 event_id 中指明它所延伸的事件,並在 delta.index 中指明它所延伸的內容區塊:
{
"type": "event_delta",
"event_id": "sevt_01abc...",
"delta": {
"type": "content_delta",
"index": 0,
"content": {
"type": "text",
"text": "Here is the summary"
}
}
}當 agent.thinking 事件被預覽時,只會發出 event_start。之後不會有 event_delta 事件,而結束該預覽的緩衝 agent.thinking 事件不攜帶任何思考內容;它是進度訊號,而非內容載體。
與持久化事件不同,event_start 與 event_delta 本身沒有 id 或 processed_at。它們攜帶的唯一識別碼是其所預覽之事件的 id。
每個支援事件增量的 SDK 都包含一個累加器輔助工具,為您處理 index 的簿記工作。Go、Java、Ruby 與 C# 的輔助工具還會以事件的 id 作為累加中預覽的鍵;使用 Python、TypeScript 與 PHP 的輔助工具時,您需自行維護該對應表,並將每個增量合併到其 id 對應的項目中。當您需要自訂簿記時,手動模式在每種語言中也都適用:將其套用於生成的事件類型即可。
在手動模式中,將預覽視為暫存緩衝區,將緩衝事件視為記錄。以 (event_id, index) 作為緩衝區的鍵。依每個模型請求進行對帳:一個回合以單一個 session.status_running 事件開啟,接著在正常完成的回合中,每個模型請求依序產生 span.model_request_start、event_start、各個 event_delta 事件、緩衝的 agent.message,最後是 span.model_request_end(位於 Span 事件分頁中)。在傳輸線路上,這是該序列中被預覽的部分,與該連線的其他緩衝事件交錯出現:
event_start {"event": {"type": "agent.message", "id": "sevt_01abc..."}}
event_delta {"event_id": "sevt_01abc...", "delta": {"type": "content_delta", "index": 0, "content": {"type": "text", "text": "..."}}}
...
agent.message {"id": "sevt_01abc...", "content": [...]}event_delta 行會針對每個文字片段重複一次。在每個事件抵達時加以處理:
event_start 時,記下所宣告的 id。識別碼永遠一致:event_start.event.id、每個 event_delta.event_id,以及緩衝 agent.message 的 id 都是相同的值。event_delta 時,將 delta.content.text 附加到 (event_id, delta.index) 的項目,並呈現累積中的文字。某個 index 的第一個增量會建立該項目。agent.message 抵達時,以 id 進行比對,捨棄累加的預覽,改為呈現該訊息的內容。span.model_request_end 時,關閉任何尚未由其緩衝事件對帳的預覽。不會再有更多增量傳來。如果回合發生錯誤或被中斷,緩衝事件可能永遠不會抵達;但 span.model_request_end 仍然會。此模式所依賴的保證:
(event_id, index) 為鍵,依抵達順序串接預覽的增量,會得到緩衝事件中 content[index].text 的前綴(是前綴,不一定是完整文字,因為增量在負載下可能被捨棄)。event_id 最多發出一個 event_start,而緩衝事件是該連線針對該 id 傳遞的最後一項內容。# 預覽快照,以事件 id 為鍵。accumulate_managed_agents_event 會將每個
# event_start / event_delta 折疊成 agent.message 快照;緩衝的
# agent.message 會取代它。
previews: dict[str, BetaManagedAgentsAgentMessageEvent] = {}
# 在此連線上啟用 agent.message 預覽
with client.beta.sessions.events.stream(
session.id, event_deltas=["agent.message"]
) as stream:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [{"type": "text", "text": "Describe the repo in one sentence."}],
},
],
)
for event in stream:
match event.type:
case "event_start":
snapshot = accumulate_managed_agents_event(None, event)
if snapshot is not None:
previews[event.event.id] = snapshot
print(f"event_start {event.event.type} {event.event.id}")
case "event_delta":
preview = accumulate_managed_agents_event(previews.get(event.event_id), event)
if preview is not None:
previews[event.event_id] = preview
text = "".join(block.text for block in preview.content)
print(f"event_delta preview: {text!r}")
case "agent.message":
# 緩衝事件才是正式記錄:它會取代並關閉預覽
preview = accumulate_managed_agents_event(previews.pop(event.id, None), event)
text = "".join(block.text for block in preview.content)
print(f"agent.message {event.id} {text!r}")
case "span.model_request_end":
# 不會再有 delta 了。關閉所有其
# 緩衝事件從未到達的預覽。
for event_id in previews:
print(f"span.model_request_end closing preview for {event_id}")
previews.clear()
case "session.status_idle":
break在多代理工作階段中,每個工作階段執行緒在 GET /v1/sessions/{session_id}/threads/{thread_id}/stream 都有自己的事件串流,並接受相同的 event_deltas[] 參數與相同的值。預覽在設計上是以執行緒為範圍的:一個連線只預覽它正在讀取的執行緒。子執行緒的預覽會在該子執行緒自己的串流上傳遞,絕不會交叉發布到工作階段層級的串流,後者的預覽範圍維持在主要執行緒。若要在模型生成時觀看子代理的文字,請開啟該子代理的執行緒串流。
執行緒串流的路徑很容易弄錯:它是 /threads/{thread_id}/stream,而非 /events/stream(後者僅存在於工作階段層級),且沒有 /threads/{thread_id}/events/stream 端點。
預覽事件本身不會改變。event_start 與 event_delta 在執行緒串流上的形狀與在工作階段層級串流上相同,且累加與對帳模式可照原樣套用。唯一的調整在於簿記:每個串流連線執行一個累加器實例。
# 列出工作階段的執行緒並挑選一個子執行緒:子執行緒帶有非 null 的
# parent_thread_id,而主執行緒的 parent_thread_id 為 null。
THREAD_ID=$(
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/sessions/$SESSION_ID/threads?beta=true" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" |
jq -er 'first(.data[] | select(.parent_thread_id != null)).id'
)
# 子執行緒的串流接受與工作階段串流相同的 event_deltas[] 參數。
# 請對方括號進行百分比編碼(%5B%5D)並為 URL 加上引號。
exec {stream}< <(
curl --fail-with-body -sS -N \
"https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/stream?beta=true&event_deltas%5B%5D=agent.message" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "accept: text/event-stream"
)
while IFS= read -r -u "$stream" event_line; do
[[ $event_line == data:* ]] || continue
event_json=${event_line#data: }
case $(jq -r '.type' <<<"$event_json") in
event_delta)
jq -j '.delta.content.text' <<<"$event_json"
;;
agent.message)
# 緩衝的事件是權威記錄;請呈現其內容。
printf '\n'
jq -j '.content[] | select(.type == "text") | .text' <<<"$event_json"
printf '\n'
;;
session.thread_status_idle)
break
;;
esac
done
exec {stream}<&-讀取迴圈在收到 session.thread_status_idle 時結束,這是當工作階段執行緒的回合完成且執行緒進入閒置時所發出的事件。
預覽是為了回應速度而調校的。請依據以下限制進行建構:
agent.message 仍會完整抵達。切勿將累加的預覽視為最終結果。agent.message。沒有辦法重新請求遺漏的增量。agent.thinking:agent.thinking 預覽僅發出 event_start 作為思考區塊已開始的訊號;之後不會有 event_delta 事件。event_start 與 event_delta 僅存在於即時串流上。它們不會出現在工作階段的事件歷史記錄(GET /v1/sessions/{session_id}/events)或任何工作階段執行緒的事件歷史記錄中。如果串流的行為不如您預期:
| 您看到的情況 | 代表的意義 |
|---|---|
串流有緩衝事件但沒有 event_start 或 event_delta | 您正在讀取的連線未選擇加入(event_deltas[] 是依連線套用,而非依工作階段),或該回合從未觸及您正在串流的執行緒。預覽是以執行緒為範圍的,因此請列出工作階段的執行緒(GET /v1/sessions/{session_id}/threads)以找出是哪一個執行了。 |
| 串流 URL 回傳 404 | 路徑或某個 ID 錯誤,或請求完全未攜帶 managed-agents beta 標頭。執行緒端點受 beta 管控,因此沒有該標頭時它們不存在。 |
指明 event_deltas 的 400 | 僅接受 agent.message 與 agent.thinking。 |
當代理呼叫自訂工具時:
agent.custom_tool_use 事件,包含工具名稱與輸入。stop_reason: requires_action 的 session.status_idle 事件暫停。阻塞中的事件 ID 位於 stop_reason.event_ids 陣列中。user.custom_tool_result 事件,在 custom_tool_use_id 參數中傳入事件 ID 以及結果內容。running。with client.beta.sessions.events.stream(session.id) as stream:
for event in stream:
if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
match stop_reason.type:
case "requires_action":
for event_id in stop_reason.event_ids:
# 查詢該自訂工具使用事件並執行它
tool_event = events_by_id[event_id]
result = call_tool(tool_event.name, tool_event.input)
# 將結果回傳
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.custom_tool_result",
"custom_tool_use_id": event_id,
"content": [{"type": "text", "text": result}],
},
],
)
case "end_turn":
break當權限政策要求在工具執行前進行確認時:
agent.tool_use 或 agent.mcp_tool_use 事件。stop_reason: requires_action 的 session.status_idle 事件暫停。阻塞中的事件 ID 位於 stop_reason.event_ids 陣列中。user.tool_confirmation 事件,在 tool_use_id 參數中傳入事件 ID。將 result 設為 "allow" 或 "deny"。使用 deny_message 說明拒絕的原因。running。with client.beta.sessions.events.stream(session.id) as stream:
for event in stream:
if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
match stop_reason.type:
case "requires_action":
for event_id in stop_reason.event_ids:
# 核准待處理的工具呼叫
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": event_id,
"result": "allow",
},
],
)
case "end_turn":
break工作階段在互動之間會持續存在。除非工作階段被明確刪除,否則對話歷史記錄會被保留。當工作階段進入閒置時,其沙箱會建立檢查點,保留完整的沙箱狀態,包括檔案系統、已安裝的套件,以及代理建立的任何檔案。這讓您能夠從非活動狀態乾淨地恢復。
若要恢復工作階段,請照常向其傳送 user.message 事件:
# 在正式環境中,請傳入您要恢復之 session 的已儲存 ID。
ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
events:
- type: user.message
content:
- type: text
text: Now run the tests against the changes you made earlier.
YAML以預算建立的工作階段會暫停而非超支。當工作階段追蹤的定價成本達到上限時,平台會在每個執行緒的下一個模型請求之前將其暫停,而工作階段會以 budget_reached 的 stop_reason 進入閒置,而非終止。使總額超過上限的那個請求會執行到完成,因此 session.usage 快照所回報的 list_cost 可能顯示為等於或略微超過上限。在串流上,暫停會以三個事件依序抵達:
stop_reason: budget_reached 的 session.thread_status_idle,每個執行緒暫停時各一個。session.usage,工作階段累計用量與追蹤定價成本的快照。stop_reason: budget_reached 的 session.status_idle。session.usage 事件永遠緊接在此閒置事件之前。若某個執行緒的最後一個請求既跨越上限又完成其回合,則它在自己的 session.thread_status_idle 事件上回報 end_turn,而工作階段仍回報 budget_reached;請以工作階段層級的 stop_reason 為依據來偵測暫停。
當工作階段處於上限時,它僅接受用於結清已進行中工作的事件:user.tool_confirmation、user.tool_result、user.custom_tool_result 與 user.interrupt。任何會啟動新工作的事件,包括 user.message,都會以指明該清單的 400 錯誤被拒絕。當工作階段同時有一個執行緒在等待工具詢問、另一個執行緒在上限處暫停時,工作階段層級的 stop_reason 是 requires_action,而非 budget_reached:結清該詢問不會觸發模型請求,因此請照常回應它。
沒有任何事件能恢復在上限處暫停的工作階段。請改為更新工作階段的預算:將上限變更為高於已消耗定價成本的任何值,或透過以 "budget": null 更新工作階段來移除預算,都會自動恢復暫停的工作。關於定價成本如何追蹤以及完整的預算更新語意,請參閱工作階段預算。
傳送 system.message 事件,為代理提供具特權的系統層級上下文,適用於隨附的回合以及所有後續回合。與代理定義上的 system 欄位(設定頂層「system prompt」(系統提示))不同,system.message 的內容會以 role: "system" 回合的形式附加到工作階段的系統上下文中,而非取代該提示。當代理在工作階段中途需要更新的系統層級指引時使用它:不同的角色設定、修訂後的限制,或在執行期間擷取、應在往後塑造模型行為的上下文。
ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
events:
- type: system.message
content:
- type: text
text: "The user's current timezone is America/New_York."
YAML當工作階段以 stop_reason: requires_action 處於閒置時,system.message 僅在同一請求中跟隨於工具結果事件之後時才會被接受;若單獨傳送或與 user.message 一起傳送,則會被拒絕,直到待處理的工具事件被解決為止。content 接受 1–1000 個文字項目。
session 物件包含一個 usage 欄位,記錄該 session 的累計用量:token 數量、伺服器工具使用、活躍時間,以及追蹤的定價成本。請在 session 進入閒置狀態後擷取該 session,以讀取最新的總計數值。
{
"id": "sesn_01...",
"status": "idle",
"usage": {
"input_tokens": 5000,
"output_tokens": 3200,
"cache_read_input_tokens": 20000,
"cache_creation": {
"ephemeral_5m_input_tokens": 2000,
"ephemeral_1h_input_tokens": 0
},
"list_cost": {
"amount": "187",
"currency": "USD"
},
"active_seconds": 342.5,
"server_tool_use": {
"web_search_requests": 3,
"web_fetch_requests": 0
}
}
}input_tokens 回報未快取的輸入 token,output_tokens 回報該 session 中所有模型呼叫的輸出 token 總數。cache_read_input_tokens 欄位回報從提示快取(prompt cache)讀取的 token,而 cache_creation 物件則依快取存留時間細分快取建立 token(ephemeral_5m_input_tokens 與 ephemeral_1h_input_tokens)。快取項目預設使用 5 分鐘的 TTL,因此在該時間範圍內連續進行的輪次可受益於快取讀取,從而降低每個 token 的成本。
list_cost 是該 session 依公開定價計算的累計消耗,以字串形式表示的整數美分,並附帶貨幣代碼。active_seconds 是該 session 至少有一個執行緒正在執行的累計時間;並行執行緒的重疊活動只計算一次,這與 session 的 stats 物件中的 active_seconds 不同,後者會將每個執行緒各自的活躍時間加總。這個去重後的數值是 session 執行時間成本的計價依據。server_tool_use 計算由伺服器執行的工具請求以供計價:網頁搜尋請求按每次請求計入定價成本,而網頁擷取請求不收取每次請求費用且不計量,因此 web_fetch_requests 顯示為 0。每個 session 執行緒各自的 usage 也帶有 list_cost 與 active_seconds。各執行緒的數值是獨立四捨五入的,且不包含 session 的執行時間成本,因此它們的加總不會精確等於 session 的 list_cost;session 的數值才是權威數值。
您不必輪詢 session 來觀察這些總計數值。session.usage 事件會在 session 串流與事件歷史中攜帶相同的累計快照(usage 物件,加上 session 的 budget,當 session 沒有預算時為 null)。它是在閒置狀態轉換時發出,而非依計時器發出:session 會在進入閒置狀態前立即發出一次(無論停止原因為何),並在執行緒因 session 預算而暫停時發出一次。因此,串流讀取端無需額外擷取,即可看到一個輪次的最終成本,或觸及預算之工作的最終成本。
若要強制執行支出上限,請設定 session 預算,而非自行輪詢用量並停止 session。平台會持續為 session 的消耗計價,一旦 session 的定價成本達到上限,便會在每個執行緒的下一次模型請求之前將其暫停;請參閱達到 session 預算以了解這在串流上的呈現方式。
Claude Console 提供代理 session 的視覺化時間軸檢視。前往 Console 中的 Claude Managed Agents 區段即可查看:
session.error 事件傳達Was this page helpful?