Decision Memory
Part of the expertise layer. Read the overview →
SmartMemory tracks decisions as first-class memory objects with confidence scoring, provenance chains, and lifecycle management. Decisions capture not just what was decided, but why and how confident the system is.
Why Decision Memory Matters
Agents and users make decisions constantly. Without tracking them:
- The same decision gets re-evaluated repeatedly
- Contradictory decisions go unnoticed
- There's no audit trail for "why did we choose X?"
Decision Memory solves this by storing decisions with full provenance, confidence scoring, and conflict detection.
Decision Types
| Type | Description | Example |
|---|---|---|
inference | Conclusion drawn from evidence | "The performance issue is caused by N+1 queries" |
preference | Stated or detected preference | "User prefers dark mode for coding" |
classification | Categorization of something | "This issue is a security vulnerability" |
choice | Explicit selection between options | "We chose PostgreSQL over MySQL" |
belief | Held belief or conviction | "Microservices are overused for small teams" |
policy | Rule or guideline to follow | "Always write tests before implementation" |
Lifecycle
Decisions follow a lifecycle:
graph LR
A[Created] --> B[Active]
B --> C[Superseded]
B --> D[Retracted]
B --> B
- Active: Current, valid decision
- Superseded: Replaced by a newer decision (linked via
SUPERSEDESedge) - Retracted: Withdrawn, no longer valid
Confidence Scoring
Each decision has a confidence score (0.0–1.0) that evolves over time:
from smartmemory.models.decision import Decision
decision = Decision(
content="PostgreSQL is better for our use case",
decision_type="choice",
confidence=0.8
)
# Supporting evidence increases confidence (diminishing returns)
decision.reinforce("benchmark_result_id")
# confidence: 0.8 → 0.82
# Contradicting evidence decreases confidence (15% penalty)
decision.contradict("mysql_benchmark_id")
# confidence: 0.82 → 0.697Automatic Decay
The DecisionConfidenceEvolver applies time-based decay:
- Decisions inactive for 30+ days start losing confidence (5% per cycle)
- Decisions below 0.1 confidence are automatically retracted
- Recently reinforced decisions are protected from decay
Belief & Contest (Dempster-Shafer)
The single confidence scalar can't distinguish a contested decision (strong
support and strong contradiction) from an unevidenced one — both look like
"medium confidence". SmartMemory adds Dempster-Shafer evidence belief over the
frame {hold, retract} to separate them:
| Field | Meaning |
|---|---|
belief_hold | committed lower bound that the decision should stand |
plausibility_hold | upper bound (adds the ignorance band) |
ignorance | how much is genuinely unknown (no evidence either way) |
contest | how disputed the decision is — high only when support and contradiction are both strong |
contest is included in decision API responses (so SDK/UI consumers get it
without recomputation); the belief reads are computed properties. A decision with
3 supporting and 3 contradicting pieces of evidence reports contest ≈ 0.77 while
an untouched decision reports contest = 0.0 — even though both show the same
stability. See the
decision belief contract.
Creating Decisions
Programmatic
from smartmemory.decisions.manager import DecisionManager
manager = DecisionManager(smart_memory)
decision = manager.create(
content="We should use Redis for caching",
decision_type="choice",
confidence=0.85,
domain="infrastructure",
tags=["caching", "architecture"],
evidence_ids=["benchmark_123", "analysis_456"],
)Via Maya Commands
/decide We should use Redis for caching
/decide I prefer Python for backend work
/decide Always run tests before deployingVia Extraction
The DecisionExtractor detects decisions in natural text:
from smartmemory.plugins.extractors.decision import DecisionExtractor
extractor = DecisionExtractor()
result = extractor.extract("""
I decided to use PostgreSQL for the new project.
I believe microservices are overkill for our team size.
We should always write integration tests for API endpoints.
""")
# result['decisions'] contains 3 detected decisions:
# - choice: "use PostgreSQL for the new project"
# - belief: "microservices are overkill for our team size"
# - policy: "always write integration tests for API endpoints"It also extracts from reasoning traces:
from smartmemory.models.reasoning import ReasoningTrace
trace = ReasoningTrace(steps=[...])
decisions = extractor.extract_from_trace(trace)
# Decisions linked to the trace via PRODUCED edgeQuerying Decisions
List Active Decisions
from smartmemory.decisions.queries import DecisionQueries
queries = DecisionQueries(smart_memory)
# All active decisions
decisions = queries.get_active_decisions()
# Filtered by domain and type
decisions = queries.get_active_decisions(
domain="infrastructure",
decision_type="choice",
min_confidence=0.7,
)Search by Topic
decisions = queries.get_decisions_about(topic="database")Maya Commands
/beliefs # List all active decisions
/beliefs databases # Search decisions about databases
/why PostgreSQL # Explain why a decision was madeProvenance Chains
Every decision tracks its origin:
provenance = queries.get_decision_provenance(decision_id)
# Returns:
# {
# "decision": Decision(...),
# "reasoning_trace": ReasoningTrace(...) or None,
# "evidence": [MemoryItem(...), ...],
# "superseded": [Decision(...), ...],
# }Causal Chains
Traverse cause-and-effect relationships:
chain = queries.get_causal_chain(
decision_id,
direction="both", # "causes", "effects", or "both"
max_depth=3,
)
# Returns:
# {
# "decision": Decision(...),
# "causes": [...], # What led to this decision
# "effects": [...], # What this decision caused
# }Conflict Detection
Find decisions that may contradict each other:
conflicts = manager.find_conflicts(decision)
# Candidate gate: semantic search + content-overlap heuristic.
# Results are ranked by pairwise contest severity (how confidently both decisions
# assert their contradictory claims), most-contested first.
# Drop weakly-held conflicts:
conflicts = manager.find_conflicts(decision, min_contest=0.3)The REST route POST /memory/decisions/{id}/conflicts accepts the same
min_contest query parameter.
Superseding Decisions
When a decision is replaced:
new_decision = Decision(
content="Actually, MongoDB fits better for our document-heavy workload",
decision_type="choice",
confidence=0.9,
)
result = manager.supersede(
old_decision_id,
new_decision,
reason="New benchmarks show MongoDB handles our access patterns better",
)
# Old decision marked as "superseded"
# SUPERSEDES edge created between new → oldRetracting Decisions
When a decision is no longer valid:
manager.retract(decision_id, reason="Requirements changed — JWT no longer needed")
# Decision status set to "retracted"
# Retracted decisions are excluded from active queriesGraduation from Plans
When a Plan completes, it can optionally graduate to a decision record — preserving the task decomposition as a permanent "we decided to do X" record:
from smartmemory.plans import PlanManager
plan_manager = PlanManager(memory)
result = plan_manager.create("Auth redesign", tasks=[...])
# ... tasks completed ...
# Graduate creates a decision with DERIVED_FROM edge back to plan
decision_id = plan_manager.complete(result["plan_id"], graduate=True)The graduated decision has decision_type="choice", source_type="imported", and an evidence link to the original plan.
Graph Edges
Decision Memory introduces these edge types:
| Edge | From → To | Meaning |
|---|---|---|
PRODUCED | ReasoningTrace → Decision | Trace generated this decision |
DERIVED_FROM | Decision → Memory | Decision based on this evidence |
SUPERSEDES | Decision → Decision | New decision replaces old |
CONTRADICTS | Decision → Decision | Decisions conflict |
INFLUENCES | Decision → Memory | Decision affects other memories |
Configuration
DecisionExtractor
from smartmemory.plugins.extractors.decision import DecisionExtractorConfig
config = DecisionExtractorConfig(
min_confidence=0.7, # Minimum confidence for extracted decisions
min_content_length=20, # Minimum content length
)DecisionConfidenceEvolver
from smartmemory.plugins.evolvers.decision_confidence import DecisionConfidenceConfig
config = DecisionConfidenceConfig(
min_confidence_threshold=0.1, # Auto-retract below this
decay_after_days=30, # Start decay after inactivity
decay_rate=0.05, # 5% confidence loss per cycle
enable_decay=True,
)REST API
Decision Memory is exposed via 12 endpoints at /memory/decisions/:
| Method | Path | Description |
|---|---|---|
| POST | /create | Create a decision |
| GET | / | List active decisions |
| GET | /search | Search by topic |
| GET | /pending | List pending decisions |
| GET | /{id} | Get a decision |
| POST | /{id}/supersede | Replace a decision |
| POST | /{id}/retract | Retract a decision |
| POST | /{id}/reinforce | Add supporting evidence |
| POST | /{id}/contradict | Add counter-evidence |
| GET | /{id}/provenance | Full provenance chain |
| GET | /{id}/causal-chain | Causal traversal |
| POST | /{id}/conflicts | Find conflicts |