# 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 字元截斷的碰撞監控機制
