feat: Phase 7D — confidence decay on unreferenced cold memories
Daily job multiplies confidence by 0.97 (~2-month half-life) for
active memories with reference_count=0 AND idle > 30 days. Below
0.3 → auto-supersede with audit. Reversible via reinforcement
(which already bumps confidence back up).
Rationale: stale memories currently rank equal to fresh ones in
retrieval. Without decay, the brain accumulates obsolete facts
that compete with fresh knowledge for context-pack slots. With
decay, memories earn their longevity via reference.
- decay_unreferenced_memories() in service.py (stdlib-only, no cron
infra needed)
- POST /admin/memory/decay-run endpoint
- Nightly Step F4 in batch-extract.sh
- Exempt: reinforced (refcount > 0), graduated, superseded, invalid
- Audit row per supersession ("decayed below floor, no references"),
actor="confidence-decay". Per-decay rows skipped (chatty, no
human value — status change is the meaningful signal).
- Configurable via env: ATOCORE_DECAY_* (exposed through endpoint body)
Tests: +13 (basic decay, reinforcement protection, supersede at floor,
audit trail, graduated/superseded exemption, reinforcement reversibility,
threshold tuning, parameter validation, cross-run stacking).
401 → 414.
Next in Phase 7: 7C tag canonicalization (weekly), then 7B contradiction
detection.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1517,6 +1517,39 @@ def api_graduation_status() -> dict:
|
||||
return out
|
||||
|
||||
|
||||
class DecayRunBody(BaseModel):
|
||||
idle_days_threshold: int = 30
|
||||
daily_decay_factor: float = 0.97
|
||||
supersede_confidence_floor: float = 0.30
|
||||
|
||||
|
||||
@router.post("/admin/memory/decay-run")
|
||||
def api_decay_run(body: DecayRunBody | None = None) -> dict:
|
||||
"""Phase 7D — confidence decay on unreferenced memories.
|
||||
|
||||
One-shot run (daily cron or on-demand). For active memories with
|
||||
reference_count=0 and idle for >30 days: multiply confidence by
|
||||
0.97 (~2-month half-life). Below 0.3 → auto-supersede with audit.
|
||||
|
||||
Reversible: reinforcement bumps confidence back up. Non-destructive:
|
||||
superseded memories stay queryable with status filter.
|
||||
"""
|
||||
from atocore.memory.service import decay_unreferenced_memories
|
||||
|
||||
b = body or DecayRunBody()
|
||||
result = decay_unreferenced_memories(
|
||||
idle_days_threshold=b.idle_days_threshold,
|
||||
daily_decay_factor=b.daily_decay_factor,
|
||||
supersede_confidence_floor=b.supersede_confidence_floor,
|
||||
)
|
||||
return {
|
||||
"decayed_count": len(result["decayed"]),
|
||||
"superseded_count": len(result["superseded"]),
|
||||
"decayed": result["decayed"][:20], # cap payload
|
||||
"superseded": result["superseded"][:20],
|
||||
}
|
||||
|
||||
|
||||
@router.post("/admin/memory/extend-reinforced")
|
||||
def api_extend_reinforced() -> dict:
|
||||
"""Phase 6 C.3 — batch transient-to-durable extension.
|
||||
|
||||
Reference in New Issue
Block a user