flowchart LR
subgraph Client["Client"]
User["使用者"]
end
subgraph Frontend["Open WebUI"]
WebUI["Chat UI"]
Tool["Tools / Function"]
LLM["LLM"]
end
subgraph AIService["Anomaly Detection Service"]
RFAPI["RF Model API<br/>(FastAPI)"]
Logs["Logs 時間與相關指標"]
end
User -->|"查某時的系統狀態"| WebUI
WebUI -->|"呼叫 Tool"| Tool
Tool -->|"HTTP POST"| RFAPI
RFAPI --> Logs
Logs --> RFAPI
RFAPI -->|"異常結果"| Tool
Tool -->|"組成 Prompt"| LLM
LLM -->|"分析報告(給Soc參考)"| WebUI
WebUI --> User
目錄
緣起
期末的時候 LLM 資安課程有個期末報告是要做 Poc (Proof of concept),每個組別要研究市面上或是學術裡的文獻,針對現有的不足之處提出改進措施,再做出產品驗證構想,主題必須要跟資安相關。1我苦思了很久要做什麼,只知道我比較想做跟 Web 有關的,然後想到跟時間序列有關的就網路流量異常偵測,但不確定具體的落地情境。最後在倒數第 2 週,老師分享了他在 IBM 做資安分析的組織結構跟每個職位負責的內容,突然靈光一閃想到不如利用這個情境來設想可能會有的需求。於是結合前面的構想想出了「可解釋性網絡流量異常檢測系統」的 Poc,它的最初概念是這樣
使用機器學習來偵測網路流量異常的系統很多,結合 LLM 可以用來生報告,此外還可以再檢視一次資料,確定資料符合異常標準。
如果用統計學的角度來看,LLM 扮演的是另一個 Selection Criteria 角色,使預測結果更加精確,使用兩個Selection Criteria 在統計學也不是甚麼罕見的手法,理論上應該可行,只是 LLM 會不會因為輕判或其他因素判別錯誤是另一個議題。
不過,因為看起來可行極高,市面也有類似產品。2再加上我在找資料的過程中發現可以走破電腦也能執行的路線,所以最後直接用 3b小模型 + 隨機森林的組合完成我的報告交出去,Credit 也好好的被省下來了。但最近再檢視一次我才發現,設計的不好XD,於是先來回顧一次我的設計,以及哪裡出了問題。
系統架構
Open WebUI:一個自定義極強的開源 WebUI,可以連 Ollama 下載下來的模型,官方 github 在此。這裡用的環境是 Docker 連本機 Ollama。
Logs 時間與相關指標:這裡隨機森林拿來預測的其實不是網站的原始登入 log 資訊,而是經過轉化再計算的流量指標,下節會提到。
Soc:資安團隊中的基層,負責回報異常事件,本次提供的資料(理論上)可以協助他們撰寫 Security Event Analysis 的報告,減少他們的文書壓力。
這裡之所以使用隨機森林,主要是訓練速度快、預測結果也不輸其他知名的好模型,以及我的資料集是模擬資料不必用太好的模型效果也會很好。另外使用 FastAPI 原因是為了讓兩邊的系統服務可以藉由 API 好好串一起,同時保有各自獨立的功能,架構設計較穩。
製作過程
1. 使用 python faker 套件產出假資料
產出假資料的訴求為「貼近真實使用情境」,因此讓生成資料有週期性,模擬系統真實登入情況。同時也在上 label 加入 「label 不一定正確」的機制,考驗模型在特徵混亂時的反應。
混淆類型有:
漏報:有 6% 的進階潛伏攻擊行為在標籤上被標記為正常(0)
誤報:有 4% 的普通日常/維運人潮,因為特徵太像攻擊,標籤被意外標記為異常(1)
最後生成 5000 筆資料,正常與異常的標示為:
總計統計分鐘數: 5000
正常狀態分鐘數(Label 0): 2526
異常狀態分鐘數(Label 1): 2474
將資料儲存成 json 跟 csv 檔,其中 csv 用於訓練模型。
json 資料範例
{
"timestamp": "2026-06-02 08:25:49",
"ip": "41.61.225.212",
"uri": "/products",
"status_code": 200,
"response_time_ms": 1665,
"user_agent": "Mozilla/5.0 (Macintosh; PPC Mac OS X 10_9_2 rv:2.0; ti-ER) AppleWebKit/534.29.5 (KHTML, like Gecko) Version/5.0 Safari/534.29.5",
"method": "GET",
"is_anomaly": 0
},
| 欄位 | 解說 |
|---|---|
timestamp |
請求發生時間(伺服器記錄時間) |
ip |
發送請求的用戶端 IP 位址,可用於流量分析、地理位置判斷或安全稽核。 |
uri |
使用者存取的 API 或網頁路徑:/search,表示執行搜尋功能。 |
status_code |
HTTP 回應狀態碼,200 代表請求成功並正常回傳結果。 |
response_time_ms |
伺服器處理並回應此請求所花費的時間)。 |
user_agent |
用戶端瀏覽器與裝置資訊。 |
method |
HTTP 請求方法:GET,表示向伺服器讀取資料而非新增或修改資料。 |
is_anomaly |
正常(0)或異常(1)流量 |
整理成 CSV 後欄位後,欄位變成更便於隨機森林分類的形式:
| 欄位名稱 | 中文名稱 | 計算方式 | 功用與意義 |
|---|---|---|---|
minute |
分鐘時間窗 | YYYY-MM-DD HH:MM | 每筆資料代表 1 分鐘的流量統計結果 |
requests_per_min |
每分鐘請求數 | 該分鐘內 Request 總數 | 衡量流量大小,突增可能代表 DDoS、爬蟲或掃描行為 |
unique_ips |
獨立 IP 數 | 不重複 IP 數量 | 觀察流量來源是否分散,多 IP 可能代表殭屍網路攻擊 |
ip_density_index |
IP 密度指數 | Requests ÷ Unique IPs | 衡量單一 IP 平均發送多少請求,高值通常表示集中式攻擊 |
avg_response_time |
平均回應時間 | 平均 Response Time | 系統負載增加時通常會上升 |
max_response_time |
最大回應時間 | Max(Response Time) | 偵測極端延遲事件 |
p95_response_time |
P95 回應時間 | 95 百分位數 Response Time | 衡量大部分使用者體驗,比平均值更能反映效能瓶頸 |
4xx_ratio |
4xx 錯誤比例 | 4xx 數量 ÷ 總請求數 | 掃描、爆破登入、錯誤 API 呼叫時常增加 |
5xx_ratio |
5xx 錯誤比例 | 5xx 數量 ÷ 總請求數 | 伺服器異常、系統崩潰或過載指標 |
login_fail_rate |
登入失敗率 | 失敗登入 ÷ 登入請求數 | 偵測暴力破解、帳號猜測攻擊 |
bot_attack_ua_ratio |
可疑 Bot UA 比例 | 可疑 UA 數量 ÷ 總請求數 | 偵測自動化攻擊工具與掃描器 |
label |
異常標籤 | 0 或 1 | 機器學習預測目標,1=異常、0=正常 |
- 可疑 Bot UA:UA 的全名為 User-Agent,即範例資料中的
"user_agent": "Mozilla/5.0 (Macintosh; PPC Mac OS X 10_9_2 rv:2.0; ti-ER) AppleWebKit/534.29.5 (KHTML, like Gecko) Version/5.0 Safari/534.29.5"部份。每個 HTTP Request 都會帶有一個 User-Agent Header,用來表示發出請求的客戶端。
2. 訓練隨機森林模型
將資料的 70 % 拿去訓練,並且在測試集測試,結果數據如下
Accuracy: 0.954
Confusion Matrix:
| Actual Predicted | Class 0 | Class 1 |
|---|---|---|
| Class 0 | 707 | 39 |
| Class 1 | 30 | 724 |
- Report:
| Class | Precision | Recall | F1-Score | Support |
|---|---|---|---|---|
| 0 | 0.96 | 0.95 | 0.95 | 746 |
| 1 | 0.95 | 0.96 | 0.95 | 754 |
| Metric | Precision | Recall | F1-Score | Support |
|---|---|---|---|---|
| Accuracy | - | - | 0.95 | 1500 |
| Macro Avg | 0.95 | 0.95 | 0.95 | 1500 |
| Weighted Avg | 0.95 | 0.95 | 0.95 | 1500 |
即使資料有混淆的情況,模型的預測效果依然有 95.4 %,已經非常接近實務上的數據。從 Confusion Matrix 來看,只有 39 筆的正常資料備判定異常;30 筆異常資料被判定為正常。且對於正常資料的判定正確率有 96 %,代表多數情況應可用隨機森林解決,少部分誤判資料可以再用 LLM 檢視一次。
隨機森林擷取的特徵,以 IP 密度指數最高。
| Rank | Feature | Importance | Contribution (%) |
|---|---|---|---|
| 1 | ip_density_index |
0.499463 | 49.95% |
| 2 | bot_attack_ua_ratio |
0.287956 | 28.80% |
| 3 | avg_response_time |
0.059619 | 5.96% |
| 4 | p95_response_time |
0.036047 | 3.60% |
| 5 | 4xx_ratio |
0.035068 | 3.51% |
| 6 | requests_per_min |
0.027547 | 2.75% |
| 7 | unique_ips |
0.023760 | 2.38% |
| 8 | max_response_time |
0.018792 | 1.88% |
| 9 | login_fail_rate |
0.010998 | 1.10% |
| 10 | 5xx_ratio |
0.000750 | 0.08% |
3. 儲存 RF 模型
模型本體
rf_model.pkl模型訓練參數
rf_training_feature.pkl
4. 建立腳本 rf-api.py
用於建立 API 服務,只要將前面生成的 CSV 與此腳本放在同一資料夾下,每次開始使用時,只要 API 啟動 ,LLM 會藉由此 api 呼叫訓練好的隨機森林模型。
步驟
- 安裝套件
pip install fastapi uvicorn
- 寫腳本
重點在於當使用者指定時間點時,LLM 會將其轉成符合格式的字串,經由 API 傳給隨機森林,隨機森林計算的數值跟發現的異常部分要回傳給 LLM 。
from fastapi import FastAPI, Query, HTTPException
import joblib
import pandas as pd
from datetime import datetime
app = FastAPI()
# 1. 載入你訓練好的隨機森林模型
model = joblib.load("rf_model.pkl")
# 這是要觀測的 CSV 檔案路徑
CSV_FILE_PATH = "features-test.csv"
def get_dynamic_data(start_str: str, end_str: str):
"""從 CSV 中動態篩選出指定時間區間的資料"""
df = pd.read_csv(CSV_FILE_PATH)
df['minute'] = pd.to_datetime(df['minute'])
start_dt = pd.to_datetime(start_str)
end_dt = pd.to_datetime(end_str)
mask = (df['minute'] >= start_dt) & (df['minute'] <= end_dt)
filtered_df = df.loc[mask].copy()
return filtered_df
@app.get("/check_status")
def check_status(
start_time: str = Query(..., description="開始時間,如: 2026-06-06 08:00"),
end_time: str = Query(..., description="結束時間,如: 2026-06-06 12:00")
):
try:
# 1. 動態撈取該時段的 CSV 資料
df_period = get_dynamic_data(start_time, end_time)
if df_period.empty:
return {
"status": "no_data",
"message": f"在 {start_time} 到 {end_time} 之間找不到任何監控紀錄。"
}
# 2. 準備特徵矩陣 (X)
feature_cols = [
'requests_per_min', 'unique_ips', 'ip_density_index',
'avg_response_time', 'max_response_time', 'p95_response_time',
'4xx_ratio', '5xx_ratio', 'login_fail_rate', 'bot_attack_ua_ratio'
]
X = df_period[feature_cols]
# 3. 模型預測
predictions = model.predict(X)
df_period['pred_label'] = predictions
# 計算總時間長度(分鐘數)與真實異常率
total_minutes = len(df_period)
anomaly_minutes = (df_period['pred_label'] == 1).sum()
actual_anomaly_rate = (anomaly_minutes / total_minutes) * 100
# 4. 提取統計數據(不管 RF 判斷為何,這些都是硬證據)
total_requests = int(df_period['requests_per_min'].sum())
avg_unique_ips = float(df_period['unique_ips'].mean())
avg_p95_time = float(df_period['p95_response_time'].mean())
max_4xx_ratio = float(df_period['4xx_ratio'].max()) * 100
max_5xx_ratio = float(df_period['5xx_ratio'].max()) * 100
avg_bot_ratio = float(df_period['bot_attack_ua_ratio'].mean()) * 100
summary_stats = {
"total_requests": total_requests,
"avg_unique_ips": round(avg_unique_ips, 1),
"actual_anomaly_rate_percent": round(actual_anomaly_rate, 2)
}
# 5. 格式化「隨機森林認為異常」的時間點日誌,翻譯成白話文
anomalies = df_period[df_period['pred_label'] == 1]
log_lines = []
if not anomalies.empty:
# 如果誤報過多導致行數破表,我們只挑選回應時間最慘或錯誤率最高的前 5 筆,防止 LLM 看暈
top_anomalies = anomalies.sort_values(by=['p95_response_time', '4xx_ratio'], ascending=False).head(5)
for idx, row in top_anomalies.iterrows():
time_str = row['minute'].strftime('%Y-%m-%d %H:%M')
line = (
f" - [{time_str}] -> 流量: {int(row['requests_per_min'])}次, "
f"不重複IP數: {int(row['unique_ips'])}個, "
f"P95回應時間: {row['p95_response_time']}ms, "
f"4xx錯誤率: {row['4xx_ratio']*100:.2f}%, "
f"5xx錯誤率: {row['5xx_ratio']*100:.2f}%, "
f"惡意爬蟲UA比例: {row['bot_attack_ua_ratio']*100:.2f}%"
)
log_lines.append(line)
anomaly_summary = "\n".join(log_lines) if log_lines else "(此時段內無任何分鐘被隨機森林標記為異常)"
# 統一回傳 success,將主導權交還給前端 Tool 的系統提示詞
return {
"status": "success",
"rf_decision": "ANOMALY_DETECTED" if anomaly_minutes > 0 else "SYSTEM_NORMAL",
"message": f"隨機森林模型判定該區間有 {actual_anomaly_rate:.2f}% 的分鐘數據處於異常狀態。",
"stats": summary_stats,
"logs": (
f"【整體查詢時段監控指標概況】:\n"
f"- 總請求流量: {total_requests} 次\n"
f"- 平均不重複連線 IP 數: {round(avg_unique_ips, 1)} 個\n"
f"- 全時段平均 P95 回應時間: {round(avg_p95_time, 2)} ms\n"
f"- 最高 4xx 客戶端錯誤率: {max_4xx_ratio:.2f}%\n"
f"- 最高 5xx 伺服器錯誤率: {max_5xx_ratio:.2f}%\n"
f"- 平均惡意爬蟲 UA 比例: {avg_bot_ratio:.2f}%\n\n"
f"【隨機森林模型標記為 1 (異常) 的時間點詳細數據摘要】:\n{anomaly_summary}"
)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"系統錯誤: {str(e)}")其中CSV_FILE_PATH = "features-test.csv"的部分是並未用於訓練或測試,完全重新生成的資料,與 rf-api.py放在同一個資料夾下。
- 儲存後啟動 api
uvicorn rf-api:app --reload網址應為 http://127.0.0.1:8000 ,在 /docs 檢查 API 運作,請求有回應表示正常。

