
Estudos — quando estudei os embeddings que pensam pelo sistema
A pergunta que começou o estudo
No meio do ecossistema, dois sistemas diferentes precisam entender o que o usuário quis dizer. O Arachne busca documentos na web, extrai conteúdo, responde perguntas. O Yurumi guarda a memória do sistema — conversas, decisões, conhecimento acumulado. Ambos dependem de uma peça invisível: o modelo de embedding, aquele que transforma texto em vetor.
Eu sabia que cada um usava um modelo diferente. O que eu não sabia — e resolvi estudar — era por que cada modelo estava onde estava, e se a escolha ainda fazia sentido depois de meses de evolução.
O estudo começou com uma pergunta simples: qual modelo de embedding entrega o melhor resultado para cada tipo de consulta que o sistema faz? A resposta, como quase toda resposta honesta, veio em forma de tradeoff.
O contexto — dois sistemas, uma fundação
Os dois sistemas compartilham a mesma arquitetura de busca: texto de entrada → modelo de embedding → vetor → similaridade no vector store → resultados ranqueados. Mas o que cada um busca é diferente:
- Arachne recebe perguntas de usuário sobre conteúdo web. As queries são curtas, diretas, sobre tópicos variados. O embedding precisa ser rápido e razoável — um match de 80% já entrega contexto suficiente pro LLM montar a resposta.
- Yurumi busca na memória do ecossistema. As queries são difusas — “como foi configurado o watchdog do WSL?” — e o resultado precisa ser preciso, porque o agente que pergunta depende dessa memória pra agir. O embedding precisa entender intenção, não só palavra.
Dois problemas diferentes, que — eu suspeitava — pediam modelos diferentes.
Os três modelos do estudo
Selecionei três candidatos que representavam espectros diferentes de custo e qualidade:
colibri (v1.0 local): o modelo mais leve do ecossistema. Roda em CPU, 50ms por embedding, 384 dimensões. É o que o Arachne usa por padrão. Perfeito para queries curtas onde velocidade importa mais que precisão semântica.
bge (base-en-v1.5): o padrão ouro local. 768 dimensões, roda em GPU, ~200ms. É o que o Yurumi usa como embedding principal. Entende contexto, captura nuances, mas pesa no batch.
openai (text-embedding-3-small): o padrão ouro pago. 1536 dimensões, qualidade superior, mas custa por token. É o fallback do Yurumi quando o bge não encontra match bom o suficiente.
Cada modelo tem uma assinatura que define o tradeoff: velocidade, qualidade e custo formam um triângulo onde você só maximiza dois de cada vez.
O benchmark caseiro
Montei um teste simples — nada de papers acadêmicos, nada de datasets padronizados. Usei 50 queries reais que o ecossistema já tinha processado, divididas em três categorias:
- Queries curtas (2-5 palavras): “watchdog WSL timeout”, “capa AI lifelog”, “deploy portifolio”
- Queries técnicas (6-15 palavras): “como configurar o health check do Arachne no k3s”, “qual o segredo do rate limit do /api/recusar”
- Queries difusas (16+ palavras): “qual foi a decisão sobre o modelo de embedding do Yurumi e por que o bge foi escolhido”
Para cada query, rodei o embedding nos três modelos e comparei o top-3 retornado contra o que eu considerava o resultado correto (baseado no conteúdo real da base).
def benchmark(query, expected, models):
for name, model in models.items():
vec = model.encode(query)
results = search(vec, top_k=3)
match = 1 if expected in results else 0
print(f"{name}: {match}/1 — {results}")
O que o estudo revelou
Os resultados confirmaram suspeitas e revelaram surpresas:
| Modelo | Curta (50) | Técnica (50) | Difusa (50) | Latência | Custo |
|---|---|---|---|---|---|
| colibri | 40/50 (80%) | 33/50 (66%) | 28/50 (56%) | ~50ms | Grátis (CPU) |
| bge | 44/50 (88%) | 45/50 (90%) | 42/50 (84%) | ~200ms | Grátis (GPU) |
| openai | 47/50 (94%) | 48/50 (96%) | 47/50 (94%) | ~300ms | $0.002/K token |
O colibri é surpreendentemente bom para queries curtas — 80% de match com latência de 50ms e zero custo de GPU. É o modelo certo para o Arachne, que faz centenas de queries por dia e precisa de resposta rápida.
O bge é o equilibrado: perde pouco para o openai em queries técnicas e difusas, e roda de graça na GPU local. Mas o custo não é só financeiro — é de RAM. Cada batch de embedding consome ~1.2GB de VRAM, e em dias de pouca memória disponível no host, o bge falha silenciosamente.
O openai é o padrão ouro, mas o custo por token inviabiliza como embedding primário para o volume do ecossistema (~3000 consultas/dia). Ele fica como fallback — quando o bge retorna confiança baixa, a query sobe pra openai.
Onde cada um foi parar
O estudo não mudou as escolhas — ele as confirmou com dados. Cada modelo está onde deveria:
- Arachne: colibri v1.0. Velocidade importa mais que precisão (o LLM cobre gaps). 50ms por query vs 200ms faria diferença no tempo de resposta que o usuário percebe.
- Yurumi: bge como primário, openai como fallback. Precisão importa porque a memória errada gera ação errada. O fallback existe para os 16% de queries difusas que o bge perde.
- Nenhum: um modelo único para ambos os sistemas. O custo de padronizar seria perder desempenho em um dos dois casos de uso.
Aprendizados
- O modelo certo depende do que você busca, não do que você tem. O colibri não é “pior” que o bge — é o modelo certo para queries curtas onde o LLM cobre os gaps. O bge não é “pior” que o openai — é o modelo certo para o volume do ecossistema.
- Benchmark com dados reais vale mais que paper. Os 50 queries reais revelaram um padrão que nenhum dataset padronizado mostraria: o colibri é consistentemente bom em queries curtas, mesmo com 384 dimensões.
- Custo não é só dinheiro. O bge é “grátis” mas consome 1.2GB de VRAM por batch. Em dias de pouca memória, o “grátis” vira indisponível — e o fallback pago salva.
- Fallback não é plano B, é arquitetura. O openai como fallback do bge não é desperdício — é a válvula que garante que o sistema nunca fica cego quando o modelo local falha.
O estudo não respondeu “qual embedding é melhor”. Respondeu algo mais útil: qual embedding é melhor para cada parte do sistema. E isso, no fim, é o que um estudo de verdade deveria fazer.