上一篇把 AI SRE 的架構定下來,這篇接著把它實作出來。
接下來會說明調查流程與 query 設定放在哪裡,以及 metrics、logs、traces 經過整理後,LLM 實際會收到哪些資料。
目前 AI SRE 已經分成幾個主要部分:

Runner 負責接收 Grafana webhook,把告警放進 queue,再交給後面的 Agent 處理。調查程式會按照 SOP 查詢觀測資料,整理成固定格式後交給 LLM。如果固定流程取得的資料不足,Runner 才會開放 Grafana MCP,讓 LLM 補查缺少的 metrics 或 logs。
LLM 會將證據串成因果關係,並依照固定格式產生診斷報告。整個流程只會透過 Grafana datasource proxy 讀取 Prometheus、Loki 與 Tempo,沒有 Kubernetes 的操作權限。
AI SRE 要查的 PromQL 都放在 sre/core/queries.yaml。集中管理查詢語法、參數與輸出格式。
目前定義了四種 metrics query:
| Query | 用途 |
|---|---|
service_metrics |
查詢成功率、RPS、P50 與 P99,並比較穩定狀態 |
infra_metrics |
查詢 HikariCP 資料庫連線池、JVM heap 與 Tomcat thread 使用量 |
platform_health |
查詢 Pod 重啟、OOMKilled、CPU throttle(CPU 被限制使用)與 node pressure |
service_health |
一次比較多個服務的錯誤率與 P99,用來判斷影響範圍 |
以 P99 為例,設定裡會記錄參數、單位、數值增加是否代表惡化與 PromQL:
service_metrics:
params:
service: {required: true, enum: service}
http_route: {required: false}
range: {default: 30m, max: 3h}
baseline: true
metrics:
p99:
unit: ms
higher_is_better: false
expr: |
histogram_quantile(0.99,
sum(rate(litebank_duration_milliseconds_bucket{
service_name="{{ service }}",
span_kind="SPAN_KIND_SERVER"{{ route }}}[{{ range }}])) by (le))
調查程式會從告警取得服務、API 與時間區間,再套入範本。LLM 無需自己拼 PromQL,查詢區間也有上限,避免過度查詢。
Prometheus 的原始 metrics 會依照服務、API、Pod 等 label 組合拆成多條 time series,每條 time series 又包含一段時間內的資料點。直接將這些資料交給 LLM,資料量很快就會膨脹。
因此調查程式會先透過 PromQL 聚合,再整理成目前值、穩定狀態、變化百分比、趨勢與單位:
{
"p99": {
"current": 2002.0,
"baseline": 1423.0,
"delta_pct": 40.7,
"trend": "degrading",
"unit": "ms",
"baseline_source": "1d"
}
}
LLM 可以直接看出目前調查區間的 P99 為 2002ms,穩定狀態參考值為 1423ms,目前高出約 40.7%。這些特徵值足以先判斷該指標是否偏離穩定狀態。需要深入確認時,值班人員可以根據調查結果中的 query 與事故時間,回到 Grafana 查看 Prometheus 的 time series。
只查告警服務很容易看到一半。異常可能從下游一路傳回來,也可能已經影響上游入口。
所以服務之間的依賴關係放在 sre/core/topology.yaml,使用「服務→下游服務」的方式記錄:
edges:
api-gateway:
- user-service
- account-service
- teller-service
- exchange-service
teller-service:
- transaction-service
- account-service
例如 teller-service 告警時,調查程式會從 topology 找出它的上游呼叫方、服務本身與下游依賴,再透過前面提到的 service_health,一次查詢這些服務的錯誤率與 P99。
Topology 只負責提供「要查哪些服務」,實際是否受影響仍然要看本次查到的 metrics。這樣報告中的連鎖範圍才有當下的觀測資料支撐,LLM 也不需要憑印象猜測上下游關係。
目前 topology 先由人工維護,後續還要定期對照 spanmetrics,避免服務關係已經改變。
Metrics 可以說明「哪裡變慢」與「惡化多少」,logs 和 traces 則用來補上「當時發生什麼事」與「時間花在哪一段」。
目前這兩種查詢實作在 sre/core/tools/,會透過 Grafana datasource proxy 分別讀取 Loki 與 Tempo。
Logs 先使用服務名縮小範圍:
{namespace="lite-bank", container="<service>"}
接著使用 Loki pattern API 將類似的 log 合併,最多保留前五種模式與出現次數:
{
"top_patterns": [
{
"pattern": "Connection is not available, request timed out after <_>ms",
"count": 312
}
],
"pattern_total": 1
}
固定流程會先保留這些 log pattern。若調查資料的 insufficient 為 true,LLM 才能透過 Grafana MCP,依照 pattern 裡的關鍵字補查原始 log。
Critical 告警還會向 Tempo 查詢超過一秒的慢 trace 樣本:
{ resource.service.name = "<service>" && duration > 1s }
| select(name, span.http.route)
程式會依照 operation 合併查詢結果,保留出現次數、平均與最高延遲、HTTP route,以及幾個 trace ID:
{
"slow_operations": [
{
"operation": "AuthService.login",
"count": 12,
"max_ms": 909.7,
"avg_ms": 640.2,
"http_route": "/api/v1/auth/login"
}
],
"sampled_traces": [
{
"trace_id": "24ae7bba6212847a1ab39488b0307eaa",
"duration_ms": 922
}
]
}
Tempo 搜尋只會回傳部分符合條件的慢 trace,無法保證這些就是整段時間內最慢的幾筆。因此,報告會將它標記為抽樣資料,不會直接當成最慢 trace 排名。
前面的 metrics query、topology、logs 和 traces 都準備好之後,接著才能定義整體調查順序。
調查 SOP 放在 sre/core/sop.yaml。這份 YAML 負責定義每個步驟要呼叫哪個工具、查詢多長時間,以及哪些條件需要分流或降級:
steps:
- id: dependency_scan
action: service_health
with:
services: "{{ neighbors_of(service) }}"
range: 15m
- id: business_metrics
action: service_metrics
with:
service: "{{ service }}"
http_route: "{{ http_route }}"
range: 30m
baseline: 1d
- id: resource_platform
action: [infra_metrics, platform_health]
with: {service: "{{ service }}", range: 30m}
- id: log_patterns
action: loki_patterns
with: {service: "{{ service }}", range: 15m}
- id: trace_drilldown
action: tempo_slow
with: {service: "{{ service }}", range: 15m}
run_if: "severity == 'critical' and tools.tempo.available"
這條 SOP 會先確認告警服務與上下游的影響範圍,再查詢業務 metrics、應用程式資源與平台狀態,接著整理 logs。Critical 告警才會繼續查詢慢 traces,避免每一種告警都跑完整流程。
取得各步驟的查詢結果後,流程會按照 sop.yaml 的門檻,先將數值轉成 signals,再產生問題範圍與層級的 hints。
HikariCP 等待數 14、node pressure 0
→ hikaricp_saturated=true、node_memory_pressure=false
→ layer_hint=app
signals 會記錄哪些條件已經超過門檻,例如 hikaricp_saturated=true。程式再根據這些結果產生 hints,初步指出問題可能發生在哪個範圍與層級。LLM 會接著對照 metrics、logs 與 traces,判斷真正的故障原因。
流程執行完 SOP 後,會將結果整理成固定格式的調查資料,再交給 LLM 判讀。以下再用一組簡化後的示意資料,只節錄主要欄位;實際輸出還會保留每個步驟的 query、時間區間與執行狀態:
{
"alert": {
"service": "teller-service",
"severity": "critical"
},
"steps": {
"business_metrics": {
"action": "service_metrics",
"result": {
"p99": {"current": 2002, "baseline": 1423, "trend": "degrading"}
}
},
"log_patterns": {
"action": "loki_patterns",
"result": {
"top_patterns": [
{"pattern": "Connection timed out after <_>ms", "count": 312}
],
"pattern_total": 1
}
},
"trace_drilldown": {
"action": "tempo_slow",
"result": {
"slow_operations": [
{"operation": "POST /api/v1/withdrawals", "max_ms": 909.7}
],
"sampled_traces": [
{"trace_id": "24ae7bba6212847a1ab39488b0307eaa"}
],
"trace_count": 3
}
}
},
"signals": {
"hikaricp_saturated": true,
"node_memory_pressure": false,
"has_error_pattern": true
},
"hints": {"scope_hint": "single_service", "layer_hint": "app"},
"degraded": [],
"insufficient": false
}
每個 steps 都會保留使用的工具與整理後的結果。查詢失敗或略過的步驟會放進 degraded;現有證據仍無法提供方向時,insufficient 會設成 true,允許 LLM 進一步補查。
SOP 負責調查順序與資料整理,LLM 接手後開始做因果推論與撰寫報告。
SKILL.md 將 LLM 的工作限制在三個步驟:
| 步驟 | LLM 要做的事 |
|---|---|
| 讀取調查資料 | 使用 Runner 已完成的調查結果,不重新執行 SOP |
| 根因推論 | 閱讀 metrics、logs、traces、signals 與 hints,將證據串成因果關係 |
| 產生報告 | 依照 docs/sre/report-template.md 輸出固定章節的診斷報告 |
LLM 提出的根因必須有 metrics、logs 或 traces 支撐。證據無法指向明確結論時,報告要降低信心程度;無法從觀測資料確認的觸發原因,只能列為待人工確認。
如果調查資料中的 insufficient 是 true,Skill 才會允許 LLM 呼叫 MCP,最多只能呼叫四次,查詢時間區間上限為一小時。找到足夠證據就停,避免 Agent 一路查下去。
報告會區分實際觀察、根因推論與待確認事項,並且保留 Grafana 連結與可重跑的 query。Runner 完成調查後,會將結論、影響與建議動作回覆在同一則 Slack 告警訊息下方,完整報告另存成 Markdown 檔案。
實際把程式做完後,各項功能分別放在下面幾個檔案和目錄:
| 實作 | 負責內容 |
|---|---|
queries.yaml |
管理 metrics query、參數、時間區間與特徵化輸出 |
topology.yaml |
提供服務依賴關係,讓調查可以檢查連鎖影響 |
sre/core/tools/ |
查詢 Loki 與 Tempo,將 logs 與 traces 整理成摘要 |
sop.yaml |
定義固定調查順序、分類規則、降級與證據不足的處理 |
SKILL.md |
指導 LLM 閱讀調查資料、進行因果推論與產生報告 |
實作先寫到這裡。下一篇會讓 lite-bank 真的發生異常,再看 AI SRE 能不能根據 metrics、logs 與 traces 找到問題。
今天就先寫到這,我們明天見!