在 VM 執行的話沒有辦法直接訪問網址,解法是在 VM 環境裡用 python 腳本初步驗證:
import requests
r = requests.get("http://127.0.0.1:8000/openapi.json")
print(r.json()["paths"])5. 在 Open Web UI 設定
- 到工作區>工具>建立 Tool
使用以下的程式碼
import requests
from pydantic import BaseModel, Field
from typing import Optional
class Tools:
def __init__(self):
pass
def check_system_anomaly(self, start_time: str, end_time: str) -> str:
"""
當使用者詢問某個特定時間區間系統有沒有異常、流量狀態或需要分析日誌時呼叫此工具。
:param start_time: 開始時間,格式為 'YYYY-MM-DD HH:MM' (例如: '2026-06-06 08:00')。
:param end_time: 結束時間,格式為 'YYYY-MM-DD HH:MM' (例如: '2026-06-06 12:00')。
"""
url = "http://host.docker.internal:8000/check_status"
params = {"start_time": start_time, "end_time": end_time}
try:
response = requests.get(url, params=params, timeout=10)
if response.status_code != 200:
return f"系統 API 回傳錯誤碼: {response.status_code},請確認 FastAPI 終端機狀態。"
data = response.json()
# 1. 處理無監控數據的防呆情況
if data.get("status") == "no_data":
return (
f"系統回報:在 {start_time} 到 {end_time} 之間找不到任何監控數據。"
)
# 2. 獲取核心數據結構
stats = data.get("stats", {})
logs = data.get("logs", "")
rf_decision = data.get("rf_decision", "UNKNOWN")
# 3. 完美包裝雙重審計任務,將最終的推推翻權力交給 LLM
return (
f"【系統維運數據雙重審計任務】\n"
f"你現在扮演資深雲端架構師與資深運維專家(SRE)。請嚴格根據以下真實數據進行繁體中文分析,絕對禁止憑空捏造任何未提及的數字、具體IP地址(如192.168.x.x)或特定未知的狀態碼。\n\n"
f"🤖 [1. 隨機森林模型預測結論]:\n"
f"- 模型標記為異常的時間比例:{stats.get('actual_anomaly_rate_percent', 0)}%\n"
f"- 模型基礎判定狀態:{rf_decision}\n\n"
f"📊 [2. 基礎監控日誌數據(鐵證)]:\n"
f"{logs}\n\n"
f"📝 【請嚴格遵循以下運維邏輯產出最終報告】:\n"
f"1. **數據真實性審查(核心判斷)**:\n"
f" - 仔細檢查日誌中的『全時段平均 P95 回應時間』。如果是常態毫秒級(例如低於 500ms)且『最高 5xx 錯誤率』為 0.00%,即使隨機森林模型的異常比例很高,你也必須主動指出:『**隨機森林模型在此處判斷為誤報(False Positive)**,實際系統各項指標均非常健康正常。』,並宣告系統無風險。\n"
f" - 如果回應時間確實破千(ms)或 5xx 錯誤率明顯飆高,請認同隨機森林的判斷,並開始深入分析真正的效能瓶頸。\n"
f"2. **最終報告結構**:\n"
f" - 【模型判定與稽核結論】(必須明確點出是否屬於模型誤報)\n"
f" - 【數據指標解讀】(引用數據中的總流量、回應時間、錯誤率進行白話說明)\n"
f" - 【專家建議應變措施】(若健康則寫無須動作、持續監控即可;若異常則給出優化方向)"
)
except Exception as e:
return f"連線到異常偵測 API 失敗,錯誤原因: {str(e)}。請確認 FastAPI 有跑起來。"其中 url = "http://host.docker.internal:8000/check_status",是因為本次的執行環境設定在 Docker 的 Open WebUI,但是剛剛做好的 rf-api 不在 docker 裡,故調整語法讓它能接到。
儲存為 rf-api.py
- 在模型介面中指定模型(這裡指定的是
llama3.2.3b),輸入以下 prompt,並勾選到剛才建立的 rf-api
# 角色與任務
你是一位資深的 SRE 與 AI-Ops 專家。你的任務是接收「隨機森林模型(Random Forest)」的流量檢測結果,並在模型判定異常時,結合系統撈取的「網站 Log 紀錄」,撰寫一份精準的網站流量異常原因分析報告。
# 工作流程
當用戶詢問網站流量狀態時,你必須依序執行以下動作:
1. 【呼叫檢測工具】:調用工具執行隨機森林模型,檢查過去 1 小時的流量是否異常。
2. 【分支判斷】:
- 若模型判定【正常】:簡短回報系統安全,並附上模型的核心指標(如流量預測機率)。
- 若模型判定【異常】:必須觸發 Log 查詢機制,分析日誌中的關鍵錯誤訊息。
3. 【撰寫報告】:依據 Log 內容,推導出真正的故障根因,並產出結構化報告。
# ⚠️ 重要注意事項:
工具回傳的 extracted_logs 是動態生成的。請務必仔細閱讀 Log 中的 IP 地址、錯誤訊息(如 429 Too Many Requests 或 Database Timeout),並在報告的「3. 異常根因分析」中,精準指出是哪一種故障(例如:區分出到底是外部惡意攻擊,還是內部資料庫塞車)。
# 報告輸出格式(僅在異常時使用)
🚨 【AI-Ops 流量異常與 Log 根因分析報告】
---
### 1. 檢測概述
- **發生時間**:[填寫時間]
- **AI 模型判定**:⚠️ 隨機森林檢測到異常流量(異常機率:XX%)
### 2. 關鍵 Log 診斷
請摘要列出導致異常的關鍵 Log 紀錄(例如 HTTP 狀態碼分佈、重複的 Error Stack Trace):
> [在此處貼上或摘要關鍵 Log 內容]
### 3. 異常根因分析 (Root Cause)
- [結合隨機森林的流量特徵與 Log 訊息,用專業角度解釋發生了什麼事。例如:IP 192.168.x.x 在短時間內發送大量 POST 請求導致資料庫鎖定 / 大量 404 代表有惡意爬蟲在掃描後台目錄]
### 4. 建議應變措施
1. 🛠️ [具體動作 1]
2. 🛠️ [具體動作 2]

