Construindo um Assistente Pessoal¶
Este guia mostra como dar a um agente de IA memória igual a de um humano usando o Arandu — pra ele lembrar o que ouviu, o que falou, quem falou o quê, e o que fez.
O Problema¶
Pensa em como a tua memória funciona. Quando tu conversa com alguém:
- Tu lembra o que a pessoa te contou — "Pedro me disse que mora em Porto Alegre"
- Tu lembra o que tu falou — "Eu recomendei um restaurante pra ele"
- Tu lembra o que aprendeu sobre outra pessoa na conversa — "Pedro mencionou que a namorada Ana é designer"
- Tu lembra o que tu fez — "Eu criei um documento pro Pedro"
- Quando alguém pergunta sobre qualquer assunto, tu lembra tudo que sabe, independente de quem te contou ou quando
Um agente de IA não tem nada disso por padrão. Cada sessão começa em branco. O agente não sabe o que aconteceu antes, quem disse o quê, ou o que ele fez. Ele não tem memória.
O Arandu dá ao seu agente o mesmo tipo de memória que um humano tem. Cada fato carrega sobre quem é, quem disse, e quando — e a busca funciona como a lembrança humana: pergunta sobre um assunto, recebe tudo que é relevante.
Arquitetura¶
Um assistente pessoal precisa de três camadas de memória:
| Camada | Ferramenta | O que guarda |
|---|---|---|
| Memória semântica | Arandu | Fatos sobre pessoas, preferências, ações, conhecimento |
| Memória procedimental | System prompt / config | Como o assistente deve se comportar |
| Memória episódica | Sistema de arquivos / banco | Logs diários, boards de tarefas, registros estruturados |
O Arandu cuida da memória semântica — a camada de "o que eu sei". Ele extrai fatos de linguagem natural, reconcilia com o conhecimento existente, e recupera por relevância.
Padrão Principal: Gravar as Duas Pontas¶
O segredo: gravar tanto as mensagens do usuário quanto as respostas do assistente.
# Usuário falou → gravar com speaker_name do usuário
await memory.write(
agent_id="meu-assistente",
message="Minha namorada Ana é designer, ela trabalha na Stone",
speaker_name="Pedro",
)
# Fatos extraídos:
# "Ana é designer" (entidade: person:ana)
# "Ana trabalha na Stone" (entidade: person:ana, organization:stone)
# "Ana é namorada do Pedro" (entidade: person:ana)
# Assistente respondeu → gravar com speaker_name do assistente
await memory.write(
agent_id="meu-assistente",
message="Criei a nota 'Ana.md' no vault e agendei lembrete de aniversário pro dia 15/05",
speaker_name="Assistente",
)
# Fatos extraídos:
# "Assistente criou nota Ana.md no vault" (entidade: person:assistente)
# "Assistente agendou lembrete de aniversário pro dia 15/05" (entidade: person:assistente)
O speaker_name resolve pronomes: "eu criei" com speaker_name="Assistente" vira "Assistente criou". Cada fato carrega o metadado speaker dizendo quem falou.
Fluxo de Sessão¶
┌─────────────────────────────────────────────────────┐
│ Início da sessão │
│ │
│ 1. retrieve(query="contexto recente") │
│ → Recupera o que o assistente sabe e fez │
│ → Injeta no system prompt como contexto │
│ │
├─────────────────────────────────────────────────────┤
│ Durante a conversa │
│ │
│ 2. Usuário manda mensagem │
│ → write(message=msg, speaker_name="Pedro") │
│ │
│ 3. Assistente processa e responde │
│ → retrieve(query=msg) pra buscar contexto │
│ → Gera resposta │
│ → write(message=resposta, speaker_name="Assist") │
│ │
├─────────────────────────────────────────────────────┤
│ Fim da sessão │
│ │
│ Nada especial. Tudo já foi gravado durante │
│ a conversa. A próxima sessão começa com retrieve. │
└─────────────────────────────────────────────────────┘
Exemplos de Retrieval¶
Uma vez que as duas pontas estão gravadas, o assistente consegue responder perguntas sobre qualquer pessoa e assunto:
# "O que eu sei sobre a Ana?"
result = await memory.retrieve(agent_id="meu-assistente", query="Ana")
# Retorna TODOS os fatos sobre Ana, independente de quem falou:
# - Ana é designer (Pedro contou)
# - Ana trabalha na Stone (Pedro contou)
# - Assistente criou nota Ana.md (Assistente fez)
# "O que eu fiz recentemente?"
result = await memory.retrieve(agent_id="meu-assistente", query="o que foi feito recentemente")
# Retorna ações do assistente:
# - Criou nota Ana.md
# - Agendou lembrete de aniversário
# Restrito a uma entidade específica
result = await memory.retrieve(
agent_id="meu-assistente",
query="trabalho",
entity_keys=["person:ana"],
)
# Retorna só fatos sobre trabalho relacionados à Ana
entity_keys aceita aliases também — passa "person:ana" mesmo que a canônica seja person:ana_silva, que o SDK resolve via memory_entity_aliases. Chaves que não resolvem aparecem em result.warnings pro caller distinguir "sem matches" de "chave errada":
result = await memory.retrieve(
agent_id="meu-assistente",
query="trabalho",
entity_keys=["person:ana", "person:unknown"],
)
# result.warnings → ["entity_key 'person:unknown' not found (not canonical, no matching alias)"]
Provenance do Speaker¶
Todo fato carrega o campo speaker dizendo quem falou a mensagem de onde foi extraído. Disponível tanto no FactDetail (via get/get_all) quanto no ScoredFact (via retrieve):
result = await memory.retrieve(agent_id="meu-assistente", query="Stone")
for fact in result.facts:
print(f"[{fact.speaker}] {fact.fact_text}")
# Output:
# [Pedro] Ana trabalha na Stone
# [Assistente] Stone é uma fintech brasileira, fundada em 2012
Nenhum concorrente persiste provenance do speaker nativamente — isso é um diferencial do Arandu.
Gerenciando a Memória¶
O assistente pode inspecionar e gerenciar sua própria memória:
# Listar entidades conhecidas
entities = await memory.entities(agent_id="meu-assistente")
for e in entities:
print(f"[{e.entity_type}] {e.display_name} ({e.fact_count} fatos)")
# [person] Pedro (12 fatos)
# [person] Ana (5 fatos)
# [organization] Stone (3 fatos)
# [person] Assistente (8 fatos)
# Listar fatos de uma entidade específica
ana_facts = await memory.get_all(
agent_id="meu-assistente",
entity_keys=["person:ana"],
)
# Deletar um fato específico
await memory.delete(agent_id="meu-assistente", fact_id="algum-uuid")
# Resetar toda a memória (use com cuidado)
await memory.delete_all(agent_id="meu-assistente")
Exemplo Completo¶
Um assistente pessoal mínimo com memória:
import asyncio
from arandu import MemoryClient
from arandu.providers.openai import OpenAIProvider
async def handle_message(
memory: MemoryClient,
agent_id: str,
user_msg: str,
speaker: str,
) -> str:
# 1. Gravar mensagem do usuário
await memory.write(
agent_id=agent_id,
message=user_msg,
speaker_name=speaker,
)
# 2. Recuperar contexto relevante
context = await memory.retrieve(agent_id=agent_id, query=user_msg)
# 3. Gerar resposta (substitua pela sua chamada LLM)
llm = memory._llm # reutiliza o provider do SDK
response = await llm.complete(
messages=[
{
"role": "system",
"content": f"Você é um assistente pessoal. Contexto da memória:\n{context.context}",
},
{"role": "user", "content": user_msg},
]
)
# 4. Gravar resposta do assistente
await memory.write(
agent_id=agent_id,
message=response.text,
speaker_name="Assistente",
)
return response.text
async def main():
provider = OpenAIProvider(api_key="sk-...")
memory = MemoryClient(
database_url="postgresql+psycopg://memory:memory@localhost:5432/memory",
llm=provider,
embeddings=provider,
)
await memory.initialize()
agent_id = "assistente-pedro"
try:
# Início da sessão: verificar o que aconteceu antes
recent = await memory.retrieve(agent_id=agent_id, query="contexto recente")
if recent.facts:
print("Retomando com contexto de sessões anteriores...")
# Simular conversa
reply = await handle_message(
memory, agent_id,
"Minha namorada Ana é designer na Stone",
speaker="Pedro",
)
print(f"Assistente: {reply}")
reply = await handle_message(
memory, agent_id,
"O que você sabe sobre a Ana?",
speaker="Pedro",
)
print(f"Assistente: {reply}")
finally:
await memory.close()
asyncio.run(main())
Como o Arandu se Compara¶
| Capacidade | Mem0 | Zep | Letta | Arandu |
|---|---|---|---|---|
| Extração de fatos do texto | Sim | Sim | Não (manual) | Sim |
| Filtro no retrieve | user_id/agent_id |
group_ids |
Labels de bloco | entity_keys |
| Provenance do speaker | Não | Indireta (lookup no episódio) | Não | Sim (nativo) |
| Grafo de entidades | Opcional | Sim | Não | Sim |
| Operações CRUD | Sim | Sim | Sim | Sim |
| Reconciliação (dedup) | Sim | Automática | Manual | Sim (via LLM) |