新帳號 免費註冊,送 10 次呼叫,最高 $1,免綁卡。

LLM 代理可觀測性:追蹤與工作階段,重現 10 種問題

目錄
  1. LLM 代理的追蹤、跨度與工作階段是什麼?
  2. 各種問題該看哪個檢視?
  3. LLM 代理為什麼很慢?
  4. LLM 代理為什麼這麼貴?
  5. 收到使用者回報後,如何為 LLM 代理除錯?
  6. 不是自己寫的程式代理,也能追蹤嗎?
  7. 能讓模型幫你讀追蹤紀錄嗎?
  8. 每個代理都該記錄什麼?
  9. 閘道端追蹤有哪些看不到的部分?
  10. 常見問題
  11. 什麼是 LLM 代理可觀測性?
  12. 延伸閱讀

要建立 LLM 代理的可觀測性,先將模型呼叫分成兩個層級:追蹤(trace)以時間軸呈現代理的一次執行;工作階段(session)則涵蓋一段完整對話及其總費用。每次呼叫只要帶上 3 個請求標頭,LLM 閘道就能從請求紀錄建立這兩種檢視,不需要追蹤 SDK。這裡的閘道是位於程式碼與模型供應商之間、記錄每筆請求的代理伺服器。我們將這套做法用在一個小型客服代理和 Claude Code 上,重現了 10 種常見問題:3 種拖慢代理、4 種增加費用,另有 3 種讓代理無法正常運作。

TL;DR

  • 只要 3 個請求標頭(X-Trace-Id、X-Span-Id、X-Session-Id),就能將 Synthorai 閘道的請求紀錄整理成追蹤與工作階段,不需要追蹤 SDK。
  • 同時執行 3 項彼此獨立的子代理檢查,耗時中位數從 3.7 s 降至 1.8 s,費用不變。
  • 將 GPT-6 Luna 提示詞第一行的時間戳記移到別處,從空快取開始測試,快取命中率從 0% 升至 73%。
  • 一次 Claude Code 執行花費 $0.1728,其中 3 個代理各自的第一次呼叫就占了 72%。

本文是 7 篇系列指南的總覽。以下各節摘要介紹其中一篇指南,列出實測數據,並附上連結,完整程式碼與畫面截圖都在各篇指南中。所有測試都在 2026 年 10 月 10 日與 11 日執行。延遲數據是 5 次執行的中位數,其餘數據皆取自單次執行。程式碼位於 github.com/synthorai-io/use-cases。

LLM 代理的追蹤、跨度與工作階段是什麼?

追蹤涵蓋代理處理一則使用者訊息或一項任務的所有動作;跨度(span)是追蹤中的一個步驟;工作階段則是由多筆追蹤組成的完整對話。在我們的客服代理中,一段有 2 輪互動的客戶對話,就是包含 2 筆追蹤的 1 個工作階段,每筆追蹤有 5 或 6 次模型呼叫。部分呼叫來自子代理,也就是主代理為了處理部分任務而啟動的另一個代理。子代理有自己的提示詞,而且通常使用較便宜的模型。

名稱涵蓋範圍標頭哪些呼叫使用相同值
追蹤代理的一次執行X-Trace-Id該次執行的每次呼叫
跨度執行中的一次模型呼叫X-Span-Id僅該次呼叫
工作階段一段對話X-Session-Id該段對話的每次呼叫
代理發出呼叫的子代理X-Agent-Id該子代理的每次呼叫
標籤使用者、功能、版本X-Synthorai-Metadata任何需要分組或篩選的呼叫

識別碼是自行指定的字串,每個最多 128 個可列印 ASCII 字元。標籤是只含字串值的單層 JSON 物件,每筆請求最多 10 個標籤,每個值最多 128 位元組。閘道會在回應中帶回識別碼,但不會將這些標頭轉送給模型供應商。使用 OpenAI Python SDK 時,單筆請求的標頭放在 extra_headers:

