上一篇把壓測情境、驗收結果、K6 summary 與服務 metrics 都打包成 bundle.json。接下來要讓 AI 根據這份 bundle.json 產生報告內容。
最後產生的報告固定分成五章:
Day 19 原本將「實際執行結果」與「發現與注意事項」分成兩章。進入實作後,我覺得實際執行結果應該和發現放在一起看,所以將這兩章合併成一章。
前四章的內容都能直接從 bundle.json 取得,並由程式產生。驗收判定已在建立 bundle.json 前完成,產生報告時只會帶入結果。第五章開頭會先列出 HTTP request 數量、RPS、錯誤率、延遲與完成操作數量,這些固定數字同樣由程式產生。
AI 負責整理第五章的各項發現。它會根據 bundle.json 寫下主要發現、已知事實、可能原因與待驗證事項。
接下來會從 manifest.yaml 找到同一次測試的 bundle.json,檢查 run ID、測試類型、服務與 operation 範圍是否一致,避免拿錯 bundle.json 產生報告。
接著把提示詞跟 bundle.json 交給 AI。Prompt 主要包含四個部分:
REPORT_DRAFT JSON。REPORT_DRAFT 是 AI 分析結果的中間產物,記錄驗收判定、主要發現、已知事實、可能原因、待驗證事項與對應的證據位置。
當 AI 回覆後,程式會先嘗試將內容解析成 JSON,再檢查欄位是否符合 REPORT_DRAFT 格式。如果 AI 多寫了解釋文字、輸出格式錯誤或缺少必要欄位,程式會帶著格式提醒重新呼叫,預設最多嘗試三次。
成功後會產生 draft.json,結構大致如下:
{
"schema_version": "1.0",
"format": "REPORT_DRAFT_V1",
"run_id": "<RUN_ID>",
"acceptance_echo": {
"verdict": "FAIL",
"conditions": [
{
"id": "http-error-rate",
"verdict": "FAIL"
},
{
"id": "login-latency",
"verdict": "FAIL"
}
]
},
"findings": [
{
"id": "F1",
"title": "登入延遲與 HTTP 錯誤率超過門檻",
"severity": "HIGH",
"confidence": "MEDIUM",
"affected_scope": {
"services": ["user-service"],
"operations": ["login"]
},
"known_facts": [
{
"label": "整體 HTTP 錯誤率",
"value": 15.42,
"unit": "%",
"evidence_ref": "acceptance.conditions[id=http-error-rate].actual"
},
{
"label": "登入 P99",
"value": 30171.8,
"unit": "ms",
"evidence_ref": "acceptance.conditions[id=login-latency].actual"
}
],
"possible_causes": [
"登入高峰期間的 CPU throttling 可能影響登入"
],
"verification_needed": [
"比對登入失敗期間的服務 metrics 與 log"
],
"evidence_refs": [
"acceptance.conditions[id=http-error-rate]",
"acceptance.conditions[id=login-latency]"
]
}
]
}
acceptance_echo 會請 AI 把 bundle.json 裡的測試結果再抄一次,包含整體是否通過,以及每一項門檻的判定。程式拿兩邊的結果比對,就能發現 AI 有沒有把 FAIL 寫成 PASS。
每一項 finding 都要分開記錄已知事實、可能原因與待驗證事項。帶有數字的事實還要附上 evidence_ref,指回 bundle.json 裡的實際位置。這讓後續檢查可以直接拿 AI 寫的內容與原始證據比對。
這一步只確認 JSON 與欄位結構正確。數字是否抄對、evidence_ref 能不能找到資料,會留到下一步檢查。
草稿產生後,程式會將 draft.json 與 bundle.json 比對,確認整體與每一項驗收判定有沒有寫錯、引用的數字是否相同,以及 evidence_ref 能不能找到對應資料。
程式也會檢查 AI 有沒有把可能原因直接寫成結論。遇到資料不足時,信心程度不能標成 HIGH,提到的服務與 operation 也不能超出這次測試範圍。只要其中一項不符合規則,程式就會列出問題並停止產生報告。接著再請 AI 修改草稿,完成後重新執行檢查。
例如 AI 在草稿中寫下「HTTP 錯誤率為 15.42%」,並將 evidence_ref 指向 bundle.json 裡的 HTTP 錯誤率。程式會沿著這個位置找到實際值,再確認兩邊的數字是否相同。如果 AI 寫成 1.542%,或是引用到其他指標,草稿就不會通過檢查。
草稿通過檢查後,程式會讀取設定檔中指定的 Grafana Dashboard UID 清單,取得各 Dashboard 的 panel,再對照 bundle.json 整理成候選清單。每個候選都會記錄是否有資料。
以下節錄其中一筆候選:
[
{
"candidate_id": "operation_p99:login",
"dashboard_uid": "ai-k6-k6-load-test",
"panel_id": 5,
"title": "P99 延遲",
"question": "使用者登入的延遲趨勢如何?",
"has_data": true,
"unavailable_reason": null,
"evidence_ref": "k6.results.operations.login.http_req_duration_ms.p99",
"operation": "login"
}
]
這筆候選代表登入 P99 panel。has_data: true 表示這次測試有資料,evidence_ref 則指出 bundle.json 裡可以對照的數值。後續 AI 只需要從候選清單挑選 candidate_id,不需要自己填寫 Dashboard UID 或 panel ID。
程式接著會把完整的候選清單、draft.json 裡的 findings 與選圖規則組成 prompt。Prompt 會要求 AI 從有資料的候選中選擇最多三張圖,並將每張圖對應到一項 finding。選出的圖要能幫助說明主要 finding,沒有適合的圖也可以完全不選。最後,AI 必須用指定的 JSON 格式回傳選圖結果。
以 F1 為例,AI 判斷登入 P99 panel 可以幫助說明登入延遲超過門檻,因此產生以下選圖結果:
[
{
"candidate_id": "operation_p99:login",
"finding_id": "F1",
"number": 1,
"reason": "F1 提到登入 P99 超過門檻,這張圖可以呈現登入延遲的變化"
}
]
candidate_id 來自前面的候選清單,finding_id 指定圖片要放在哪一項發現下面,number 決定圖片順序,reason 則記錄 AI 選擇這張圖的原因。
AI 回傳選圖結果後,程式會先確認候選有資料、finding_id 存在,且選圖數量與順序符合規則。全部通過後,才補上 Dashboard UID、panel ID 與查詢對象,產生 panel-selection.json。接著程式讀這份檔案,呼叫 Grafana Render API 產生 PNG,並將圖片名稱、時間範圍、查詢變數與對應的 finding_id 寫入 grafana-report-evidence.json。
圖片準備完成後,程式會讀取 bundle.json、draft.json 與 grafana-report-evidence.json,組合成固定五章的 report.md。前四章與第五章開頭的流量結果由 bundle.json 產生,第五章後面再加入 AI 整理的 findings。選好的圖片會根據 finding_id,放到對應的發現下面。
這篇完成了從 bundle.json 到正式壓測報告的流程。程式負責產生固定內容、檢查結果與組合報告,AI 則整理 findings 並選擇圖表。
下一篇會直接查看產出的真實報告,看看 AI 如何呈現報告中的注意事項。
今天就先寫到這,我們明天見!