iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
生成式 AI

AI 產品與架構設計之旅:從 0 到 1,再到 Day 2系列 第 21

Day 21: 當警報響起時 - 從 Grafana Alert 到自動化問題追蹤

  • 分享至 

  • xImage
  •  

嗨大家,我是 Debuguy。

昨天我們成功讓 ChatBot 能處理 Kibana URL,把複雜的格式轉換交給程式碼處理。今天要來處理另一個更常見的場景:Grafana 警報

凌晨三點的噩夢

📱 凌晨 3:17,手機震動把你從夢中驚醒...

🚨 [CRITICAL] HTTP 4xx/5xx Error Rate Alert
Production API 出現大量錯誤
查看詳細: https://play.grafana.org/d/play-elastic-web-logs?orgId=1&from=893966418000&to=893967033401&viewPanel=panel-5

@debuguy @on-call-team

你掙扎著爬起來,揉揉眼睛看著這個連結,心裡開始盤算:

「又要先看 Grafana,再去 Kibana 翻 log,然後...%#@!為什麼每次都要重複這套流程?」

標準的痛苦 SOP

收到這種警報時的流程:

  1. 被嚇醒(如果你夠幸運還在睡的話)
  2. 點開 Grafana 連結看圖表
  3. 猜測可能的原因
  4. 去 Kibana 查 log
  5. 在一堆 log 裡找線索
  6. 如果找不到就拉其他人起來

每個 on-call 工程師都經歷過這個循環。問題不在於技術難度,而在於:

  • 半夜腦袋不清楚
  • 重複性的手動操作
  • 容易漏掉關鍵資訊

「如果有個 AI 能在我爬起來之前就先看過一遍,該有多好?」

理想中的自動化流程

我想要的是這樣的體驗:

🚨 Grafana Alert 發出(同時 @bot)
  ↓
Bot 自動解析警報中的 Grafana URL
  ↓ 
從 URL 參數識別觸發的 Dashboard 和 Panel
  ↓
透過 Grafana API 取得該 Panel 的查詢設定
  ↓
提取資料來源和查詢條件
  ↓
直接去 Elasticsearch 查相關 Log
  ↓
生成結構化的問題分析報告
  ↓
值班工程師醒來就看到完整分析,決定下一步行動

核心概念:讓 AI 做初步分析,人類做最終決策。

不是要 AI 自動修 bug(那太危險),而是要它幫我們完成那些重複性的資訊收集和初步判斷。

為什麼不是加個工具就能搞定?

天真的第一次嘗試

既然昨天已經接好 Elasticsearch MCP,今天再加 Grafana MCP 應該很簡單吧?

# docker-compose.yml
services:
  grafana-mcp:
    image: mcp/grafana
    ports:
      - "8000:8000"
    environment:
      GRAFANA_URL: ${GRAFANA_URL}
      GRAFANA_SERVICE_ACCOUNT_TOKEN: ${GRAFANA_SERVICE_ACCOUNT_TOKEN}
    command: ["-t", "streamable-http"]
// GenKit/config/mcp-config.json
{
  "mcpServers": {
    "grafana": {
      "type": "streamable-http",
      "url": "http://grafana-mcp:8000/mcp"
    }
  }
}

「看吧,輕鬆加好了!現在只要告訴 AI 怎麼用就行了。」

我天真地寫了個 Prompt:

幫我看一下這個 Grafana 警報為什麼叫了,原因是什麼?

然後發現... AI 完全不知道該怎麼辦。

問題比想像中複雜

讓我仔細分析一下,為什麼 Grafana 的自動化比 Kibana 還要困難?

困難 1:URL 只是個「指標」,不是「內容」

讓我們比較一下昨天的 Kibana URL 和今天的 Grafana URL:

Kibana URL(昨天):

https://demo.elastic.co/app/discover#/?_g=(time:(from:'2025-10-04T14:30:00.000Z',to:'2025-10-04T15:00:00.000Z'))&_a=(query:(language:kuery,query:'log.level:%20%22error%22%20'))