resp = client.chat.completions.create(
    model="gpt-6-luna",
    messages=messages,
    extra_headers={
        "X-Session-Id": session_id,   # one per conversation
        "X-Trace-Id": trace_id,       # one per user message
        "X-Span-Id": "plan",          # one per call
        "X-Synthorai-Metadata": '{"feature":"support-agent","_user":"u_1002"}',
    },
)

如果代理已經透過其他工具傳送追蹤識別碼,可能不必再加任何設定。這 3 個識別碼若有缺漏,閘道會分別從 OpenTelemetry、LiteLLM、Langfuse、Open WebUI、Claude Code 或 OpenAI Codex CLI 的對應標頭讀取。OpenTelemetry 是追蹤的開放標準,其 traceparent 標頭包含追蹤識別碼。用 3 個 HTTP 標頭追蹤 LLM 代理 會逐步建立示範代理:先用 GPT-6 Luna 規劃,接著執行 2 個本機工具、用 DeepSeek V4.1 Flash 平行執行 3 項檢查,最後再呼叫模型產生回答。示範執行共發出 21 次模型呼叫,費用為 $0.0017。

各種問題該看哪個檢視?

系列中的每個問題,都能在主控台的特定位置,或透過主控台以外的紀錄查詢方式找到答案。主控台以瀑布圖呈現每筆追蹤:每次模型呼叫各占一條橫條,共用同一條時間軸。平行執行的呼叫會重疊,必須等待前一個完成的呼叫則會排成階梯。

問題查看位置指南
這次執行為什麼花了 8 秒?追蹤摘要:比較閘道時間與應用程式時間代理延遲
這段對話哪一輪最貴?工作階段頁面,每列一筆追蹤代理費用
快取命中率為什麼是 0%?工作階段頁面的快取命中欄位代理費用
這位使用者昨天的請求發生了什麼事?在追蹤清單中用標籤搜尋代理除錯
Claude Code 的哪個子代理花了這些錢?工作階段頁面的代理表格依子代理分析 Claude Code 費用
能在聊天視窗裡問這些問題嗎?Claude 中的 MCP 連接器用 Claude 查詢 LLM 紀錄
自己的程式能調查紀錄嗎?MCP 工具或 REST API讓代理使用紀錄查詢工具

每個追蹤頁面也都有比較按鈕,可在同一頁顯示兩筆追蹤,依名稱配對各次呼叫,並讓兩張瀑布圖共用時間軸。系列中的每項修正,都用這個方式比對修正前後的執行結果。

LLM 代理為什麼很慢?

先看追蹤頂端的時間拆分。它會將總經過時間,也就是使用者從第一筆請求到最後一筆回應的等待時間,分成閘道時間與應用程式時間。只要至少有一筆模型請求尚未完成,就計入閘道時間;只有自己的程式碼在執行時,則計入應用程式時間。示範追蹤的總經過時間為 8.5 s,其中閘道占 7.0 s,應用程式占 1.6 s,因此應該先檢查模型呼叫。延遲指南重現了 3 種原因,每種都執行 5 次:

原因追蹤中的現象修正方式修正前中位數修正後中位數
彼此獨立的呼叫逐一送出橫條排成階梯用 asyncio.gather 一起送出,這是 Python 同時等待多次呼叫的方式3.7 s1.8 s
呼叫之間的程式碼執行太慢your app 那一列很長同時執行 4 次查詢11.0 s9.2 s
一行就能回答的問題也啟用推理輸出 token 數遠多於實際回答reasoning_effort="none"5 個回答共 25.6 s10.2 s

在程式碼過慢的案例中,兩次執行的模型呼叫耗時相近,改變的是應用程式時間:指南中並列的兩次執行,your app 那一列從 4.6 s 降至 1.3 s。換模型無法縮短這段時間,必須先用自己的效能分析工具或 OpenTelemetry 跨度找原因。第三個案例使用 GPT-6 Luna。reasoning_effort 是控制模型回答前推理程度的請求參數,設為 none 後,5 個回答的輸出 token 從 1,150 降至 190。LLM 代理為什麼很慢? 提供這 3 個案例的程式碼與並排追蹤畫面。

LLM 代理為什麼這麼貴?

