Files
ForgeLM/docs/plans/2026-04-03-memory-architecture-chat-user-consolidation.md
T

17 KiB

Memory Architecture: Chat + User Consolidation Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Introduzir uma arquitetura de memória segura e incremental que extraia memórias estruturadas com o modelo local models/embedding/gemma-3-270m-it-Q8_0.gguf, separe memória de chat e memória de usuário, consolide memórias entre chats sem duplicação e prepare o terreno para uso dinâmico em prompts futuros.

Architecture: A implementação será dividida em duas pipelines desacopladas: (1) extração de memória de chat a partir de mensagens concluídas em um chat específico; (2) consolidação de memória de usuário a partir das memórias de chat já processadas. Para evitar quebrar o fluxo atual, a extração será feita em background, com schema novo e compatibilidade retroativa com user_memories. A primeira fase não altera o prompt principal do chat; apenas cria, valida, deduplica e persiste a memória corretamente.

Tech Stack: React/TypeScript, Tauri frontend, SQLite local (db.ts), API compatível OpenAI do runtime local, modelo GGUF local gemma-3-270m-it-Q8_0.gguf, Vitest.


Princípios de segurança desta implementação

  1. Não quebrar o chat atual: memória nova roda em background e falha silenciosamente.
  2. Não misturar memória bruta com prompt principal ainda: primeiro persistimos corretamente, depois conectamos ao prompt.
  3. Não usar mensagens inteiras como memória final: só fatos estruturados, classificados e deduplicados.
  4. Separação explícita:
    • chat memories = fatos extraídos de um chat específico
    • user memories = fatos consolidados a partir de vários chats
  5. Idempotência: nunca reprocessar o mesmo insumo sem necessidade.
  6. Deduplicação conservadora: primeiro por chave/categoria/valor normalizado; sem embeddings sofisticados nesta fase.
  7. Modelo único para extração/classificação: usar gemma-3-270m-it-Q8_0.gguf como extractor/classifier local.

Resultado esperado desta fase

Ao final desta implementação:

  • cada chat poderá gerar memórias estruturadas próprias;
  • memórias de usuário serão derivadas das memórias de chat já consolidadas;
  • duplicatas não serão reinseridas;
  • memórias terão tipo/classificação/confiança/origem;
  • o sistema estará pronto para usar essas memórias em prompts dinâmicos futuros, sem ainda acoplar tudo ao chat principal.

Modelo de dados recomendado

Tabela 1: chat_memories

Representa fatos persistidos derivados de um chat.

Campos sugeridos:

  • id INTEGER PRIMARY KEY
  • chat_id INTEGER NOT NULL
  • source_message_id INTEGER NULL
  • memory_key TEXT NOT NULL
  • memory_value TEXT NOT NULL
  • memory_category TEXT NOT NULL
  • memory_scope TEXT NOT NULL DEFAULT 'chat'
  • confidence REAL NOT NULL
  • evidence TEXT NULL
  • first_seen_at INTEGER NOT NULL
  • last_seen_at INTEGER NOT NULL
  • normalized_value TEXT NOT NULL
  • superseded_by INTEGER NULL
  • created_at INTEGER NOT NULL
  • updated_at INTEGER NOT NULL

Categorias permitidas inicialmente:

  • identity
  • profile
  • preference
  • interest
  • habit
  • project
  • constraint
  • goal
  • topic_pattern
  • skill

Tabela 2: user_memories_v2

Representa fatos consolidados a partir de chat_memories.

Campos sugeridos:

  • id INTEGER PRIMARY KEY
  • memory_key TEXT NOT NULL
  • memory_value TEXT NOT NULL
  • memory_category TEXT NOT NULL
  • confidence REAL NOT NULL
  • normalized_value TEXT NOT NULL
  • supporting_chat_memory_ids TEXT NOT NULL (JSON array)
  • evidence_summary TEXT NULL
  • first_seen_at INTEGER NOT NULL
  • last_seen_at INTEGER NOT NULL
  • created_at INTEGER NOT NULL
  • updated_at INTEGER NOT NULL

Tabela 3: memory_jobs

Fila/log de processamento para idempotência.

Campos sugeridos:

  • id INTEGER PRIMARY KEY
  • job_type TEXT NOT NULL (chat_extract | user_consolidate)
  • target_id INTEGER NOT NULL
  • status TEXT NOT NULL (pending | running | done | failed)
  • attempts INTEGER NOT NULL DEFAULT 0
  • last_error TEXT NULL
  • created_at INTEGER NOT NULL
  • updated_at INTEGER NOT NULL

Prompt recomendado para extração de memória de chat

Arquivo novo sugerido: tauri-app/src/prompts/memory-extraction.ts

Prompt base

