# ADR-005:KM 品質分數加入時間衰減 - **Status**: Accepted - **Date**: 2026-04-18 - **Decision Maker**: 統帥(顧問補充建議第 3 條) - **Author**: Claude ## Context `ai_insights` 表中的 KM 記錄有一個「品質分數(`avg_quality`)」欄位,由用戶的 👍/👎 回饋計算。 問題:原始設計只取回饋的 **靜態平均分**,沒有考慮時間因素。 **失效場景**: - 半年前的「換季保濕策略洞察」被判定為高品質(當時收到 5 個 👍) - 但今天是夏天,RAG 仍優先抓取它 → OpenClaw 回答品質下降 - 競品策略也在演化,6 個月前的競品分析今日已完全無效 ## Decision **KM 品質分數採用時間衰減公式**: ``` Effective_Score = Base_Score × exp(−λ × days_since_created) ``` | 參數 | 值 | 說明 | |---|---|---| | `λ` (decay_rate) | `0.01` | 半衰期約 70 天(ln(2)/0.01) | | `Base_Score` | `avg_quality`(0~1) | 原始回饋分數平均 | | `days_since_created` | `NOW() - created_at` | 洞察建立至今天數 | **實際效果**: | 天數 | Effective Score(假設 Base=1.0) | |---|---| | 0 天(剛建立) | 1.00 | | 30 天 | 0.74 | | 70 天 | 0.50(半衰期) | | 180 天 | 0.16 | | 365 天 | 0.03(幾乎不被選中) | **例外**(不套時間衰減): - `insight_type = 'structural'`(結構性規則,如「MOMO 商品代碼格式永遠是 I...」) - `insight_type = 'constitutional'`(專案憲法類) - 手動標記 `decay_exempt = true` 的記錄 ## Alternatives Considered | 方案 | 拒絕原因 | |---|---| | 固定天數截斷(只看近 90 天) | 硬截斷損失部分有效的長期記憶 | | 不做衰減(原始設計) | 舊記錄永遠佔據 RAG top 5,導致回答跑偏 | | 每隔一段時間批次刪除老記錄 | 不可逆,人工操作風險大 | ## Consequences ### Positive - RAG 自動偏好新鮮知識,回答隨業務演化自我更新 - 舊洞察不消失(審計/回溯仍可查),只是自然淡出 ranking - 半衰期可調(λ 改小 = 記得更久,λ 改大 = 遺忘更快) ### Negative / Trade-offs - `ORDER BY` 子句需在 SQL 中即時計算 exponential,有輕微效能影響 - 緩解:pgvector HNSW 先做 ANN 近似過濾,再在 Python 層做衰減重排 - 開發者需理解「分數不是靜態的」,debug 時要考慮時間因素 ## Implementation Notes ```sql -- RAG 查詢時計算有效分數 SELECT insight_text, created_at, avg_quality, avg_quality * EXP(-0.01 * EXTRACT(EPOCH FROM (NOW() - created_at)) / 86400.0) AS effective_score FROM ai_insights WHERE insight_type NOT IN ('structural', 'constitutional') AND decay_exempt IS NOT TRUE ORDER BY embedding <-> :query_vector -- 先做語意過濾 LIMIT 20; -- 然後在 Python 層取 effective_score 前 5 ``` ```python # services/openclaw_learning_service.py import math from datetime import datetime, timezone def compute_effective_score(base_score: float, created_at: datetime, decay_rate: float = 0.01) -> float: """時間衰減品質分數""" days = (datetime.now(timezone.utc) - created_at.replace(tzinfo=timezone.utc)).days return base_score * math.exp(-decay_rate * days) ``` **新欄位需求**(Migration 005): ```sql ALTER TABLE ai_insights ADD COLUMN decay_exempt BOOLEAN DEFAULT FALSE; CREATE INDEX ON ai_insights (insight_type, decay_exempt); ``` ## Related ADRs - ADR-001:三 Agent 分工(OpenClaw 應用層 RAG 的取數來源) - ADR-002:pgvector(儲存層,含 HNSW 索引) - ADR-003:本地 embedding(採集層,與品質分數無關但同表)