iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
AI 自動化

NodeRED × Google agy CLI 打造個人 Windows 智慧管家系列 第 24

Day 24:E2E 實戰層 1:採集層的資料契約

  • 分享至 

  • xImage
  •  

前面幾天已經示範過檔案掃描、系統負載採集與 JSONata。今天不再重教這些零件,而是把它們組成 E2E 流程的第一個穩定邊界:採集層只提供事實,並輸出後續層可以依賴的 Task Envelope。

本文同步發布於 GitHub:2026-18th-it-ironman

採集層在整體流程中的位置

https://ithelp.ithome.com.tw/upload/images/20260919/20124959mSPnFWGUaW.png

Windows 現況 → 採集 → 契約清洗 → 決策 → 執行

今天的重點不是「怎麼讀記憶體」或「怎麼列出 Downloads」。這一層真正要解決的是:不同來源如何放進同一筆任務、空資料如何表示,以及下游如何知道資料是否完整。

Task Envelope:後續層共同依賴的介面

採集層輸出至少包含以下欄位:

欄位 責任 注意事項
task_id 追蹤同一筆任務 每次採集都要唯一
task_type 指示 Day25 選擇哪個決策契約 不可用模糊字串代替
source 標示資料來源 方便日後查 Audit Trail
input.metrics 當下環境資訊 只描述現況,不直接下處置決定
input.files 檔案事件清單 沒有檔案時仍為[]
input.collected_at 採集時間 使用 ISO 8601

採集層不應輸出 AI 的 decision、執行用的 action 或任意 PowerShell 指令。把這些責任留給後續層,才能讓採集流程可重複執行,也不會因為資料剛進來就觸發高風險動作。

雙軌資料如何合併與標準化

本篇 Flow 使用 osfspath 模組取得記憶體現況與 Downloads 檔案清單,但結果不再散落於臨時變數,而是透過防禦性採集與 JSONata 前置標準化,封裝進固定結構:

// 1. 採集記憶體現況事實 (純客觀數據,不預先下處置結論)
const total = os.totalmem();
const free = os.freemem();
const memPercent = Math.round(((total - free) / total) * 100);

// 2. 掃描 Downloads 並過濾未完成檔案 (.crdownload 等)
const dlDir = path.join(os.homedir(), 'Downloads');
let pendingFiles = [];
if (fs.existsSync(dlDir)) {
    pendingFiles = fs.readdirSync(dlDir)
        .filter(name => !name.startsWith('.') && !name.endsWith('.crdownload'))
        .map(name => {
            const fullPath = path.join(dlDir, name);
            const stat = fs.statSync(fullPath);
            return stat.isFile() ? { filename: name, path: fullPath, size_bytes: stat.size, stable: true } : null;
        })
        .filter(Boolean).slice(0, 5);
}

接著透過 Change 節點的 JSONata 表達式,消除不必要的環境雜訊,整合成標準 Task Envelope:

{
  "task_id": "task_1724389200000_abc123",
  "task_type": "CLASSIFY_FILE",
  "source": "DAY24_INGESTION",
  "input": {
    "collected_at": "2026-09-16T08:00:00.000Z",
    "download_dir": "C:/Users/example/Downloads",
    "metrics": { "mem_percent": 62, "is_heavy": false },
    "file_count": 1,
    "files": [
      { "filename": "Docker_Installer.exe", "size_bytes": 1048576, "stable": true }
    ]
  }
}

💡 原理解析:為什麼採集層只採集「事實」,並用 JSONata 前置標準化?

  • 關注點分離(Separation of Concerns):採集層的唯一職責是「忠實感測現場」,絕不在這一步驟判斷「記憶體是否太高該殺進程」或「檔案該丟去哪裡」。將處置策略留給 Day 25 決策大腦,採集器才能保持 100% 冪等且安全。
  • 防禦性型別保證(Defensive Typing):目錄空無一物時,files 仍明確保證為空陣列 [] 而不是 nullundefined;同時以 stable: true 排除正在寫入中的暫存檔,確保下游 Day 25 遍歷或讀取屬性時永不拋出 TypeError
  • 契約穩定性(Contract Stability):無論底層作業系統未來如何升級或擴充硬體檢測項,所有環境現場均被收斂在 input 邊界內,上層的通用任務協議外殼永不破損。

邊界條件與驗收

  • Downloads 不存在:輸出空清單並保留錯誤狀態,不能讓整筆訊息消失。
  • 檔案正在下載:標記 stable: false,不交給後續分類。
  • 同時觸發兩筆任務:task_id 必須不同,不能共用可變的全域資料。
  • 記憶體超過門檻:只標記 is_heavy,不在採集層呼叫 AI 或執行命令。

驗收時要檢查的不只是 Debug 有輸出,而是每一筆訊息都能被 Day25 直接接收,且 filesmetrics、時間與來源欄位的型別穩定。


完整 Flow

在 Node-RED 點擊「右上角選單」➔「匯入」即可一鍵部署:

https://ithelp.ithome.com.tw/upload/images/20260919/20124959uRJ7RqMdJq.png

本範例 Flow 位置:👉 下載


今日總結與明日預告

今天我們建立了 E2E 系統第一層的關鍵防線:採集層只提供結構穩固的客觀事實,不越權做出業務決策。透過雙軌現場捕獲與 JSONata 標準化清洗,產出下游可 100% 依賴的 Universal Task Envelope。

明天我們將進入 Day 25:AI 決策大腦,只依賴這份標準 Envelope,透過多任務路由表與 JSON Schema 硬約束,精準分流並產生可被信賴的結構化處置建議!


上一篇
Day 23:端到端實戰啟動(Windows AI 自動化系統架構)
下一篇
Day 25:E2E 實戰層 2:任務路由與決策契約
系列文
NodeRED × Google agy CLI 打造個人 Windows 智慧管家29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言