Memory System (Português (Brasil))
Escolhendo um Provedor de Embeddings (v3.8.16+)
Seção intitulada “Escolhendo um Provedor de Embeddings (v3.8.16+)”O mecanismo de memória do OmniRoute oferece suporte a quatro fontes de embeddings (src/lib/memory/embedding/). Cada uma apresenta diferentes relações de compromisso entre latência, custo, qualidade do modelo e complexidade de configuração.
As Fontes de Embeddings
Seção intitulada “As Fontes de Embeddings”| Provedor | Fonte | Latência | Custo | Qualidade | Configuração |
|---|---|---|---|---|---|
transformers |
Modelo ONNX local (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Grátis | Boa | Apenas npm install |
static |
Vetores pré-calculados (em cache) | <1ms | Grátis | N/D (depende de acerto no cache) | Nenhuma |
remote |
API da OpenAI / Cohere / Voyage | ~100-300ms | $0.02-0.10/1M tokens | Excelente | Chave de API |
auto |
Seleciona a melhor fonte disponível em tempo de execução | Igual à fonte selecionada | Grátis | Igual à fonte selecionada | Nenhuma |
| (cache) | Camada LRU em memória sobre qualquer fonte | <1ms (acerto), latência total (falha) | Grátis | Igual à fonte subjacente | Sempre ativa (não é uma fonte selecionável) |
Árvore de Decisão
Seção intitulada “Árvore de Decisão” Qual é o contexto da sua implantação? │ ┌───────────┼───────────┬──────────────┐ │ │ │ │ DEV/TESTE PROD PEQUENA PROD GRANDE EDGE / OFFLINE │ │ │ │ ▼ ▼ ▼ ▼ transformers transformers remote (Qdrant) transformers (grátis, sem API) (melhor qualidade) (sem internet) │ │ │ │ └────────┬──┴───────────┴──────────────┘ │ ▼ SEMPRE adicione a camada `cache` por cima (LruCache envolve qualquer provedor)Configuração do Banco de Dados e da API
Seção intitulada “Configuração do Banco de Dados e da API”As opções de embeddings da memória são configuradas por meio da API/interface de Configurações, não por variáveis de ambiente. As chaves relevantes do banco de dados de configurações, em Configurações (normalizeMemorySettings em src/lib/memory/settings.ts), são:
memoryEmbeddingSource:"transformers"(local),"remote"(baseada em API, por exemplo, OpenAI),"static"(armazenamento externo) ou"auto"memoryEmbeddingProviderModel: identificador do modelo para fontes remotas/estáticas (por exemplo,"text-embedding-3-small")memoryTransformersEnabled:true|falsememoryStaticEnabled:true|falsememoryVectorStore:"sqlite-vec","qdrant"ou"auto"
Modelo Local (transformers)
Seção intitulada “Modelo Local (transformers)”Usa transformers.js internamente para executar modelos locais:
# Variáveis de ambiente lidas no código (src/lib/memory/embedding/index.ts):MEMORY_TRANSFORMERS_MODEL=Xenova/all-MiniLM-L6-v2 # Repositório do modelo no HFMEMORY_STATIC_MODEL=minishlab/potion-base-8M # Modelo potion estático do HFMEMORY_STATIC_CACHE_DIR=<DATA_DIR>/embeddings # Diretório do cacheCache LRU de Embeddings
Seção intitulada “Cache LRU de Embeddings”O cache está sempre ativo por padrão e é configurado por meio de variáveis de ambiente:
MEMORY_EMBEDDING_CACHE_MAX=1000 # Máximo de itens em cacheMEMORY_EMBEDDING_CACHE_TTL_MS=300000 # TTL (5 min)Números de Desempenho
Seção intitulada “Números de Desempenho”Benchmark em um servidor x86 típico de 4 núcleos (textos com ~100 tokens cada):
| Provedor | p50 | p95 | p99 | Custo / 1 milhão de embeddings |
|---|---|---|---|---|
transformers (CPU) |
80ms | 180ms | 350ms | Gratuito |
remote (OpenAI) |
120ms | 220ms | 400ms | ~$0,02 (ada-002) / $0,13 (3-large) |
static (Qdrant) |
15ms | 30ms | 60ms | Depende da hospedagem do Qdrant |
cache (acerto) |
<1ms | <1ms | 2ms | Gratuito |
Padrões de Extração de Fatos (v3.8.16+)
Seção intitulada “Padrões de Extração de Fatos (v3.8.16+)”O módulo extraction.ts (src/lib/memory/extraction.ts) usa correspondência de padrões com expressões regulares para extrair fatos estruturados de mensagens de conversas. Entender esses padrões ajuda você a ajustar a qualidade da extração para seu caso de uso.
Categorias de Padrões Padrão
Seção intitulada “Categorias de Padrões Padrão”| Categoria | Exemplo de padrão | Captura |
|---|---|---|
| PREFERENCE_PATTERNS | "I prefer <X>", "I like <X>", "I hate <X>" |
Preferências do usuário |
| DECISION_PATTERNS | "I'll use <X>", "I decided to <X>", "I went with <X>" |
Decisões do usuário (episódicas) |
| PATTERN_PATTERNS | "I usually <X>", "I always <X>", "I never <X>" |
Padrões comportamentais persistentes |
Exemplos de Padrões (Simplificados)
Seção intitulada “Exemplos de Padrões (Simplificados)”// De src/lib/memory/extraction.tsconst PREFERENCE_PATTERNS = [ /\bI\s+(?:really\s+)?prefer\s+([^.,\n]+)/gi, /\bI\s+(?:really\s+)?like\s+([^.,\n]+)/gi, /\bI\s+(?:hate|dislike|avoid)\s+([^.,\n]+)/gi,];const DECISION_PATTERNS = [ /\bI'?(?:ll|will)\s+use\s+([^.,\n]+)/gi, /\bI\s+(?:have\s+)?decided\s+(?:to\s+)?([^.,\n]+)/gi,];const PATTERN_PATTERNS = [/\bI\s+usually\s+([^.,\n]+)/gi, /\bI\s+always\s+([^.,\n]+)/gi];O Que É Extraído
Seção intitulada “O Que É Extraído”Quando um usuário diz:
“I prefer TypeScript. I’ll use Postgres for this project. I always commit before pushing. I don’t like Python.” A extração produz 4 memórias:
Chave Categoria Tipo Conteúdo preference:typescriptpreferência factual “TypeScript” decision:postgres_for_this_projectdecisão episódica “Postgres for this project” pattern:commit_before_pushingpadrão factual “commit before pushing” preference:pythonpreferência factual “Python”
Limites da Extração
Seção intitulada “Limites da Extração”Para evitar extrações descontroladas, aplicam-se os seguintes limites:
| Tamanho mínimo do conteúdo | 3 caracteres | | Tamanho máximo do conteúdo | 500 caracteres |
Quando Desativar a Extração
Seção intitulada “Quando Desativar a Extração”A extração é executada automaticamente sempre que a memória está habilitada; não há uma opção separada
exclusiva para extração. Para desativá-la, desabilite completamente a memória (enabled: false
por meio de PUT /api/settings/memory). Considere fazer isso quando:
- Você tem um alto volume de mensagens e o custo da extração não é insignificante
- Suas conversas são, em sua maioria, transitórias (bate-papo, depuração), sem valor de longo prazo
- Você já está capturando o contexto por meio de plugins personalizados
Ajuste do RRF Híbrido (v3.8.16+)
Seção intitulada “Ajuste do RRF Híbrido (v3.8.16+)”O algoritmo Reciprocal Rank Fusion (RRF) combina resultados do FTS5 (palavras-chave) e de vetores (semânticos). O parâmetro k controla quanto peso é atribuído aos resultados com classificação mais baixa.
A Fórmula
Seção intitulada “A Fórmula”Para cada memória candidata, a pontuação RRF é:
RRF(d) = Σ 1 / (k + rank_i(d))Onde:
ké a constante (padrão 60)rank_i(d)é a classificação do documentodno i-ésimo sistema de recuperação (FTS, vetor)- A soma abrange todos os sistemas de recuperação
Como k Afeta os Resultados
Seção intitulada “Como k Afeta os Resultados”Valor de k |
Efeito | Mais adequado para |
|---|---|---|
k=0 |
Fusão pura de classificações (sem suavização) | Referência teórica |
k=10-30 |
Atribui muito peso aos principais resultados; classificações baixas pouco contribuem | Quando os 3 primeiros resultados geralmente estão corretos |
k=60 (padrão) |
Equilibrado — todos os 10 primeiros resultados contribuem de maneira significativa | Recuperação de uso geral |
k=100+ |
Mais uniforme — até resultados com classificação baixa podem dominar caso apareçam em vários sistemas | Quando a revocação > precisão é essencial |
Ajustando k na Prática
Seção intitulada “Ajustando k na Prática”# PadrãoMEMORY_RRF_K=60
# Precisão agressiva (memória pequena, poucos documentos)MEMORY_RRF_K=20
# Revocação máxima (memória grande, consultas variadas)MEMORY_RRF_K=120Exemplo com k=20:
- Classificação 1 no FTS → contribuição
1/21 = 0.048 - Classificação 10 no FTS → contribuição
1/30 = 0.033 - Classificação 1 no vetor → contribuição
0.048 - Máximo combinado:
0.096
Exemplo com k=60:
- Classificação 1 no FTS → contribuição
1/61 = 0.016 - Classificação 10 no FTS → contribuição
1/70 = 0.014 - Classificação 1 no vetor → contribuição
0.016 - Máximo combinado:
0.033
Com um k mais alto, a diferença relativa entre a primeira e a décima posição é menor, portanto o algoritmo depende mais do consenso entre os sistemas de recuperação do que da confiança na primeira posição.
Quando Alterar k
Seção intitulada “Quando Alterar k”| Sintoma | Experimente |
|---|---|
| O primeiro resultado sempre vence, mas está errado | Diminuir k (por exemplo, 20) — a confiança na primeira posição importa mais |
| A resposta correta está entre as 5 primeiras, mas não em primeiro lugar | Aumentar k (por exemplo, 100) — uma pontuação mais uniforme recompensa o consenso |
| A revocação é alta, mas a precisão é baixa | Diminuir k — torne a classificação mais precisa |
| A revocação é baixa (documentos relevantes ausentes) | Aumentar k — dê uma chance aos documentos com classificação mais baixa |
Ponderação do RRF
Seção intitulada “Ponderação do RRF”A fusão recíproca de classificações usa pesos iguais para a classificação vetorial semântica e a classificação da pesquisa de texto completo:
RRF(d) = 1/(k + rank_vector) + 1/(k + rank_fts)Não há variáveis de ambiente para ajustar pesos individuais (MEMORY_RRF_VECTOR_WEIGHT/MEMORY_RRF_FTS_WEIGHT não existem).
Estratégia de sumarização (v3.8.16+)
Seção intitulada “Estratégia de sumarização (v3.8.16+)”O módulo summarization.ts (src/lib/memory/summarization.ts) compacta memórias antigas para manter pequeno o conjunto ativo, preservando a capacidade de recuperação.
Quando a sumarização é acionada
Seção intitulada “Quando a sumarização é acionada”| Acionador | Limite (padrão) |
|---|---|
| Acionamento manual via API | não se aplica |
O que é sumarizado
Seção intitulada “O que é sumarizado”Dois pontos de entrada são exportados de summarization.ts:
summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000)— condensa as memórias de uma sessão em um único texto de resumo, limitado por um orçamento de tokens.summarizeMemoriesOlderThan(apiKeyId, days, dryRun)— a compactação baseada em idade usada pela API: seleciona todas as memórias mais antigas quedays, cria uma única memória de resumo condensada a partir delas e, quandodryRunéfalse, exclui os originais. PassedryRun: truepara visualizar o conjunto candidato e o total de tokens sem modificar nada.
Não há uma etapa de agrupamento por tag/chave nem uma pontuação por memória de “essencial vs. sumarizável” — a seleção é feita exclusivamente pelo limite de idade, e o texto do resumo é uma linha condensada, prefixada pelo tipo, para cada candidata.
Acionando a sumarização
Seção intitulada “Acionando a sumarização”A sumarização é manual / opcional — a configuração autoSummarize é false por
padrão, portanto nada é compactado automaticamente. Acione-a por meio da API:
curl -X POST http://localhost:20128/api/memory/summarize \ -H "Authorization: Bearer $OMNIROUTE_KEY"Para mantê-la desativada, basta deixar autoSummarize com seu valor padrão (false).
Dicas para a qualidade da sumarização
Seção intitulada “Dicas para a qualidade da sumarização”- Visualize primeiro com
dryRun—summarizeMemoriesOlderThan(..., true)retorna a lista de candidatas e a contagem total de tokens, permitindo confirmar o que seria mesclado antes de excluir os originais. - Execute a sumarização durante horários de baixo tráfego se você tiver um grande corpus de memórias — a chamada ao LLM é a parte lenta
# Estilo cron: sumarizar diariamente às 3h0 3 * * * curl -X POST http://localhost:20128/api/memory/summarize \ -H "Authorization: Bearer $OMNIROUTE_KEY"Padrão de provedor MemoryBackend
Seção intitulada “Padrão de provedor MemoryBackend”Fonte da verdade:
src/lib/memory/backend.ts,src/lib/memory/genericBackend.ts,src/lib/memory/manager.tsTestes:src/lib/memory/__tests__/generic-backend.test.ts
O padrão de provedor MemoryBackend introduz uma camada de abstração de backend conectável sobre o mecanismo de memória existente. Em vez de ficar vinculado a uma única implementação de armazenamento, o sistema de memória agora oferece suporte a vários backends (SQLite, Obsidian, Notion e backends HTTP personalizados), com roteamento configurável de primário/fallback.
Arquitetura
Seção intitulada “Arquitetura”┌──────────────────────────────────────────────────────────┐│ Rotas da API ││ (src/app/api/memory/route.ts) │└──────────────────────┬───────────────────────────────────┘ │┌──────────────────────▼───────────────────────────────────┐│ MemoryManager ││ Orquestrador singleton (manager.ts) ││ ││ Primário ──► Backend A (por exemplo, SQLite) ││ Fallback ──► Backend B (por exemplo, Obsidian) ││ Backend C (por exemplo, Notion via GenericBackend) │└──────────────────────┬───────────────────────────────────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼┌────────────┐ ┌────────────┐ ┌──────────────────┐│ Backend │ │ Backend │ │ Backend de ││ SQLite │ │ Obsidian │ │ memória genérico ││ │ │ │ │ (HTTP) │└────────────┘ └────────────┘ └──────────────────┘Interface principal (backend.ts)
Seção intitulada “Interface principal (backend.ts)”Todo backend deve implementar a interface MemoryBackend:
interface MemoryBackend { readonly id: string; readonly displayName: string;
// CRUD create(input: CreateMemoryInput): Promise<Memory>; get(id: string): Promise<Memory | null>; update(id: string, updates: Partial<...>): Promise<boolean>; delete(id: string): Promise<boolean>; list(filter: MemoryFilter): Promise<{ data: Memory[]; total: number; byType: Record<string, number> }>;
// Pesquisa search(config: SearchConfig): Promise<Memory[]>;
// Integridade health(): Promise<HealthCheckResult>;
// Ciclo de vida (opcional) initialize?(): Promise<void>; shutdown?(): Promise<void>;}MemoryManager (manager.ts)
Seção intitulada “MemoryManager (manager.ts)”Orquestrador singleton que:
- Registra backends por meio de
register(backend)— chamado na inicialização a partir deindex.ts - Configura o primário e os fallbacks por meio de
configure(primary, fallbacks) - Roteia operações CRUD/pesquisas para o primário, com uma cadeia de fallback em caso de falha
- Verifica a integridade de todos os backends periodicamente
Comportamento de fallback:
| Operação | Primário | Fallbacks |
|---|---|---|
create |
✅ Somente o primário | ❌ |
get |
✅ Tenta primeiro o primário | ✅ Fallback se for null |
update |
✅ Somente o primário | ✅ Sincronização sem aguardar |
delete |
✅ Somente o primário | ✅ Sincronização sem aguardar |
list |
✅ Somente o primário | ❌ |
search |
✅ Primeiro o primário | ✅ Fallback em caso de erro |
GenericMemoryBackend (genericBackend.ts)
Seção intitulada “GenericMemoryBackend (genericBackend.ts)”Um conector HTTP genérico que adapta qualquer API REST a um MemoryBackend. Útil para:
- Notion — conecte por meio da API do Notion
- Obsidian — conecte por meio da API REST local do Obsidian
- Backends personalizados — qualquer serviço que exponha uma API RESTful de memória
Configuração:
interface GenericBackendConfig { baseUrl: string; // URL base da API do backend apiKey?: string; // Token Bearer para autenticação headers?: Record<string, string>; // Cabeçalhos HTTP personalizados timeout?: number; // Tempo limite da solicitação (padrão: 30000ms) backendType?: string; // Para registro de logs
// Substituições de endpoints (os padrões usam convenções REST) endpoints?: { search?: string; // padrão: "/memories/search" create?: string; // padrão: "/memories" list?: string; // padrão: "/memories" get?: string; // padrão: "/memories/{id}" update?: string; // padrão: "/memories/{id}" delete?: string; // padrão: "/memories/{id}" health?: string; // padrão: "/health" };
// Mapeamentos de nomes de parâmetros de consulta queryParams?: { query?/apiKeyId?/limit?/offset?/strategy?/maxTokens?/type?/sessionId?/orderBy?/orderDir?/options? };
// Mapeamentos de nomes de parâmetros de caminho pathParams?: { id?/memoryId? };}Os backends conhecidos são pré-configurados em KNOWN_BACKENDS:
createKnownBackend("obsidian"); // → GenericMemoryBackend apontando para localhost:27123createKnownBackend("notion"); // → GenericMemoryBackend apontando para api.notion.com/v1Backends integrados
Seção intitulada “Backends integrados”SQLiteBackend (sqliteBackend.ts)
Seção intitulada “SQLiteBackend (sqliteBackend.ts)”O backend primário padrão. Encapsula o armazenamento de memória existente baseado em SQLite usando src/lib/memory/store.ts. Registrado automaticamente na inicialização.
import { sqliteBackend } from "./sqliteBackend";memoryManager.register(sqliteBackend);ObsidianBackend (obsidianBackend.ts)
Seção intitulada “ObsidianBackend (obsidianBackend.ts)”Encapsula a integração existente com o Obsidian (src/lib/memory/obsidianBackend.ts). Conecta-se a um cofre do Obsidian por meio da API REST local do Obsidian.
Configurações
Seção intitulada “Configurações”As configurações do backend de memória são armazenadas na tabela de configurações do aplicativo e gerenciadas por meio de src/lib/memory/settings.ts:
| Configuração | Chave de ambiente/configuração | Padrão | Descrição |
|---|---|---|---|
| Backend primário | memoryPrimaryBackend |
"sqlite" |
ID do backend primário |
| Backends de fallback | memoryFallbackBackends |
[] |
IDs ordenados dos backends de fallback |
| Configurações dos backends | memoryBackendConfigs |
{} |
Substituições de configuração por backend |
As configurações são normalizadas por meio de normalizeMemorySettings() e armazenadas em cache em getMemorySettings().
Fluxo de inicialização
Seção intitulada “Fluxo de inicialização”Inicialização do aplicativo → importações de index.ts (efeito colateral): registram SQLiteBackend → initMemoryBackends() chamado a partir do ciclo de vida do aplicativo: 1. Carregar configurações (getMemorySettings) 2. Configurar backend primário + fallback 3. Inicializar todos os backends (verificação de integridade) 4. Pronto para solicitaçõesAdicionando um novo backend
Seção intitulada “Adicionando um novo backend”- Implemente a interface
MemoryBackendemsrc/lib/memory/<name>Backend.ts - Exporte a partir de
src/lib/memory/index.ts - Registre com
memoryManager.register(yourBackend)na inicialização - Configure por meio das configurações: defina
memoryPrimaryBackendcomo o ID do seu backend - Teste usando
src/lib/memory/__tests__/generic-backend.test.tscomo referência
Exemplo: backend Brain
Seção intitulada “Exemplo: backend Brain”import { createGenericMemoryBackend } from "./genericBackend";
const brainBackend = createGenericMemoryBackend("brain", "BK-Brain", { baseUrl: process.env.BRAIN_API_URL || "http://localhost:9099", apiKey: process.env.BRAIN_API_KEY, endpoints: { search: "/api/memory/search", create: "/api/memory", health: "/api/health", },});
memoryManager.register(brainBackend);Verificação
Seção intitulada “Verificação”Testes unitários
Seção intitulada “Testes unitários”npx vitest run src/lib/memory/__tests__/generic-backend.test.ts --reporter=verboseSaída esperada: 35 testes, todos aprovados, abrangendo:
- Construtor (2)
- Verificação de integridade (4) — sucesso, falha 500, erro de rede, latência
- Inicialização (2) — sucesso, falha
- Criação (2) — endpoint padrão, endpoint personalizado
- Obtenção (4) — sucesso, 404 → null, lançamento de erro diferente de 404, parâmetros de caminho personalizados
- Atualização (2) — sucesso, 404 → false
- Exclusão (2) — sucesso, 404 → false
- Listagem (2) — parâmetros de consulta, nomes de parâmetros personalizados
- Pesquisa (3) — parâmetros de consulta, endpoint personalizado, serialização de opções
- Cabeçalhos de autenticação (2) — token Bearer, cabeçalhos personalizados
- Fábrica (1)
Verificação de tipos
Seção intitulada “Verificação de tipos”npm run typecheck:coreEsperado: 0 erros.
HagiCode
HagiCode é um ambiente de programação com agentes, fluxos estruturados, execução multiagente e visualizações Hero Dungeon.
Transforme ideias em software útil com um fluxo de trabalho com agentes mais inteligente, rápido e agradável.

- SmartFluxos estruturados transformam intenções em um caminho executável da ideia à entrega.
- EfficientFluxos multiagente mantêm pesquisa, implementação e revisão em andamento simultaneamente.
- FunO Hero Dungeon torna longas sessões de programação mais visuais e colaborativas.