Image RAG que realmente funciona: um playbook de CTO para indexação em escala

Por Diogo Hudson Dias
Machine learning engineer in a São Paulo office reviewing a visual search dashboard with image thumbnails and performance charts on dual monitors.

Sua "busca visual" foi ao ar. Usuários digitam canecas de cerâmica vermelhas e seu sistema retorna uma torradeira, um tênis e um cachorro. A confiança despenca. Os executivos querem respostas. A verdade: não é o seu modelo. São suas decisões de indexação — e a falta de higiene de avaliação.

Uma leva recente de posts mostrou como equipes indexam imagens para RAG e busca visual. Útil — mas a maioria ignora as arestas operacionais difíceis: escolha e versionamento de embeddings, fusão multivetorial, trade‑offs de approximate nearest neighbor (ANN), orçamentos de re‑ranking e um harness de avaliação simples que você pode rodar toda noite. Se você lidera uma organização de engenharia, este é o playbook para entregar Image RAG que realmente recupera o que os usuários querem, a 100+ QPS, com custo previsível.

Comece pela intenção do usuário, não pelos modelos

Antes de escolher CLIP vs. SigLIP ou FAISS vs. Milvus, defina as intenções que você precisa atender. Intenções diferentes implicam embeddings, índices e filtros diferentes.

  • Similaridade visual pura: "Mostre produtos que se parecem com esta foto." O embedding global de imagem funciona; recursos de cor/textura ajudam.
  • Busca texto‑para‑imagem: "botas Chelsea de couro preto." Você precisa de alinhamento cross‑modal forte e cobertura multilíngue.
  • Recall em nível de objeto: "encontre imagens com um logo pequeno em um boné." Você precisa de detecção/segmentação para amplificar sinais fracos.
  • Texto na imagem: "embalagem que diz gluten‑free." OCR importa mais do que similaridade visual.
  • Conformidade/segurança: filtros de conteúdo e joins em metadados devem ser de primeira classe, não remendos.

Escolha o conjunto mínimo de intenções que você consegue ganhar agora e torne‑as explícitas na sua planilha de KPIs. O resto deriva disso.

Uma arquitetura de referência que não vai te surpreender em produção

1) Ingestão e deduplicação

  • Armazene os originais em object storage (S3/GCS) com um asset_id estável.
  • Compute um hash perceptual (pHash ou aivhash) para deduplicar quase idênticos e reduzir churn de indexação.
  • Normalize para um tamanho padrão de embedding (por exemplo, 224/256/384 no maior lado) e armazene uma miniatura.

2) Gere features (não só um embedding)

  • Embedding global de imagem: Comece com CLIP ViT‑B/32 (512d) ou SigLIP (tipicamente 768d). Variantes do SigLIP tendem a ir melhor em consultas multilíngues e detalhes finos, ao custo de vetores um pouco maiores.
  • Embedding de legenda: Rode um captionador de imagens rápido na ingestão (por exemplo, BLIP‑base) para produzir 1–2 legendas curtas. Alimente essas legendas no seu modelo padrão de embedding de texto (por exemplo, E5‑large, 1024d) para criar um segundo vetor por imagem. Isso compra recall para consultas de texto de cauda longa sem depender de metadados frágeis.
  • Rótulos de objetos: Opcional, mas de alto impacto para recall em nível de objeto. Use um detector (YOLOv8/DETR). Mantenha os top 5–10 rótulos como metadado, não vetores.
  • OCR: Se o texto na imagem importa, rode PaddleOCR ou Tesseract. Indexe o texto extraído no seu mecanismo de busca tradicional e armazene como campo filtrável.

Sim, isso dá mais trabalho do que "só CLIP". Também transforma sua busca de um demo em um produto.

3) Armazene com versionamento desde o primeiro dia

  • Em uma tabela de metadados (Postgres/Spanner), mantenha asset_id, tenant_id, flags de segurança, updated_at e campos de embedding_version para cada tipo de vetor (por exemplo, image_v1, caption_v2).
  • Persista vetores em chunks colunares por shard (por exemplo, arquivos Parquet de 100k linhas) para reindexação barata e movimentações em batch.
  • Toda linha de vetor carrega model_name, dim, dtype, checksum. Sem exceções.

