Some checks failed
CD Pipeline / deploy (push) Failing after 59s
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml) - 部署模式: rsync Python 檔案至 188 → docker restart (volume mount) - Dockerfile/requirements 變動時自動重建 Docker image - 部署通知: Telegram (開始/成功/失敗) - 健康檢查: https://mo.wooo.work/health (最多 5 次重試) - 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.6 KiB
3.6 KiB
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
-- 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
# 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):
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(採集層,與品質分數無關但同表)