arandu¶
Long-term memory for AI agents. Extract facts from conversations, resolve entities, reconcile knowledge over time, and retrieve relevant context - all backed by PostgreSQL and pgvector.
The name "Arandu" comes from the Guarani word meaning "wisdom acquired through experience" - literally "listening to time." Just as the Guarani concept describes knowledge built through lived experience, Arandu gives your AI agent the ability to accumulate, consolidate, and recall knowledge over time.
Why arandu?¶
Most AI agents are stateless. They forget everything between sessions. arandu gives your agent a persistent, structured memory that grows smarter over time:
- Automatic fact extraction - The write pipeline uses LLMs to extract entities, facts, and relationships from natural language.
- Entity resolution - Recognizes that "my wife Ana", "Ana", and "her" all refer to the same person, using a 3-phase resolver (exact → fuzzy → LLM).
- Knowledge reconciliation - Decides whether new information should ADD, UPDATE, or DELETE existing facts. No duplicates, no stale data.
- Multi-signal retrieval - Combines semantic search (pgvector), keyword matching, graph traversal, and recency scoring to find the most relevant facts.
- Background maintenance - Clustering, consolidation, and importance scoring keep memory organized and fresh - like how your brain consolidates during sleep.
- Provider-agnostic - Bring your own LLM and embedding provider via simple Python protocols. OpenAI and Anthropic (Claude) providers included.
Installation¶
With OpenAI support (recommended):
Requirements¶
- Python 3.11+
- PostgreSQL with the pgvector extension
Quick Start¶
import asyncio
from arandu import MemoryClient
from arandu.providers.openai import OpenAIProvider
async def main():
# 1. Set up providers
provider = OpenAIProvider(api_key="sk-...")
# 2. Create client
memory = MemoryClient(
database_url="postgresql+psycopg://user:pass@localhost/mydb",
llm=provider,
embeddings=provider,
)
# 3. Initialize tables (idempotent)
await memory.initialize()
# 4. Write — extracts facts automatically
result = await memory.write(
agent_id="user_123",
message="I live in São Paulo and work at Acme Corp as a backend engineer.",
)
print(f"Added {len(result.facts_added)} facts, resolved {len(result.entities_resolved)} entities")
# 5. Retrieve — finds relevant context
context = await memory.retrieve(
agent_id="user_123",
query="where does the user live and work?",
)
print(context.context)
# 6. Cleanup
await memory.close()
asyncio.run(main())
How It Works¶
Write Pipeline¶
Every message goes through four stages: the LLM extracts structured facts, entities are resolved to canonical records, new facts are reconciled against existing knowledge, and decisions (ADD/UPDATE/NOOP/DELETE) are executed.
→ Learn more about the Write Pipeline
Read Pipeline¶
Queries go through a deterministic planner that decides retrieval strategy (no LLM call), then three parallel signals are merged, optionally reranked, and compressed into a context string.
→ Learn more about the Read Pipeline
Background Jobs¶
Periodic background jobs keep memory organized and fresh - like sleep-time processing in the brain.
→ Learn more about Background Jobs
Architecture¶
arandu is designed around three principles:
- Protocol-based DI - LLM and embedding providers are injected via
typing.Protocol. No vendor lock-in. - Fail-safe by default - Every LLM call has timeouts and fallbacks. A failed extraction still logs the event. A failed reconciliation defaults to ADD.
- Composition over inheritance - Small, focused modules composed into pipelines. No deep class hierarchies.
→ Learn more about the Design Philosophy
Next Steps¶
-
Getting Started
Full setup guide: PostgreSQL, pgvector, first write and retrieve.
-
Concepts
Deep dive into how each pipeline works and why.