✅ 時間範圍用 ISO 8601(標準格式)
✅ 查詢條件直接寫在 URL 裡
✅ 所有資訊一目了然

Grafana URL(今天):

https://play.grafana.org/d/play-elastic-web-logs?orgId=1&from=893966418000&to=893967033401&viewPanel=panel-5

❌ 時間用 Unix timestamp(毫秒)
❌ 查詢條件完全沒有
❌ 只有 dashboard 和 panel 的 ID

關鍵差異:

Grafana URL 更像是一個「書籤」,它只告訴你去哪裡看,但不告訴你在看什麼。要知道查詢條件,必須:

  1. 透過 API 取得 Dashboard 的設定
  2. 從 JSON 中找到對應的 Panel
  3. 解析 Panel 的 datasource 和 query

困難 2:資料來源的多樣性

Grafana 最大的優勢也是最大的挑戰:它可以接入任何資料源。

  • Prometheus(監控指標)
  • Elasticsearch(日誌搜尋)
  • MySQL(關係型資料庫)
  • InfluxDB(時序資料庫)
  • ...還有幾十種

這意味著 AI 光看 Grafana URL,根本不知道:

  • 資料從哪來
  • 用什麼語法查詢
  • 怎麼組合查詢條件

困難 3:查詢語法又是新的一種

當我終於讓 AI 成功從 Grafana API 取得 Panel 設定後,打開一看:

Grafana Query Editor

查詢語法是 Lucene
例如 status >= 400 的查詢語法是 status:[400 TO *]

「天啊,昨天才搞定 Kibana 的 KQL,今天又來一個 Lucene...」

好消息是,冷靜下來想想:

  • Lucene 是 Elasticsearch 的底層查詢語言
  • Elasticsearch 原生就支援 Lucene 語法
  • 可以用 query_string 直接接受 Lucene
{
  "query": {
    "bool": {
      "must": [
        {
          "query_string": {
            "query": "<從_Grafana_解析出的_Lucene_查詢>"
          }
        }
      ]
    }
  }
}

「又是一個可以直接填空的模板!」

困難 4:時間格式又不一樣了

Grafana 用的是 Unix timestamp(毫秒),但 Elasticsearch 需要 ISO 8601 格式。

// Grafana URL 的時間參數
from=893966418000
to=893967033401

// Elasticsearch 需要的格式
"@timestamp": {
  "gte": "1998-05-01T06:20:18.000Z",
  "lte": "1998-05-01T06:30:33.401Z"
}

小結:四個必須克服的挑戰

盤點一下,要讓 AI 自動分析 Grafana 警報,我們需要處理:

  1. ⚠️ 從 URL 只能拿到 dashboard ID 和 panel ID,查詢條件需要另外取得
  2. ⚠️ 不知道資料來自哪個系統,要透過 API 追蹤 datasource
  3. ⚠️ Lucene 查詢語法,需要轉換成 Elasticsearch Query DSL
  4. ⚠️ Unix timestamp 轉 ISO 8601,格式標準化

「如果是人類工程師,我們也是一步一步來解決這些問題的。那 AI 也需要一個清楚的 SOP。」

解決方案:結構化的四步驟流程

從人類的思路出發

讓我們倒回來想,如果是我自己要查這個警報:

1. 點開 Grafana URL
   ↓
2. 看到觸發警報的 Panel(圖表)
   ↓
3. 點進 Panel 設定,查看 Query 內容
   ↓
4. 看到是查 Elasticsearch 的某個 index
   ↓
5. 把查詢條件複製到 Kibana 或 Elasticsearch
   ↓
6. 調整時間範圍後執行查詢
   ↓
7. 分析 log 找出問題

關鍵洞察:人類工程師也需要「分步驟」來拆解這個任務。

