AI Agents

Como dar memória a um agente de IA: padrões de persistência de estado para produção

Alejandro Rioja
Alejandro Rioja
8 min de leitura
TL;DR

Agentes sem estado — aqueles que esquecem tudo quando o Worker termina — funcionam bem para tarefas únicas. No momento em que um agente precisa lembrar o que aconteceu ontem, reconhecer um cliente recorrente ou construir sobre resultados anteriores, você precisa de memória. Existem três padrões: memória de trabalho (contexto em andamento, vive no KV durante a execução), memória episódica (o que aconteceu e quando, um log consultável) e memória semântica (o que você sabe, recuperado via busca vetorial ou dados estruturados). Conecte o padrão certo ao trabalho certo.

Newsletter gratuita

Toda quarta-feira. 28.400+ operadores. Zero enrolação.

Índice

Atualizado junho 2026.

TL;DR: Agentes sem estado — aqueles que esquecem tudo quando o Worker termina — funcionam bem para tarefas únicas. No momento em que um agente precisa lembrar o que aconteceu ontem, reconhecer um cliente recorrente ou construir sobre resultados anteriores, você precisa de memória. Existem três padrões: memória de trabalho (contexto em andamento, vive no KV durante a execução), memória episódica (o que aconteceu e quando, um log consultável) e memória semântica (o que você sabe, recuperado via busca vetorial ou dados estruturados). Conecte o padrão certo ao trabalho certo.

[Perspectiva do operador] Já bati contra o muro do agente sem estado mais de uma vez. O agente de resposta social que continuava se apresentando para clientes com quem havia falado 20 vezes. O agente de briefing diário que sinalizava o mesmo problema quatro dias seguidos porque não tinha memória de tê-lo sinalizado ontem. Adicionar o tipo certo de memória resolveu ambos. É isso que eu uso.

Por que agentes sem estado continuam falhando

Um agente sem estado começa cada execução apenas com o que você passa explicitamente: o prompt do sistema, a mensagem do usuário e os dados que você obtém no momento da invocação. Ele não tem consciência de execuções anteriores, usuários anteriores ou decisões anteriores.

Para uma tarefa de classificação única — ler um comentário, retornar uma categoria — sem estado está correto. É rápido, barato e previsível.

A superfície de falha aparece no momento em que você precisa de continuidade:

  • Um agente orientado ao cliente que não reconhece o histórico do cliente
  • Um agente de conteúdo que recomenda um artigo que já recomendou semana passada
  • Um agente de moderação que continua re-escalando um caso resolvido
  • Um briefing diário que exibe o mesmo alerta obsoleto indefinidamente

Todos esses são sintomas do mesmo problema: o agente não tem como carregar contexto entre execuções.

Três tipos de memória

O framework que acho útil em produção:

  1. Memória de trabalho — o que o agente sabe agora, durante uma única execução. Mantida no KV ou na memória durante a vida da invocação.
  2. Memória episódica — o que aconteceu e quando. Um log estruturado que o agente lê no início de cada execução para se orientar.
  3. Memória semântica — o que ele sabe sobre o mundo, clientes ou uma base de conhecimento. Recuperada via consultas estruturadas ou busca vetorial quando relevante.

Você nem sempre precisa das três. A maioria dos agentes que executo precisa de trabalho + episódica. A memória semântica é a mais difícil de construir e só merece seu lugar quando a base de conhecimento é grande demais para caber na janela de contexto.

Memória de trabalho: contexto em andamento

A memória de trabalho é o estado que vive durante a duração de uma execução do agente. A forma mais simples são variáveis no escopo da função. A forma mais interessante é uma chave KV compartilhada que subtarefas dentro da mesma execução leem e escrevem.

Meu agente de resposta social usa memória de trabalho para acumular contexto ao processar um lote de comentários em uma mensagem de fila. Ele lê o histórico de conversas recente de cada cliente do KV no início, adiciona novo contexto durante o processamento e escreve de volta no final.

typescript
// workers/social-reply.ts

