AtomSpace-inspired memory engine for AI agents. Stores beliefs as graph nodes with Bayesian truth values, emotional valence, and attention weights in a single SQLite file with vector indexing. No extra infra to maintain. Just Plug & Play.
When an agent calls remember(), Smrti embeds the text, resolves any entities it mentions (via a 4-tier cascade: exact → alias → fuzzy → embedding), and stores the result as a typed graph node (concept, belief, episode, or goal) carrying a Bayesian truth value, an attention weight, and an emotional valence score. Every observation is appended to an immutable evidence log — truth values are never mutated directly.
On recall(), the query is embedded and matched against the tenant-partitioned vector index (sqlite-vec KNN). Results are expanded one hop through the graph, then ranked by a salience formula that blends semantic similarity, short/long-term attention, confidence, and emotional intensity. When a memory has strong negative valence (e.g. a past outage), salience weights shift dynamically so critical errors outrank recent trivia. Each result is classified as critical_warning, known_antipattern, or context.
Consolidation happens automatically in all server modes (MCP, REST, proxy) on a configurable timer (default 60s, set SMRTI_REFLECT_INTERVAL). Each cycle merges pending evidence via PLN Bayesian revision, decays attention, promotes high-importance nodes to long-term memory, resolves contradictions by weakening the less confident belief, and prunes low-salience atoms. You can also trigger it manually via reflect(). A personality profile (16 tunable hyperparameters) governs every weight and threshold in this pipeline.
- Graph-structured memory — Concepts, beliefs, episodes, and goals as typed atoms with relation edges
- Bayesian truth maintenance — Probabilistic Logic Networks (PLN) for merging independent observations
- Personality-driven retrieval — 6 presets with 16 tunable hyperparameters that shape what gets surfaced
- Multi-tenant isolation — Tenant/space overlay model with cross-space reads and single-space writes
- Three server modes — MCP (stdio), REST API, and OpenAI-compatible proxy
- Entity resolution — 4-tier cascade: exact match, alias lookup, fuzzy (RapidFuzz), embedding similarity
- Zero external services — Single SQLite file with sqlite-vec for KNN search, ONNX embeddings on CPU
pip install smrtifrom smrti import Smrti
mem = Smrti(db_path="~/.smrti/memory.db", personality="balanced")
# Store memories
mem.remember("Alice prefers TypeScript", probability=0.9, valence=0.3)
mem.remember("The deploy pipeline is broken", probability=0.95, valence=-0.7)
# Recall by semantic similarity + salience
results = mem.recall("programming languages")
for r in results:
print(f"{r.atom.label} (salience={r.salience:.2f}, confidence={r.atom.truth.confidence:.2f})")
# Assert a belief with evidence
mem.believe("Python is the best language for ML", probability=0.85, evidence="Team survey results")
# Consolidate: decay, promote, prune, resolve contradictions
epoch = mem.reflect()
print(f"Updated {epoch.beliefs_updated} beliefs, pruned {epoch.atoms_pruned} atoms")
mem.close()# Initialize a database
smrti init --db ~/.smrti/memory.db --personality balanced
# Check status
smrti status
# Start servers
smrti serve mcp # MCP stdio server (for Claude, etc.)
smrti serve rest # FastAPI on :8420
smrti serve proxy # OpenAI-compatible proxy on :8421Exposes 6 tools over stdio for direct LLM integration (Claude, etc.):
| Tool | Description |
|---|---|
remember |
Store an observation or episode |
recall |
Semantic search with salience scoring |
believe |
Assert a belief with truth value |
reflect |
Run a consolidation epoch |
forget |
Lower confidence on a memory |
status |
Get memory statistics |
smrti serve mcpConfigure via environment variables:
export SMRTI_DB=~/.smrti/memory.db
export SMRTI_PERSONALITY=balanced
export SMRTI_TENANT_ID=default
export SMRTI_SPACE=default
export SMRTI_READ_SPACES=default,shared # comma-separated
export SMRTI_REFLECT_INTERVAL=60 # auto-consolidation interval in seconds (0 to disable)Full CRUD over HTTP on port 8420:
smrti serve rest --host 0.0.0.0 --port 8420# Store a memory
curl -X POST http://localhost:8420/remember \
-H "Content-Type: application/json" \
-d '{"content": "Alice prefers TypeScript", "probability": 0.9}'
# Recall
curl -X POST http://localhost:8420/recall \
-d '{"query": "programming languages", "top_k": 5}'
# Run consolidation
curl -X POST http://localhost:8420/reflect
# Get status
curl http://localhost:8420/statusDrop-in replacement for https://api.openai.com/v1/chat/completions. Intercepts requests, injects relevant memories into the system prompt, and stores the exchange afterward.
smrti serve proxy --host 0.0.0.0 --port 8421 --upstream https://api.openai.comUse it from any OpenAI-compatible client:
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8421/v1",
api_key="sk-..." # forwarded to upstream
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "What do you know about Alice?"}],
extra_headers={
"X-Smrti-Tenant-Id": "user_123",
"X-Smrti-Write-Space": "work",
"X-Smrti-Read-Spaces": "work,personal",
}
)The proxy automatically:
- Recalls relevant memories from the specified read spaces (using recent conversation context, not just the last message)
- Classifies each memory by severity (
critical_warning,known_antipattern,context) and injects them as structured XML tags into the system prompt - Stores user messages and the assistant response as episodes
Configure with:
export SMRTI_UPSTREAM_URL=https://api.openai.com # or any OpenAI-compatible API
export SMRTI_RECALL_TOP_K=5
export SMRTI_RECALL_MIN_CONFIDENCE=0.3
export SMRTI_QUERY_MODE=concat # "concat" (default) or "last" for last-message-only
export SMRTI_QUERY_CONTEXT_MSGS=5 # number of recent messages to include in query
export SMRTI_QUERY_MAX_CHARS=500 # max characters for the recall query
export SMRTI_REFLECT_INTERVAL=60 # auto-consolidation interval in seconds (0 to disable)Smrti uses a two-level isolation model:
- Tenant — Hard boundary. Different tenants never share atoms. Maps to a user or organization.
- Space — Soft boundary within a tenant. Memories are written to one space but can be read from multiple.
# Read from multiple spaces, write to one
mem = Smrti(
tenant_id="user_123",
write_space="work",
read_spaces=["work", "personal", "shared"]
)
# Each space can have its own personality
mem.set_personality("analytical")Six built-in presets control retrieval behavior, decay rates, and emotional dynamics:
| Preset | Bias | Use Case |
|---|---|---|
balanced |
Equal weights across all signals | General-purpose agents |
analytical |
High confidence weight, low valence | Logical reasoning, data-driven decisions |
curious |
High STI weight, fast decay | Exploration, novelty-seeking |
empathetic |
High valence weight, emotional propagation | Relationship-focused agents |
maverick |
Slow decay, high propagation | Independent, contrarian reasoning |
deterministic |
Fast learning, slow decay, laser focus | Agentic workflows, code gen, deployments |
Each preset tunes 16 hyperparameters. To create a custom personality, start from a preset and override individual values via the personality DB table or the /personality API endpoint.
Salience weights — control how retrieval ranks results (should sum to ~1.0):
| Parameter | Default | Effect |
|---|---|---|
w_similarity |
0.35 | Weight of embedding cosine similarity |
w_sti |
0.25 | Weight of short-term importance (recency/access) |
w_confidence |
0.20 | Weight of truth value confidence |
w_lti |
0.10 | Weight of long-term importance |
w_valence |
0.10 | Weight of emotional intensity (dynamically boosted when valence < -0.5) |
Belief dynamics — govern how confidence evolves over time:
| Parameter | Default | Effect |
|---|---|---|
confidence_decay_rate |
0.02 | Per-epoch confidence decay. Higher = memories fade faster |
confidence_update_lr |
0.3 | Learning rate for PLN evidence merges. Higher = new evidence has more impact |
min_confidence_to_surface |
0.1 | Floor below which atoms are excluded from recall results |
Attention dynamics — control what stays in focus:
| Parameter | Default | Effect |
|---|---|---|
sti_decay_rate |
0.1 | Per-epoch STI decay. Higher = faster attention loss |
sti_boost_on_access |
0.5 | STI added each time an atom is recalled. Higher = stronger recency bias |
sti_propagation_factor |
0.15 | Fraction of STI boost propagated to linked atoms. Higher = broader activation |
lti_promotion_threshold |
0.7 | Cumulative STI required to increment LTI. Higher = harder to become permanent |
Emotional dynamics — shape how valence influences behavior:
| Parameter | Default | Effect |
|---|---|---|
valence_weight |
0.2 | Global scaling factor for emotional influence on salience |
valence_propagation |
0.1 | Fraction of valence propagated to linked atoms during epochs |
mood_inertia |
0.8 | Resistance to mood shifts (0 = reactive, 1 = stable) |
graph TD
subgraph Facade
S["Smrti<br/><small>remember · recall · believe · reflect · forget · status</small>"]
end
subgraph Servers
MCP["mcp.py<br/><small>MCP stdio</small>"]
REST["rest.py<br/><small>FastAPI :8420</small>"]
PROXY["proxy.py<br/><small>OpenAI proxy :8421</small>"]
end
subgraph Core
AS["AtomSpace"]
DB["Database"]
EMB["Embedder"]
MOD["Models"]
end
subgraph Retrieval
FAN["fan_out"]
SAL["salience"]
CLS["classify"]
end
subgraph Evolution
EPO["epoch"]
TRU["truth"]
CON["connections"]
end
subgraph Extraction
RES["resolve"]
ALI["aliases"]
end
subgraph Storage
SQL["SQLite + sqlite-vec<br/><small>multilingual-MiniLM-L12-v2 · 384d · ONNX CPU</small>"]
end
MCP & REST & PROXY --> S
S --> Core & Retrieval & Evolution & Extraction
Core & Retrieval & Evolution & Extraction --> SQL
Retrieval pipeline: Embed query → KNN over tenant partition → filter to read spaces → 1-hop graph expansion → salience scoring → top-k
Salience formula:
S = w_sim × similarity + w_sti × sti + w_conf × confidence + w_lti × lti + w_val × |valence| × intensity
When valence < -0.5, weight shifts dynamically from w_sti to w_val so critical errors outrank recent trivia.
Consolidation epoch (runs automatically every SMRTI_REFLECT_INTERVAL seconds, or manually via reflect()):
- Process pending evidence via Bayesian update
- Decay STI and confidence
- Promote high-STI atoms to LTI
- Resolve contradictions (weaken less confident belief)
- Discover cross-domain connections (every 10th epoch)
- Prune atoms below confidence/LTI floors
| Atom Type | Purpose | Example |
|---|---|---|
concept |
Reusable entities | "Alice", "Python", "OpenAI" |
belief |
Probabilistic facts | "Alice prefers TypeScript" |
episode |
Timestamped observations | "User asked about deployment" |
goal |
Desired states | "Finish the migration by Friday" |
relation |
Edges between atoms | Alice → works_at → Acme Corp |
Each atom carries:
- TruthValue —
probability[0,1] andconfidence[0,1], merged via PLN revision - AttentionValue —
sti(short-term importance, decays fast) andlti(long-term, accumulates) - Valence — emotional tone [-1,1] and intensity [0,1]
pytest tests/ -vMIT