4) Escolha seu índice ANN pela escala, não pelo hype

Estimativas de ordem de grandeza ajudam você a evitar a primeira reconstrução.

  • HNSW em CPU (FAISS/Milvus): Ótimo até ~10M vetores por índice com latência sub‑50 ms em recall decente. O orçamento de memória é aproximadamente 2–3× a memória bruta do vetor. Exemplo: 768‑d FP16 = 1,5 KB/vetor. 10M vetores ≈ 15 GB de dados de vetor + 15–30 GB de overhead do grafo ⇒ 30–45 GB de RAM.
  • IVF‑PQ em CPU: Compacte para ~64–128 bytes/vetor com quantização por produto. O recall cai um pouco, mas o gasto de memória cai uma ordem de magnitude. Bom para 10–100M de escala. Rebuild ou manutenção pesada são necessários conforme a distribuição deriva.
  • GPU ANN (FAISS‑GPU): Use quando você precisa de orçamentos p95 agressivos em alto QPS, ou re‑ranking pesado na mesma GPU. Não queime VRAM para armazenar grafos HNSW enormes; reserve‑a para compute e use códigos IVF‑PQ para manter a residência pequena.

Regra prática: se você está abaixo de 5M assets por tenant, HNSW é o mais simples que funciona. Acima de 10M, reserve orçamento para IVF‑PQ com rebuild periódico. Acima de 50M, planeje sharding rígido por tenant, região e versão de embedding — ou um serviço gerenciado se seu time não puder bancar a operação.

5) Pipeline de consulta que equilibra recall e latência

  1. Faça o embedding da consulta:
    • Consulta de texto: use o embedding de texto correspondente (cabeça de texto do CLIP ou E5). Normalize os vetores (L2) para similaridade por cosseno.
    • Consulta por imagem: compute o embedding global de imagem. Opcional: rode um OCR rápido e adicione termos a um filtro de correspondência obrigatória se seu domínio precisar.
  2. Busque em múltiplos índices: Consulte o índice de imagem global e o índice de legenda. Obtenha o top‑k (por exemplo, k=400) de cada um.
  3. Fusão e filtros: Soma ponderada dos scores (aprenda os pesos offline). Aplique filtros de tenant, segurança e estoque/disponibilidade.
  4. Re‑rank: Leve os top 200 e rode um avaliador cross‑modal mais forte para reordenar (por exemplo, um cross‑encoder do CLIP ou um re‑ranker visão‑linguagem pequeno). Reserve 40–120 ms em uma única GPU de data center para 200 pares, dependendo do modelo. Se você não puder ter uma GPU, use um re‑ranker bilinear mais leve para um ganho de 15–30 ms e aceite uma pequena perda de qualidade.

Almeje p95 end‑to‑end abaixo de 200 ms a 100 QPS em um deployment por região. Você chega lá com dois nós de índice em CPU (64–96 vCPU cada) e uma GPU modesta para re‑ranking.

Quanto isso custa (com números reais)

  • Tamanho do vetor: 768‑d FP16 = ~1,5 KB/vetor; 512‑d FP16 = ~1,0 KB/vetor. 1M imagens = 1–1,5 GB de vetores brutos por índice.
  • Memória do HNSW: 2–3× a memória de vetor na prática. Planeje 3–5 GB de RAM por 1M imagens para 768‑d FP16, incluindo overhead. Confortável em servidores commodity com 128–256 GB de RAM para escala de 10M.
  • Memória do IVF‑PQ: 64–128 bytes/vetor. 10M imagens ≈ 0,6–1,2 GB de códigos, mais centróides e overhead. Você troca um pouco de recall por uma economia enorme.
  • Throughput de embedding: Em uma única GPU de data center de médio porte (por exemplo, classe L4/T4), uma torre de imagem CLIP‑B/32 a 224 px roda ~30–60 img/s. 1M imagens levam ~5–9 horas‑GPU. Captioning com um BLIP‑base pequeno a 3–5 img/s levará 55–90 horas‑GPU; faça uma vez e, depois, incrementalmente.
  • Custo de re‑ranking: Um modelo cross‑modal pequeno avaliando 200 candidatos/consulta pode saturar uma única L4 a ~100–200 QPS, dependendo do batch e da precisão. Se o orçamento de latência estiver apertado, faça sharding horizontal das GPUs de re‑rank.

