# arandu

**Memória de longo prazo para agentes de IA.** Extraia fatos de conversas, resolva entidades, reconcilie conhecimento ao longo do tempo e recupere contexto relevante — tudo com PostgreSQL e pgvector.

> *O nome "Arandu" vem da palavra Guarani que significa "sabedoria adquirida pela experiencia" — literalmente "ouvir o tempo." Assim como o conceito Guarani descreve o conhecimento construido atraves da vivencia, o Arandu da ao seu agente de IA a capacidade de acumular, consolidar e recuperar conhecimento ao longo do tempo.*

---

## Por que arandu?

A maioria dos agentes de IA é stateless. Eles esquecem tudo entre sessões. O `arandu` dá ao seu agente uma memória persistente e estruturada que fica mais inteligente com o tempo:

- **Extração automática de fatos** — O write pipeline usa LLMs para extrair entidades, fatos e relacionamentos de linguagem natural.
- **Entity resolution** — Reconhece que "minha esposa Ana", "Ana" e "ela" se referem à mesma pessoa, usando um resolver de 3 fases (exact → fuzzy → LLM).
- **Reconciliação de conhecimento** — Decide se uma informação nova deve ADD, UPDATE ou DELETE fatos existentes. Sem duplicatas, sem dados obsoletos.
- **Retrieval multi-signal** — Combina busca semântica (pgvector), keyword matching, graph traversal e scoring de recência para encontrar os fatos mais relevantes.
- **Manutenção em background** — Clustering, consolidation e importance scoring mantêm a memória organizada e atualizada — como o cérebro consolida durante o sono.
- **Provider-agnostic** — Traga seu próprio LLM e embedding provider via protocolos Python simples. Provider OpenAI incluído.

## Instalação

```bash
pip install arandu
```

Com suporte a OpenAI (recomendado):

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

### Requisitos

- Python 3.11+
- PostgreSQL com a extensão [pgvector](https://github.com/pgvector/pgvector)

## Quick Start

```python
import asyncio
from arandu import MemoryClient
from arandu.providers.openai import OpenAIProvider

async def main():
    # 1. Configurar providers
    provider = OpenAIProvider(api_key="sk-...")

    # 2. Criar client
    memory = MemoryClient(
        database_url="postgresql+psycopg://user:pass@localhost/mydb",
        llm=provider,
        embeddings=provider,
    )

    # 3. Inicializar tabelas (idempotente)
    await memory.initialize()

    # 4. Write — extrai fatos automaticamente
    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 — encontra contexto relevante
    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())
```

## Como Funciona

### Write Pipeline

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

Toda mensagem passa por quatro estágios: o LLM extrai fatos estruturados, entidades são resolvidas para registros canônicos, novos fatos são reconciliados contra o conhecimento existente, e as decisões (ADD/UPDATE/NOOP/DELETE) são executadas.

> [Saiba mais sobre o Write Pipeline](concepts/write-pipeline.md)

### Read Pipeline

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

Queries passam por um LLM planner que decide a estratégia de retrieval, depois três sinais paralelos são combinados, opcionalmente rerankeados, e comprimidos em uma string de contexto.

> [Saiba mais sobre o Read Pipeline](concepts/read-pipeline.md)

### Background Jobs

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

Jobs periódicos em background mantêm a memória organizada e atualizada — como o processamento durante o sono no cérebro.

> [Saiba mais sobre Background Jobs](concepts/background-jobs.md)

## Arquitetura

O `arandu` é projetado em torno de três princípios:

1. **DI baseada em Protocol** — LLM e embedding providers são injetados via `typing.Protocol`. Sem vendor lock-in.
2. **Fail-safe por padrão** — Toda chamada LLM tem timeouts e fallbacks. Uma extração falha ainda registra o evento. Uma reconciliação falha tem default ADD.
3. **Composição sobre herança** — Módulos pequenos e focados compostos em pipelines. Sem hierarquias profundas de classes.

> [Saiba mais sobre a Filosofia de Design](concepts/design-philosophy.md)

## Próximos Passos

<div class="grid cards" markdown>

- :material-rocket-launch:{ .lg .middle } **Primeiros Passos**

    ---

    Guia completo de setup: PostgreSQL, pgvector, primeiro write e retrieve.

    [:octicons-arrow-right-24: Primeiros Passos](getting-started.md)

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

    ---

    Deep dive em como cada pipeline funciona e por quê.

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

</div>