You are a local memory extraction and classification engine.

Your task is to read a conversation fragment and extract ONLY durable, reusable memory items.

Return ONLY valid JSON as an array.

Each item must follow this schema:
{
  "memory_key": "snake_case_key",
  "memory_value": "concise factual value",
  "memory_category": "identity|profile|preference|interest|habit|project|constraint|goal|topic_pattern|skill",
  "confidence": 0.0,
  "evidence": "short quote or short rationale",
  "is_user_memory_candidate": true
}

Extraction policy:
- Extract durable facts, not temporary requests.
- Extract user preferences, likes/dislikes, recurring interests, profession, age, location, constraints, active projects, goals, skills, and patterns in what the user usually searches or asks about.
- Prefer atomic facts.
- Do not repeat equivalent items.
- If a fact is uncertain, lower confidence.
- If nothing durable exists, return [].
- Never output markdown.

Prompt adicional para consolidação de memória de usuário

You are a user memory consolidator.

Input: existing user memories + candidate chat memories.
Goal: produce ONLY new or improved user-level memories.

Rules:
- Do not repeat existing memories.
- Merge semantically equivalent facts.
- Prefer the most stable formulation.
- Keep only durable cross-chat knowledge.
- If a fact appears in multiple chats, increase confidence.
- Return ONLY valid JSON array with the same schema.

Fases de implementação

Task 1: Congelar o comportamento atual com testes de regressão

Files:

  • Create: tauri-app/src/hooks/useMemoryExtractor.test.ts
  • Test: tauri-app/src/components/ChatView.test.tsx

Step 1: Escrever teste de regressão do fluxo atual Validar que, se o extractor falhar, o chat principal continua funcionando.

Step 2: Rodar teste para confirmar baseline Run: cd tauri-app && npm test -- src/hooks/useMemoryExtractor.test.ts Expected: FAIL inicialmente (arquivo novo).

Step 3: Criar teste mínimo de não-regressão Cobrir:

  • extractor não bloqueia envio de mensagem
  • erro de memória não quebra render/chat

Step 4: Rodar testes Run: cd tauri-app && npm test -- src/hooks/useMemoryExtractor.test.ts src/components/ChatView.test.tsx Expected: PASS

Step 5: Commit

git add tauri-app/src/hooks/useMemoryExtractor.test.ts tauri-app/src/components/ChatView.test.tsx
git commit -m "test: add memory extraction regression coverage"

Task 2: Introduzir schema novo sem remover o antigo

Files:

  • Modify: tauri-app/src/lib/db.ts
  • Modify: tauri-app/src/lib/types.ts
  • Create: tauri-app/src/lib/memory-types.ts
  • Test: tauri-app/src/lib/db.test.ts

Step 1: Escrever testes falhos para novas tabelas Cobrir criação idempotente de:

  • chat_memories
  • user_memories_v2
  • memory_jobs

Step 2: Adicionar migração aditiva Em db.ts, usar ensureTable/ensureColumn (ou equivalente local) sem remover user_memories atual.

Step 3: Declarar tipos explícitos Criar interfaces para:

  • ChatMemory
  • UserMemoryV2
  • MemoryJob
  • MemoryCandidate

Step 4: Rodar testes Run: cd tauri-app && npm test -- src/lib/db.test.ts Expected: PASS

Step 5: Commit

git add tauri-app/src/lib/db.ts tauri-app/src/lib/types.ts tauri-app/src/lib/memory-types.ts tauri-app/src/lib/db.test.ts
git commit -m "feat: add chat and user memory schema"

Task 3: Configurar o modelo local de memória sem tocar no modelo principal

Files:

  • Create: tauri-app/src/config/memory.ts
  • Modify: tauri-app/src/config/ai.ts or tauri-app/src/config/ai/index.ts
  • Test: tauri-app/src/features/runtime/runtime-utils.test.ts

Step 1: Escrever teste falho de resolução da configuração Configurar constante para:

  • MEMORY_MODEL_PATH = "models/embedding/gemma-3-270m-it-Q8_0.gguf"
  • MEMORY_MODEL_ID = "gemma-3-270m-it-Q8_0"

Step 2: Implementar configuração dedicada A memória deve usar modelo separado do chat principal.

Step 3: Adicionar fallback seguro Se o arquivo não existir, a memória desabilita-se sem derrubar o app.

Step 4: Rodar testes Run: cd tauri-app && npm test -- src/features/runtime/runtime-utils.test.ts Expected: PASS

Step 5: Commit

git add tauri-app/src/config/memory.ts tauri-app/src/config/ai.ts tauri-app/src/config/ai/index.ts tauri-app/src/features/runtime/runtime-utils.test.ts
git commit -m "feat: configure dedicated local memory model"

