MÓDULO 5.1 GA

💾 Estratégias de memória: curto, longo prazo e summarização hierárquica

Como dar 'memória' a um agente: buffer de turn, summarização hierárquica, vetor de longo prazo, recall sob demanda.

6
Tópicos
60
Minutos
Intermediário
Nível
Prático
Tipo

Modelos não têm memória entre chamadas — você reconstrói a janela cada vez. Aqui você aprende a simular memória: buffer de últimos turns, sumarização incremental, vetor para longo prazo, recall por similaridade. Um chat 'que lembra' é resultado dessas técnicas combinadas.

🏗️ Camadas de memória

Não é uma estratégia única — é um stack de camadas, cada uma com trade-off de custo, fidelidade e velocidade.

💡 Não invente memória — comece com buffer

Para a maioria dos casos, buffer de 10 últimos turns + system prompt resolve. Adicione sumarização quando buffer estourar; vetor quando precisar de meses-atrás real.

1

📥 Buffer de turns: memória de curto prazo

Últimos N turns

Para chats curtos (5-10 turns), buffer FIFO dos últimos N turns inteiros é o suficiente. Modelos atendem bem em janela <10k. Implementação trivial: deque(maxlen=N). Não invente memória vetorial dia 1.

buffer simples
from collections import deque

buffer = deque(maxlen=10)

def turn(user_msg: str) -> str:
    buffer.append({'role': 'user', 'content': user_msg})
    msgs = [{'role':'system', 'content': SYSTEM}] + list(buffer)
    resp = client.chat(messages=msgs)
    buffer.append({'role': 'assistant', 'content': resp.content})
    return resp.content
📑 Resumo navegável
O que é: Manter os últimos N turns inteiros na janela. Simples, eficaz para conversas curtas.
Por que aprender: Em chats curtos (5-10 turns), buffer puro basta. Modelos atendem bem com janela <10k.
Conceitos-chave: Sliding window, message buffer, FIFO eviction.
2

📝 Summarização incremental

Comprimir o que sai do buffer

Quando o buffer estoura, em vez de descartar mensagens antigas, sumarize. Sumário fica no system prompt; perde detalhe mas mantém continuidade. Trigger típico: a cada 10 turns, sumarize os 5 mais antigos em parágrafo curto.

sumarização por trigger
def maybe_sumarizar(state):
    if len(state.buffer) >= 10:
        antigos = list(state.buffer)[:5]
        novos = list(state.buffer)[5:]
        sumario = llm.generate(
            'Sumarize esta conversa em 1 parágrafo:\n' + format(antigos)
        )
        state.sumario = state.sumario + '\n' + sumario
        state.buffer = deque(novos, maxlen=10)
📑 Resumo navegável
O que é: Quando buffer estoura, sumariza turns antigos em parágrafo. Sumário fica no system prompt.
Por que aprender: Mantém continuidade sem inflar janela. Trade-off: perde detalhe do que foi sumarizado.
Conceitos-chave: Recursive summarization, sliding-window summarization, hierarchical.
3

🌳 Sumarização hierárquica: árvore de memória

Múltiplos níveis

Para chats muito longos (centenas de turns), uma única sumarização perde detalhe demais. Hierarquia: turns viram sumário-de-1; sumários-de-1 viram sumário-de-2; etc. Estrutura tipo árvore. MemGPT (Packer et al. 2023) é a referência sistemática.

níveis típicos

  • Nível 0: turns brutos (últimos 5).
  • Nível 1: sumário dos 5 turns anteriores (1 parágrafo).
  • Nível 2: sumário das últimas 10 sessões (1 parágrafo).
  • Nível 3: perfil do usuário consolidado (estrutura JSON).
📑 Resumo navegável
O que é: Sumariza turns em sumário-de-1; sumários-de-1 em sumário-de-2; etc. Estrutura tipo árvore.
Por que aprender: Para chats longos (centenas de turns), hierarquia preserva detalhe nos níveis superiores.
Conceitos-chave: Tree summarization, multi-level memory, MemGPT.
4

