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>
107 lines
3.6 KiB
Markdown
107 lines
3.6 KiB
Markdown
# 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(採集層,與品質分數無關但同表)
|