Bancos de dados vetoriais gerenciados tornam o preço nebuloso (unidades de capacidade, pods, camadas de RPS). Se você mesmo hospedar FAISS ou Milvus, seus principais custos serão RAM (para HNSW) ou CPU (para builds de IVF‑PQ) e uma linha pequena de GPU para re‑rank. Dá para rodar um deployment crível de 10M assets por poucos milhares/mês em infra se você já tem um footprint de Kubernetes.

Avaliação: pare de discutir, meça recall@k

Você não precisa de um laboratório de pesquisa para medir se a recuperação está boa. Você precisa de 200–1.000 pares consulta‑asset rotulados e duas métricas.

  • Recall@10: Fração de consultas em que uma correspondência conhecida e boa aparece no top 10. Para e‑commerce e moderação de UGC, você quer ≥0,6 antes de sair trombeteando o recurso.
  • NDCG@10: Uma pontuação graduada que recompensa a ordenação. Defina rótulos simples: perfeito/aceitável/ruim. Você precisa de ≥0,7 para parar de irritar usuários com resultados quase certos no topo.

Construa um job noturno que:

  1. Amostra 50 consultas frescas dos logs de produção (após limpeza de PII).
  2. Roda os índices atual e canário.
  3. Calcula recall@10 e NDCG@10.
  4. Mostra quais consultas regrediram e posta no Slack com miniaturas.

Essa thread no Slack vai te poupar semanas de debate circular. Também vai te manter honesto quando você trocar embeddings ou re‑rankers.

Versionamento e migração sem downtime

  • Versione todo vetor: image_v1, caption_v1. Quando trocar de modelo, crie image_v2, construa novos índices em paralelo e faça dupla escrita durante o backfill.
  • Backfill em shards: Mova 5–10% do tráfego para v2 conforme cada shard ficar pronto. Compare métricas lado a lado. Faça rollback se o recall noturno cair.
  • Mantenha duas gerações quentes: Mantenha v1 e v2 por 2–4 semanas. Depois, remova v1 para recuperar RAM/disco.

Não versionar é a causa número um de "nossos resultados ficaram estranhos na terça passada".

Multi‑tenancy e compliance, os bloqueadores pouco glamorosos

  • Shard por tenant quando a união de assets puder vazar correspondências sensíveis entre negócios (comum em B2B SaaS). Filtros rígidos são mais baratos que arrependimentos.
  • Criptografe em repouso os vetores. Eles não são imagens reversíveis, mas são sinais sensíveis sobre seu corpus.
  • Remoções precisam de um caminho de primeira classe: mantenha o mapeamento de index_id para asset_id e propague deletes em minutos, não dias.

Falhas comuns que seguimos vendo

  • Um vetor para tudo: As equipes dependem de um único embedding global e se perguntam por que consultas de texto de cauda longa falham. Adicione vetores baseados em legenda e seu recall salta sem trabalho pesado de modelo.
  • Sem normalização: Esquecer de normalizar por L2 leva a um cosseno que não é cosseno. Normalize na escrita e na consulta.
  • Ignorando cor/textura: Para moda e móveis, adicione um filtro barato de histograma de cor ou aprenda pesos que valorizem similaridade de crominância. Isso importa.
  • Deriva do índice: IVF‑PQ precisa de rebuild periódico à medida que novos assets deslocam a distribuição. Defina uma cadência de rebuild (por exemplo, mensal) e amarre às métricas de recall.
  • Misturando tenants: Já vimos equipes "otimizarem" co‑indexando tenants por throughput e depois gastarem um trimestre desfazendo vazamentos de dados. Não faça.
  • Melhorias não verificáveis: Lançar mudanças de modelo sem o harness de 200 consultas garante surpresas. Meça ou não envie.

Comprar vs. construir: um framework de decisão

