# ADR-007:AI 學習雙寫規範(DB + KM 同步寫入) - **Status**: Accepted - **Date**: 2026-04-18 - **Decision Maker**: 統帥 - **Author**: Claude ## Context 在 EwoooC 三 Agent 分工架構中(ADR-001),所有 AI 產出(洞察、競品分析、對話、PPT 內容)最終需要沉澱。 若只寫入一個 sink,會導致: - 只寫 DB:RAG 無法語意搜尋過去的洞察 - 只寫 KM(vector):精確 period/sku 查詢無法命中 歷史教訓(2026-04-18): - 舊版 `openclaw_learning_service.py` 有 `store_insight()` API,但只寫入 DB,未向量化 - 導致 PPT 每次重新呼叫 AI 生成,歷史簡報失真(內容不一致) ## Decision **所有 AI 產出必須雙寫 DB + KM(pgvector)**,且為同一事務(atomic)。 ``` AI 產出(洞察/分析/對話) │ ▼ NemoTron: tool call store_insight() │ ├─→ PostgreSQL ai_insights(結構化欄位 + metadata) │ ↓ async └─→ Hermes: embed → pgvector embedding 欄位更新同一行 ``` **What 必須雙寫**: | 產出類型 | DB 表 | embedding 欄位 | |---|---|---| | PPT 洞察 | `ai_insights` | `embedding vector(1024)` | | 競品分析 | `ai_insights` | `embedding vector(1024)` | | 用戶對話記錄 | `conversations` | `embedding vector(1024)` | | 週報 meta-analysis | `ai_insights` | `embedding vector(1024)` | **What 不需雙寫**(不需語意搜尋): - Telegram 告警派發記錄(只做審計) - 原始爬蟲資料(`price_records`, `competitor_prices`) - 用戶操作 log(`audit_logs`) ## Consequences ### Positive - RAG 永遠能找到歷史洞察,PPT 內容跨時一致 - cache-aside 精確命中(同 period+type → 0.3s 回)+ 語意 fallback(近似查詢) ### Negative / Trade-offs - 每次寫入有兩個 I/O(DB + embedding),延遲增加約 100-200ms - Hermes 主機 OOM 時 embedding 會失敗(需 retry queue) ## Implementation Notes ```python # 雙寫入口點(atomic) async def store_insight_with_embedding(insight: InsightModel): # Step 1: 寫 DB(同步) insight_id = db.execute(INSERT INTO ai_insights ...) # Step 2: 排隊 embedding(異步,不阻塞主流) embedding_queue.put({"id": insight_id, "text": insight.text}) # Hermes worker(獨立進程) def embedding_worker(): while True: item = embedding_queue.get() vec = embed_text(item["text"]) # 呼叫 Ollama bge-m3 db.execute(UPDATE ai_insights SET embedding = :vec WHERE id = :id) ``` ## Related ADRs - ADR-001:三 Agent 分工(NemoTron 負責寫入觸發,Hermes 負責 embedding) - ADR-002:pgvector(KM 儲存層) - ADR-003:本地 embedding(embedding 引擎) - ADR-005:時間衰減(品質分數策略)