所以我設計了一個四步驟的分析流程,模擬人類工程師的思路。

設計輔助工具:時間轉換

在開始四步驟之前,我們需要先解決時間格式的問題。

你可能會想:

「這麼簡單的功能,應該有現成的 library 吧?」

確實有,但我的需求很單純:

  • ✅ 只需要「Unix → ISO 8601」這一個功能
  • ✅ 要自動判斷是秒還是毫秒
  • ✅ 要有清楚的錯誤訊息和驗證

幾行程式碼就能搞定的事,為什麼要多一個依賴?

// GenKit/tools/time_tools.ts
export const createUnixToIsoTool = (ai: Genkit) => {
  return ai.dynamicTool({
    name: 'unix_to_iso',
    description: 'Convert Unix timestamp to ISO 8601 UTC format. Supports both seconds and milliseconds timestamps.',
    inputSchema: z.object({
      timestamp: z.string().describe('Unix timestamp as string in seconds or milliseconds')
    }),
    outputSchema: z.object({
      iso8601: z.string().describe('The ISO 8601 UTC formatted date string'),
      original: z.string().describe('The original timestamp input'),
      detected_format: z.string().describe('Whether timestamp was detected as seconds or milliseconds')
    })
  }, async ({ timestamp }) => {
    const numTimestamp = parseFloat(timestamp);
    
    if (isNaN(numTimestamp) || numTimestamp < 0) {
      throw new Error('Invalid timestamp: must be a positive number');
    }
    
    let milliseconds: number;
    let detectedFormat: string;
    
    // 自動偵測格式:大於 100000000000 的認為是毫秒
    if (numTimestamp >= 100000000000) {
      milliseconds = numTimestamp;
      detectedFormat = 'milliseconds';
    } else {
      milliseconds = numTimestamp * 1000;
      detectedFormat = 'seconds';
    }
    
    const date = new Date(milliseconds);
    
    if (isNaN(date.getTime())) {
      throw new Error('Invalid timestamp: results in invalid date');
    }
    
    return {
      iso8601: date.toISOString(),
      original: timestamp,
      detected_format: detectedFormat
    };
  });
};

設計重點:

  1. 自動偵測格式(不用告訴它是秒還是毫秒)
  2. 完整的驗證(檢查數字是否有效、日期是否合理)
  3. 清楚的錯誤訊息
  4. 結構化輸出

四步驟分析流程

昨天處理 Kibana URL 時我們學到:與其讓 AI 自由發揮,不如給它明確的 SOP。

想像你在教一個新人 on-call:

❌ 不好的教法:

「你去看一下那個警報,然後查查看是什麼問題。」

✅ 好的教法:

「收到警報後,第一步先解析 URL 拿到 dashboard ID 和時間範圍,第二步去 Grafana 查這個 panel 的設定...」

AI 也一樣,需要明確的步驟指引。

Step 1: 解析 URL 參數

目的: 從警報訊息中提取關鍵資訊

從 Grafana 警報訊息中找到 URL,提取四個關鍵參數:
- dashboardUid: 識別是哪個儀表板
- panelId: 確定觸發警報的面板
- from: 時間範圍起點(Unix timestamp)
- to: 時間範圍終點(Unix timestamp)

URL template: 
https://play.grafana.org/d/<dashboardUid>?orgId=1&from=<from>&to=<to>&viewPanel=<panelId>

給 AI 看範例,它就知道怎麼解析。

Step 2: 追蹤 Panel 設定

目的: 找出這個 Panel 到底在查什麼

這是最複雜的一步,需要拆成三個子步驟:

a. 獲取面板設定:
   - 使用 get_dashboard_property 工具
   - 傳入 dashboardUid 和 jsonPath
   - 取得面板的完整設定 JSON

b. 解析面板設定:
   - 從 JSON 找到 targets 陣列
   - 解析出 Lucene 查詢語法
   - 記錄 datasource 的 UID

