feat(p3.2+adr-100): Model Version Tracker + SLO 自治 + KB rot cleaner
Wave 8 P3.2 模型版本追蹤 + ADR-100 SLO 自我治理 + 配套: P3.2 — Model Version Tracking: - model_version_probe.py (268 行) — 探測 Ollama / OpenRouter 等 provider 的 model version - model_version_tracker.py (101 行) — 對齊 PG provider_version_history 表 - migrations/p3_2_provider_version_history.sql + rollback — 25 行 schema - db/models.py +32 行 — ProviderVersionHistory ORM ADR-100 — AI 自主化 SLO: - docs/adr/ADR-100-ai-autonomous-slo.md (167 行) — 飛輪 SLO 設計與閾值 - ops/monitoring/slo-rules.yml (254 行) — Prometheus SLO recording rules + alerts - ops/monitoring/tests/test_slo_rules.yaml (242 行) — promtool unit tests 整合修改: - main.py +72 行 — Lifespan 啟動 model_version_probe + KB rot cleaner schedule - gitea_webhook.py +45 行 — webhook 接收 model 版本變化通知 - ci_auto_repair.py / evidence_snapshot.py / pre_decision_investigator.py — 配合接線 新測試: - test_kb_rot_cleaner_schedule.py (120 行) — 9 tests pass - test_slo_rules.yaml — promtool 驗收 Tests: 9 passed (test_kb_rot_cleaner_schedule) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Multiple Engineers (P3.2 + ADR-100) <noreply@anthropic.com>
This commit is contained in:
@@ -509,6 +509,51 @@ async def lifespan(_app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
except Exception as e:
|
||||
logger.warning("knowledge_decay_loop_schedule_failed", error=str(e))
|
||||
|
||||
# ADR-087 Phase 6: KB 腐爛清理(月度)— 每月 1 號 03:00 台北時間
|
||||
# 掃描 knowledge_entries 中腐爛條目(廢棄 K8s API / Prometheus pattern / 180d 未引用)
|
||||
# 2026-04-27 P3.1-T3 by Claude
|
||||
try:
|
||||
from src.utils.timezone import now_taipei
|
||||
from datetime import datetime as _dt
|
||||
|
||||
async def _run_kb_rot_cleaner_loop() -> None:
|
||||
from src.jobs.kb_rot_cleaner import get_kb_rot_cleaner
|
||||
import asyncio as _asyncio
|
||||
while True:
|
||||
try:
|
||||
now = now_taipei()
|
||||
# 計算下次月初 3 點(台北時間)
|
||||
if now.day == 1 and now.hour < 3:
|
||||
next_run = now.replace(hour=3, minute=0, second=0, microsecond=0)
|
||||
elif now.month == 12:
|
||||
next_run = now.replace(
|
||||
year=now.year + 1, month=1, day=1,
|
||||
hour=3, minute=0, second=0, microsecond=0,
|
||||
)
|
||||
else:
|
||||
next_run = now.replace(
|
||||
month=now.month + 1, day=1,
|
||||
hour=3, minute=0, second=0, microsecond=0,
|
||||
)
|
||||
sleep_sec = (next_run - now).total_seconds()
|
||||
logger.info("kb_rot_cleaner_next_run", next_run=next_run.isoformat(), sleep_sec=int(sleep_sec))
|
||||
await _asyncio.sleep(sleep_sec)
|
||||
try:
|
||||
result = await get_kb_rot_cleaner().run()
|
||||
logger.info("kb_rot_cleaner_completed", stale_count=result.stale_count, total=result.total_scanned)
|
||||
except Exception as _e:
|
||||
logger.exception("kb_rot_cleaner_failed", error=str(_e))
|
||||
except _asyncio.CancelledError:
|
||||
break
|
||||
except Exception as _e:
|
||||
logger.exception("kb_rot_cleaner_loop_error", error=str(_e))
|
||||
await _asyncio.sleep(3600) # 1h 後重試
|
||||
|
||||
asyncio.create_task(_run_kb_rot_cleaner_loop())
|
||||
logger.info("kb_rot_cleaner_loop_scheduled", trigger="monthly_day1_03h_taipei")
|
||||
except Exception as e:
|
||||
logger.warning("kb_rot_cleaner_loop_schedule_failed", error=str(e))
|
||||
|
||||
# ADR-083 Phase 3: Fine-tune JSONL 匯出(每週)— EvidenceSnapshot × AgentSession → JSONL
|
||||
# 2026-04-15 ogt + Claude Sonnet 4.6(亞太): Phase 3 初始建立
|
||||
try:
|
||||
@@ -590,6 +635,33 @@ async def lifespan(_app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
except Exception as e:
|
||||
logger.warning("ollama_failover_system_start_failed", error=str(e))
|
||||
|
||||
# 2026-04-27 P3.2.2 by Claude — AI Provider 版本追蹤(每 1 小時)
|
||||
# 探測 5 Provider(ollama/ollama_188/gemini/claude/openclaw_nemo)版本
|
||||
# 寫入 ai_provider_version_history;版本變更時 log warning,P3.2.3 alerter 後續整合
|
||||
try:
|
||||
async def _run_model_version_tracker_loop() -> None:
|
||||
from src.services.model_version_tracker import get_model_version_tracker
|
||||
tracker = get_model_version_tracker()
|
||||
while True:
|
||||
try:
|
||||
await asyncio.sleep(3600) # 每 1 小時
|
||||
result = await tracker.run_probe_cycle()
|
||||
logger.info(
|
||||
"model_version_probe_cycle_done",
|
||||
probed=result["probed"],
|
||||
changed=result["changed"],
|
||||
)
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception as _loop_err:
|
||||
logger.exception("model_version_tracker_loop_error", error=str(_loop_err))
|
||||
await asyncio.sleep(60) # 錯誤後 1 分鐘重試
|
||||
|
||||
asyncio.create_task(_run_model_version_tracker_loop())
|
||||
logger.info("model_version_tracker_scheduled", interval_sec=3600)
|
||||
except Exception as e:
|
||||
logger.warning("model_version_tracker_schedule_failed", error=str(e))
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown
|
||||
|
||||
Reference in New Issue
Block a user