Paridade: o espelho que mediu a memória
Arachne·

Paridade: o espelho que mediu a memória

6 min de leitura← Voltar para timeline

Existe um momento em toda migração em que o plano vira pergunta: e se o novo for pior? O Arachne tinha um pipeline RAG próprio — chunking, embeddings locais, busca híbrida. O Yurumi, meu motor de memória agêntica, agora fazia a mesma coisa com indexação dedicada. Migrar era óbvio no papel. Mas “óbvio no papel” é exatamente a frase que antecede os piores deploys da história.

Então o corte virou contrato: eu só troco o motor quando um espelho provar que os dois concordam.

Shadow dual-write: espionar sem interromper

A ideia central é antiga — rodar o novo sistema em shadow mode, em paralelo com o antigo, sem que nenhum usuário perceba. No Arachne, toda vez que um chunk entra no índice local, o mesmo chunk é espelhado pro motor do Yurumi por um worker em background. O request path nunca espera o espelho: se o motor está fora, o token não existe ou a fila enche, o chunk é contabilizado como perdido e a vida segue. O fallback local continua sendo a única fonte de verdade visível.

# flag separada: shadow (medir) != cutover (trocar)
if shadow_enabled and token:
    _shadow_queue.put_nowait(chunk)  # worker assíncrono espelha
# request path nunca bloqueia — fila cheia = drop contabilizado

Esse desenho tem duas propriedades que valem ouro. Primeira: zero risco. Se o Yurumi explodir, ninguém nota, porque ninguém está lendo dele ainda. Segunda: observabilidade grátis — o espelho expõe estatísticas de fila, drops e erros, então você descobre que o motor está doente antes de depender dele.

O detalhe que quase me queimou: token de autenticação. O cliente HTTP encapsula o token e não expõe o atributo — o preflight de sanidade lia um atributo que não existia e marcava tudo como “sem credencial”. O teste E2E pegou; o fix foi ler a credencial de onde ela realmente vive. Lição micro mas recorrente: wrapper não é transparente.

O harness: 20 perguntas que ninguém inventou

Espelhar dados não prova nada sozinho — eu preciso comparar respostas. O harness de paridade pega perguntas reais do histórico de conversas do sistema (não perguntas sintéticas que eu escreveria pra favorecer o novo motor) e consulta os dois mundos: o pipeline local e o espelho do Yurumi.

A métrica é overlap@10: de 10 resultados que cada lado devolve, quantos são os mesmos? O overlap é fracionário (5/10 = 0.5) e a média das consultas resume a paridade. A primeira rodada com 20 perguntas reais:

Métrica Valor
Consultas espelhadas 18/20
Overlap@10 médio 0.511
Overlap mínimo 0.2
Latência média (local) 1.59s
Latência média (motor) 0.8s

E aí vem a parte desconfortável do post: 0.511 é um número que parece ruim e é bom.

Por que metade de overlap é o desejado

Overlap de 1.0 significaria que os dois motores são o mesmo motor. Não são — de propósito. O pipeline local do Arachne é denso: embedding vetorial direto. O Yurumi usa busca híbrida com fusão de três sinais e re-ranking por cross-encoder — ele reordena os resultados, não só os recupera.

Se os dois concordassem perfeitamente, a migração seria inútil: eu estaria trocando infraestrutura por nada. O overlap ~0.5 diz “os dois acham as mesmas coisas no topo, com ordem diferente” — que é exatamente o contrato que eu queria. A prova definitiva veio do canário de cutover: 4 perguntas reais enviadas ao motor novo em ambiente de desenvolvimento, com veredito PASS — o motor respondeu com conteúdo correto nas 4.

Latência: o subproduto que ninguém planeja

O número que mais me surpreendeu não foi o overlap, foi a latência: o motor novo respondeu duas vezes mais rápido na média (0.8s vs 1.59s). E olha o detalhe da primeira linha da rodada: uma pergunta antiga demorou 22 segundos no pipeline local contra 1.86 no espelho. Consulta fria, índice local reconstruindo estado em memória — o pipeline local paga pedágio quando o cache esfria, o motor dedicado não.

Migração que melhora latência de graça é migração que se paga.

O padrão: migrar é medir, não mover

O playbook que fica:

Fase Papel Regra
Shadow Espelha escritas, ninguém lê Nunca bloqueia o request path
Paridade Harness sobre tráfego real Pergunta inventada favorece quem a inventou
Canário Cutover limitado, ambiente de dev Veredito binário PASS/FAIL
Cutover Troca com rollback de 1 comando Rollback testado ANTES do corte

A lição grande: a parte difícil de uma migração não é mover os dados — é provar que nada piorou sem ter que descobrir em produção. Shadow dual-write transformou “eu acho que o novo é bom” em “eu tenho 20 perguntas reais dizendo que sim”. O resto da migração virou burocracia.

E o bom senso de sempre: o espelho não é o produto. É o instrumento de medição que deixa o produto mudar sem medo.

~/lifelog — bash
$cat about.txt
╔══════════════════════════════════════╗
║  Samuel Medeiros                    ║
║  Senior Software Engineer           ║
║  Stack: Python · TypeScript · Rust  ║
║  Projetos: Arachne, Dogwalk,        ║
║            Capivara, TatuEngine      ║
╚══════════════════════════════════════╝
      
$