c. 獲取資料源資訊:
   - 使用 get_datasource_by_uid 工具
   - 取得 Elasticsearch 的 index 名稱

透過明確的子步驟,AI 知道該找什麼、怎麼找。

Step 3: 查詢 Elasticsearch

目的: 組合查詢條件,撈出相關 log

1. 使用 unix_to_iso 工具轉換時間範圍

2. 組合 Elasticsearch Query DSL:
   {
     "query": {
       "bool": {
         "must": [
           {
             "query_string": {
               "query": "<LUCENE_QUERY_FROM_GRAFANA>"
             }
           },
           {
             "range": {
               "@timestamp": {
                 "gte": "<ISO_8601_FROM>",
                 "lte": "<ISO_8601_TO>"
               }
             }
           }
         ]
       }
     },
     "size": 100
   }

3. 使用 elasticsearch search 工具執行查詢

給了完整的模板,AI 只需要填空。

Step 4: 生成分析報告

目的: 把發現整理成人類看得懂的報告

分析日誌並撰寫報告,包含:
- 摘要:1-2 句話概括問題
- 影響範圍:受影響的服務和用戶
- 立即建議:可執行的下一步
- 額外背景:技術細節和觀察

實際運作:從警報到報告

讓我們看看當警報進來時,整個系統是怎麼運作的:

📱 凌晨 3:17 - Grafana 警報進入 Slack(@bot)
  ↓
🤖 Bot 被觸發,開始執行
  ↓
📊 Step 1: 解析 URL
   ✓ dashboardUid: play-elastic-web-logs
   ✓ panelId: panel-5
   ✓ 時間範圍: 893966418000 ~ 893967033401
  ↓
🔍 Step 2: 追蹤 Panel 設定
   ✓ 調用 get_dashboard_property
   ✓ 解析出 Lucene 查詢: response:404
   ✓ 調用 get_datasource_by_uid
   ✓ 取得 index: logs-*
  ↓
🕐 轉換時間格式
   ✓ from: 1998-05-01T06:20:18.000Z
   ✓ to: 1998-05-01T06:30:33.401Z
  ↓
📝 Step 3: 查詢 Elasticsearch
   ✓ 組合 Query DSL
   ✓ 執行查詢
   ✓ 取得 100 筆相關 log
  ↓
📋 Step 4: 生成分析報告
   ✓ 分析 log 找出異常模式
   ✓ 評估優先級和影響範圍
   ✓ 提供可執行建議
  ↓
💬 發送報告到 Slack thread
  ↓
😴 工程師醒來時,分析已經完成

產出報告範例

🚨 Grafana 警報分析

📌 摘要
在 1998-05-01 06:20-06:30 期間,Production API 出現大量 404 錯誤,
主要集中在 /api/users/legacy 端點。