Não há problema em usar um banco vetorial gerenciado. A questão é onde está seu risco.

  • Escolha gerenciado se:
    • Você está abaixo de 10M assets, multi‑região é mandatório e você não pode alocar expertise de ANN na prática.
    • Seu perfil de compras prefere pagar um prêmio por SLAs operacionais.
    • Você consegue conviver com preço opaco e lock‑in de fornecedor por 12–24 meses.
  • Escolha self‑host se:
    • Você já roda Kubernetes e tolera operar FAISS/Milvus.
    • Seu dataset cresce além de 10–50M e eficiência de RAM importa.
    • Você precisa de acoplamento apertado com suas GPUs de re‑rank e filtros customizados em tempo de consulta.

Um híbrido é comum: auto‑hospede HNSW para os índices quentes de tenants, e descarregue índices frios ou experimentais para um serviço gerenciado.

Quão rápido você chega lá (um plano realista de 4 semanas)

Se você tem dados rotulados e um baseline de devops, dá para colocar um sistema crível de pé em um mês.

  • Semana 1: Ingestão + dedupe com pHash + embedding global v1 (CLIP/SigLIP). Protótipo com FAISS HNSW. Filtros rígidos funcionando.
  • Semana 2: Geração de legendas + embedding de legenda. Busca em índices duplos + fusão de scores. Harness básico de recall@10 com 200 pares.
  • Semana 3: Serviço de re‑ranker em GPU, p95 abaixo de 250 ms. Caminho canário para embeddings v2.
  • Semana 4: Endurecimento: caminho de remoção (takedown), sharding por tenant, relatórios noturnos de avaliação no Slack, dashboards de métricas. Comece o backfill de assets históricos.

Este é o tipo de pod que tocamos com equipes nos EUA a partir do Brazil: 6–8 horas de sobreposição, entregáveis claros e nada de teatro de pesquisa. A stack é propositalmente sem graça.

Ajustes avançados quando você precisar

  • Destilação e quantização: Se a memória estiver apertada, treine um embedding aluno (por exemplo, 384‑d) a partir do seu modelo professor no seu domínio, depois quantize para FP16/INT8. Você costuma manter 90–95% do recall com metade dos bytes.
  • Expansão em tempo de consulta: Adicione sinônimos ou descritores curtos (por exemplo, para moda: cor, material). Melhore o recall sem aumentar a pressão sobre o índice.
  • Fusão híbrida BM25 + vetores: Para domínios com muito OCR, BM25 no texto extraído somado aos scores vetoriais supera qualquer um isolado.
  • Re‑rank temporal: Se frescor importa, aplique decaimento por idade nos scores após a fusão. Não deixe uma imagem viral dominar por semanas.

O que colocar no dashboard

Sua revisão semanal deve mostrar:

  • Recall@10 e NDCG@10 por intenção (visual, texto, OCR) e por tenant.
  • Latência p95 end‑to‑end; tempo p95 de ANN; tempo p95 de re‑rank.
  • Cardinalidade do índice, memória por índice e data do último rebuild.
  • Top 10 consultas que regrediram vs. a semana passada, com miniaturas.
  • SLA de remoção: tempo do pedido até a purga no índice.

Se você não acompanhar, vai redescobrir os mesmos problemas todo trimestre.

Em resumo

Image RAG que funciona não é um avanço de pesquisa. É um conjunto de escolhas de engenharia sóbrias e um tantinho de disciplina. Use dois vetores por asset, escolha o ANN certo para sua escala, reserve orçamento para re‑ranking e meça recall toda noite. Faça isso e seus usuários vão parar de ver cachorros quando buscarem por canecas.

Principais conclusões

  • A escolha de modelo é secundária; disciplina de indexação e avaliação determinam qualidade no mundo real.
  • Use indexação multivetorial (imagem global + legenda) para elevar recall sem heroísmo.
  • Escolha HNSW abaixo de ~10M assets; planeje IVF‑PQ e rebuilds além disso.
  • Faça re‑ranking do top 100–200 com um modelo cross‑modal mais forte; reserve 40–120 ms em uma GPU pequena.
  • Versione todo embedding; faça dupla escrita e canário antes de virar tráfego.
  • Entregue um harness de recall@10/NDCG de 200–1.000 pares e rode‑o toda noite; deixe o Slack avisar quando você regredir.
  • Shard por tenant e torne remoções (takedowns) de primeira classe para evitar pesadelos de compliance.

Ready to scale your engineering team?

Tell us about your project and we'll get back to you within 24 hours.

Start a conversation