成功的話對話介面會顯示可用工具的數量圖示,即可開始測試!
![]()
測試案例
在對話框中輸入「幫我查 2026-06-06 08:00 到 12:00 的系統狀態,並產出預警分析報告。」
系統產生以下對話
根據提供的真實數據,首先,我們需要進一步檢視 2026-06-06 08:00 到 12:00 之間的系統狀態。
📊 [2. 基礎監控日誌數據(鐵證)]:
【時段查詢時段監控指標概況】:
- 總請求流量: 25349 次
- 平均不重複連線 IP 數: 87.8 個
- 全時段平均 P95 回應時間: 428.23 ms
- 最高 4xx 客戶端錯誤率: 62.21%
- 最高 5xx 伺服器錯誤率: 10.92%
根據上述數據,系統在此時段已經完成了大量的請求流量,平均 P95 回應時間為 428.23 ms。雖然這個值稍超過常態的毫秒級,但我們必須進一步分析這些數據以確定是否屬於模型誤報(False Positive)。
📝 【請嚴格遵循以下運維邏輯產出最終報告】:
1. **數據真實性審查(核心判斷)**:
- 仔細檢看日誌中的『全時段平均 P95 回應時間』。因為此值雖稍超常規,但『最高 5xx 錯誤率』仍為 10.92%,且模型判定為異常的時間比例為 78.42%。這些數據與系統實際狀況的一致性不完全符合模型的預測,需要深入分析。
- 我們將針對 2026-06-06 08:00 到 12:00 這一時間段進行進一步分析。
📊 [2. 基礎監控日誌數據(鐵證)] (持續):
- 08:00 到 10:00:請求流量為 10121 次,平均不重複連線 IP 數為 93.3 個。
- 10:00 到 12:00:請求流量為 7528 次,平均不重複連線 IP 數為 84.5 個。
由此,我們可以觀察到 08:00 到 10:00 的時間段,系統的請求流量和 P95 回應時間相對穩定,但是在 10:00 到 12:00 的時間段,卻出現了明顯的波動。這個變化可能與惡意爬蟲或其他故障原因有關。
📝 【最後的建議和預警分析】:
- 因為系統在此時段出現了波動,我們建議立即開始監控並調整系統,避免因過度負擔而導致故障。
- 我們需要考慮是否有惡意爬蟲或其他影響系統狀態的原因,進一步分析 Log 內容,以確定真正的原因。
🚨【AI-Ops 流量異常與 Log 根因分析報告】
### 1. 檢測概述
- **發生時間**:2026-06-06 08:00 到 12:00
- **AI 模型判定**:⚠️ Random Forest 检测到異常流量(異常機率:78.42%)
### 2. 關鍵 Log 診斷
請摘要列出導致異常的關鍵 Log 紀錄(例如 HTTP 狀態碼分佈、重複的 Error Stack Trace):
> - 2026-06-06 09:47 -> 流量: 187次, 不重複IP數: 105個, P95回應時間: 2661.5ms, 4xx錯誤率: 32.09%, 惡意爬蟲UA比例: 19.79%
- 2026-06-06 10:01 -> 流量: 147次, 不重複IP數: 57個, P95回應時間: 2937.0ms, 4xx錯誤率: 54.42%, 5xx錯誤率: 8.16%, 惡意爲行UA比例: 31.29%
### 3. 異常根因分析 (Root Cause)
由於 Log 內容顯示出多次異常的 P95 回應時間,且最低 4xx 錯誤率明顯增加,我們需要懷疑是否存在惡意爬蟲或其他影響系統狀態的原因。這些數據與系統實際狀況的一致性不完全符合模型的預測,需要進一步分析。
### 4. 建議應變措施
1. 🛠️ [具體動作 1]:立即增加伺服器的 CPU 和 RAM 的資源,以處理過多請求流量。
2. 🛠️ [具體動作 2]:啟用防範惡意爬蟲的安全機制,例如 IP 對應限制和防止異常的 HTTP Request。