🎯 影響範圍
- 受影響服務:User Profile API
- 具體影響:使用者無法訪問舊版個人資料頁面
- 受影響路徑:/api/users/legacy/*

💡 立即建議
1. 檢查最近是否有移除舊版 API 端點的部署
2. 確認前端是否還在呼叫已廢棄的端點
3. 考慮加回 301 redirect 或友善的錯誤訊息

📝 額外背景
- 錯誤集中在 06:23-06:27,可能與定時任務有關
- User-Agent 顯示主要來自 iOS app v2.3.1
- 建議更新 app 或加回相容性支援

這份報告的價值:

  • ✅ 工程師一眼就知道嚴重程度
  • ✅ 有明確的下一步行動
  • ✅ 包含足夠的技術細節供判斷

從反應式到主動式的轉變

以前 vs 現在

以前的流程:

1. 被嚇醒 😱
2. 點開 Grafana 看圖表
3. 腦霧中猜測可能原因
4. 去 Kibana 翻 log
5. 在幾千筆 log 裡找線索
6. 如果找不到...拉其他人起來
7. 30 分鐘過去了

心理狀態: 焦慮、不確定、壓力大

現在的流程:

1. 被叫醒(這個躲不掉 😅)
2. 打開 Slack 看 AI 已經準備好的分析報告
3. 基於報告評估情況
4. 做出明智的決策
5. 10 分鐘搞定

心理狀態: 有掌控感、清楚狀況、知道下一步

這不只是效率提升,更是心理壓力的減輕。

改變的本質

從「摸黑前進」到「有地圖導航」

以前半夜被叫起來,你不知道:

  • 問題有多嚴重
  • 該從哪裡開始查
  • 需要多久才能解決

現在你不再是「從零開始找問題」,而是「驗證 AI 的分析並做決策」。

這個轉變看似微小,但對凌晨三點的值班工程師來說,意義重大。

技術決策的反思

為什麼要寫這麼詳細的 Prompt?

你可能會想:「現在的 LLM 這麼強,為什麼不讓它自己想辦法?」

試過了,不行。

沒有明確指引時,AI 會:

  • 跳過某些步驟
  • 用錯誤的參數呼叫工具
  • 在遇到問題時放棄而不是嘗試備援方案
  • 生成格式不一致的報告
  • 有時 AI 反覆調用工具,除了時間變長,花費的 $ / token 也隨之增加

關鍵:

AI 很聰明,但它需要知道「期望的工作流程」是什麼。

就像一個聰明的新人,你不能只說「去把這個問題修好」,而是要教他「這類問題通常怎麼查、要注意什麼」。

連續對話的重要性

在這個架構設計下,AI 失誤的機會降低了,但有時還是會失誤。或是一開始給的 prompt 沒有給足資訊。

這時候 Slack Bot 有連續對話的能力就會很有幫助。可以給予 Bot 新的 context,讓它延續前面已經完成的事項,繼續做更深入的分析與查找。

這就是為什麼從 Day 5 開始我們就建立了多輪對話的能力。

小結:從 0 到 1 的完成

從 Day 1 的「想要一個能解決問題的 Bot」到今天的「自動化警報分析系統」,我們用了 21 天的時間,真正實現了從 0 到 1 的產品創造。

技術層面的完整性

Day 21 完成了:

  • ✅ Grafana MCP 整合
  • ✅ Unix timestamp 轉換工具
  • ✅ Lucene 到 Elasticsearch DSL 的處理
  • ✅ 結構化的四步驟分析流程
  • ✅ 自動化的警報分析報告

產品層面的價值實現

我們創造的不只是技術,而是:

  • 🎯 降低值班壓力 - 半夜不用從零開始摸索
  • 🚀 加快反應速度 - 從 30 分鐘縮短到 10 分鐘
  • 📊 提高分析品質 - 系統化的檢查不會漏掉線索
  • 🔍 增加透明度 - Trace link 讓決策有依據

設計哲學的昇華

Day 21 的核心洞察:

最好的 AI 工具不是取代工程師,而是成為工程師最強的 first responder。

AI 負責:

  • 🤖 快速收集和整理資訊
  • 🤖 執行標準化的分析流程
  • 🤖 提供結構化的初步判斷

人類負責:

  • 👨‍💻 基於分析做出最終決策
  • 👨‍💻 處理需要創意的解決方案
  • 👨‍💻 評估業務影響和優先級

完整的原始碼在這裡,包含 Grafana 整合和自動化分析流程!


AI 的發展變化很快,目前這個想法以及專案也還在實驗中。但也許透過這個過程大家可以有一些經驗和想法互相交流,歡迎大家追蹤這個系列。

也歡迎追蹤我的 Threads @debuguy.dev


上一篇
Day 20: 當 AI 也需要「翻譯蒟蒻」- 從提示詞到製作工具
下一篇
Day 22: 當重構遇上「相容」的真相 - 一個關於思考過程消失的故事
系列文
AI 產品與架構設計之旅:從 0 到 1,再到 Day 222
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言