Objetivo da Aula
Diferenciar estado volátil (de sessão) e memória persistente (entre sessões) num agente de IA
Explicar por que o LLM em si não tem estado, e por que toda "memória" de um agente é responsabilidade do harness
Projetar um modelo transacional de escrita de estado que sobrevive a falhas no meio de uma tarefa
Escolher entre estratégias de persistência (arquivo, banco relacional, banco vetorial) conforme o tipo de dado
Reconhecer os riscos de estado inconsistente entre o que o agente "acha" que fez e o que de fato aconteceu
Por que isso importa
Um LLM é, por construção, sem estado (stateless): cada chamada de API é independente, e o modelo não "lembra" de nada entre uma chamada e outra, exceto o que está explicitamente no contexto daquela chamada. Toda ilusão de continuidade — um agente que "lembra" o que fez ontem, que retoma uma tarefa longa de onde parou, que aprende preferências de um usuário ao longo do tempo — é inteiramente construída pelo harness, não pelo modelo.
Essa distinção parece óbvia quando dita em voz alta, mas é sistematicamente esquecida na hora de arquitetar. Times constroem agentes assumindo implicitamente que "o agente sabe o que já fez", quando na verdade o agente só sabe o que o harness reinjetou no contexto da chamada atual. Se essa reinjeção falhar, for incompleta, ou estiver dessincronizada da realidade (o harness "acha" que um arquivo foi escrito, mas a escrita falhou silenciosamente), o agente vai operar sobre uma versão da realidade que não existe — com total confiança, porque para o modelo, o que está no contexto É a realidade.
Impacto direto no seu trabalho: toda vez que você projeta um agente que executa uma tarefa de múltiplas etapas — refatorar um módulo, processar um lote de tickets, migrar dados — a pergunta de arquiteto não é "o agente vai lembrar o que fez até agora", é "como eu garanto que o registro do que foi feito é sempre verdadeiro, mesmo se o processo cair no meio?". Essa é uma pergunta sobre engenharia de dados transacionais, não sobre capacidade do modelo.
Conceitos Fundamentais
Duas categorias de estado
ESTADO VOLÁTIL (escopo: uma sessão/tarefa)
- Histórico de turnos da conversa atual
- Resultado da última ferramenta chamada
- Variáveis de progresso ("etapa 3 de 7")
- Arquivos abertos/em edição nesta execução
Vive em: memória de processo, cache, ou tabela temporária. Pode ser perdido sem violar nenhum contrato com o usuário — a sessão simplesmente reinicia.
MEMÓRIA PERSISTENTE (escopo: entre sessões)
- Decisões e resultados de tarefas concluídas
- Preferências aprendidas de um usuário/organização
- Histórico de auditoria (quem pediu o quê, quando)
- Estado de tarefas de longa duração ainda em curso (ex: uma migração que roda por dias)
Vive em: banco de dados, arquivo versionado, ou armazenamento durável. Perder isso É uma violação de contrato — dados de negócio real dependem disso.
A fronteira entre as duas categorias é uma decisão de arquitetura, não uma verdade universal: um dado que é volátil num agente de suporte simples (o histórico da conversa atual) pode precisar ser persistente num agente que constrói perfil de atendimento ao longo de meses. O critério prático: se perder esse dado exige que um humano refaça trabalho ou perca uma decisão de negócio, é memória persistente; se perder esse dado só significa recomeçar a sessão, é estado volátil.
Fundamento: Statelessness do LLM
"Stateless" significa que o componente não retém informação entre invocações — cada chamada é processada isoladamente, sem conhecimento do que aconteceu antes, exceto o que é explicitamente fornecido como entrada. É a mesma propriedade de uma função pura em programação funcional ou de um handler HTTP sem sessão. A implicação arquitetural é direta: se o harness não reconstruir e reinjetar o histórico relevante a cada chamada, esse histórico, para efeitos práticos do modelo, nunca existiu. Não existe "memória de fundo" no LLM que o harness possa simplesmente consultar — tudo precisa passar pelo contexto explícito.
O problema da dessincronização: estado percebido vs. estado real
O risco mais sutil em gestão de estado de agentes não é "perder dado" — é ter um estado que parece correto no registro do harness, mas não corresponde à realidade do sistema externo. Isso acontece quando uma ação é registrada como concluída antes de sua confirmação real ter sido verificada:
Sequência com risco de dessincronização:
1. Agente decide: "vou escrever o arquivo X"
2. Harness REGISTRA no estado: "arquivo X escrito" ✅
3. Harness CHAMA a operação de escrita
4. A escrita FALHA (disco cheio, permissão negada,
timeout de rede)
5. Próximo turno: harness reinjeta estado dizendo
"arquivo X foi escrito com sucesso"
6. Agente prossegue assumindo que X existe — e todas as
decisões seguintes partem de uma premissa falsa
Sequência correta (registro após confirmação):
1. Agente decide: "vou escrever o arquivo X"
2. Harness CHAMA a operação de escrita
3. Harness AGUARDA confirmação real da operação
4a. Sucesso confirmado → harness REGISTRA "arquivo X
escrito" ✅
4b. Falha → harness REGISTRA "escrita de X falhou,
motivo: disco cheio" e agente recebe esse fato no
próximo turno para decidir como reagirA regra geral: o estado só é atualizado depois que a ação real é confirmada, nunca antes. Essa é uma regra básica de sistemas transacionais tradicionais (nunca marcar uma transação como "commitada" antes do commit real acontecer), e se aplica integralmente à gestão de estado de agentes de IA — só que aqui o custo de errar é maior, porque o "cliente" do estado errado é um modelo que vai tomar decisões subsequentes com confiança total na informação recebida.
Aprofundamento Técnico
Escolhendo onde persistir: arquivo, relacional, vetorial
Nem todo estado persistente merece o mesmo tipo de armazenamento. A escolha certa depende do padrão de acesso e do tipo de consulta que o harness vai precisar fazer:
| Tipo de estado | Armazenamento recomendado |
|---|---|
| Log de auditoria append-only | Arquivo/log estruturado (JSONL) ou tabela de eventos append-only |
| Estado de tarefa em progresso (retomável, com etapas) | Banco relacional (linha por tarefa, coluna de etapa atual, transações ACID para updates) |
| Preferências/config por usuário | Banco relacional ou key-value (acesso por chave, baixa complexidade de consulta) |
| Memória semântica de longo prazo ("o que já discutimos sobre X") | Banco vetorial (busca por similaridade — aprofundado na Parte 2, RAG e embeddings) |
| Snapshot de arquivos versionados (código, specs) | Sistema de controle de versão (git) — não reinventar o que já existe |
Um erro comum de arquitetos que vêm de "prototipagem com IA" é tentar usar busca vetorial/semântica para todo tipo de estado, inclusive dados que têm estrutura clara e consulta exata (por exemplo, "qual é a etapa atual da tarefa 4521"). Isso é engenharia excessiva: uma consulta exata por ID pertence a um banco relacional simples, com transações ACID garantindo que o estado da tarefa nunca fique inconsistente mesmo sob concorrência ou falha no meio da escrita. Busca vetorial resolve um problema diferente — recuperar informação por similaridade semântica quando não se sabe a chave exata — e será tratada em profundidade na Parte 2 deste livro.
Transações e retomada de tarefas longas
Para tarefas que rodam por muito tempo (minutos a dias) e que podem ser interrompidas — por falha, por limite de recursos, por decisão de pausar — o estado precisa ser modelado como uma máquina de etapas persistida, não como uma variável em memória do processo:
Exemplo de linha na tabela tarefas_agente:
| id | etapa_atual | status | checkpoint (JSON) |
|---|---|---|---|
| 4521 | 3 de 7 | em_execucao | {"arquivos_ja_processados": ["a.py", "b.py"]} |
function retomar_tarefa(id):
tarefa = ler_do_banco(id) # SEMPRE a fonte de verdade,
# nunca memória de processo
if tarefa.status == "em_execucao":
contexto = reconstruir_contexto(tarefa.checkpoint)
continuar_da_etapa(tarefa.etapa_atual, contexto)
elif tarefa.status == "falhou":
avaliar_se_retoma_ou_escala_para_humano(tarefa)Se o processo que executa o agente cair no meio da etapa 3, a tarefa não se perde — ela é retomada a partir do último checkpoint confirmado no banco, não a partir de alguma suposição sobre "onde o agente parou". Isso exige que cada etapa seja desenhada com um ponto de checkpoint claro: um estado intermediário que pode ser persistido e a partir do qual a execução pode continuar sem repetir trabalho já concluído nem pular trabalho pendente.
Trade-off: consistência forte vs. custo de latência
Persistir estado após cada micro-decisão do agente (consistência forte, uma escrita transacional por passo) tem custo de latência — cada turno do agente espera uma confirmação de escrita em disco/banco antes de prosseguir. Para agentes de baixo risco (Nível 0-1 do capítulo 1.3), esse custo pode não valer a pena; um checkpoint a cada N passos, ou apenas ao final de blocos lógicos de trabalho, é suficiente. Para agentes de alto raio de impacto, ou que operam sobre dados que não podem ser recriados, a consistência forte a cada passo é o preço de entrada — a alternativa (perder o registro de uma ação irreversível já executada) é estritamente pior que a latência extra. Essa é, novamente, uma decisão calibrada pelo raio de impacto da tarefa, não uma regra única aplicada a todo sistema.
Padrões e Armadilhas
Padrões recomendados
Padrão 1: separe claramente "estado de progresso" de "estado de resultado". O checkpoint de uma tarefa longa (em que etapa ela está) e o resultado de negócio que ela produz (o dado final que outros sistemas vão consumir) merecem tabelas ou coleções distintas. Misturar os dois numa única estrutura torna difícil responder, de forma simples, "quais tarefas ainda estão em progresso" sem varrer dados de resultado que não têm relação com o controle de fluxo.
Padrão 2: trate todo checkpoint como idempotente. Se o harness tentar registrar o mesmo checkpoint duas vezes (por exemplo, por causa de uma retentativa após timeout de rede, onde a escrita original na verdade teve sucesso), a segunda escrita não deveria duplicar efeito nem gerar erro fatal — deveria ser reconhecida como já aplicada e seguir em frente. Isso normalmente se resolve com uma chave de idempotência (um identificador único por etapa) verificada antes de cada escrita.
Padrão 3: exponha o estado de tarefas de longa duração para observabilidade humana. Uma tarefa que roda por horas ou dias sem nenhuma superfície visível de "onde ela está agora" é, na prática, uma caixa-preta operacional. Um painel simples que lê diretamente da tabela de estado (sem depender do agente "reportar" seu próprio progresso via texto) permite que um humano perceba uma tarefa travada antes que o limite de tentativas ou o prazo de negócio seja estourado.
Armadilhas comuns
⚠️ Armadilha 1: guardar estado crítico apenas em memória do processo. O que acontece: o progresso da tarefa é mantido numa variável do processo do agente, sem persistência em banco. Um restart de deploy, um crash, ou uma escalabilidade horizontal (o próximo turno é atendido por outra instância do processo) apaga esse progresso silenciosamente. Versão correta: qualquer estado cuja perda exija retrabalho humano precisa estar em armazenamento durável, nunca só em memória de processo.
⚠️ Armadilha 2: confiar no texto gerado pelo modelo como fonte de verdade do estado. O que acontece: o harness lê a resposta do modelo ("terminei a etapa 3, prossegui para a etapa 4") e atualiza o estado com base no que o modelo disse ter feito, sem confirmar a ação real. Isso reintroduz exatamente o risco de dessincronização descrito na seção anterior — o texto gerado por um componente probabilístico não é, por si só, uma confirmação de efeito real. Versão correta: o estado só avança quando o harness confirma, de forma independente, que a ação correspondente realmente ocorreu.
⚠️ Armadilha 3: checkpoint granular demais, aumentando custo sem aumentar segurança. O que acontece: um checkpoint é persistido a cada micro-ação (cada leitura de arquivo, cada linha processada), multiplicando escritas transacionais sem que a granularidade extra reduza de fato o risco de perda de trabalho relevante. Versão correta: o checkpoint deveria corresponder a unidades de trabalho que, se perdidas, exigiriam retrabalho não trivial — nem mais granular, nem menos, que essa unidade.