Task 4: Implementar prompt e parser de extração estruturada

Files:

  • Create: tauri-app/src/prompts/memory-extraction.ts
  • Create: tauri-app/src/lib/memory-parser.ts
  • Create: tauri-app/src/lib/memory-parser.test.ts

Step 1: Escrever testes falhos para parsing de JSON robusto Cobrir:

  • array JSON puro
  • JSON com ruído ao redor
  • array vazio
  • item inválido descartado
  • normalização de memory_key
  • clamp de confiança

Step 2: Implementar parser minimalista Funções sugeridas:

  • parseMemoryCandidates(raw: string): MemoryCandidate[]
  • normalizeMemoryCandidate(candidate): MemoryCandidate | null

Step 3: Rodar testes Run: cd tauri-app && npm test -- src/lib/memory-parser.test.ts Expected: PASS

Step 4: Commit

git add tauri-app/src/prompts/memory-extraction.ts tauri-app/src/lib/memory-parser.ts tauri-app/src/lib/memory-parser.test.ts
git commit -m "feat: add structured memory extraction prompt and parser"

Task 5: Extrair memórias de chat em background

Files:

  • Modify: tauri-app/src/hooks/useMemoryExtractor.ts
  • Create: tauri-app/src/lib/chat-memory-repository.ts
  • Test: tauri-app/src/hooks/useMemoryExtractor.test.ts

Step 1: Escrever testes falhos para pipeline de chat memory Cobrir:

  • usa modelo de memória dedicado
  • envia prompt de extração correto
  • salva em chat_memories
  • marca job/mensagem como processado
  • não salva duplicata exata

Step 2: Refatorar o hook atual O hook atual não deve mais gravar direto em user_memories. Agora deve:

  • ler mensagens recentes
  • chamar o modelo de memória
  • parsear candidatos
  • persistir em chat_memories
  • registrar job concluído

Step 3: Implementar deduplicação conservadora Deduplicar por:

  • chat_id
  • memory_key
  • normalized_value

Step 4: Rodar testes Run: cd tauri-app && npm test -- src/hooks/useMemoryExtractor.test.ts Expected: PASS

Step 5: Commit

git add tauri-app/src/hooks/useMemoryExtractor.ts tauri-app/src/lib/chat-memory-repository.ts tauri-app/src/hooks/useMemoryExtractor.test.ts
git commit -m "feat: extract chat-scoped memories with local model"

Task 6: Consolidar memórias de usuário a partir das memórias de chat

Files:

  • Create: tauri-app/src/lib/user-memory-consolidator.ts
  • Create: tauri-app/src/lib/user-memory-repository.ts
  • Create: tauri-app/src/lib/user-memory-consolidator.test.ts

Step 1: Escrever testes falhos da consolidação Cobrir:

  • lê chat_memories já processadas
  • compara com user_memories_v2
  • cria apenas memórias novas
  • melhora memória existente quando confiança aumenta
  • não duplica fatos equivalentes

Step 2: Implementar consolidador em duas etapas Etapa A:

  • selecionar candidatas recentes de chat_memories Etapa B:
  • chamar o modelo com:
    • memórias de usuário já existentes
    • novas memórias de chat
  • receber apenas novos fatos consolidados

Step 3: Persistir com rastreabilidade Salvar também supporting_chat_memory_ids.

Step 4: Rodar testes Run: cd tauri-app && npm test -- src/lib/user-memory-consolidator.test.ts Expected: PASS

Step 5: Commit

git add tauri-app/src/lib/user-memory-consolidator.ts tauri-app/src/lib/user-memory-repository.ts tauri-app/src/lib/user-memory-consolidator.test.ts
git commit -m "feat: consolidate user memories from chat memories"

Task 7: Definir política de classificação e uso futuro em prompts

Files:

  • Create: tauri-app/src/lib/memory-prompt-context.ts
  • Create: tauri-app/src/lib/memory-prompt-context.test.ts
  • Modify: tauri-app/src/hooks/useMemoryExtractor.ts

Step 1: Escrever teste falho para construção de contexto dinâmico Sem injetar ainda no chat principal, criar builder que monte:

  • chat memory context
  • user memory context separados e filtrados por confiança/categoria.

Step 2: Implementar builder de contexto Regras iniciais:

  • identity/profile/preferences/constraints/goals com prioridade alta
  • topic_pattern/interests limitados por recência e confiança
  • sem repetir fatos equivalentes

Step 3: Rodar testes Run: cd tauri-app && npm test -- src/lib/memory-prompt-context.test.ts Expected: PASS

Step 4: Commit

git add tauri-app/src/lib/memory-prompt-context.ts tauri-app/src/lib/memory-prompt-context.test.ts tauri-app/src/hooks/useMemoryExtractor.ts
git commit -m "feat: prepare structured memory prompt context builder"

