ADR 編號修正: - ADR-023 failure-auto-repair → ADR-028 - ADR-025 cicd-ai-integration → ADR-029 新增 ADR: - ADR-021: Playbook 更新驗證 - ADR-022: Sentry 整合架構 - ADR-027: Incident-Approval 同步 - ADR-028: 失敗自動修復閉環 - ADR-029: CI/CD AI 整合 (原 ADR-025) 更新: - ADR-018: LLM 測試策略狀態更新 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
197 lines
5.4 KiB
Markdown
197 lines
5.4 KiB
Markdown
# ADR-018: LLM 測試策略
|
||
|
||
| 屬性 | 值 |
|
||
|------|-----|
|
||
| **狀態** | 延緩 (Deferred) - 方案 A 先行 |
|
||
| **建立日期** | 2026-03-26 |
|
||
| **決策者** | 首席架構師 + 統帥 |
|
||
| **關聯** | Phase 12.3, #68, #69 |
|
||
|
||
## 背景
|
||
|
||
現有 LLM 測試 (`test_model_regression.py`, `test_prompt_validation.py`) 存在以下問題:
|
||
|
||
1. **字串精確匹配** - 檢查 "AWOOOI" 或 "kubectl" 是否存在
|
||
2. **LLM 非確定性** - 相同 prompt 產生不同輸出
|
||
3. **CI 依賴 Live LLM** - Ollama 不穩定導致 CI 失敗
|
||
4. **無語意理解** - 無法判斷「意思相近」
|
||
|
||
## 決策
|
||
|
||
採用 **三層測試策略**:
|
||
|
||
### Tier 1: 結構驗證 (CI 必跑)
|
||
|
||
```
|
||
測試目標: 輸出符合 Schema
|
||
執行時機: 每次 CI
|
||
Mock 策略: 使用錄製的 Golden Responses
|
||
```
|
||
|
||
**原則**:
|
||
- 驗證 JSON Schema,不驗證內容
|
||
- 驗證 kubectl 語法有效性,不驗證具體命令
|
||
- 驗證風險等級為有效 Enum,不驗證「應該是 HIGH」
|
||
|
||
**實作**:
|
||
```python
|
||
# ❌ 錯誤: 字串匹配
|
||
lambda r: "CRITICAL" in r.upper()
|
||
|
||
# ✅ 正確: Schema 驗證
|
||
class LLMProposalOutput(BaseModel):
|
||
risk_level: Literal["LOW", "MEDIUM", "HIGH", "CRITICAL"]
|
||
kubectl_command: str
|
||
reasoning: str
|
||
|
||
def validate_output(response: str) -> bool:
|
||
try:
|
||
parsed = LLMProposalOutput.model_validate_json(response)
|
||
return True
|
||
except ValidationError:
|
||
return False
|
||
```
|
||
|
||
### Tier 2: 屬性測試 (Nightly)
|
||
|
||
```
|
||
測試目標: 輸出符合不變量
|
||
執行時機: 每晚 / PR Merge
|
||
Mock 策略: Live LLM + 寬鬆閾值
|
||
```
|
||
|
||
**不變量範例**:
|
||
- kubectl 命令必須可解析 (用 shlex.split)
|
||
- 風險等級必須為有效值
|
||
- 回應長度 < 500 字
|
||
- 繁體中文比例 > 30% (用 regex 檢測)
|
||
|
||
**實作**:
|
||
```python
|
||
def test_kubectl_parseable(llm_response):
|
||
"""kubectl 命令必須可被 shell 解析"""
|
||
import shlex
|
||
cmd = extract_kubectl(llm_response)
|
||
if cmd:
|
||
tokens = shlex.split(cmd) # 若無法解析會拋 ValueError
|
||
assert tokens[0] == "kubectl"
|
||
|
||
def test_risk_level_valid(llm_response):
|
||
"""風險等級必須是有效值"""
|
||
risk = extract_risk_level(llm_response)
|
||
assert risk in {"LOW", "MEDIUM", "HIGH", "CRITICAL"}
|
||
```
|
||
|
||
### Tier 3: 語意品質 (Weekly/Manual)
|
||
|
||
```
|
||
測試目標: 輸出語意品質
|
||
執行時機: 每週 / 手動觸發
|
||
Mock 策略: Live LLM + Embedding 比對
|
||
```
|
||
|
||
**原則**:
|
||
- 使用 Embedding 計算語意相似度
|
||
- 與 Golden Response 比對,相似度 > 0.7 為通過
|
||
- 統計通過率,8/10 通過 = 整體通過
|
||
|
||
**實作**:
|
||
```python
|
||
async def test_semantic_similarity(llm_response, golden_response):
|
||
"""語意相似度測試"""
|
||
from sentence_transformers import SentenceTransformer
|
||
|
||
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
|
||
emb1 = model.encode(llm_response)
|
||
emb2 = model.encode(golden_response)
|
||
|
||
similarity = cosine_similarity(emb1, emb2)
|
||
assert similarity > 0.7, f"相似度 {similarity:.2f} 低於閾值 0.7"
|
||
```
|
||
|
||
## 測試分層總覽
|
||
|
||
| Tier | 執行時機 | Mock | 驗證內容 | 失敗處理 |
|
||
|------|---------|------|---------|---------|
|
||
| **1** | 每次 CI | Golden Response | Schema + 語法 | 阻擋合併 |
|
||
| **2** | Nightly | Live LLM | 不變量屬性 | 告警 + Issue |
|
||
| **3** | Weekly | Live LLM + Embedding | 語意品質 | 報告 + 趨勢追蹤 |
|
||
|
||
## 實作計畫
|
||
|
||
### Phase 1: Schema 強制 (1 天)
|
||
|
||
1. 修改 OpenClaw Prompt 強制 JSON 輸出
|
||
2. 建立 `LLMProposalOutput` Pydantic Model
|
||
3. 所有 LLM 回應經過 Schema 驗證
|
||
|
||
### Phase 2: Golden Response 錄製 (1 天)
|
||
|
||
1. 執行現有測試,錄製成功回應
|
||
2. 儲存為 `tests/fixtures/golden_responses.json`
|
||
3. CI 改用 Golden Response Mock
|
||
|
||
### Phase 3: 屬性測試重構 (2 天)
|
||
|
||
1. 將 lambda validators 改為屬性測試
|
||
2. 新增 kubectl 解析驗證
|
||
3. 新增風險等級 Enum 驗證
|
||
|
||
### Phase 4: 語意測試 (2 天)
|
||
|
||
1. 整合 sentence-transformers
|
||
2. 建立語意相似度基準線
|
||
3. 設定 Weekly 排程
|
||
|
||
## 替代方案
|
||
|
||
| 方案 | 優點 | 缺點 | 決定 |
|
||
|------|------|------|------|
|
||
| **Skip 測試** | 簡單 | 無品質保證 | ❌ |
|
||
| **增加 Retry** | 簡單 | 治標不治本 | ❌ |
|
||
| **降低閾值** | 簡單 | 漏檢問題 | ❌ |
|
||
| **三層策略** | 完整 | 實作成本 | ✅ |
|
||
|
||
## 風險
|
||
|
||
1. **Embedding 模型載入時間** - 使用輕量模型 (MiniLM)
|
||
2. **Golden Response 過時** - 定期更新機制
|
||
3. **語意閾值調校** - 初期從寬,逐步收緊
|
||
|
||
## 成功指標
|
||
|
||
- Tier 1 測試 100% 通過率 (CI 不再因 LLM 波動失敗)
|
||
- Tier 2 測試 > 90% 通過率 (Nightly)
|
||
- Tier 3 測試 > 80% 通過率 (Weekly)
|
||
- 無 LLM 相關 CI 失敗導致的開發阻塞
|
||
|
||
## 參考
|
||
|
||
- [LLM Evaluation Best Practices](https://www.anthropic.com/research/evaluations)
|
||
- [Property-Based Testing](https://hypothesis.readthedocs.io/)
|
||
- [Sentence Transformers](https://www.sbert.net/)
|
||
|
||
---
|
||
|
||
## 2026-03-26 決策更新
|
||
|
||
**狀態**: 延緩 (Deferred)
|
||
|
||
**實際採用方案**: 方案 A - 最小改動優先
|
||
|
||
| 項目 | 實作內容 |
|
||
|------|---------|
|
||
| 確定性參數 | `temperature: 0.0`, `seed: 42` |
|
||
| CI 分層 | LLM 測試移至 Nightly |
|
||
| 超時調整 | 300 秒 (CPU 推理) |
|
||
| 繁體中文 | System Prompt 強制繁中 |
|
||
|
||
**原因**:
|
||
1. 三層策略實作成本高 (~1000 行代碼)
|
||
2. 方案 A 已解決 CI 不穩定問題
|
||
3. 優先確保 CI 穩定,後續再評估是否需要完整框架
|
||
|
||
**評估文件**: `docs/evaluations/2026-03-26_llm_testing_evaluation.md`
|
||
|
||
**後續**: 若方案 A 效果良好,此 ADR 維持 Deferred;若仍有問題,再實施三層策略
|