Files
ewoooc/docs/adr/ADR-032-rag-autonomous-learning-loop.md
OoO c29ce83653 docs(adr): ADR-032 RAG 自主學習迴圈 + ADR-033 三護欄
Operation Ollama-First v5.0 / Phase 12 Wave 2 收尾

ADR-032 — RAG 自主學習迴圈
- 雙表分離:rag_query_log (audit) / learning_episodes (蒸餾池) / ai_insights (知識庫)
- Distiller 規則引擎(純 Hermes 零 LLM 成本)
- PromotionGate 4 階段晉升閘
- Telegram 反饋環(rag_feedback / promotion_review keyboard)
- feature flag RAG_ENABLED 預設 OFF
- V1-V4 驗收 SQL(命中率 / 晉升通過率 / 反饋分布 / embedding 一致性)

ADR-033 — RAG 三護欄(Owen v5.0 鐵律)
- 護欄 #1 Promotion Gate:強制反饋門檻,weight>=0.8 必經人工驗收
- 護欄 #2 Firecrawl 資源:Docker mem_limit:2g + chrome-reaper sidecar + 1.8GB 告警
- 護欄 #3 BGE-M3 一致性:embedding_signature SHA1[:12] + 啟動跨主機驗證
- 五案否決理由完整(包含「不要反饋按鈕」「不限資源」「:latest 接受漂移」)

Migration Plan 對照:
   migration 026/028 schema + service 已落地
   Phase 12+ 補:embedding 寫入 / worker cron / Telegram 推播 / Firecrawl 部署 / signature 回填

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 00:01:19 +08:00

