diff --git a/docs/HARD_RULES.md b/docs/HARD_RULES.md index 8d9b251e..8d04d444 100644 --- a/docs/HARD_RULES.md +++ b/docs/HARD_RULES.md @@ -520,6 +520,51 @@ grep -rn "target_function_name" apps/ packages/ --include="*.py" --- +## 🔴🔴🔴 Prompt-Model 同步鐵律 — LLM Schema Drift 禁令 + +> 血的教訓 (2026-04-17): `prompts.py` 列了 6 個 `suggested_action` 值,`models/ai.py` 只有 4 個。 +> NemoTron 輸出 `"investigate"` → Pydantic ValidationError → `analysis_result = None` +> → 全系統 fallback,所有 Telegram 卡片顯示「待分析」,持續數週未被察覺。 + +### 強制鐵律 + +| 情境 | 必做動作 | +|------|---------| +| **修改 `prompts.py` 的 Enum/格式/欄位** | 同步檢查並更新 `models/ai.py` Pydantic Schema,確保兩者完全一致 | +| **新增 LLM 輸出欄位** | 先在 Pydantic 加上 `field_validator` 含 `fallback`,再改 Prompt | +| **任何接收 LLM JSON 的 Model** | 必須有 `mode="before"` validator 處理:大小寫/別名/未知值 fallback | + +### 三層防護缺一不可 + +```python +@field_validator("suggested_action", mode="before") +@classmethod +def normalize_suggested_action(cls, v): + if isinstance(v, str): + normalized = v.upper().replace("-", "_") + # 1. 別名映射 + alias_map = {"DIAGNOSE": "INVESTIGATE", "MONITOR": "OBSERVE", ...} + normalized = alias_map.get(normalized, normalized) + # 2. 未知值 fallback — 絕不讓 Pydantic 爆炸導致 analysis_result = None + try: + MyEnum(normalized) + return normalized + except ValueError: + return "NO_ACTION" # 安全預設值 + return v +``` + +### 禁止靜默死亡 + +Pydantic `ValidationError` 時,log 必須明確印出: +- 哪個欄位失敗 +- LLM 實際輸出了什麼值 +- `pydantic_validation_failed` event(已實作於 `openclaw.py`) + +違反此鐵律 = 破壞生產環境,必須立即 revert。 + +--- + ## 🔴🔴 Proactive Execution & Circuit Breaker — 主動執行與熔斷機制 > 整合 `feedback_proactive_execution.md`(主動執行,2026-04-05)與孤島開發停問規則。