先將追蹤清單依費用排序,再開啟最貴的工作階段,逐輪查看快取命中與輸入欄位。快取命中率是從供應商提示詞快取讀取的輸入 token 比例;供應商對先前看過的提示詞前綴,只收取原輸入價格的一部分。費用指南重現了 4 種原因。示範規模很小,金額都不到 1 美分,實際可套用的是改善比例:

原因出現位置修正前修正後
提示詞第一行放了時間戳記快取命中率維持 0%0% 命中快取73% 命中快取,輸入費用降低 66%
歷史紀錄每次重送所有工具結果每輪輸入持續增加24,546 個輸入 token8,053 個輸入 token
重試迴圈同一筆追蹤有過多呼叫,狀態碼全是 20010 次呼叫2 次呼叫
簡單步驟用了昂貴模型工作階段頁面的代理表格$0.0012$0.0003

第一列使用 GPT-6 Luna,從空快取開始測試。指南也介紹了另一種無法命中快取的情況:送出使用者訊息時附加一段指令,儲存訊息時卻沒有保留,導致每輪重建的提示詞都與上一輪實際送出的不同。將訊息依實際送出的內容儲存後,該段對話的命中率從 0% 升至 76%。在歷史紀錄的案例中,輸入 token 增至 3 倍,輸入費用卻只增加 21%,因為其中 71% 是從快取讀取。最後一列中,GPT-6.1 Sol 執行的 3 項子代理檢查占工作階段費用的 81%;改用 DeepSeek V4.1 Flash 執行相同檢查,費用只要十分之一。LLM 代理為什麼這麼貴? 逐一說明這些案例。

收到使用者回報後,如何為 LLM 代理除錯?

先用標籤找出使用者的執行紀錄,再從追蹤中找出出問題的那次呼叫。出錯的呼叫往往也會回傳狀態碼 200。只要每筆請求都帶有 _user 標籤,在追蹤清單的搜尋欄輸入 metadata._user = u_1002,就能找出該使用者的工作階段與追蹤。除錯指南接著重現了 3 種失敗情況:

  • 回傳 404 後重試。呼叫不存在的模型識別碼,95 ms 後失敗,費用為 $0。重試沿用相同追蹤識別碼,並使用新的跨度名稱(policy-check.retry),因此失敗呼叫與重試會在瀑布圖中相鄰顯示。
  • 狀態碼是 200,回答卻被截斷。設定 max_tokens=40 並保留推理功能時,連續 3 次呼叫 DeepSeek V4.1 Flash 都回傳狀態碼 200,輸出恰好是 40 個 token,回答內容卻是空的,因為推理耗盡了額度。追蹤中會看到相同跨度出現 3 次,輸出數量都等於上限。將上限提高至 2,000 並關閉推理後,只需 1 次呼叫,就能用 49 個 token 回答。只提高上限也有效,但那次呼叫用了 1,876 個推理 token,耗時 22 s。
  • 第二輪回傳 400。儲存的歷史紀錄遺失了一筆工具呼叫,導致 GPT-6 Luna 在第二輪於 0.5 s 內拒絕請求,未收費;第一輪則正常完成。

第二種失敗說明了為什麼每次呼叫都該記錄 finish_reason,這個回應欄位會指出模型停止的原因。值為 length 代表回答碰到了 max_tokens 上限,即使狀態碼是 200,也應視為失敗。為 LLM 代理除錯 列出每個代理都該記錄的 5 個欄位,本文接近結尾處也會再列一次。

不是自己寫的程式代理,也能追蹤嗎?

不必修改 Claude Code 就能追蹤,因為它原本就會在每筆請求附上工作階段識別碼與代理識別碼。設定 2 個環境變數就能讓它改用閘道,第 3 個變數則會補上各子代理的類型:

export ANTHROPIC_BASE_URL="https://synthorai.io"   # no /v1: Claude Code adds /v1/messages itself
export ANTHROPIC_API_KEY="<your gateway key>"
export CLAUDE_CODE_GATEWAY_HINT_HEADERS=1          # optional: names sub-agent types such as Explore