250 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-032: RAG 自主學習迴圈 — Distiller + PromotionGate + 反饋環
- **Status**: Accepted
- **Date**: 2026-05-03
- **Decision Maker**: 統帥
- **Author**: Operation Ollama-First v5.0Phase 11 落地後追認)
- **Supersedes**: 無
- **Related**: ADR-002pgvector 唯一向量庫、ADR-007持久化雙寫、ADR-028LLM 路由統一準則、ADR-029Hermes-First 雙塔分工、ADR-033RAG 三護欄)
---
## Context
戰役 v5.0 Wave 1 完成後momo-pro 已具備 ai_calls / mcp_calls / ai_call_budgets 觀測層,但仍是「無狀態 LLM 用戶」 — 每次 Hermes/OpenClaw 提問都重新燒 token重複問題沒被攔截。
Phase 0 audit 發現:
- 統帥 Telegram 答題 30%+ 是同一類問題「PChome 補貼」「家電促銷檔期」「SKU 競爭力分析」)
- ai_insights 已累積 14k+ 筆pgvector + bge-m3但**沒有 RAG 攔截層**,全部走 LLM
- 預估30% 流量可被 RAG cache 攔截 = 月省 ~9M Hermes/OpenClaw tokens
**Owen 提出三大風險**v5.0 強化護欄):
1. **學習污染**LLM 幻覺自動進 RAG → 正反饋錯誤循環ADR-033 護欄 #1
2. **資源消耗**:自建 Firecrawl Playwright 池吃 188 主機記憶體ADR-033 護欄 #2
3. **Embedding 一致性**bge-m3 floating tag → RAG 召回率悄悄退化ADR-033 護欄 #3
本 ADR 鎖定 **「LLM 結果 → 蒸餾 → Promotion Gate → ai_insights → RAG」自主學習迴圈** 的設計與護欄。
---
## Decision
### 1. 雙表分離設計
| 表 | 用途 | 保留期 | PII 等級 |
|---|---|---|---|
| `rag_query_log` (migration 027) | 每次 RAG 查詢的 audit log | 90 天 | 中query_text 可能含用戶問題)|
| `learning_episodes` (migration 028) | LLM/MCP 結果蒸餾池 | 永久(蒸餾溯源)| 低distilled_text 已過 PII redact|
| `ai_insights` (既有) | 已驗收的知識庫 | 永久 | 經 PromotionGate 過濾 |
### 2. 自主學習迴圈
```
┌─────────────────────────────────────────────────────────────┐
│ LLM 呼叫Hermes/OpenClaw
│ ↓ │
│ RAG-first 攔截cosine >= 0.85 命中) │
│ ↓ 命中 ↓ miss │
│ return synthesize LLM 跑 │
│ (rag_hit=true) ↓ │
│ Distiller 蒸餾 │
│ ↓ │
│ learning_episodes (pending) │
│ ↓ │
│ PromotionGate 4 階段 │
│ ↓ │
│ ai_insights (approved) │
│ ↓ │
│ 下次 RAG 查得到 │
└─────────────────────────────────────────────────────────────┘
```
### 3. Distiller 規則引擎(純 Hermes零 LLM 成本)
```python
QUALITY_RULES = {
'mcp_result': lambda c: 0.8 if len(c) > 200 and len(keywords(c)) >= 2 else 0.4,
'llm_json_ok': lambda c: 0.9, # 結構化 JSON + status='ok'
'llm_text': lambda c: 0.6 if has_zh_numbers(c) else 0.3,
'thumb_up': lambda _: 1.0, # 用戶 👍 反饋
'thumb_down': lambda _: 0.0, # 負樣本不晉升
}
```
**為何不用 LLM 蒸餾?** 避免循環燒錢Distiller 跑頻率高,用 LLM 等於每次 RAG miss 都燒兩次)。
### 4. PromotionGate 4 階段晉升閘v5.0 護欄 #1
```python
class PromotionGate:
STAGE_1_AUTO_QUALITY = 0.7 # 蒸餾品質分
STAGE_2_HALLUCINATION_RULES = [
'可能/也許/我猜測 + 缺具體數字',
'自相矛盾(同段含 A=X 又 A=Y',
'引用不存在 SKU/品牌(查 DB',
]
STAGE_3_DEDUP_THRESHOLD = 0.95 # cosine 相似度
STAGE_4_HUMAN_REVIEW_WEIGHT = 0.8 # 高權重必經 👍/👎
```
**鐵律**weight ≥ 0.8 的 episode **不能跳 Stage 4**,必須推 Telegram 等 24h 反饋。
| 結果 | promotion_status |
|---|---|
| 4 階段全過 | `approved` → 寫入 ai_insights |
| Stage 1 失敗 | `rejected_quality` |
| Stage 2 失敗 | `rejected_hallucination` |
| Stage 3 失敗 | `rejected_duplicate` |
| Stage 4 推送等待 | `awaiting_review` |
| 24h 無反饋 | `expired`weight 降為 0.5,不晉升但保留)|
| 用戶 👎 | `rejected_human` |
### 5. Telegram 反饋環(強制晉升門檻)
```python
# services/telegram_templates.py
def rag_feedback_keyboard(rag_query_log_id: int) -> dict:
return {'inline_keyboard': [[
{'text': '👍 有用', 'callback_data': f'rag_fb:{id}:5'},
{'text': '👎 沒用', 'callback_data': f'rag_fb:{id}:1'},
]]}
def promotion_review_keyboard(episode_id: int) -> dict:
return {'inline_keyboard': [[
{'text': '✅ 通過晉升', 'callback_data': f'pg_ok:{id}'},
{'text': '❌ 拒絕', 'callback_data': f'pg_no:{id}'},
]]}
```
`routes/openclaw_bot_routes.py` 三組 callback handler 已就位Phase 11 落地)。
### 6. Feature Flag 灰度
```python
RAG_ENABLED = os.getenv('RAG_ENABLED', 'false').lower() == 'true'
RAG_DEFAULT_THRESHOLD = float(os.getenv('RAG_DEFAULT_THRESHOLD', '0.85'))
RAG_DEFAULT_TOP_K = int(os.getenv('RAG_DEFAULT_TOP_K', '5'))
```
**預設 OFF**,戰前部署後行為與 v4.x 完全相同。灰度開啟條件:
1. ANTHROPIC_API_KEY 已設Phase 7 已備)
2. learning_episodes 累積 100+ 筆
3. RAG_ENABLED=true + threshold=0.90(保守起步)
4. 1 週後 feedback_score ≥ 4 比率 > 70% → threshold 降至 0.85
### 7. 失敗安全fire-and-forget 哲學)
| 失敗模式 | 行為 |
|---|---|
| DB 寫入 rag_query_log 失敗 | 主流程不爆logger.warning |
| embedding 失敗 | 不查 DB 直接 fallback LLM |
| signature 不一致 | log warning + 不採該筆 hit |
| Distiller 寫 learning_episodes 失敗 | LLM 結果照樣回 caller |
| PromotionGate Stage 1-3 失敗 | episode 留 learning_episodes不晉升即可無 DB 副作用)|
---
## Alternatives Considered
| 方案 | 否決理由 |
|---|---|
| **A. 直接 ai_insights 寫入(無蒸餾池)** | LLM 幻覺直接污染知識庫,無 PromotionGate 阻擋(核心風險)|
| **B. LLM 蒸餾(用 Gemini 寫 distill prompt** | 循環燒錢:每次 RAG miss → LLM call → 又燒 LLM 蒸餾 = 2× 成本 |
| **C. 純 push 不 pull無反饋按鈕** | 統帥無法糾正幻覺正反饋錯誤循環Owen 強調的痛點)|
| **D. 跳過 dedupStage 3** | ai_insights 將累積大量重複RAG 查詢無謂耗時 |
| **E. 用 ChromaDB / Qdrant 替代 pgvector** | 違反 ADR-002pgvector 唯一)+ 增加運維面 |
---
## Consequences
### 正面5
1. **重複問題攔截**:預估月省 ~9M Hermes/OpenClaw tokensHermes 流量 -30%
2. **自主學習**:每次 LLM 結果都進蒸餾池,知識庫持續成長
3. **錯誤可糾正**:👎 反饋直接降權,避免幻覺循環污染
4. **零 LLM 蒸餾成本**Distiller 純規則引擎
5. **PII 安全**query_text 截 4KB + human_approver SHA1[:8]
### 負面3
1. **複雜度↑**:兩表 + 4 階段閘 + 反饋環,新人理解曲線陡
2. **Stage 4 人工驗收延遲**:高權重 episode 必須等 24h 才能晉升
3. **embedding 寫入路徑暫缺**(已知 limitationStage 3 dedup 待 Phase 12+ 補
### 風險4
1. **Distiller 規則漂移**:規則引擎可能漏判幻覺 → mitigate by Stage 4 人工驗收
2. **Stage 4 人工疲勞**:統帥不可能 24h 看 Telegram → mitigate by `expired` 自動降級
3. **ai_insights 膨脹**:學習迴圈累積快 → mitigate by Stage 3 dedupPhase 12+ 啟用)
4. **PromotionGate worker cron 未掛**(已知):需 Phase 12+ 排程任務
---
## Verification
### V1RAG 攔截率(部署 1 週後)
```sql
SELECT
COUNT(*) FILTER (WHERE saved_call) AS hit_count,
COUNT(*) AS total,
ROUND(100.0 * COUNT(*) FILTER (WHERE saved_call) / COUNT(*), 1) AS hit_pct
FROM rag_query_log
WHERE queried_at > NOW() - INTERVAL '7 days';
-- 期望 hit_pct >= 25%
```
### V2晉升通過率
```sql
SELECT promotion_status, COUNT(*)
FROM learning_episodes
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY promotion_status;
-- 期望 approved + awaiting_review 占 >50%rejected_hallucination < 10%
```
### V3反饋分布
```sql
SELECT feedback_score, COUNT(*)
FROM rag_query_log
WHERE feedback_score IS NOT NULL
GROUP BY feedback_score;
-- 期望 score=5 比率 > 60%
```
### V4Embedding 一致性v5.0 護欄 #3
```sql
SELECT embedding_signature, COUNT(*)
FROM ai_insights
WHERE embedding IS NOT NULL
GROUP BY embedding_signature;
-- 期望單一簽名(多個 = 模型版本漂移,需處理)
```
---
## Migration Plan
| Phase | 工作 | 狀態 |
|---|---|---|
| 11.1 | rag_query_log + learning_episodes schema | ✅ migration 027/028 commit 2f20d8d |
| 11.2 | rag_service.py + learning_pipeline.py | ✅ commit c7d6db3 |
| 11.3 | Hermes/OpenClaw RAG-first 整合 | ✅ commit c7d6db3 |
| 11.4 | Telegram 反饋按鈕 + callback | ✅ commit c7d6db3 |
| 11.5 | learning_episodes.embedding 寫入 | ⏳ Phase 12+ |
| 11.6 | PromotionGate worker cron 掛排程 | ⏳ Phase 12+ |
| 11.7 | awaiting_review Telegram 推播 | ⏳ Phase 12+callback 已就位)|
| 11.8 | RAG_ENABLED=true 灰度啟用 | ⏳ 1 週觀察期後 |
---
## References
- `migrations/027_create_rag_query_log.sql`
- `migrations/028_create_learning_episodes.sql`
- `services/rag_service.py`532 行)
- `services/learning_pipeline.py`750 行)
- `tests/test_rag_service.py` + `test_learning_pipeline.py` + `test_promotion_gate.py`70 unit tests
- `docs/phase11_db_design_20260503.md`
- ADR-002pgvector 唯一向量庫)
- ADR-007雙寫保證
- ADR-029Hermes-First 雙塔分工)
- ADR-033RAG 三護欄)— 即將補