🗃️ Memória vetorial: longo prazo

Embedding de turns + recall

Para 'lembrar' meses depois, sumário hierárquico não basta — você precisa indexar turns como vetores e recuperar por similaridade quando relevante. Cada turn vira embedding; em novo turn, busca turns similares ao tópico atual e injeta no contexto. Fica grande? Use TTL ou compactação periódica.

memória vetorial básica
def turn_com_memoria(query):
    relevantes = vector_store.search(query, top_k=3, namespace=user_id)
    msgs = [
        Message(SYSTEM, SYSTEM_PROMPT),
        Message(SYSTEM, f'Contexto histórico relevante:\n{relevantes}'),
        *recent_buffer,
        Message(USER, query),
    ]
    resp = client.chat(msgs)
    vector_store.add(embed(f'Q: {query}\nA: {resp.content}'), namespace=user_id)
    return resp.content
📑 Resumo navegável
O que é: Cada turn vira embedding indexado. No próximo turn, busca turns relevantes ao tópico atual e injeta.
Por que aprender: Permite chats que 'lembram' de conversas de meses atrás sem manter tudo na janela.
Conceitos-chave: Long-term memory, episodic memory, recall on demand, retrieval-augmented memory.
5

🔍 Recall sob demanda: tool de memória

Agente busca a memória

Alternativa ao injection automático: agente tem tool buscar_memoria(termos) e decide quando usar. Mais explícito (debugável); custo: 1 roundtrip extra quando aciona. Padrão útil em assistentes onde a memória é explícita ('lembre-se que eu sou vegetariano').

tool de memória explícita
tool_memoria = Tool(
    name='buscar_memoria',
    description='Busca em conversas anteriores. Use quando o usuário se referir a algo dito antes.',
    parameters={'type': 'object', 'properties': {
        'termos': {'type': 'string', 'description': 'palavras-chave da memória'}
    }, 'required': ['termos']}
)
📑 Resumo navegável
O que é: Padrão alternativo: agente tem tool 'buscar_memoria' e decide quando precisa de info antiga.
Por que aprender: Mais explícito que injection automático. Custo: mais roundtrips, mas precisão maior.
Conceitos-chave: Active recall, memory tool, on-demand retrieval.
6

🧠 Personalização: persona + perfil do usuário

Memória como facto

Memória conversacional ≠ estado do usuário. Perfil estruturado (nome, preferências, contexto profissional) fica em JSON pequeno no system prompt — atualizado incrementalmente quando o usuário diz algo novo. Mais estável e barato que injetar histórico inteiro a cada turn.

perfil de usuário no system prompt
perfil = {
    'nome': 'Nei',
    'idioma_preferido': 'pt-BR',
    'expertise': 'engenharia de software, LLM em produção',
    'notas': ['vegetariano', 'reside em SC, Brasil']
}

system = (
    SYSTEM_PROMPT_BASE + '\n\n'
    f'Perfil do usuário:\n{json.dumps(perfil, ensure_ascii=False, indent=2)}'
)
📑 Resumo navegável
O que é: Mantém ficha estruturada do usuário (nome, preferências, contexto profissional) atualizada incremental.
Por que aprender: Diferente de memória conversacional — é estado do usuário. Cabe no system prompt como JSON pequeno.
Conceitos-chave: User profile, structured memory, preference modeling.

📑 Resumo navegável dos tópicos

