549 lines
17 KiB
Markdown
549 lines
17 KiB
Markdown
# 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
|
|
```text
|
|
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
|
|
```text
|
|
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**
|
|
```bash
|
|
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**
|
|
```bash
|
|
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**
|
|
```bash
|
|
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**
|
|
```bash
|
|
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**
|
|
```bash
|
|
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**
|
|
```bash
|
|
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**
|
|
```bash
|
|
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**
|
|
```bash
|
|
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**
|
|
```bash
|
|
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:
|
|
```bash
|
|
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:
|
|
```bash
|
|
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**
|
|
```bash
|
|
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
|