---
title: Memória
description: Aproveitando o sistema de memória unificado no CrewAI para aprimorar as capacidades dos agentes.
icon: database
mode: "wide"
---

## Visão Geral

O CrewAI oferece um **sistema de memória unificado** -- uma única classe `Memory` que substitui memórias de curto prazo, longo prazo, entidades e externa por uma API inteligente. A memória usa um LLM para analisar o conteúdo ao salvar (inferindo escopo, categorias e importância) e suporta recall com profundidade adaptativa e pontuação composta que combina similaridade semântica, recência e importância.

Você pode usar a memória de quatro formas: **standalone** (scripts, notebooks), **com Crews**, **com Agentes** ou **dentro de Flows**.

## Início Rápido

```python
from crewai import Memory

memory = Memory()

# Armazenar -- o LLM infere escopo, categorias e importância
memory.remember("Decidimos usar PostgreSQL para o banco de dados de usuários.")

# Recuperar -- resultados ranqueados por pontuação composta (semântica + recência + importância)
matches = memory.recall("Qual banco de dados escolhemos?")
for m in matches:
    print(f"[{m.score:.2f}] {m.record.content}")

# Ajustar pontuação para um projeto dinâmico
memory = Memory(recency_weight=0.5, recency_half_life_days=7)

# Esquecer
memory.forget(scope="/project/old")

# Explorar a árvore de escopos auto-organizada
print(memory.tree())
print(memory.info("/"))
```

## Quatro Formas de Usar Memória

### Standalone

Use memória em scripts, notebooks, ferramentas CLI ou como base de conhecimento independente -- sem agentes ou crews necessários.

```python
from crewai import Memory

memory = Memory()

# Construir conhecimento
memory.remember("O limite da API é 1000 requisições por minuto.")
memory.remember("Nosso ambiente de staging usa a porta 8080.")
memory.remember("A equipe concordou em usar feature flags para todos os novos lançamentos.")

# Depois, recupere o que precisar
matches = memory.recall("Quais são nossos limites de API?", limit=5)
for m in matches:
    print(f"[{m.score:.2f}] {m.record.content}")

# Extrair fatos atômicos de um texto mais longo
raw = """Notas da reunião: Decidimos migrar do MySQL para PostgreSQL
no próximo trimestre. O orçamento é de $50k. Sarah liderará a migração."""

facts = memory.extract_memories(raw)
# ["Migração de MySQL para PostgreSQL planejada para o próximo trimestre",
#  "Orçamento da migração de banco de dados é $50k",
#  "Sarah liderará a migração do banco de dados"]

for fact in facts:
    memory.remember(fact)
```

### Com Crews

Passe `memory=True` para configurações padrão, ou passe uma instância `Memory` configurada para comportamento customizado.

```python
from crewai import Crew, Agent, Task, Process, Memory

# Opção 1: Memória padrão
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, writing_task],
    process=Process.sequential,
    memory=True,
    verbose=True,
)

# Opção 2: Memória customizada com pontuação ajustada
memory = Memory(
    recency_weight=0.4,
    semantic_weight=0.4,
    importance_weight=0.2,
    recency_half_life_days=14,
)
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, writing_task],
    memory=memory,
)
```

Quando `memory=True`, a crew cria um `Memory()` padrão e repassa a configuração de `embedder` da crew automaticamente. Todos os agentes compartilham a memória da crew, a menos que um agente tenha sua própria.

Após cada tarefa, a crew extrai automaticamente fatos discretos da saída da tarefa e os armazena. Antes de cada tarefa, o agente recupera contexto relevante da memória e o injeta no prompt da tarefa.

### Com Agentes

Agentes podem usar a memória compartilhada da crew (padrão) ou receber uma visão com escopo para contexto privado.

```python
from crewai import Agent, Memory

memory = Memory()

# Pesquisador recebe um escopo privado -- só vê /agent/researcher
researcher = Agent(
    role="Researcher",
    goal="Encontrar e analisar informações",
    backstory="Pesquisador experiente com atenção aos detalhes",
    memory=memory.scope("/agent/researcher"),
)

# Escritor usa memória compartilhada da crew (sem memória própria)
writer = Agent(
    role="Writer",
    goal="Produzir conteúdo claro e bem estruturado",
    backstory="Escritor técnico experiente",
    # memory não definido -- usa crew._memory quando a crew tem memória habilitada
)
```

