Memória e Contexto
Histórico de Conversa
Persistir mensagens em banco, Redis ou sessão e reconstruir messages[] a cada chamada.
Nesta aula você vai
- Salvar mensagens user/assistant após cada turno
- Carregar últimas N mensagens ao montar prompt
- Manter sessionId estável por conversa
Histórico de Conversa
Objetivos
- Criar contexto persistente entre mensagens
- Exercício: salvar e recuperar últimas mensagens
Da amnésia à continuidade
Na aula anterior vimos o problema. Agora a correção: cada turno salva o que foi dito e o próximo turno reconstrói o array messages[] a partir do storage.
É como editar um roteiro de teatro — a cada cena nova, o ator (LLM) recebe o script atualizado, não só a última fala.
Schema SQL mínimo
CREATE TABLE chat_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_session ON chat_messages(session_id, created_at);
Simples de propósito. Produção pode adicionar user_id, tokens, deleted_at — mas o fluxo base é este.
Fluxo no endpoint
app.post('/api/chat', async (req, res) => {
const sessionId = req.cookies.chat_session;
const { message } = req.body;
await db.insertMessage(sessionId, 'user', message);
const history = await db.getLastMessages(sessionId, 20); // últimas 20
const messages = [
{ role: 'system', content: SYSTEM_PROMPT },
...history.map((m) => ({ role: m.role, content: m.content })),
];
const reply = await chat(messages);
await db.insertMessage(sessionId, 'assistant', reply);
res.json({ reply });
});
Ordem importa:
- Salva mensagem do usuário
- Carrega histórico
- Chama LLM
- Salva resposta do assistente
- Devolve ao frontend
Conversa que passa no teste:
Usuário: "Me chamo Ana."
Assistente: "Olá, Ana! Como posso ajudar?"
Usuário: "Qual meu nome?"
Assistente: "Você se chama Ana."
Se falhar, o bug está no storage ou na montagem de messages[] — não no modelo.
Redis como alternativa
Lista por sessão — rápido, volátil se não persistir:
await redis.rpush(`chat:${sessionId}`, JSON.stringify({ role: 'user', content: message }));
await redis.ltrim(`chat:${sessionId}`, -40, -1); // mantém últimas 20 pares ≈ 40 entries
Bom para MVP; produção costuma usar Redis + PostgreSQL (Redis quente, PG arquivo).
PHP Session (protótipo)
session_start();
$_SESSION['chat'][] = ['role' => 'user', 'content' => $message];
// limite
$_SESSION['chat'] = array_slice($_SESSION['chat'], -20);
Limitação: não escala horizontalmente sem sticky session. Serve para demo local, não para deploy sério.
Exercício
- Implemente
insertMessage+getLastMessages - Converse: "Me chamo Ana" → "Qual meu nome?" — deve funcionar
- Abra nova sessão (novo cookie) — deve esquecer
O terceiro passo confirma que a memória está ligada à sessão, não ao modelo.
Privacidade
- TTL: apagar sessões > 30 dias
- LGPD: histórico é dado pessoal se identificar usuário — documente retenção
- Não logue conteúdo completo em produção sem necessidade
Diálogo jurídico ↔ engenharia:
DPO: "Quanto tempo guardamos conversas?"
Dev: "30 dias, depois job apaga."
DPO: "Documenta na política de privacidade."
Resumo
- Salve user + assistant após cada turno
- Reconstrua
messages[]do storage, não do cliente - Limite N mensagens desde o início — custo e context window
Histórico funcionando — mas conversas longas encarecem e podem estourar limite. Na próxima aula: janela de contexto, resumo e compressão.