---
title: Agent 獨立更新與心跳改造（需求紀錄）
status: draft
date: 2026-11-01
source: 決策者 2026-11-01 在 189 推 Agent 1.4.8 後提出
related: FR-167（線上更新）待辦 59／60、STATE §5 已定裁示「不再用心跳帶訊息」
---

# Agent 獨立更新與心跳改造：需求紀錄

> 下一階段要討論並開始做。這份只記決策者的需求原話、目前的做法、這次實際踩到的問題，
> 與每一條會碰到的地方；方案取捨留到討論時做，這裡不先定。

## 決策者的五點需求（2026-11-01 原話整理）

1. **主系統跟 Agent 要分開來**，不要像現在都包在同一包裡面。
2. **要可以各別分開更新**。
3. **Agent 那邊也要有更新中心**，可以做離線更新，或是跟主系統發動更新需求。
4. **R2 上面的安裝包要分開來**。
5. **不要再用心跳帶資料**，有滿多地方要重寫。（已列為 FR-167 STATE 已定裁示）

## 為什麼現在要做

2026-11-01 為了修 Agent 的兩個問題（容器版 Office 證據預覽失敗、主控台 Profile 不能選），實際走過一次的流程：

| 步驟 | 花的時間 | 問題 |
|---|---|---|
| 出 Agent 1.4.8 | 約 20 分 | 本身沒問題 |
| 為了把 Agent 帶出去，連主系統也出一包 1.27.10 | 約 36 分 | 主系統本身只有一個套件改動，卻要整包重出 |
| 客戶先升主系統（停機約 7 分） | 7 分 | 只為了修 Agent，主系統卻要停機 |
| 主系統升完才能推 Agent | — | 推送跟著主系統版本走 |
| 推送後等心跳才開始、升完等心跳才回結果 | 每段最多 5 分 | 121 升級 12:31:39 已成功，主系統 12:36:30 才知道 |
| 升級途中 8 格進度一直停在第 1 格 | — | 欄位對不上（待辦 60），再加上進度靠輪詢 |

結論：Agent 的修正速度被主系統的發版節奏與心跳間隔綁住。

## 目前的做法（要改掉的部分）

### 包

- Agent compose 包（約 1.8 GB）在主系統出包時被放進 `guidant-ai-<主系統版>.tar.gz` 的 `agent/` 底下，一起簽、一起上 R2（`v1/<主系統版>/`）。
- 客戶主機升級後，包解在 `upgrade/bundles/guidant-ai-<主系統版>/agent/`，更新中心 Agent 區只能推「這一版主系統附的那個 Agent」。
- 發布清單（release.json）只有主系統一條版本線，`min_agent_version` 是主系統要求 Agent 至少幾版。
- 離線單一檔也只有主系統一種。

### 更新中心

- 只有主系統有更新中心；Agent 區是主系統更新中心裡的一塊，只能「推送這版附的 Agent」。
- Agent 主控台沒有更新入口。Agent 1.4.2 以前沒有遠端更新能力，只能 `sudo guidant-agent-compose upgrade <包>` 手動升。

### 心跳承載的東西

主系統每次心跳回應夾帶（`core/plugins/remote_agent.py` 的 `heartbeat_extras` 與 `update_extra`）：

| 夾帶內容 | 主系統這邊在哪 | Agent 讀的鍵 |
|---|---|---|
| 升級指令（要不要升、目標版、sha） | `_agent_update.py` | `update` |
| 弱點資料庫版本 | `_agent_vulndb.py` | `vulndb` |
| 工具參數表版本 | `_agent_tool_schemas.py` | `tool_schemas_version` |
| LDAP 設定 | `_agent_ldap_config.py` | — |
| 授權掃描目標名稱 | `allowed_target_names_heartbeat_extra` | `names`／`hosts`／`networks` |
| 離線登入天數 | `_agent_offline_login_policy.py` | — |
| 檢測 Profile 清單 | `_agent_profile_sync.py` | `profiles`／`profiles_check` |
| 檔案保護設定 | `_agent_file_protect.py` | — |

Agent 每次心跳上報夾帶（`guidant_agent/sync/app/enroll.py` 的 `heartbeat_fields`）：Profile 同步狀態、升級結果（`upgrade_result`）、弱點資料庫狀態、outbox 數量、重啟原因、LDAP 來源等。主系統用 `heartbeat_payload_observers`（`record_profile_states`、`record_upgrade_result`）處理。

主系統主動找 Agent 的通道已經有：FR-039 資料面（mTLS＋短效 JWT，`https://<agent>:8443`），`POST /update/notify`、`GET /update/progress` 是這條。