Esse padrão dá ao pesquisador descobertas privadas enquanto o escritor lê da memória compartilhada da crew.

### Com Flows

Todo Flow possui memória integrada. Use `self.remember()`, `self.recall()` e `self.extract_memories()` dentro de qualquer método do flow.

```python
from crewai.flow.flow import Flow, listen, start

class ResearchFlow(Flow):
    @start()
    def gather_data(self):
        findings = "PostgreSQL suporta 10k conexões simultâneas. MySQL limita a 5k."
        self.remember(findings, scope="/research/databases")
        return findings

    @listen(gather_data)
    def write_report(self, findings):
        # Recuperar pesquisas anteriores para fornecer contexto
        past = self.recall("benchmarks de performance de banco de dados")
        context = "\n".join(f"- {m.record.content}" for m in past)
        return f"Relatório:\nNovas descobertas: {findings}\nContexto anterior:\n{context}"
```

Veja a [documentação de Flows](/concepts/flows) para mais informações sobre memória em Flows.


## Escopos Hierárquicos

### O Que São Escopos

As memórias são organizadas em uma árvore hierárquica de escopos, similar a um sistema de arquivos. Cada escopo é um caminho como `/`, `/project/alpha` ou `/agent/researcher/findings`.

```
/
  /company
    /company/engineering
    /company/product
  /project
    /project/alpha
    /project/beta
  /agent
    /agent/researcher
    /agent/writer
```

Escopos fornecem **memória dependente de contexto** -- quando você faz recall dentro de um escopo, busca apenas naquela ramificação da árvore, melhorando tanto a precisão quanto o desempenho.

### Como a Inferência de Escopo Funciona

Quando você chama `remember()` sem especificar um escopo, o LLM analisa o conteúdo e a árvore de escopos existente, e sugere o melhor posicionamento. Se nenhum escopo existente é adequado, ele cria um novo. Com o tempo, a árvore de escopos cresce organicamente a partir do conteúdo -- você não precisa projetar um esquema antecipadamente.

```python
memory = Memory()

# LLM infere escopo a partir do conteúdo
memory.remember("Escolhemos PostgreSQL para o banco de dados de usuários.")
# -> pode ser colocado em /project/decisions ou /engineering/database

# Você também pode especificar o escopo explicitamente
memory.remember("Velocidade do sprint é 42 pontos", scope="/team/metrics")
```

### Visualizando a Árvore de Escopos

```python
print(memory.tree())
# / (15 records)
#   /project (8 records)
#     /project/alpha (5 records)
#     /project/beta (3 records)
#   /agent (7 records)
#     /agent/researcher (4 records)
#     /agent/writer (3 records)

print(memory.info("/project/alpha"))
# ScopeInfo(path='/project/alpha', record_count=5,
#           categories=['architecture', 'database'],
#           oldest_record=datetime(...), newest_record=datetime(...),
#           child_scopes=[])
```

### MemoryScope: Visões de Subárvore

Um `MemoryScope` restringe todas as operações a uma ramificação da árvore. O agente ou código que o utiliza só pode ver e escrever dentro daquela subárvore.

```python
memory = Memory()

# Criar um escopo para um agente específico
agent_memory = memory.scope("/agent/researcher")

# Tudo é relativo a /agent/researcher
agent_memory.remember("Encontrados três papers relevantes sobre memória de LLM.")
# -> armazenado em /agent/researcher

agent_memory.recall("papers relevantes")
# -> busca apenas em /agent/researcher

# Restringir ainda mais com subscope
project_memory = agent_memory.subscope("project-alpha")
# -> /agent/researcher/project-alpha
```

### Boas Práticas para Design de Escopos

- **Comece plano, deixe o LLM organizar.** Não projete demais sua hierarquia de escopos antecipadamente. Comece com `memory.remember(content)` e deixe a inferência de escopo do LLM criar estrutura conforme o conteúdo se acumula.

- **Use padrões `/{tipo_entidade}/{identificador}`.** Hierarquias naturais emergem de padrões como `/project/alpha`, `/agent/researcher`, `/company/engineering`, `/customer/acme-corp`.

- **Escopo por preocupação, não por tipo de dado.** Use `/project/alpha/decisions` em vez de `/decisions/project/alpha`. Isso mantém conteúdo relacionado junto.

