--- 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 ```python memory = Memory(embedder={ "provider": "openai", "config": { "model_name": "text-embedding-3-small", # "api_key": "sk-...", # ou defina OPENAI_API_KEY }, }) ``` ```python memory = Memory(embedder={ "provider": "ollama", "config": { "model_name": "mxbai-embed-large", "url": "http://localhost:11434/api/embeddings", }, }) ``` ```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", }, }) ``` ```python memory = Memory(embedder={ "provider": "google-generativeai", "config": { "model_name": "gemini-embedding-001", # "api_key": "...", # ou defina GOOGLE_API_KEY }, }) ``` ```python memory = Memory(embedder={ "provider": "google-vertex", "config": { "model_name": "gemini-embedding-001", "project_id": "your-gcp-project-id", "location": "us-central1", }, }) ``` ```python memory = Memory(embedder={ "provider": "cohere", "config": { "model_name": "embed-english-v3.0", # "api_key": "...", # ou defina COHERE_API_KEY }, }) ``` ```python memory = Memory(embedder={ "provider": "voyageai", "config": { "model": "voyage-3", # "api_key": "...", # ou defina VOYAGE_API_KEY }, }) ``` ```python memory = Memory(embedder={ "provider": "amazon-bedrock", "config": { "model_name": "amazon.titan-embed-text-v1", # Usa credenciais AWS padrão (sessão boto3) }, }) ``` ```python memory = Memory(embedder={ "provider": "huggingface", "config": { "model_name": "sentence-transformers/all-MiniLM-L6-v2", }, }) ``` ```python memory = Memory(embedder={ "provider": "jina", "config": { "model_name": "jina-embeddings-v2-base-en", # "api_key": "...", # ou defina JINA_API_KEY }, }) ``` ```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", }, }) ``` ```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) ``` ### 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. |