async function processComment(
  comment: SocialCommentEvent,
  env: Env
): Promise<void> {
  // Carregar histórico recente deste cliente do KV (memória de trabalho)
  const historyKey = `customer:${comment.userId}:history`;
  const rawHistory = await env.AGENT_KV.get(historyKey);
  const history: ConversationTurn[] = rawHistory
    ? JSON.parse(rawHistory)
    : [];

  // Construir prompt do sistema contextual a partir do histórico
  const systemPrompt = buildSystemPrompt(history);

  const response = await anthropic.messages.create({
    model: "claude-opus-4-8",
    max_tokens: 512,
    system: systemPrompt,
    messages: [{ role: "user", content: comment.text }],
  });

  const reply =
    response.content[0].type === "text" ? response.content[0].text : "";

  // Atualizar histórico — manter últimos 10 turnos, TTL 30 dias
  const updatedHistory: ConversationTurn[] = [
    ...history.slice(-9),
    { role: "assistant", content: reply, timestamp: comment.timestamp },
  ];
  await env.AGENT_KV.put(historyKey, JSON.stringify(updatedHistory), {
    expirationTtl: 60 * 60 * 24 * 30,
  });

  await postReply(comment, reply, env);
}

Dois pontos a notar. O histórico está limitado a 10 turnos — injete uma janela deslizante, não deixe crescer ilimitadamente. E o TTL é de 30 dias: se um cliente ficar em silêncio por um mês, o histórico expira e o agente começa do zero. Ambos são intencionais.

Memória episódica: o que aconteceu e quando

A memória episódica é o log do agente. Um registro estruturado de execuções passadas que o agente lê no início de cada nova execução para evitar se repetir.

Meu agente de briefing diário estava exibindo os mesmos alertas obsoletos todos os dias porque cada execução não tinha consciência do que já havia sido sinalizado. A solução: um log estruturado de alertas passados que o agente lê antes de gerar o briefing.

typescript
// workers/daily-brief.ts

interface AlertLogEntry {
  id: string;
  surfacedAt: string; // timestamp ISO
  resolvedAt?: string;
  summary: string;
}

async function buildDailyBrief(env: Env): Promise<void> {
  const [emails, calendar, tasks] = await Promise.all([
    fetchOvernightEmails(env),
    fetchTodayCalendar(env),
    fetchTopTasks(env),
  ]);

  // Carregar memória episódica: o que já foi sinalizado
  const rawLog = await env.AGENT_KV.get("brief:alert-log");
  const alertLog: AlertLogEntry[] = rawLog ? JSON.parse(rawLog) : [];

  // Filtrar apenas alertas recentes e não resolvidos
  const sevenDaysAgo = new Date(
    Date.now() - 7 * 24 * 60 * 60 * 1000
  ).toISOString();
  const recentAlerts = alertLog.filter(
    (e) => e.surfacedAt > sevenDaysAgo && !e.resolvedAt
  );

  const brief = await synthesizeBrief(
    { emails, calendar, tasks, recentAlerts },
    env
  );

  // Atualizar o log com novos alertas sinalizados nesta execução
  const newAlerts: AlertLogEntry[] = brief.newAlerts.map((a) => ({
    id: crypto.randomUUID(),
    surfacedAt: new Date().toISOString(),
    summary: a,
  }));

  const updatedLog = [...alertLog, ...newAlerts].slice(-100); // manter últimos 100
  await env.AGENT_KV.put("brief:alert-log", JSON.stringify(updatedLog));

  await writeToWorkspace(brief.content, env);
}

O agente agora sabe o que já disse. Alertas duplicados ficam fora do briefing até que o problema subjacente mude. Quando marco um alerta como resolvido, ele sai da lista ativa.

Este padrão se generaliza: qualquer agente que produz decisões, sinalizações ou recomendações se beneficia de um log. O log é barato (alguns KB no KV), o retorno é alto (sem mais saídas redundantes).

Memória semântica: o que você sabe

A memória semântica é a base de conhecimento. Ela responde “o que você sabe sobre X?” no momento da consulta, em vez de colocar tudo no prompt do sistema antecipadamente.

A forma mais simples é uma busca estruturada no KV ou em um banco de dados. Meu agente de reservas do Pickleland consulta perfis de clientes e preferências de quadra antes de redigir confirmações:

typescript
// workers/booking-agent.ts

interface CustomerProfile {
  userId: string;
  preferredCourts: string[];
  experienceLevel: "beginner" | "intermediate" | "advanced";
  specialNotes: string;
}

async function draftConfirmation(
  booking: BookingEvent,
  env: Env
): Promise<string> {
  // Obter perfil do cliente do KV (memória semântica — conhecimento factual)
  const profileKey = `customer:${booking.userId}:profile`;
  const rawProfile = await env.AGENT_KV.get(profileKey);
  const profile: CustomerProfile | null = rawProfile
    ? JSON.parse(rawProfile)
    : null;

  const systemPrompt = profile
    ? `Você redige confirmações de reserva personalizadas. Este cliente prefere ${profile.preferredCourts.join(", ")}, é um jogador ${profile.experienceLevel}. ${profile.specialNotes}`
    : "Você redige confirmações de reserva para uma instalação de pickleball.";

  const response = await anthropic.messages.create({
    model: "claude-haiku-4-5-20251001",
    max_tokens: 256,
    system: systemPrompt,
    messages: [
      {
        role: "user",
        content: `Redija uma confirmação para: ${JSON.stringify(booking)}`,
      },
    ],
  });

  return response.content[0].type === "text" ? response.content[0].text : "";
}