- **Mantenha profundidade rasa (2-3 níveis).** Escopos profundamente aninhados ficam muito esparsos. `/project/alpha/architecture` é bom; `/project/alpha/architecture/decisions/databases/postgresql` é demais.

- **Use escopos explícitos quando souber, deixe o LLM inferir quando não souber.** Se está armazenando uma decisão de projeto conhecida, passe `scope="/project/alpha/decisions"`. Se está armazenando saída livre de um agente, omita o escopo e deixe o LLM decidir.

### Exemplos de Casos de Uso

**Equipe multi-projeto:**
```python
memory = Memory()
# Cada projeto recebe sua própria ramificação
memory.remember("Usando arquitetura de microsserviços", scope="/project/alpha/architecture")
memory.remember("API GraphQL para apps cliente", scope="/project/beta/api")

# Recall em todos os projetos
memory.recall("decisões de design de API")

# Ou dentro de um projeto específico
memory.recall("design de API", scope="/project/beta")
```

**Contexto privado por agente com conhecimento compartilhado:**
```python
memory = Memory()

# Pesquisador tem descobertas privadas
researcher_memory = memory.scope("/agent/researcher")

# Escritor pode ler de seu próprio escopo e do conhecimento compartilhado da empresa
writer_view = memory.slice(
    scopes=["/agent/writer", "/company/knowledge"],
    read_only=True,
)
```

**Suporte ao cliente (contexto por cliente):**
```python
memory = Memory()

# Cada cliente recebe contexto isolado
memory.remember("Prefere comunicação por email", scope="/customer/acme-corp")
memory.remember("Plano enterprise, 50 licenças", scope="/customer/acme-corp")

# Docs de produto compartilhados são acessíveis a todos os agentes
memory.remember("Limite de taxa é 1000 req/min no plano enterprise", scope="/product/docs")
```


## Fatias de Memória (Memory Slices)

### O Que São Fatias

Um `MemorySlice` é uma visão sobre múltiplos escopos, possivelmente disjuntos. Diferente de um escopo (que restringe a uma subárvore), uma fatia permite recall de várias ramificações simultaneamente.

### Quando Usar Fatias vs Escopos

- **Escopo**: Use quando um agente ou bloco de código deve ser restrito a uma única subárvore. Exemplo: um agente que só vê `/agent/researcher`.
- **Fatia**: Use quando precisar combinar contexto de múltiplas ramificações. Exemplo: um agente que lê de seu próprio escopo mais conhecimento compartilhado da empresa.

### Fatias Somente Leitura

O padrão mais comum: dar a um agente acesso de leitura a múltiplas ramificações sem permitir que ele escreva em áreas compartilhadas.

```python
memory = Memory()

# Agente pode fazer recall de seu próprio escopo E do conhecimento da empresa,
# mas não pode escrever no conhecimento da empresa
agent_view = memory.slice(
    scopes=["/agent/researcher", "/company/knowledge"],
    read_only=True,
)

matches = agent_view.recall("políticas de segurança da empresa", limit=5)
# Busca em /agent/researcher e /company/knowledge, mescla e ranqueia resultados

agent_view.remember("nova descoberta")  # Levanta PermissionError (somente leitura)
```

### Fatias de Leitura e Escrita

Quando somente leitura está desabilitado, você pode escrever em qualquer um dos escopos incluídos, mas deve especificar qual escopo explicitamente.

```python
view = memory.slice(scopes=["/team/alpha", "/team/beta"], read_only=False)

# Deve especificar escopo ao escrever
view.remember("Decisão entre equipes", scope="/team/alpha", categories=["decisions"])
```


## Pontuação Composta

Os resultados do recall são ranqueados por uma combinação ponderada de três sinais:

```
composite = semantic_weight * similarity + recency_weight * decay + importance_weight * importance
```

Onde:
- **similarity** = `1 / (1 + distance)` do índice vetorial (0 a 1)
- **decay** = `0.5^(age_days / half_life_days)` -- decaimento exponencial (1.0 para hoje, 0.5 na meia-vida)
- **importance** = pontuação de importância do registro (0 a 1), definida no momento da codificação

Configure diretamente no construtor do `Memory`:

```python
# Retrospectiva de sprint: favorecer memórias recentes, meia-vida curta
memory = Memory(
    recency_weight=0.5,
    semantic_weight=0.3,
    importance_weight=0.2,
    recency_half_life_days=7,
)

# Base de conhecimento de arquitetura: favorecer memórias importantes, meia-vida longa
memory = Memory(
    recency_weight=0.1,
    semantic_weight=0.5,
    importance_weight=0.4,
    recency_half_life_days=180,
)
```

Cada `MemoryMatch` inclui uma lista `match_reasons` para que você possa ver por que um resultado ficou na posição que ficou (ex.: `["semantic", "recency", "importance"]`).


## Camada de Análise LLM

A memória usa o LLM de três formas:

1. **Ao salvar** -- Quando você omite escopo, categorias ou importância, o LLM analisa o conteúdo e sugere escopo, categorias, importância e metadados (entidades, datas, tópicos).
2. **Ao fazer recall** -- Para recall profundo/automático, o LLM analisa a consulta (palavras-chave, dicas temporais, escopos sugeridos, complexidade) para guiar a recuperação.
3. **Extrair memórias** -- `extract_memories(content)` quebra texto bruto (ex.: saída de tarefa) em afirmações de memória discretas. Os agentes usam isso antes de chamar `remember()` em cada afirmação para que fatos atômicos sejam armazenados em vez de um bloco grande.

