docs(adr): 起草 ADR-054/055/056/057 — Phase 25 三方向架構決策
ADR-054: DIAGNOSE Privacy-First Routing (已批准) - _local_fallback_chain 設計決策 - NEMOTRON privacy_level=local 首席架構師裁示 - 全部 local 失敗 → REJECT + Telegram ADR-055: Knowledge Auto-Harvesting (已批准) - AUTO_RUNBOOK DRAFT + ANTI_PATTERN PUBLISHED 設計理由 - compute_hash() 碰撞風險說明 - Fire-and-forget GC 防護強制規範 ADR-056: Config Drift Detection 四層架構 (已批准) - Detector→Analyzer→Interpreter→Remediator 職責邊界 - AI 只做意圖分析不做修復決策 - adopt() 暫停 + _recent_reports Phase 1 限制 ADR-057: adopt() Gitea PR API 實作路徑 (草案,待批准) - 解決 API Pod git add -A 安全風險 - PR review 流程保障 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
96
docs/adr/ADR-054-diagnose-privacy-first-routing.md
Normal file
96
docs/adr/ADR-054-diagnose-privacy-first-routing.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# ADR-054: DIAGNOSE Privacy-First Routing(本地隱私邊界)
|
||||
|
||||
> **狀態**: ✅ 已批准
|
||||
> **日期**: 2026-04-04
|
||||
> **決策者**: 統帥 + 首席架構師
|
||||
> **首席架構師評分**: 技術背書
|
||||
> **關聯 ADR**: ADR-052 (AI Provider Registry)
|
||||
> **實作**: Phase 25 P0 — commit 3455044, a562db4
|
||||
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
### 問題陳述
|
||||
|
||||
`DIAGNOSE` 任務類型(根本原因分析、服務診斷)會接收到包含機密基礎設施資訊的 Incident 資料(Pod 名稱、錯誤堆疊、服務拓撲)。在 ADR-052 的 AI Provider Registry 架構下,DIAGNOSE 原本沿用 `_fallback_chain`,可能將機密資料傳送至雲端 Provider(Gemini、Claude)。
|
||||
|
||||
### 驅動因素
|
||||
|
||||
1. **零信任原則**: 機密基礎設施資料不應離開內網
|
||||
2. **合規要求**: 避免系統診斷資料上傳至第三方雲端
|
||||
3. **能力需求**: 診斷任務需要高推理能力,不能只用 Ollama
|
||||
|
||||
---
|
||||
|
||||
## 決策
|
||||
|
||||
### D1: 建立 `_local_fallback_chain`
|
||||
|
||||
```python
|
||||
# ai_router.py
|
||||
self._local_fallback_chain: list[tuple[AIProviderEnum, str]] = [
|
||||
(AIProviderEnum.NEMOTRON, self._nemotron_default), # NIM 188,主力
|
||||
(AIProviderEnum.OLLAMA, self._ollama_summary), # Ollama 188,備援
|
||||
]
|
||||
```
|
||||
|
||||
DIAGNOSE 任務強制使用此鏈,不允許降級至任何 `privacy_level="cloud"` 的 Provider。
|
||||
|
||||
### D2: NEMOTRON Provider 的 privacy_level 裁定
|
||||
|
||||
**首席架構師裁示 (2026-04-04)**:
|
||||
- NEMOTRON Provider 對應 NVIDIA NIM 部署在 **192.168.0.188**(內網 GPU 主機)
|
||||
- 非 NVIDIA 官方雲端 API(api.nvidia.com)
|
||||
- 屬於 local infra,`privacy_level = "local"`,可納入 DIAGNOSE 隱私邊界
|
||||
|
||||
```python
|
||||
# nemotron.py
|
||||
@property
|
||||
def privacy_level(self) -> str:
|
||||
return "local" # NIM 部署在 192.168.0.188(內網 GPU)
|
||||
```
|
||||
|
||||
### D3: 全部 Local Provider 不可用時 → REJECT + Telegram
|
||||
|
||||
```python
|
||||
# 全部 local provider 失敗或不可用時
|
||||
await tg.send_text("⚠️ DIAGNOSE 本地 Provider 不可用,需要人工介入")
|
||||
return AIResult(success=False, reason="REJECT_PRIVACY_BOUNDARY")
|
||||
```
|
||||
|
||||
不降級至雲端。必要時通知人工介入。
|
||||
|
||||
### D4: route_sync() 同步版本同樣適用隱私邊界
|
||||
|
||||
`route_sync()` 補齊相同的 DIAGNOSE → `_local_fallback_chain` 判斷,確保同步路徑不繞過隱私邊界。
|
||||
|
||||
---
|
||||
|
||||
## 後果
|
||||
|
||||
### 正面影響
|
||||
|
||||
- DIAGNOSE 資料的隱私保護有制度性保障
|
||||
- 失敗時告警人工介入,不靜默降級至雲端
|
||||
- NIM 188 具備足夠診斷能力(Nemotron-4 340B 量化版)
|
||||
|
||||
### 負面影響 / 風險
|
||||
|
||||
- 若 NIM 188 離線且 Ollama 超時,DIAGNOSE 功能暫時中斷
|
||||
- 需要維護 NIM 188 的高可用性
|
||||
|
||||
### 未決事項
|
||||
|
||||
- NIM 188 是否需要 Startup Probe(K0.5,待維護窗口)
|
||||
- 若未來引入第三個 local provider,`_local_fallback_chain` 需更新
|
||||
|
||||
---
|
||||
|
||||
## 替代方案
|
||||
|
||||
| 方案 | 理由未採用 |
|
||||
|------|-----------|
|
||||
| 允許 DIAGNOSE 降級至 Gemini | 違反零信任原則 |
|
||||
| 只用 Ollama 做 DIAGNOSE | 能力不足,診斷品質差 |
|
||||
| 所有任務類型一律 local-only | 過於保守,影響正常業務功能 |
|
||||
121
docs/adr/ADR-055-knowledge-auto-harvesting.md
Normal file
121
docs/adr/ADR-055-knowledge-auto-harvesting.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# ADR-055: Knowledge Auto-Harvesting(AUTO_RUNBOOK + ANTI_PATTERN)
|
||||
|
||||
> **狀態**: ✅ 已批准
|
||||
> **日期**: 2026-04-04
|
||||
> **決策者**: 統帥 + 首席架構師
|
||||
> **首席架構師評分**: 技術背書
|
||||
> **關聯 ADR**: ADR-052 (AI Provider Registry), ADR-043 (Knowledge Base)
|
||||
> **實作**: Phase 25 P1 — commit 3455044
|
||||
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
### 問題陳述
|
||||
|
||||
AutoRepair 執行修復後,成功/失敗的知識停留在系統日誌中,無法被後續決策利用。特別是:
|
||||
|
||||
1. **成功修復** → 修復步驟應被收錄為可參考的 Runbook
|
||||
2. **失敗修復** → 失敗模式應被記錄以防止重複嘗試相同方法
|
||||
|
||||
Knowledge Base 存在但靠人工填寫,形成知識孤島。
|
||||
|
||||
---
|
||||
|
||||
## 決策
|
||||
|
||||
### D1: AUTO_RUNBOOK — 成功修復自動產生草稿 Runbook
|
||||
|
||||
```python
|
||||
# auto_repair_service.py — execute_auto_repair() 成功後
|
||||
task = asyncio.create_task(gen.generate_runbook(incident, playbook, result))
|
||||
self._pending_tasks.add(task)
|
||||
task.add_done_callback(self._pending_tasks.discard)
|
||||
```
|
||||
|
||||
- EntryType: `AUTO_RUNBOOK`
|
||||
- EntryStatus: `DRAFT`(需要人工審核後才升為 APPROVED)
|
||||
- Nemotron 生成內容,失敗時有最小化 fallback
|
||||
|
||||
**理由**: 自動生成的 Runbook 品質未經驗證,保持 DRAFT 讓 SRE 審核,避免錯誤知識被自動採納。
|
||||
|
||||
### D2: ANTI_PATTERN — 失敗修復直接發布防災記錄
|
||||
|
||||
```python
|
||||
# auto_repair_service.py — execute_auto_repair() 失敗後
|
||||
task = asyncio.create_task(
|
||||
gen.generate_anti_pattern(incident, playbook, fail_result, symptoms_hash)
|
||||
)
|
||||
self._pending_tasks.add(task)
|
||||
task.add_done_callback(self._pending_tasks.discard)
|
||||
```
|
||||
|
||||
- EntryType: `ANTI_PATTERN`
|
||||
- EntryStatus: `PUBLISHED`(直接發布,無需人工審核)
|
||||
- 理由:失敗記錄的目的是「防止重蹈覆轍」,早一秒發布早一秒保護
|
||||
|
||||
### D3: SymptomPattern.compute_hash() — O(1) 防災閘門
|
||||
|
||||
```python
|
||||
def compute_hash(self) -> str:
|
||||
key = (
|
||||
"|".join(sorted(self.alert_names)) + "||"
|
||||
+ "|".join(sorted(self.affected_services)) + "||"
|
||||
+ json.dumps(self.label_patterns, sort_keys=True)
|
||||
)
|
||||
return hashlib.sha256(key.encode()).hexdigest()[:16]
|
||||
```
|
||||
|
||||
- 16 字元 hex = 2^64 碰撞空間,工程實踐上足夠
|
||||
- 碰撞發生最壞結果:某次修復被誤攔截,需人工重試——可接受
|
||||
|
||||
### D4: evaluate_auto_repair() 加入 ANTI_PATTERN 閘門
|
||||
|
||||
```python
|
||||
# 症狀提取後、Playbook 匹配前
|
||||
symptoms_hash = symptoms.compute_hash()
|
||||
anti_patterns = await get_knowledge_service().check_anti_pattern(symptoms_hash, days=7)
|
||||
if anti_patterns:
|
||||
return AutoRepairDecision(
|
||||
can_auto_repair=False,
|
||||
reason=f"過去 7 天有失敗案例: {anti_patterns[0].title}",
|
||||
blocked_by="ANTI_PATTERN",
|
||||
)
|
||||
```
|
||||
|
||||
閘門位置:診斷症狀後、開始執行前,屬於預防性攔截。
|
||||
|
||||
### D5: Fire-and-Forget 必須有 GC 防護
|
||||
|
||||
所有 `asyncio.create_task()` 必須:
|
||||
1. 持有引用至 `self._pending_tasks`
|
||||
2. 在 `done_callback` 中從集合移除
|
||||
|
||||
```python
|
||||
task = asyncio.create_task(coro)
|
||||
self._pending_tasks.add(task)
|
||||
task.add_done_callback(self._pending_tasks.discard)
|
||||
```
|
||||
|
||||
成功分支與失敗分支必須對稱實作,不得有遺漏。
|
||||
|
||||
---
|
||||
|
||||
## 後果
|
||||
|
||||
### 正面影響
|
||||
|
||||
- Knowledge Base 自動積累修復經驗,形成正向循環
|
||||
- 失敗模式即時保護,防止重複損耗
|
||||
- SRE 工作聚焦於審核而非撰寫
|
||||
|
||||
### 負面影響 / 風險
|
||||
|
||||
- Nemotron 生成的 Runbook 品質參差,需 SRE 審核成本
|
||||
- ANTI_PATTERN 直接發布,若症狀 hash 碰撞可能誤攔正常修復
|
||||
|
||||
### 未決事項(Backlog)
|
||||
|
||||
- I2: `nvidia.chat()` system prompt 格式是否符合 NIM message format 規範
|
||||
- M1: ANTI_PATTERN 是否需要 Telegram 確認視窗(human-in-the-loop 選項)
|
||||
- M2: symptoms_hash 16 字元截斷的碰撞監控機制
|
||||
123
docs/adr/ADR-056-config-drift-detection.md
Normal file
123
docs/adr/ADR-056-config-drift-detection.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# ADR-056: Config Drift Detection — GitOps 守門員架構
|
||||
|
||||
> **狀態**: ✅ 已批准
|
||||
> **日期**: 2026-04-04
|
||||
> **決策者**: 統帥 + 首席架構師
|
||||
> **首席架構師評分**: 技術背書
|
||||
> **關聯 ADR**: ADR-052, ADR-057 (adopt() Gitea PR API)
|
||||
> **實作**: Phase 25 P2 — commit 3455044
|
||||
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
### 問題陳述
|
||||
|
||||
K8s 叢集實際狀態可能因以下原因與 Git YAML 產生漂移:
|
||||
|
||||
1. 緊急 Hotfix 直接 kubectl 操作而未提交 Git
|
||||
2. 人為誤操作(kubectl edit / kubectl set image)
|
||||
3. CI/CD 部分失敗導致 Rollout 中斷
|
||||
|
||||
目前無系統性機制偵測此類漂移,也無法自動修復。
|
||||
|
||||
---
|
||||
|
||||
## 決策
|
||||
|
||||
### D1: 四層架構(Detector → Analyzer → Interpreter → Remediator)
|
||||
|
||||
```
|
||||
GitStateReader ─┐
|
||||
├─→ DriftDetector.scan() → raw DriftReport
|
||||
K8sStateReader ─┘
|
||||
↓
|
||||
DriftAnalyzer.classify() → HIGH/MEDIUM/INFO
|
||||
↓
|
||||
NemotronDriftInterpreter.analyze() → intent + explanation
|
||||
↓
|
||||
DriftRemediator.rollback() / adopt()
|
||||
```
|
||||
|
||||
**職責邊界**:
|
||||
- Detector: 事實比對(Git vs K8s),不做判斷
|
||||
- Analyzer: 分級(白名單過濾、嚴重度分類)
|
||||
- Interpreter: AI 意圖分析,**只輸出意圖判斷,不輸出修復指令**
|
||||
- Remediator: 確定性修復執行,不使用 AI 決定修復策略
|
||||
|
||||
### D2: AI 只做意圖分析,確定性程式碼做修復
|
||||
|
||||
NemotronDriftInterpreter 分析漂移意圖(emergency_hotfix / human_error / automated_change / unknown),為人工決策提供參考。
|
||||
|
||||
修復由確定性程式碼執行(`kubectl apply` / `git push`),不允許 AI 生成 kubectl 指令。
|
||||
|
||||
**理由**: AI 生成的修復指令無法保證正確性,對 K8s 叢集的破壞難以回滾。
|
||||
|
||||
### D3: rollback() 使用 kubectl apply(覆蓋回 Git 狀態)
|
||||
|
||||
```python
|
||||
cmd = ["kubectl", "apply", "-f", target, "-n", namespace, "--dry-run=none"]
|
||||
```
|
||||
|
||||
- `resource_key` 參數支援精細範圍(按 Kind/Name 匹配 YAML 檔)
|
||||
- 失敗時只通知,不重試(避免重複操作)
|
||||
|
||||
### D4: adopt() 端點暫停開放(見 ADR-057)
|
||||
|
||||
API Pod 內執行 `git add -A` 有安全風險:
|
||||
|
||||
1. API Pod 通常無 Git 認證(SSH key / token)
|
||||
2. `git add -A` 可能誤提交敏感檔案
|
||||
3. 多副本部署下 git 操作不安全
|
||||
|
||||
暫回傳 501,ADR-057 完成後改由 Gitea PR API 實作。
|
||||
|
||||
### D5: CronJob 每小時自動掃描
|
||||
|
||||
```yaml
|
||||
schedule: "0 * * * *"
|
||||
concurrencyPolicy: Forbid # 防並發
|
||||
activeDeadlineSeconds: 300 # 5 分鐘超時
|
||||
backoffLimit: 0 # 失敗不重試
|
||||
```
|
||||
|
||||
### D6: 漂移報告暫存(in-process dict,Phase 1)
|
||||
|
||||
```python
|
||||
_recent_reports: dict[str, DriftReport] = {} # 最多 50 筆,FIFO
|
||||
```
|
||||
|
||||
此為 Phase 1 設計,多副本部署下不一致。
|
||||
|
||||
**升級路徑**: Phase 2 遷移至 `drift_reports` DB 表(migration phase9 已就緒)。
|
||||
|
||||
---
|
||||
|
||||
## 後果
|
||||
|
||||
### 正面影響
|
||||
|
||||
- 漂移偵測自動化,每小時一次無需人工
|
||||
- 四層架構職責清晰,各層可獨立替換
|
||||
- AI 只做分析不做決策,保持確定性修復
|
||||
|
||||
### 負面影響 / 風險
|
||||
|
||||
- `_recent_reports` 多副本不一致(Phase 1 已知限制)
|
||||
- adopt() 功能暫停,承認漂移需要手動 git 操作
|
||||
|
||||
### 未決事項
|
||||
|
||||
- C3: `_recent_reports` 遷移至 DB(見 Phase 2 計畫)
|
||||
- I4: `_is_allowlisted` / `_is_critical` field_path 比對邏輯需整合測試覆蓋
|
||||
- ADR-057: adopt() Gitea PR API 實作
|
||||
|
||||
---
|
||||
|
||||
## 替代方案
|
||||
|
||||
| 方案 | 理由未採用 |
|
||||
|------|-----------|
|
||||
| AI 生成 kubectl patch 指令執行修復 | AI 生成指令不可控,破壞難以回滾 |
|
||||
| 只記錄漂移不修復 | 缺少閉環,漂移長期積累 |
|
||||
| GitOps operator (Flux/ArgoCD) 完全接管 | 引入外部依賴,與現有架構整合成本高 |
|
||||
82
docs/adr/ADR-057-adopt-gitea-pr-api.md
Normal file
82
docs/adr/ADR-057-adopt-gitea-pr-api.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# ADR-057: Drift Adopt 端點 — Gitea PR API 實作路徑
|
||||
|
||||
> **狀態**: 📝 草案(待統帥批准後實作)
|
||||
> **日期**: 2026-04-04
|
||||
> **決策者**: 統帥 + 首席架構師
|
||||
> **關聯 ADR**: ADR-056 (Config Drift Detection)
|
||||
> **觸發原因**: 首席架構師 C2 裁示 — API Pod 內執行 git add -A 有安全漏洞
|
||||
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
### 問題陳述
|
||||
|
||||
ADR-056 中 `adopt()` 端點的設計目標:將 K8s 實際狀態的合法漂移反向寫回 Git(使 Git 承認此次變更)。
|
||||
|
||||
原始實作使用 `_git_push()` 在 API Pod 內執行 `git add -A && git commit && git push gitea main`,存在:
|
||||
|
||||
1. **安全風險**: `git add -A` 可能誤提交 Secret/ConfigMap 等敏感資料
|
||||
2. **多副本問題**: 多個 API Pod 同時執行 git 操作會造成 conflict
|
||||
3. **認證問題**: API Pod 需要持有 Gitea 認證,擴大攻擊面
|
||||
4. **Bypass 審核**: 直接 push main 繞過 PR review 流程
|
||||
|
||||
---
|
||||
|
||||
## 決策(草案)
|
||||
|
||||
### D1: adopt() 端點改用 Gitea PR API
|
||||
|
||||
流程:
|
||||
```
|
||||
用戶按下 adopt → API 呼叫 Gitea API 建立 PR → SRE review → merge
|
||||
```
|
||||
|
||||
具體實作:
|
||||
1. API 呼叫 `POST /api/v1/repos/{owner}/{repo}/pulls`(Gitea API)
|
||||
2. PR title: `chore: adopt drift — {namespace} {summary}`
|
||||
3. PR body: 包含漂移詳情、Nemotron 意圖分析、影響評估
|
||||
4. PR 建立後推送 Telegram 通知 SRE review
|
||||
|
||||
### D2: 分支命名規範
|
||||
|
||||
```
|
||||
drift/adopt-{report_id[:8]}-{timestamp}
|
||||
```
|
||||
|
||||
API 建立 branch 並 commit 對應 YAML 變更,再開 PR。
|
||||
|
||||
### D3: API Pod 認證方式
|
||||
|
||||
使用 Gitea API Token(現有 `GITEA_API_TOKEN` secret),只需 PR 建立權限,不需要 push main 權限。
|
||||
|
||||
---
|
||||
|
||||
## 實作前置條件
|
||||
|
||||
- [ ] 統帥批准此 ADR
|
||||
- [ ] 確認 `GITEA_API_TOKEN` 已在 K8s Secret 中(現有 secret 確認)
|
||||
- [ ] 確認 Gitea API `/repos/wooo/awoooi/pulls` 端點可用
|
||||
- [ ] 設計 YAML diff 的精確生成邏輯(只改漂移的欄位)
|
||||
|
||||
---
|
||||
|
||||
## 替代方案
|
||||
|
||||
| 方案 | 理由未採用 |
|
||||
|------|-----------|
|
||||
| 繼續 git push main(原實作) | 安全風險 + 多副本問題 |
|
||||
| 完全移除 adopt() | 失去 GitOps 閉環能力 |
|
||||
| 透過 Webhook 觸發 Gitea CI | 複雜度高,與現有 CD 流程耦合 |
|
||||
|
||||
---
|
||||
|
||||
## 狀態追蹤
|
||||
|
||||
| 項目 | 狀態 |
|
||||
|------|------|
|
||||
| 首席架構師 C2 裁示(暫停 git push) | ✅ 2026-04-04 |
|
||||
| adopt() 端點回傳 501 | ✅ 2026-04-04 |
|
||||
| ADR-057 起草 | ✅ 2026-04-05 |
|
||||
| 統帥批准 ADR-057 | ⏳ 待決 |
|
||||
| Gitea PR API 實作 | ⏳ 待批准後 |
|
||||
Reference in New Issue
Block a user