Task 8: Adicionar comando de consolidação segura e agendada

Files:

  • Create: tauri-app/src/lib/memory-orchestrator.ts
  • Modify: tauri-app/src/components/ChatView.tsx
  • Test: tauri-app/src/components/ChatView.test.tsx

Step 1: Escrever teste falho do orquestrador Cobrir:

  • após resposta concluída, roda extração de chat
  • consolidação de usuário roda desacoplada/debounced
  • falha em qualquer etapa não afeta o chat

Step 2: Implementar orquestrador leve Fluxo:

  • assistant turn complete → enqueue chat_extract
  • depois → enqueue user_consolidate com debounce maior

Step 3: Rodar testes Run: cd tauri-app && npm test -- src/components/ChatView.test.tsx Expected: PASS

Step 4: Commit

git add tauri-app/src/lib/memory-orchestrator.ts tauri-app/src/components/ChatView.tsx tauri-app/src/components/ChatView.test.tsx
git commit -m "feat: orchestrate chat and user memory processing safely"

Task 9: Estratégia de migração sem quebra

Files:

  • Create: docs/memory-migration-notes.md
  • Modify: tauri-app/src/hooks/useMemoryExtractor.ts
  • Test: tauri-app/src/lib/db.test.ts

Step 1: Definir política de coexistência

  • user_memories antiga fica preservada
  • nova pipeline escreve em chat_memories e user_memories_v2
  • nenhuma leitura crítica do app depende imediatamente da nova tabela

Step 2: Opcional: migração parcial de legado Se necessário, criar utilitário futuro de backfill de user_memories → user_memories_v2, mas não nesta fase.

Step 3: Documentar explicitamente Descrever:

  • rollback fácil
  • como desligar a feature
  • como validar DB localmente

Step 4: Commit

git add docs/memory-migration-notes.md tauri-app/src/hooks/useMemoryExtractor.ts tauri-app/src/lib/db.test.ts
git commit -m "docs: add safe migration notes for new memory architecture"

Task 10: Validação final e critérios de aceite

Files:

  • No new files required

Step 1: Rodar suíte de memória Run:

cd tauri-app && npm test -- src/hooks/useMemoryExtractor.test.ts src/lib/memory-parser.test.ts src/lib/user-memory-consolidator.test.ts src/lib/memory-prompt-context.test.ts

Expected: PASS

Step 2: Rodar suíte principal Run:

cd tauri-app && npm test
cd tauri-app && npm run build

Expected: PASS

Step 3: Validação manual Verificar:

  • mensagem normal continua funcionando
  • memórias de chat são criadas
  • memórias de usuário aparecem só quando novas/mais fortes
  • duplicatas não são inseridas
  • se modelo de memória falhar, chat continua estável

Step 4: Commit final

git add .
git commit -m "feat: add safe chat and user memory architecture"

Riscos e mitigação

Risco 1: O modelo gemma-3-270m-it-Q8_0.gguf não ser bom para extração factual

Mitigação:

  • manter parser robusto
  • limitar escopo a fatos atômicos
  • usar confiança + deduplicação conservadora
  • tratar esse modelo como extractor heurístico, não como verdade absoluta

Risco 2: Duplicação excessiva

Mitigação:

  • chave normalizada
  • valor normalizado
  • consolidação só com fatos novos ou confiança melhor

Risco 3: Impacto de performance

Mitigação:

  • debounce
  • background only
  • janelas pequenas de contexto
  • consolidação de usuário menos frequente que extração de chat

Risco 4: Quebra do fluxo atual

Mitigação:

  • schema aditivo
  • falhas silenciosas
  • nenhuma injeção automática no prompt principal nesta fase

Critérios de aceitação

  1. O chat principal continua funcionando sem regressão.
  2. O sistema usa o modelo models/embedding/gemma-3-270m-it-Q8_0.gguf exclusivamente para a pipeline de memória.
  3. Memórias de chat são estruturadas, classificadas e persistidas separadamente.
  4. Memórias de usuário são geradas a partir das memórias de chat já processadas.
  5. O sistema não duplica memórias equivalentes.
  6. Existe builder separado para uso futuro dessas memórias em prompts.
  7. Falhas na pipeline de memória não quebram chat, build ou testes.

Recomendação de rollout

Release 1

  • schema novo
  • extractor de chat
  • consolidator de usuário
  • sem injeção no prompt principal

Release 2

  • leitura controlada de user_memories_v2 + chat_memories em prompt dinâmico
  • observabilidade de qualidade

Release 3

  • integração com pesquisa web + contexto híbrido
  • memória + pesquisa + multiagente no orquestrador final