Toda análise degrada graciosamente em caso de falha do LLM -- veja [Comportamento em Caso de Falha](#comportamento-em-caso-de-falha).


## Consolidação de Memória

Ao salvar novo conteúdo, o pipeline de codificação verifica automaticamente registros similares existentes no armazenamento. Se a similaridade estiver acima de `consolidation_threshold` (padrão 0.85), o LLM decide o que fazer:

- **keep** -- O registro existente ainda é preciso e não é redundante.
- **update** -- O registro existente deve ser atualizado com novas informações (o LLM fornece o conteúdo mesclado).
- **delete** -- O registro existente está desatualizado, substituído ou contradito.
- **insert_new** -- Se o novo conteúdo também deve ser inserido como um registro separado.

Isso evita o acúmulo de duplicatas. Por exemplo, se você salvar "CrewAI garante operação confiável" três vezes, a consolidação reconhece as duplicatas e mantém apenas um registro.

### Dedup Intra-batch

Ao usar `remember_many()`, os itens dentro do mesmo batch são comparados entre si antes de atingir o armazenamento. Se dois itens tiverem similaridade de cosseno >= `batch_dedup_threshold` (padrão 0.98), o posterior é silenciosamente descartado. Isso captura duplicatas exatas ou quase exatas dentro de um único batch sem chamadas ao LLM (pura matemática vetorial).

```python
# Apenas 2 registros são armazenados (o terceiro é quase duplicata do primeiro)
memory.remember_many([
    "CrewAI supports complex workflows.",
    "Python is a great language.",
    "CrewAI supports complex workflows.",  # descartado pelo dedup intra-batch
])
```


## Saves Não-Bloqueantes

`remember_many()` é **não-bloqueante** -- ele envia o pipeline de codificação para uma thread em background e retorna imediatamente. Isso significa que o agente pode continuar para a próxima tarefa enquanto as memórias estão sendo salvas.

```python
# Retorna imediatamente -- save acontece em background
memory.remember_many(["Fato A.", "Fato B.", "Fato C."])

# recall() espera automaticamente saves pendentes antes de buscar
matches = memory.recall("fatos")  # vê todos os 3 registros
```

### Barreira de Leitura

Cada chamada `recall()` executa automaticamente `drain_writes()` antes de buscar, garantindo que a consulta sempre veja os registros mais recentes persistidos. Isso é transparente -- você nunca precisa pensar nisso.

### Encerramento da Crew

Quando uma crew termina, `kickoff()` drena todos os saves de memória pendentes em seu bloco `finally`, então nenhum save é perdido mesmo que a crew complete enquanto saves em background estão em andamento.

### Uso Standalone

Para scripts ou notebooks onde não há ciclo de vida de crew, chame `drain_writes()` ou `close()` explicitamente:

```python
memory = Memory()
memory.remember_many(["Fato A.", "Fato B."])

# Opção 1: Esperar saves pendentes
memory.drain_writes()

# Opção 2: Drenar e encerrar o pool de background
memory.close()
```


## Origem e Privacidade

Cada registro de memória pode carregar uma tag `source` para rastreamento de procedência e uma flag `private` para controle de acesso.

### Rastreamento de Origem

O parâmetro `source` identifica de onde uma memória veio:

```python
# Marcar memórias com sua origem
memory.remember("Usuário prefere modo escuro", source="user:alice")
memory.remember("Configuração do sistema atualizada", source="admin")
memory.remember("Agente encontrou um bug", source="agent:debugger")

# Recuperar apenas memórias de uma origem específica
matches = memory.recall("preferências do usuário", source="user:alice")
```

### Memórias Privadas

Memórias privadas só são visíveis no recall quando o `source` corresponde:

```python
# Armazenar uma memória privada
memory.remember("A chave de API da Alice é sk-...", source="user:alice", private=True)

# Este recall vê a memória privada (source corresponde)
matches = memory.recall("chave de API", source="user:alice")

# Este recall NÃO a vê (source diferente)
matches = memory.recall("chave de API", source="user:bob")

# Acesso admin: ver todos os registros privados independente do source
matches = memory.recall("chave de API", include_private=True)
```

Isso é particularmente útil em implantações multi-usuário ou corporativas onde memórias de diferentes usuários devem ser isoladas.


## RecallFlow (Recall Profundo)

`recall()` suporta duas profundidades:

- **`depth="shallow"`** -- Busca vetorial direta com pontuação composta. Rápido (~200ms), sem chamadas ao LLM.
- **`depth="deep"` (padrão)** -- Executa um RecallFlow em múltiplas etapas: análise da consulta, seleção de escopo, busca vetorial paralela, roteamento baseado em confiança e exploração recursiva opcional quando a confiança é baixa.

**Pulo inteligente do LLM**: Consultas com menos de `query_analysis_threshold` (padrão 200 caracteres) pulam a análise de consulta do LLM inteiramente, mesmo no modo deep. Consultas curtas como "Qual banco de dados usamos?" já são boas frases de busca -- a análise do LLM agrega pouco valor. Isso economiza ~1-3s por recall para consultas curtas típicas. Apenas consultas mais longas (ex.: descrições completas de tarefas) passam pela destilação do LLM em sub-consultas direcionadas.

```python
# Shallow: busca vetorial pura, sem LLM
matches = memory.recall("O que decidimos?", limit=10, depth="shallow")

# Deep (padrão): recuperação inteligente com análise LLM para consultas longas
matches = memory.recall(
    "Resuma todas as decisões de arquitetura deste trimestre",
    limit=10,
    depth="deep",
)
```

Os limiares de confiança que controlam o roteador do RecallFlow são configuráveis:

```python
memory = Memory(
    confidence_threshold_high=0.9,   # Só sintetizar quando muito confiante
    confidence_threshold_low=0.4,    # Explorar mais profundamente de forma mais agressiva
    exploration_budget=2,            # Permitir até 2 rodadas de exploração
    query_analysis_threshold=200,    # Pular LLM para consultas menores que isso
)
```


## Configuração de Embedder

A memória precisa de um modelo de embedding para converter texto em vetores para busca semântica. Você pode configurar de três formas.

### Passando Diretamente para o Memory

```python
from crewai import Memory

# Como um dict de configuração
memory = Memory(embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-small"}})

# Como um callable pré-construído
from crewai.rag.embeddings.factory import build_embedder
embedder = build_embedder({"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}})
memory = Memory(embedder=embedder)
```

### Via Configuração de Embedder da Crew

Quando usar `memory=True`, a configuração de `embedder` da crew é repassada:

```python
from crewai import Crew

crew = Crew(
    agents=[...],
    tasks=[...],
    memory=True,
    embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-small"}},
)
```

### Exemplos por Provedor

<AccordionGroup>
<Accordion title="OpenAI (padrão)">
```python
memory = Memory(embedder={
    "provider": "openai",
    "config": {
        "model_name": "text-embedding-3-small",
        # "api_key": "sk-...",  # ou defina OPENAI_API_KEY
    },
})
```
</Accordion>

<Accordion title="Ollama (local, privado)">
```python
memory = Memory(embedder={
    "provider": "ollama",
    "config": {
        "model_name": "mxbai-embed-large",
        "url": "http://localhost:11434/api/embeddings",
    },
})
```
</Accordion>

<Accordion title="Azure OpenAI">
```python
memory = Memory(embedder={
    "provider": "azure",
    "config": {
        "deployment_id": "your-embedding-deployment",
        "api_key": "your-azure-api-key",
        "api_base": "https://your-resource.openai.azure.com",
        "api_version": "2024-02-01",
    },
})
```
</Accordion>

<Accordion title="Google AI">
```python
memory = Memory(embedder={
    "provider": "google-generativeai",
    "config": {
        "model_name": "gemini-embedding-001",
        # "api_key": "...",  # ou defina GOOGLE_API_KEY
    },
})
```
</Accordion>

<Accordion title="Google Vertex AI">
```python
memory = Memory(embedder={
    "provider": "google-vertex",
    "config": {
        "model_name": "gemini-embedding-001",
        "project_id": "your-gcp-project-id",
        "location": "us-central1",
    },
})
```
</Accordion>

<Accordion title="Cohere">
```python
memory = Memory(embedder={
    "provider": "cohere",
    "config": {
        "model_name": "embed-english-v3.0",
        # "api_key": "...",  # ou defina COHERE_API_KEY
    },
})
```
</Accordion>

<Accordion title="VoyageAI">
```python
memory = Memory(embedder={
    "provider": "voyageai",
    "config": {
        "model": "voyage-3",
        # "api_key": "...",  # ou defina VOYAGE_API_KEY
    },
})
```
</Accordion>

<Accordion title="AWS Bedrock">
```python
memory = Memory(embedder={
    "provider": "amazon-bedrock",
    "config": {
        "model_name": "amazon.titan-embed-text-v1",
        # Usa credenciais AWS padrão (sessão boto3)
    },
})
```
</Accordion>

<Accordion title="Hugging Face">
```python
memory = Memory(embedder={
    "provider": "huggingface",
    "config": {
        "model_name": "sentence-transformers/all-MiniLM-L6-v2",
    },
})
```
</Accordion>

<Accordion title="Jina">
```python
memory = Memory(embedder={
    "provider": "jina",
    "config": {
        "model_name": "jina-embeddings-v2-base-en",
        # "api_key": "...",  # ou defina JINA_API_KEY
    },
})
```
</Accordion>

<Accordion title="IBM WatsonX">
```python
memory = Memory(embedder={
    "provider": "watsonx",
    "config": {
        "model_id": "ibm/slate-30m-english-rtrvr",
        "api_key": "your-watsonx-api-key",
        "project_id": "your-project-id",
        "url": "https://us-south.ml.cloud.ibm.com",
    },
})
```
</Accordion>

<Accordion title="Embedder Customizado">
```python
# Passe qualquer callable que receba uma lista de strings e retorne uma lista de vetores
def my_embedder(texts: list[str]) -> list[list[float]]:
    # Sua lógica de embedding aqui
    return [[0.1, 0.2, ...] for _ in texts]

memory = Memory(embedder=my_embedder)
```
</Accordion>
</AccordionGroup>

### Referência de Provedores

| Provedor | Chave | Modelo Típico | Notas |
| :--- | :--- | :--- | :--- |
| OpenAI | `openai` | `text-embedding-3-small` | Padrão. Defina `OPENAI_API_KEY`. |
| Ollama | `ollama` | `mxbai-embed-large` | Local, sem API key. |
| Azure OpenAI | `azure` | `text-embedding-ada-002` | Requer `deployment_id`. |
| Google AI | `google-generativeai` | `gemini-embedding-001` | Defina `GOOGLE_API_KEY`. |
| Google Vertex | `google-vertex` | `gemini-embedding-001` | Requer `project_id`. |
| Cohere | `cohere` | `embed-english-v3.0` | Forte suporte multilíngue. |
| VoyageAI | `voyageai` | `voyage-3` | Otimizado para retrieval. |
| AWS Bedrock | `amazon-bedrock` | `amazon.titan-embed-text-v1` | Usa credenciais boto3. |
| Hugging Face | `huggingface` | `all-MiniLM-L6-v2` | Sentence-transformers local. |
| Jina | `jina` | `jina-embeddings-v2-base-en` | Defina `JINA_API_KEY`. |
| IBM WatsonX | `watsonx` | `ibm/slate-30m-english-rtrvr` | Requer `project_id`. |
| Sentence Transformer | `sentence-transformer` | `all-MiniLM-L6-v2` | Local, sem API key. |
| Custom | `custom` | -- | Requer `embedding_callable`. |


## Configuração de LLM

A memória usa um LLM para análise de save (inferência de escopo, categorias e importância), decisões de consolidação e análise de consulta no recall profundo. Você pode configurar qual modelo usar.

```python
from crewai import Memory, LLM

# Padrão: gpt-4o-mini
memory = Memory()

# Usar um modelo OpenAI diferente
memory = Memory(llm="gpt-4o")

# Usar Anthropic
memory = Memory(llm="anthropic/claude-3-haiku-20240307")

# Usar Ollama para análise totalmente local/privada
memory = Memory(llm="ollama/llama3.2")

# Usar Google Gemini
memory = Memory(llm="gemini/gemini-2.0-flash")

# Passar uma instância LLM pré-configurada com configurações customizadas
llm = LLM(model="gpt-4o", temperature=0)
memory = Memory(llm=llm)
```

O LLM é inicializado **lazily** -- ele só é criado quando necessário pela primeira vez. Isso significa que `Memory()` nunca falha no momento da construção, mesmo que chaves de API não estejam definidas. Erros só aparecem quando o LLM é realmente chamado (ex.: ao salvar sem escopo/categorias explícitos, ou durante recall profundo).

Para operação totalmente offline/privada, use um modelo local tanto para o LLM quanto para o embedder:

```python
memory = Memory(
    llm="ollama/llama3.2",
    embedder={"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}},
)
```


## Backend de Armazenamento

- **Padrão**: LanceDB, armazenado em `./.crewai/memory` (ou `$CREWAI_STORAGE_DIR/memory` se a variável de ambiente estiver definida, ou o caminho que você passar como `storage="path/to/dir"`).
- **Backend customizado**: Implemente o protocolo `StorageBackend` (veja `crewai.memory.storage.backend`) e passe uma instância para `Memory(storage=your_backend)`.


## Descoberta

Inspecione a hierarquia de escopos, categorias e registros:

```python
memory.tree()                        # Árvore formatada de escopos e contagem de registros
memory.tree("/project", max_depth=2) # Visão de subárvore
memory.info("/project")              # ScopeInfo: record_count, categories, oldest/newest
memory.list_scopes("/")              # Escopos filhos imediatos
memory.list_categories()             # Nomes e contagens de categorias
memory.list_records(scope="/project/alpha", limit=20)  # Registros em um escopo, mais recentes primeiro
```


## Comportamento em Caso de Falha

Se o LLM falhar durante a análise (erro de rede, limite de taxa, resposta inválida), a memória degrada graciosamente:

- **Análise de save** -- Um aviso é registrado e a memória ainda é armazenada com escopo padrão `/`, categorias vazias e importância `0.5`.
- **Extrair memórias** -- O conteúdo completo é armazenado como uma única memória para que nada seja descartado.
- **Análise de consulta** -- O recall usa fallback para seleção simples de escopo e busca vetorial, então você ainda obtém resultados.

Nenhuma exceção é levantada para essas falhas de análise; apenas falhas de armazenamento ou do embedder irão levantar.


## Nota sobre Privacidade

O conteúdo da memória é enviado ao LLM configurado para análise (escopo/categorias/importância no save, análise de consulta e recall profundo opcional). Para dados sensíveis, use um LLM local (ex.: Ollama) ou garanta que seu provedor atenda aos requisitos de conformidade.


## Eventos de Memória

Todas as operações de memória emitem eventos com `source_type="unified_memory"`. Você pode escutar para timing, erros e conteúdo.

| Evento | Descrição | Propriedades Principais |
| :---- | :---------- | :------------- |
| **MemoryQueryStartedEvent** | Consulta inicia | `query`, `limit` |
| **MemoryQueryCompletedEvent** | Consulta bem-sucedida | `query`, `results`, `query_time_ms` |
| **MemoryQueryFailedEvent** | Consulta falha | `query`, `error` |
| **MemorySaveStartedEvent** | Save inicia | `value`, `metadata` |
| **MemorySaveCompletedEvent** | Save bem-sucedido | `value`, `save_time_ms` |
| **MemorySaveFailedEvent** | Save falha | `value`, `error` |
| **MemoryRetrievalStartedEvent** | Retrieval do agente inicia | `task_id` |
| **MemoryRetrievalCompletedEvent** | Retrieval do agente completo | `task_id`, `memory_content`, `retrieval_time_ms` |

Exemplo: monitorar tempo de consulta:

```python
from crewai.events import BaseEventListener, MemoryQueryCompletedEvent

class MemoryMonitor(BaseEventListener):
    def setup_listeners(self, crewai_event_bus):
        @crewai_event_bus.on(MemoryQueryCompletedEvent)
        def on_done(source, event):
            if getattr(event, "source_type", None) == "unified_memory":
                print(f"Query '{event.query}' completou em {event.query_time_ms:.0f}ms")
```


## Solução de Problemas

**Memória não persiste?**
- Garanta que o caminho de armazenamento seja gravável (padrão `./.crewai/memory`). Passe `storage="./your_path"` para usar outro diretório, ou defina a variável de ambiente `CREWAI_STORAGE_DIR`.
- Ao usar uma crew, confirme que `memory=True` ou `memory=Memory(...)` está definido.

**Recall lento?**
- Use `depth="shallow"` para contexto rotineiro do agente. Reserve `depth="deep"` para consultas complexas.
- Aumente `query_analysis_threshold` para pular a análise do LLM em mais consultas.

**Erros de análise LLM nos logs?**
- A memória ainda salva/recupera com padrões seguros. Verifique chaves de API, limites de taxa e disponibilidade do modelo se quiser análise LLM completa.

**Erros de save em background nos logs?**
- Os saves de memória rodam em uma thread em background. Erros são emitidos como `MemorySaveFailedEvent` mas não derrubam o agente. Verifique os logs para a causa raiz (geralmente problemas de conexão com LLM ou embedder).

**Conflitos de escrita concorrente?**
- As operações do LanceDB são serializadas com um lock compartilhado e reexecutadas automaticamente em caso de conflito. Isso lida com múltiplas instâncias `Memory` apontando para o mesmo banco de dados (ex.: memória do agente + memória da crew). Nenhuma ação necessária.

**Navegar na memória pelo terminal:**
```bash
crewai memory                              # Abre o navegador TUI
crewai memory --storage-path ./my_memory   # Apontar para um diretório específico
```

**Resetar memória (ex.: para testes):**
```python
crew.reset_memories(command_type="memory")  # Reseta memória unificada
# Ou em uma instância Memory:
memory.reset()                    # Todos os escopos
memory.reset(scope="/project/old")  # Apenas essa subárvore
```


## Referência de Configuração

Toda a configuração é passada como argumentos nomeados para `Memory(...)`. Cada parâmetro tem um padrão sensato.

| Parâmetro | Padrão | Descrição |
| :--- | :--- | :--- |
| `llm` | `"gpt-4o-mini"` | LLM para análise (nome do modelo ou instância `BaseLLM`). |
| `storage` | `"lancedb"` | Backend de armazenamento (`"lancedb"`, string de caminho ou instância `StorageBackend`). |
| `embedder` | `None` (OpenAI padrão) | Embedder (dict de config, callable ou `None` para OpenAI padrão). |
| `recency_weight` | `0.3` | Peso da recência na pontuação composta. |
| `semantic_weight` | `0.5` | Peso da similaridade semântica na pontuação composta. |
| `importance_weight` | `0.2` | Peso da importância na pontuação composta. |
| `recency_half_life_days` | `30` | Dias para a pontuação de recência cair pela metade (decaimento exponencial). |
| `consolidation_threshold` | `0.85` | Similaridade acima da qual a consolidação é ativada no save. Defina `1.0` para desativar. |
| `consolidation_limit` | `5` | Máx. de registros existentes para comparar durante consolidação. |
| `default_importance` | `0.5` | Importância atribuída quando não fornecida e a análise LLM é pulada. |
| `batch_dedup_threshold` | `0.98` | Similaridade de cosseno para descartar quase-duplicatas dentro de um batch `remember_many()`. |
| `confidence_threshold_high` | `0.8` | Confiança de recall acima da qual resultados são retornados diretamente. |
| `confidence_threshold_low` | `0.5` | Confiança de recall abaixo da qual exploração mais profunda é ativada. |
| `complex_query_threshold` | `0.7` | Para consultas complexas, explorar mais profundamente abaixo desta confiança. |
| `exploration_budget` | `1` | Número de rodadas de exploração por LLM durante recall profundo. |
| `query_analysis_threshold` | `200` | Consultas menores que isso (em caracteres) pulam análise LLM durante recall profundo. |
