Memory

Relation Decay

Graph memory relationships decay over time. Edge weights decrease based on a configurable half-life, allowing stale or less-relevant connections to naturally fade into the background. This keeps the knowledge graph focused on current, actively observed relationships rather than accumulating stale connections indefinitely. Decay runs as a weekly cron job every Sunday at 4:30 AM by default.

How decay works

Each graph edge carries a weight between 0 and 1 that represents the current confidence in the relationship. Decay applies an exponential half-life function to each edge based on how many days have passed since the edge was last observed:

text
weight(t) = initial_weight * 0.5 ^ (days_elapsed / halfLifeDays)

# Example with halfLifeDays = 90:
# After 0 days:  weight = 1.00
# After 45 days: weight = 0.71
# After 90 days: weight = 0.50
# After 180 days: weight = 0.25

When an edge is re-observed in a new conversation — for example, a user mentions that Alex is still working on a project — the edge weight is reset to 1.0 and the lastObserved timestamp is updated. This means actively discussed relationships never decay, only silent ones do.

Edges that fall below minimumWeight are excluded from retrieval results but are not deleted. They remain in the database as auditable history and will recover if the relationship is re-observed.

Configuration

yaml
settings:
  memory:
    decay:
      enabled: true
      halfLifeDays: 90          # Time for edge weight to halve
      minimumWeight: 0.10       # Edges below this are excluded from retrieval
      schedule: "30 4 * * 0"   # Weekly Sunday at 4:30 AM (cron syntax)

The schedule uses standard cron syntax. To run decay daily instead of weekly, change the schedule to "30 4 * * *". To disable the automatic schedule and trigger decay manually only, set enabled: false.

Viewing decayed edges

Query all edges whose weight has fallen below minimumWeight to audit what is currently excluded from retrieval:

bash
GET /memory/graph/edges?decayed=true

# Returns all edges whose current weight is below minimumWeight
# Response:
# [
#   { "from": "Alex", "to": "ProjectAlpha", "type": "works_on", "weight": 0.08, "lastObserved": "2025-09-01" },
#   { "from": "ProjectAlpha", "to": "OldFramework", "type": "uses", "weight": 0.04, "lastObserved": "2025-06-15" }
# ]

Manually triggering decay

Run a decay pass on demand without waiting for the scheduled job. Use dryRun: true to preview which edges would be affected before committing:

bash
POST /memory/graph/decay
{
  "dryRun": false
}

# Response:
# { "processed": 843, "decayed": 31, "belowMinimum": 9, "pinned": 12 }

Preventing decay on specific edges

Mark individual edges as pinned: true to exempt them from all decay calculations. Pinned edges maintain their weight indefinitely regardless of how long ago they were last observed. Use this for structural relationships that you know are permanent — like a module's dependency on a core library.

bash
# In your graph edge configuration or when writing an edge via the API:
POST /memory/graph/edges
{
  "from": "CoreAuthModule",
  "to": "JWTLibrary",
  "type": "depends_on",
  "weight": 1.0,
  "pinned": true
}