# 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 provider included.

## Installation

```bash
pip install arandu
```

With OpenAI support (recommended):

```bash
pip install arandu[openai]
```

### Requirements

- Python 3.11+
- PostgreSQL with the [pgvector](https://github.com/pgvector/pgvector) extension

## Quick Start

```python
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(
        user_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(
        user_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

```
Message → Extract (LLM) → Resolve Entities → Reconcile → Upsert
```

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](concepts/write-pipeline.md)

### Read Pipeline

```
Query → Plan (LLM) → Retrieve (semantic + keyword + graph) → Rerank → Format
```

Queries go through an LLM planner that decides retrieval strategy, then three parallel signals are merged, optionally reranked, and compressed into a context string.

→ [Learn more about the Read Pipeline](concepts/read-pipeline.md)

### Background Jobs

```
Clustering → Consolidation → Importance Scoring → Summary Refresh
```

Periodic background jobs keep memory organized and fresh — like sleep-time processing in the brain.

→ [Learn more about Background Jobs](concepts/background-jobs.md)

## Architecture

`arandu` is designed around three principles:

1. **Protocol-based DI** — LLM and embedding providers are injected via `typing.Protocol`. No vendor lock-in.
2. **Fail-safe by default** — Every LLM call has timeouts and fallbacks. A failed extraction still logs the event. A failed reconciliation defaults to ADD.
3. **Composition over inheritance** — Small, focused modules composed into pipelines. No deep class hierarchies.

→ [Learn more about the Design Philosophy](concepts/design-philosophy.md)

## Next Steps

<div class="grid cards" markdown>

- :material-rocket-launch:{ .lg .middle } **Getting Started**

    ---

    Full setup guide: PostgreSQL, pgvector, first write and retrieve.

    [:octicons-arrow-right-24: Getting Started](getting-started.md)

- :material-brain:{ .lg .middle } **Concepts**

    ---

    Deep dive into how each pipeline works and why.

    [:octicons-arrow-right-24: Write Pipeline](concepts/write-pipeline.md)

</div>