## 每一條需求會碰到的地方

### 1、2、4　包分開、各別更新、R2 分開

- **出包**：BE `scripts/build/build_bundle.sh` 不再把 Agent compose 包放進主系統包；Agent 獨立出包＋簽章＋上 R2（新路徑，例如 `agent/v1/<Agent 版>/`）。
- **更新站**：多一條成品類型（主系統／Agent），`product_releases` 或另開表、`register-draft`／`publish`／`withdraw` CLI、後台版本頁、`/v1/check`／`/v1/download-url` 要能問 Agent 的新版。
- **簽章清單**：payload 要區分成品類型，防止拿 Agent 的章冒充主系統（或反之）；主機端與 Agent 端驗章各自只認自己那一類。
- **相容性**：現在只有一向（主系統要求 Agent 最低版）。分開後要兩向：Agent 新版要求主系統最低版、主系統新版要求 Agent 最低版；不相容時更新中心要擋或提示。
- **主系統更新中心 Agent 區**：改成可以自己「檢查 Agent 新版／下載 Agent 包」，不用先升主系統；`upgrade/bundles/<主系統版>/agent/` 改成獨立的 Agent 包存放處。
- **離線**：要能單出 Agent 離線檔。
- **清理**：主系統升級第 7 步現在會保留本版 `bundles/<版>/agent/`，分開後這段邏輯要拿掉或改。

### 3　Agent 自己的更新中心

- Agent 主控台新增「更新」頁：看現行版本、看有沒有新版、離線上傳更新包、發動更新。
- 「跟主系統發動更新需求」要定義清楚：是 Agent 請主系統把包給它（主系統當中繼，Agent 不直接連外網，現行原則 `update_sync.py` 檔頭），還是 Agent 主控台發一張單給主系統更新中心排程。
- Agent 不連外網、不連更新站，是現行的安全前提（客戶內網掃描機），要不要改，討論時要明確裁。
- 權限：Agent 主控台的誰可以按更新（local-admin？離線操作者？），沿用 `CONSOLE_SCOPES` 還是新增。
- 升級本身已有主機層程式（`scripts/install/lib/upgrade_order.sh`＋systemd path 單元），可沿用，只換「單從哪來」。

### 5　不再用心跳帶資料

- 已定原則：心跳只表示存活；Agent→主系統事件走專用端點、當下送、送不到進 outbox；主系統→Agent 指令走資料面 8443。
- 上表 8 種心跳回應夾帶、Agent 上報那幾種，逐一決定新通道：
  - 「主系統有新設定要給 Agent」類（LDAP、檔案保護、離線登入天數、授權目標、工具參數、Profile 清單、弱點庫版本）：主系統在設定變更當下打 Agent 資料面通知，Agent 收到後自己來拉；或 Agent 定期拉一支版本號端點，有變才拉內容。
  - 「Agent 有事要告訴主系統」類（升級結果、升級進度、Profile 同步狀態）：Agent 當下打主系統專用端點。
  - 升級進度：Agent 主動推，或主系統畫面打開時才拉（現在是拉，但欄位對不上，見 FR-167 待辦 60）。
- 跨網段：121、60.167 屬不同網段，主系統打 Agent 8443 今天實測可通；但不能假設所有客戶網路都讓主系統主動連 Agent，要保留 Agent 主動連主系統的退路。
- 舊版相容：已部署的 Agent 只懂心跳，改造期間主系統要兩套並存一段時間；決定哪一版之後停掉心跳夾帶。

## 這次順帶記下、討論時要一併看的

- **FR-167 待辦 60**：Agent 區 8 格欄位對不上（BE `item.progress`／FE `rollout.progress`），升級結果晚 5 分鐘回。
- **FR-167 待辦 58**：Agent 主控台檔案頁把預覽 PDF 快取列成證據。
- **60.167 升級失敗**：主機升級單第 0 步 `docker create --network none guidant-ai-agent:1.4.8` 失敗（「無法讀取新映像」），121 同包同步驟過；疑該機 docker／核心問題，決策者在該機查。失敗原因主系統只拿到一句話，看不到主機細節，Agent 更新中心設計時要讓失敗細節回得來。
- **Agent 升級的 journal**：Agent 主機升級的逐行輸出在 systemd journal，Agent 主控台與診斷包都看不到。

## 下一步

- 決策者排時間討論：先過這份需求，再照 `big-feature-workflow` 出討論稿與雛形（Agent 主控台更新頁、主系統 Agent 區改版）。
- 新 FR 編號開立時把本檔搬到 `docs/features/FR-<號>-<YYMM>-agent-independent-update/`。