Para bases de conhecimento maiores — documentação de produtos, base de conhecimento de suporte, qualquer coisa grande demais para caber em uma janela de contexto — você precisa de um armazenamento vetorial. O fluxo de trabalho é: incorporar a consulta, recuperar os k fragmentos mais relevantes, injetá-los no contexto. O Cloudflare Vectorize lida com isso nativamente se você já está no Workers. Para índices maiores, usei o Upstash Vector. A escolha depende da escala, não do princípio.

A nota honesta sobre memória semântica: é a mais difícil das três de construir e manter. O índice precisa ficar atualizado. A qualidade de recuperação varia. Comece com buscas estruturadas — KV, uma tabela no D1 — e só alcance a busca vetorial quando a abordagem estruturada não conseguir cobrir a superfície de conhecimento que você precisa.

O framework de decisão de memória

Antes de adicionar qualquer memória a um agente, responda três perguntas:

  1. O agente precisa se lembrar entre execuções? Se cada invocação é genuinamente independente — uma tradução, uma classificação, uma geração única — ignore a memória. Sem estado é mais simples e mais barato.

  2. O agente está se repetindo ou agindo cego ao seu próprio histórico? Se sim, adicione memória episódica primeiro. É a correção de menor esforço e cobre a maioria das reclamações de “o agente continua fazendo X”.

  3. O agente está tratando todos os usuários ou entidades de forma idêntica quando não deveria? Se sim, adicione memória de trabalho (histórico do cliente, perfil do usuário) ou memória semântica (um sistema de busca ou recuperação).

O erro que mais vejo na prática: alguém adiciona uma enorme base de conhecimento (memória semântica) a um agente que estava falhando porque não tinha memória episódica — nenhum log do que já havia feito. A complexidade não corresponde ao problema.

O que realmente uso em produção

Em 30+ agentes:

  • Todos têm pelo menos memória de trabalho — alguma forma de estado dentro de uma execução, mesmo que seja apenas a própria janela de contexto.
  • Cerca de metade tem memória episódica — um log de execuções passadas, decisões ou sinalizações. Isso quase sempre vale a pena adicionar.
  • Três ou quatro têm memória semântica real apoiada por um armazenamento vetorial. Esses são os agentes que respondem perguntas sobre uma base de conhecimento grande e dinâmica.

O Cloudflare KV é meu armazenamento padrão para memória de trabalho e episódica. É rápido, barato e integrado nativamente ao Workers — sem cliente extra, sem credencial separada. A limitação: KV é eventualmente consistente e não é ótimo para gravações de alta frequência. Para agentes que escrevem estado muitas vezes por segundo, uso Durable Objects ou um banco de dados D1 em vez disso.

Para memória semântica apoiada por vetores, uso o Cloudflare Vectorize para índices pequenos a médios (menos de ~100K vetores) e Upstash Vector para tudo que for maior. Ambos têm clientes JavaScript de primeira classe.

A conclusão do operador

Adicione memória a um agente somente quando o comportamento sem estado estiver causando problemas reais — saídas repetidas, pontos cegos no histórico do cliente, ignorância de decisões passadas. Em seguida, escolha a camada certa: memória de trabalho para contexto em execução, episódica para o que aconteceu historicamente, semântica para o que você sabe. Comece com episódica se não tiver certeza — ela corrige o modo de falha mais comum com menos complexidade. Não recorra a um banco de dados vetorial até esgotar as buscas estruturadas. O melhor sistema de memória é o mais simples que faz o agente se comportar corretamente.


Relacionado: O stack de agentes que uso para rodar 30+ agentes em produção · Agentes por evento vs. agendados · Como meço se um agente de IA está realmente funcionando

Precisa de ajuda para projetar memória de agentes para seu caso de uso? Entre em contato — projeto sistemas de agentes em produção para equipes operadoras.

Continue lendo

Posts relacionados

Continue lendo

Receba o manual de IA na sua caixa de entrada

Toda quarta-feira. 28.400+ operadores. Zero enrolação.

↵ ver todos os resultados esc esc para fechar