一次使用 Claude Sonnet 5.5 的 Claude Code 執行,啟動了 2 個子代理來修正 1 個失敗測試,共發出 11 筆請求,費用為 $0.1728。工作階段頁面依代理拆分後可看到:2 個子代理占費用的 39%,快取寫入占 77%,而 3 個代理各自的第一次呼叫合計占 72%。半分鐘後,在快取已暖機的情況下重跑相同任務,費用為 $0.0812。依子代理分析 Claude Code 費用 也示範如何依開發者與儲存庫拆分團隊費用。

能讓模型幫你讀追蹤紀錄嗎?

可以透過 MCP 讓模型讀取,因為閘道會以 MCP 伺服器提供請求紀錄。MCP 是 Model Context Protocol,也是提供工具給模型使用的標準方式。該用哪種方式讀取紀錄,取決於誰在查詢:

查詢者使用方式模型 token實測結果
有問題要問的人在 Claude 中加入閘道連接器包含在聊天方案中3 個問題共呼叫工具 14 次,所有數字都與帳務紀錄一致
執行固定查詢的程式REST API不需要查出費用最高的 3 個工作階段,花費 $0
自行決定調查方向的代理程式中的 MCP 工具需支付費用一次費用調查花費 $0.0015

在聊天案例中,Claude Fable 5.1 透過 OAuth 連線,以瀏覽器登入取代貼上 API 金鑰,且只取得請求紀錄的讀取權限。它因此取得 4 個唯讀工具,以及一把 7 天後到期的金鑰。工具只回傳費用、token 與狀態,不包含提示詞或回答文字。在程式案例中,GPT-6 Luna 只呼叫工具 1 次,就從 26 個工作階段中找出快取、模型選擇錯誤與重試迴圈的案例,費用為 $0.0015;Claude Sonnet 5.5 則先查詢價格,再經過 3 次工具呼叫找出相同工作階段,費用為 $0.0631。用 Claude 查詢 LLM 紀錄 與 讓代理使用紀錄查詢工具 分別介紹這兩種設定。

每個代理都該記錄什麼?

5 個欄位就能涵蓋系列中的所有問題:

  • 每次執行的追蹤識別碼,以及每段對話的工作階段識別碼,透過標頭送出,也寫入自己的紀錄。
  • 每次呼叫的跨度名稱,標明步驟與嘗試次數,例如 plan-2 或 policy-check.retry。
  • 使用者、功能與版本標籤,讓使用者回報或帳單能對應到相關執行紀錄。
  • 每次呼叫的 x-request-id 回應標頭,方便日後查詢該次呼叫的實際計費金額。
  • 每次呼叫的 finish_reason 與輸出 token 數,並將 length 視為失敗。

每次呼叫也要設定用戶端逾時與 max_tokens,兩者都要留足餘裕。前面那些回傳 200 卻沒有內容的案例,就是因為只按可見回答的長度設定上限;推理模型的一次長呼叫也可能花上數分鐘。

閘道端追蹤有哪些看不到的部分?

從請求紀錄建立的追蹤只能涵蓋模型呼叫,看不到自己程式碼內部的執行細節。如同程式碼過慢的案例,閘道是從請求之間的空檔推算程式碼執行時間。工具、檢索與佇列的跨度仍需要 OpenTelemetry SDK;兩邊可以共用追蹤識別碼,因為沒有 X-Trace-Id 時,閘道會讀取 traceparent。這些追蹤也不包含提示詞與回應本文,而且只能在呼叫完成後回報費用。若要即時控管預算,就必須在自己的代理迴圈中,每收到一筆回應便依其 usage 計算費用。

常見問題

什麼是 LLM 代理可觀測性?

LLM 代理可觀測性,是指能看清楚代理每次執行做了什麼:呼叫哪些模型、執行順序、各次耗時與費用,以及哪些呼叫失敗。實作上,就是將呼叫整理成追蹤(一次執行)與工作階段(一段對話),再加上使用者與功能標籤。

延伸閱讀

← 返回部落格