面試官問「P95 延遲突然升高,你怎麼辦?」
多數人說「我去看 Logs」。
強力雇用的答案是:「我打開 Trace,找哪個 hop 吃掉了時間。」
Log 告訴你發生了什麼;Trace 告訴你在哪裡、花了多少。
面試情境
面試官:「你幫客戶部署了一個 ADK Multi-Agent 系統:並行查三個後端、彙整後做決策。上線後客戶回報:有時候 2 秒,有時候 15 秒。你不在客戶現場。你如何在 5 分鐘內定位問題?你的可觀測性設計是什麼?」
一、為什麼 Log 不夠,需要 Trace
Log 的問題:只記錄「發生了什麼」,不記錄「在哪裡、花了多久」
一個 Multi-Agent 請求的真實路徑:
User Query
│
▼
Orchestrator Agent
├── LLM Call #1(決策) ?ms
├── ParallelAgent
│ ├── Sub-Agent A ?ms
│ │ ├── Embedding Call
│ │ ├── Vector Search ← 瓶頸在這裡?
│ │ └── LLM Call #2
│ ├── Sub-Agent B ?ms
│ │ └── DB Query
│ └── Sub-Agent C ?ms
│ └── External API ← 還是這裡?
└── LLM Call #3(整合) ?ms
Log 只能告訴你:「LLM Call #3 回傳了 X」
Trace 能告訴你:「整條路徑花了 15 秒,
其中 12 秒在 Sub-Agent A 的 Vector Search」
沒有 Trace:猜
有 Trace:找到
二、Span 樹的結構設計
一條 Trace = Root Span + 多層 Child Spans(樹狀結構)
┌─────────────────────────────────────────────────────────────────┐
│ ROOT SPAN: agent.run(orchestrator) total: 15,240ms │
│ │
│ ├── SPAN: llm.generate #1 280ms │
│ │ attrs: model=gemini-2.0-flash │
│ │ input_tokens=1,200 output_tokens=85 │
│ │ finish_reason=STOP │
│ │ │
│ ├── SPAN: parallel_agent.run 14,700ms │
│ │ │ │
│ │ ├── SPAN: sub_agent.A 14,650ms ← ! │
│ │ │ ├── SPAN: embedding.create 45ms │
│ │ │ ├── SPAN: vector_search ← 14,520ms ← !! │
│ │ │ │ attrs: index=prod-v2, top_k=5 │
│ │ │ │ num_results=5, query_len=128 │
│ │ │ └── SPAN: llm.generate #2 85ms │
│ │ │ │
│ │ ├── SPAN: sub_agent.B 320ms │
│ │ │ └── SPAN: tool.db_query 290ms │
│ │ │ │
│ │ └── SPAN: sub_agent.C 480ms │
│ │ └── SPAN: tool.external_api 450ms │
│ │ │
│ └── SPAN: llm.generate #3 260ms │
│ attrs: input_tokens=3,400 output_tokens=320 │
└─────────────────────────────────────────────────────────────────┘
5 分鐘診斷:瀑布圖一眼看出 vector_search 佔了 95% 的延遲。
根本原因:vector index 過大,缺少 min-instances,每次 cold query 重新載入。
三、Span 的 Attribute 設計
每種 Span 類型應記錄的關鍵 Attributes:
Span 類型 必錄 Attributes 用途
───────────────────────────────────────────────────────────────────
agent.run agent_name, input_len, 追蹤哪個 Agent 慢
output_len, iteration_count
───────────────────────────────────────────────────────────────────
llm.generate model, input_tokens, 成本和速度監控
output_tokens, latency_ms,
finish_reason, tokens_per_sec
───────────────────────────────────────────────────────────────────
tool.call tool_name, success (bool), 找失敗的 Tool
error_type, latency_ms
───────────────────────────────────────────────────────────────────
vector_search index_name, top_k, 找 Retrieval 瓶頸
num_results, latency_ms
───────────────────────────────────────────────────────────────────
embedding.create model, input_len, latency_ms Embedding 成本
「Granular」的定義:
不只記錄「整個 Agent 跑了多久」,
而是每一個 LLM 呼叫、每一個 Tool 呼叫、每一個 Vector Search
都有自己的 Span 和精確的時間戳記。
不應該記錄在 Span Attributes 裡的:
❌ Prompt 原文(可能含有 PII)
❌ LLM 輸出原文(可能含有敏感業務資料)
✅ 只記錄長度、token 數、結構化的狀態碼
四、OpenTelemetry + Cloud Trace 整合
工具選擇:為什麼用 OpenTelemetry(OTel)
OTel 是業界標準框架:
├── 語言無關(Python、Node.js、Go、Java)
├── 後端無關(Cloud Trace、Jaeger、Datadog、Zipkin)
└── ADK 和 Vertex AI SDK 都有 OTel 整合點
替代方案比較:
┌─────────────────┬──────────────────────┬──────────────────────┐
│ │ OTel + Cloud Trace │ 直接用 Cloud Logging │
├─────────────────┼──────────────────────┼──────────────────────┤
│ 瀑布圖視覺化 │ ✅ 原生支援 │ ❌ 需要自己解析時間 │
│ 跨 Agent 追蹤 │ ✅ Trace ID 串聯 │ ❌ 要手動 JOIN Log │
│ P95 分析 │ ✅ Cloud Trace 內建 │ ❌ 要自己寫 Query │
│ 設定複雜度 │ 中(需要 Exporter) │ 低(已有 Log) │
│ 廠商鎖定 │ 低(OTel 是開放標準)│ 中(Cloud Logging) │
└─────────────────┴──────────────────────┴──────────────────────┘
結論:生產 Multi-Agent 系統,OTel + Cloud Trace 是必要的。
Log 只能做補充,不能替代 Trace。
整合架構:
ADK Agent Code
│ OTel SDK(instrument 每個 Span)
↓
OTLP Exporter(BatchSpanProcessor)
│ 批次上送,降低對 Agent 的效能影響
↓
Cloud Trace API(GCP)
│
↓
Cloud Trace UI(瀑布圖 / 延遲分佈 / Span 搜尋)
五、一條 Trace 應該回答的五個診斷問題
問題 1:哪個 hop 是延遲瓶頸?
→ 看 Span duration,找最長的那個
→ 範例:Vector Search 14,520ms(總延遲的 95%)
→ 行動:調整 Vector Search 配置(min-instances / index 優化)
問題 2:Token 在哪裡被消耗?
→ 看每個 llm.generate Span 的 input_tokens
→ 範例:Orchestrator LLM Call #3 的 input_tokens=3,400
(Sub-Agent 的結果被完整傳入,沒有做摘要)
→ 行動:在 Sub-Agent 輸出進入 Orchestrator 前做摘要截斷
問題 3:哪個 Tool 最常失敗?
→ 過濾 tool.success=false 的 Span
→ 範例:external_api 在 14:00-16:00 有 12% 失敗率
→ 行動:外部 API 問題,不是 AI 問題,通知客戶
問題 4:Sub-Agent 有沒有真正並行?
→ 看 ParallelAgent 的 child spans 時間是否重疊
→ 範例:Sub-Agent B 和 C 在等 Sub-Agent A 跑完才開始
(並行設計失效,實際是序列)
→ 行動:ADK ParallelAgent 配置問題,檢查依賴關係
問題 5:LLM 有沒有被截斷?
→ 看 llm.generate Span 的 finish_reason
→ 範例:finish_reason=MAX_TOKENS(被截斷,沒有完整輸出)
→ 行動:增加 max_output_tokens 或減少 input context
六、Sampling 策略的 Trade-off
問題:100% Trace 在高流量下成本太高
50,000 QPS × 20 Spans/request = 100 萬 Spans/秒
Cloud Trace 計費:$0.20 per 100 萬 Spans
= $17,280/天(100% Sampling)← 不可接受
分層 Sampling 策略:
流量類型 Sampling Rate 理由
──────────────────────────────────────────────────────────────
異常請求 100% 需要完整診斷資料
(latency > 5s 或 error)
──────────────────────────────────────────────────────────────
Dev / Staging 100% 找問題時需要完整資料
──────────────────────────────────────────────────────────────
Production 正常流量 1% 控制成本,統計上足夠
──────────────────────────────────────────────────────────────
Production 高峰 0.1% 高峰期成本控制
──────────────────────────────────────────────────────────────
策略:Head-based Sampling(在請求開始時決定要不要採樣)
+ Tail-based Sampling(結束後如果 latency > threshold,強制保留)
實作:
from opentelemetry.sdk.trace.sampling import (
TraceIdRatioBased, # 按比例隨機採樣
ParentBased, # 繼承 parent 的採樣決策
)
# 生產:1% 隨機 + 100% 保留異常
sampler = ParentBased(root=TraceIdRatioBased(0.01))
七、系統效應:加入 Tracing 對系統的影響
維度 有 Tracing 沒有 Tracing
──────────────────────────────────────────────────────────────────
問題定位時間 5 分鐘(看瀑布圖) 數小時(猜測 + 加 Log + 重現)
──────────────────────────────────────────────────────────────────
效能影響 < 1% 延遲增加 無
(BatchExporter 非同步上送)
──────────────────────────────────────────────────────────────────
成本 Cloud Trace 費用 節省 Trace 費用,
(依 Sampling 調整) 但付出更多工程師 Debug 時間
──────────────────────────────────────────────────────────────────
穩定性洞察 每個外部依賴的 P95 只知道整體慢,
分開可見 不知道是哪個依賴
──────────────────────────────────────────────────────────────────
Root Cause 能力 精確到 Span 層級 只能到「系統」層級
(vector_search 14s) (「系統有時候慢」)
──────────────────────────────────────────────────────────────────
合規風險 需確保 PII 不進 Spans 相對安全,但失去診斷能力
關鍵設計原則:
Tracing 是上線前必須建好的基礎設施,不是出問題後才加的補救工具。
「出問題再加 Tracing」代表你永遠無法診斷「第一次出問題」的情況。
八、Trade-off 總覽
選擇面 選項 A 選項 B 建議
─────────────────────────────────────────────────────────────────
Trace Backend Cloud Trace(GCP 原生) Jaeger/Zipkin(自建)Cloud Trace(省維護)
─────────────────────────────────────────────────────────────────
Sampling 1% Random 100% 分層策略(右欄說明)
低成本 高成本/高覆蓋
─────────────────────────────────────────────────────────────────
Instrumentation 手動 OTel ADK 內建 Logging OTel(Span 粒度更細)
─────────────────────────────────────────────────────────────────
PII 處理 記錄 Prompt 內容 只記錄 token 數/長度 只記錄數字,不記錄內容
(高 Debug 能力) (合規安全)
─────────────────────────────────────────────────────────────────
什麼場景可以跳過 Granular Tracing?
├── Dev / POC 階段(Cloud Logging 夠用)
├── 單一 LLM 呼叫的簡單應用(沒有 Multi-Agent 複雜性)
└── 流量 < 100 QPS(問題可以靠 Log 追查)
什麼場景一定要有 Granular Tracing?
├── Multi-Agent 系統(多層呼叫,Log 無法重建執行路徑)
├── 有 SLA 要求(P95 < 5s,需要量化每個 hop 的貢獻)
└── 有外部依賴(外部 API / DB,需要區分是誰的問題)
九、面試答題要點
「這道題的核心是:當 Multi-Agent 系統出問題,你如何在不重現問題的情況下找到根因。
架構設計:每個 Agent.run、LLM.generate、Tool.call、Vector Search 都有獨立的 Span,帶 latency_ms、token 數、success/fail 等 Attributes。用 OpenTelemetry SDK + Cloud Trace Exporter,BatchSpanProcessor 非同步上送,對 Agent 的效能影響 < 1%。
診斷流程:打開 Cloud Trace,過濾 latency > 10s 的請求,看瀑布圖找最長 Span。如果是 vector_search:索引或配置問題。如果是 tool.call 且 success=false:外部依賴問題。如果是 llm.generate 且 input_tokens 異常大:context 截斷問題。
Sampling 策略:Production 正常流量 1% 採樣,異常請求(latency > 5s)100% 保留。控制 Cloud Trace 成本,同時確保能診斷所有異常。
PII 原則:Span Attributes 只記錄 token 數和長度,不記錄 Prompt 原文和 LLM 輸出內容——這些可能含有 PII,不應該進入 Trace 儲存。」