1 📥 Buffer de turns: memória de curto prazo — Últimos N turns
O que é: Manter os últimos N turns inteiros na janela. Simples, eficaz para conversas curtas.
Por que aprender: Em chats curtos (5-10 turns), buffer puro basta. Modelos atendem bem com janela <10k.
Conceitos-chave: Sliding window, message buffer, FIFO eviction.
2 📝 Summarização incremental — Comprimir o que sai do buffer
O que é: Quando buffer estoura, sumariza turns antigos em parágrafo. Sumário fica no system prompt.
Por que aprender: Mantém continuidade sem inflar janela. Trade-off: perde detalhe do que foi sumarizado.
Conceitos-chave: Recursive summarization, sliding-window summarization, hierarchical.
3 🌳 Sumarização hierárquica: árvore de memória — Múltiplos níveis
O que é: Sumariza turns em sumário-de-1; sumários-de-1 em sumário-de-2; etc. Estrutura tipo árvore.
Por que aprender: Para chats longos (centenas de turns), hierarquia preserva detalhe nos níveis superiores.
Conceitos-chave: Tree summarization, multi-level memory, MemGPT.
4 🗃️ Memória vetorial: longo prazo — Embedding de turns + recall
O que é: Cada turn vira embedding indexado. No próximo turn, busca turns relevantes ao tópico atual e injeta.
Por que aprender: Permite chats que 'lembram' de conversas de meses atrás sem manter tudo na janela.
Conceitos-chave: Long-term memory, episodic memory, recall on demand, retrieval-augmented memory.
5 🔍 Recall sob demanda: tool de memória — Agente busca a memória
O que é: Padrão alternativo: agente tem tool 'buscar_memoria' e decide quando precisa de info antiga.
Por que aprender: Mais explícito que injection automático. Custo: mais roundtrips, mas precisão maior.
Conceitos-chave: Active recall, memory tool, on-demand retrieval.
6 🧠 Personalização: persona + perfil do usuário — Memória como facto
O que é: Mantém ficha estruturada do usuário (nome, preferências, contexto profissional) atualizada incremental.
Por que aprender: Diferente de memória conversacional — é estado do usuário. Cabe no system prompt como JSON pequeno.
Conceitos-chave: User profile, structured memory, preference modeling.

✓ O que FAZER

  • Buffer simples para chats curtos (<10 turns)
  • Sumarização incremental quando buffer estoura
  • Ficha estruturada do usuário no system prompt
  • Recall explícito via tool em casos críticos

✗ O que NÃO fazer

  • Implementar memória vetorial dia 1
  • Crescer janela indefinidamente
  • Spreadar info do usuário em N turns
  • Injection automático sem controle

🚫 Quando NÃO usar

💻 Exemplo de código

from collections import deque
from fec_sdk import Message, MessageRole
from fec_sdk.adapters import get_adapter

class ChatComMemoria:
    def __init__(self, max_buffer: int = 10):
        self.buffer = deque(maxlen=max_buffer)
        self.sumario_antigo = ""
        self.client = get_adapter("mock")

    def turn(self, user_msg: str) -> str:
        # Constrói janela: system com sumário + buffer + nova msg
        system = f"Histórico antigo (sumarizado): {self.sumario_antigo}"
        msgs = [Message(role=MessageRole.SYSTEM, content=system)] + list(self.buffer)
        msgs.append(Message(role=MessageRole.USER, content=user_msg))

        resp = self.client.chat(msgs)

        # Atualiza buffer
        self.buffer.append(Message(role=MessageRole.USER, content=user_msg))
        self.buffer.append(Message(role=MessageRole.ASSISTANT, content=resp.content))

        # Se buffer está cheio, sumariza o que cai fora
        if len(self.buffer) == self.buffer.maxlen:
            self._atualizar_sumario()

        return resp.content

    def _atualizar_sumario(self):
        # Pseudo: chama LLM para sumarizar buffer + sumário antigo
        pass

🏋️ Exercício hands-on

Implemente chat com 3 camadas (buffer + sumarização + vetorial). Teste com 50 turns, medindo custo médio e recall de fatos antigos. Em exercicios/modulo-5-1/.

📚 Bibliografia

🎯 Resumo do Módulo

  • Memória é construção — modelos não têm memória nativa.
  • Buffer simples resolve chats curtos.
  • Sumarização hierárquica escala para chats longos.
  • Vetor + recall permite memória de longo prazo.
  • Estado do usuário = ficha estruturada no system prompt.

Próximo Módulo:

Caching e compressão (beta)