The Bitemporal Memory Engine for Long-Horizon AI Agents
Standard RAG relies on vector databases that append new facts endlessly. If a user tells an agent "I live in New York", and 6 months later says "I just moved to London", a standard RAG system will embed both. When the agent asks "Where does the user live?", the vector database returns both. The LLM hallucinates or gets confused.
Furthermore, memories decay. A minor detail mentioned 3 years ago shouldn't have the same context weight as a major life event mentioned yesterday.
Recall is a thread-safe, bitemporal entity and fact store designed specifically for long-horizon AI companions and agents.
It solves stateful memory through:
- Bitemporal Fact Tracking: Memories are tracked by when they were true (
valid_time) and when the agent learned them (knowledge_time) using half-open[from, to)intervals. When facts change, the old fact is gracefully closed, not deleted. - Ebbinghaus Salience Decay: Memories automatically decay in relevance over time unless they are reinforced.
- Tenant-Scoped Semantic Search: Facts are cached as vector embeddings, enabling semantic similarity search within tenant boundaries.
- Deterministic Conflict Resolution: New conflicting facts overwrite old ones deterministically, ensuring the LLM always gets a clean, conflict-free context block.
- 🚀 Multi-Tenant FastAPI Server: Built-in HTTP server with Bearer auth and per-tenant SQLite isolation.
- 🧠 Vector Fallback: Perform semantic queries against memory entries when exact entity identifiers are unknown.
- 🧵 Thread-Safe SQLite WAL: SQLite WAL mode with serialized single-writer discipline for embedded and single-node agent servers.
- 🗑️ Lifecycle & Retention: Drop isolated tenant databases or vacuum retracted historical data for storage efficiency.
- ⚡ Async Python SDK: Python client (
RecallRemoteClient) for integration into agent loops.
pip install recallRun the production server via Docker, or locally with required authentication:
export OPENAI_API_KEY="sk-..."
# Master key or tenant-bound keys (e.g. "key_user123:user_123,key_user456:user_456")
export RECALL_API_KEY="your-secret-key"
uvicorn recall.server.app:app --port 8000import asyncio
from recall.remote import AsyncRecallRemoteClient
async def main():
async with AsyncRecallRemoteClient("http://localhost:8000", "your-secret-key") as client:
# 1. Ingest a user's chat message
await client.ingest_turn(
tenant_id="user_123",
speaker="User",
text="I just got a new job at OpenAI! Moving to SF tomorrow.",
conversation_id="conv_001",
)
# 2. Query memory semantically later
results = await client.search(tenant_id="user_123", query="where do they work?")
# 3. Format as a compressed XML block for your LLM Prompt
xml_context = await client.format_for_prompt(tenant_id="user_123", about_entity="User")
print(xml_context)
# <memory entity='User'>
# <fact confidence='0.95'>works_at OpenAI</fact>
# <fact confidence='0.95'>lives_in San Francisco</fact>
# </memory>
asyncio.run(main())Recall ships with a built-in CLI for quick memory introspection:
recall --tenant user_123 search "OpenAI"Recall uses a custom scoring algorithm to determine which facts are injected into your LLM's context window. The Composite Score combines:
- Semantic Similarity (40%): Cosine similarity between the agent's current query and the memory embedding.
- Retention (40%): An Ebbinghaus forgetting curve based on
elapsed_dayssince the memory was last reinforced. - Confidence (20%): The extraction confidence of the LLM pipeline.
See the engine in action. We've included a terminal-based Chat Companion that uses the embedded Recall engine to talk to you.
export OPENAI_API_KEY="sk-..."
python examples/chat_companion.pyBuilt for the future of Stateful AI.