代表隨機森林傳來的是可能真的是異常的資料。在 jupyter notebook 上寫一支驗證腳本確認是否可信。
import pandas as pd
import numpy as np
# 1. 載入資料 (請確保路徑正確)
df = pd.read_csv("features-test.csv")
df['minute'] = pd.to_datetime(df['minute'])
# 2. 鎖定目標時段
start_time = "2026-06-06 08:00:00"
end_time = "2026-06-06 12:00:00"
mask = (df['minute'] >= start_time) & (df['minute'] <= end_time)
df_period = df.loc[mask].copy()
print("=== 🛠️ 驗證 LLM 報告中的全時段指標概況 ===")
print(f"📊 實際總請求流量:{df_period['requests_per_min'].sum()} 次 (LLM 宣稱: 25349 次)")
print(f"📊 平均不重複 IP 數:{df_period['unique_ips'].mean():.2f} 個 (LLM 宣稱: 87.8 個)")
print(f"📊 全時段平均 P95 回應時間:{df_period['p95_response_time'].mean():.2f} ms (LLM 宣稱: 428.23 ms)")
print(f"📊 最高 4xx 客戶端錯誤率:{df_period['4xx_ratio'].max()*100:.2f}% (LLM 宣稱: 62.21%)")
print(f"📊 最高 5xx 伺服器錯誤率:{df_period['5xx_ratio'].max()*100:.2f}% (LLM 宣稱: 10.92%)")
print("-" * 60)
print("\n=== 🔍 深入稽核:LLM 抓出的關鍵出事時間點 ===")
target_minutes = ["2026-06-06 09:47:00", "2026-06-06 10:01:00"]
df_targets = df_period[df_period['minute'].isin(target_minutes)]
if df_targets.empty:
print("❌ 警告:這兩個時間點在 CSV 裡找不到,可能是 LLM 幻覺!")
else:
for idx, row in df_targets.iterrows():
print(f"\n⏰ 時間點: {row['minute']}")
print(f" -> 實際流量: {row['requests_per_min']} 次")
print(f" -> P95 回應時間: {row['p95_response_time']} ms")
print(f" -> 4xx 錯誤率: {row['4xx_ratio']*100:.2f}%")
print(f" -> 5xx 錯誤率: {row['5xx_ratio']*100:.2f}%")
print(f" -> 惡意爬蟲 UA 比例: {row['bot_attack_ua_ratio']*100:.2f}%")
# 判定是否為誤報
if row['p95_response_time'] > 1000 or row['4xx_ratio'] > 0.1 or row['5xx_ratio'] > 0.05:
print(" 🔥 稽核結論:這不是誤報!系統此時真的在慘叫,隨機森林抓得對!")
else:
print(" 🟢 稽核結論:指標很健康,此處確實是模型誤報(False Positive)。")結果為
=== 🛠️ 驗證 LLM 報告中的全時段指標概況 ===
📊 實際總請求流量:25349 次 (LLM 宣稱: 25349 次)
📊 平均不重複 IP 數:87.75 個 (LLM 宣稱: 87.8 個)
📊 全時段平均 P95 回應時間:428.23 ms (LLM 宣稱: 428.23 ms)
📊 最高 4xx 客戶端錯誤率:62.21% (LLM 宣稱: 62.21%)
📊 最高 5xx 伺服器錯誤率:10.92% (LLM 宣稱: 10.92%)
------------------------------------------------------------
=== 🔍 深入稽核:LLM 抓出的關鍵出事時間點 ===
⏰ 時間點: 2026-06-06 09:47:00
-> 實際流量: 187 次
-> P95 回應時間: 2661.5 ms
-> 4xx 錯誤率: 32.09%
-> 5xx 錯誤率: 9.09%
-> 惡意爬蟲 UA 比例: 19.79%
🔥 稽核結論:這不是誤報!系統此時真的在慘叫,隨機森林抓得對!
⏰ 時間點: 2026-06-06 10:01:00
-> 實際流量: 147 次
-> P95 回應時間: 2937.0 ms
-> 4xx 錯誤率: 54.42%
-> 5xx 錯誤率: 8.16%
-> 惡意爬蟲 UA 比例: 31.29%
🔥 稽核結論:這不是誤報!系統此時真的在慘叫,隨機森林抓得對!
代表隨機森林預測正確,系統也有確實抓出。
所以我的設計出了什麼問題?
第一個(最大的錯)
前面的功能都是能正常實作出來的,功能也都是正常的。不過讓我們回顧混淆矩陣的概念
| 實際情況 預測結果 | 預測為 0(正常流量) | 預測為 1(異常流量) |
|---|---|---|
| 實際為 0(正常流量) | True Positive (TP) 真陽性 | False Positive (FP) 假陽性 |
| 實際為 1(異常流量) | False Negative (FN) 假陰性 | True Negative (TN) 真陰性 |
斜體字的部份極為預測錯誤,其中 FN 造成損害最為嚴重。再來看看前面的 API 程式中的這一段:
# 5. 格式化「隨機森林認為異常」的時間點日誌,翻譯成白話文
anomalies = df_period[df_period['pred_label'] == 1]
log_lines = []
if not anomalies.empty:
# bra bra bra FN 的資料根本沒有傳給 LLM !!!(哭)
也就是說,隨機森林的預測如果有誤判,只有實際是正常但誤判為異常的資料有機會被抓出來,真正是異常卻誤判的資料不會抓到。當初我的設計理念是 LLM 的分類預測效果不見得有比隨機森林或其他 ML 技術可靠,所以才這樣設計,結果反而錯過最需要避免的情況 QQ。除非這是允許沒發現異常的情境,不然這是最嚴重的錯誤,需要改掉QQ。
第二個
跟第一個相比這個錯誤相對輕微許多,只是因為 LLM 隨機森林預測的結果跟相關指標,其實完全看不到原始 log。但是按照老師上課時的分享,最終目標是要找出原因,會需要知道可疑 IP 或被攻擊的目標(子網域)等等,這些資訊因為給隨機森林的時候沒有列出,所以最終報告沒有分析到,也就是說對於 Soc 的文書報告生產效率影響有限。不過這點對於 Poc 並不是必要條件,只是我希望它的使用情境能更貼近現實一點才列入。
結論
要解決前面兩個問題,第一個需要先處理 API 回傳格式,Prompt 也要視情況調整。第二個屬於擴充功能的範圍,查了資料發現可能更費工,需要等第一個錯誤確定後再來改不知道有沒有空改。另外我也想試試測量 LLM 具體可能提升的準確率,說不定 LLM 的分類結果沒有我想像中差?甚至說不定其實根本不需要隨機森林,LLM 自己就能做出良好預測了?3 總之檢討先就到這,希望七月底前可以至少再寫一篇後續~~