AetherMap / docs /METRICS.md
Madras1's picture
Update docs/METRICS.md
fa6d517 verified
|
Raw History Blame Contribute Delete
30.1 kB

AetherMap - Métricas, Arquitetura e Avaliação do RAG

Atualizado em: 2026-08-22

Snapshot principal dos benchmarks: 2026-05-11

Este documento é a fonte principal de métricas do AetherMap. Ele descreve a stack, a arquitetura do RAG, os modos de ablação, os benchmarks executados contra o Space e a leitura crítica dos resultados. Os números abaixo são um snapshot histórico, não medições contínuas da versão atualmente publicada.

1. O que é o AetherMap

O AetherMap é uma aplicação de cartografia semântica + RAG híbrido. Ele recebe um corpus em TXT/CSV, transforma os textos em embeddings, cria um mapa 3D navegável, agrupa documentos por similaridade, detecta duplicatas e responde perguntas com citações para os documentos recuperados.

O projeto tem duas frentes principais:

Frente Entrega
Mapa semântico Visualização 3D, clusters, top termos, riqueza lexical, entropia e duplicatas
RAG híbrido Busca semântica + busca lexical + reranking + resposta LLM com citações
  • Backend principal: app.py
  • Frontend: frontend/
  • Experimentos e benchmarks: metrics/

2. Stack de tecnologia

Backend e API

Tecnologia Uso no projeto
Python Linguagem principal do backend e dos scripts de avaliação
FastAPI API HTTP para upload, processamento, busca RAG, busca web e grafo de entidades
Uvicorn Servidor ASGI para execução da API
python-multipart Upload de arquivos via formulário
OpenAI SDK Cliente OpenAI-compatible para chamar OpenRouter
Tavily Busca web opcional em /search_web/

Machine Learning, NLP e busca

Tecnologia Uso no projeto
SentenceTransformers Runtime para carregar o modelo de embeddings e gerar vetores de corpus/query
Madras1/minilm-gooaq-mnr-v5 Embedding fine-tunado por Gabriel Yogi no GooAQ, usado como recuperador semântico customizado do AetherMap
CrossEncoder Reranking dos documentos candidatos
mmarco-mMiniLMv2-L12-H384-v1 Reranker multilíngue treinado para recuperação
FAISS Índice vetorial para busca semântica por produto interno
BM25 customizado Busca lexical sem dependência externa de rank_bm25
RRF Fusão de rankings FAISS e BM25
UMAP Projeção 3D semântica mais rica
PCA Projeção 3D rápida em fast_mode
HDBSCAN Clustering por densidade, sem número fixo de clusters
scikit-learn TF-IDF, CountVectorizer, StandardScaler, PCA
NLTK + stopwords.txt Stopwords PT/EN e stopwords customizadas
spaCy NER para grafo de entidades em PT/EN
langdetect Detecção de idioma para escolher modelo spaCy

Observabilidade e avaliação

Tecnologia Uso no projeto
Prometheus FastAPI Instrumentator Métricas HTTP automáticas
prometheus-client Histogramas de latência do LLM, busca total e etapas internas por modo de ablação
Hugging Face Datasets Carregamento de SQuAD-PT nos benchmarks
requests Execução dos benchmarks contra o Space
Mistral API LLM-as-judge externo para avaliar respostas geradas
JSON/Markdown Persistência dos resultados brutos e relatórios

Frontend

Tecnologia Uso no projeto
HTML/CSS/JavaScript Interface do usuário
Plotly/Three.js Visualização 3D e exploração semântica
Chart.js Gráficos auxiliares de métricas

3. Arquitetura do RAG

O RAG do AetherMap é um pipeline em camadas:

Camada 1 - Ingestão
  TXT/CSV
  -> extração dos textos
  -> seleção de coluna textual

Camada 2 - Representação
  textos
  -> embeddings com modelo fine-tuned para RAG
  -> normalização dos vetores
  -> índices FAISS e BM25

Camada 3 - Exploração semântica
  embeddings
  -> UMAP ou PCA para 3D
  -> HDBSCAN para clusters
  -> TF-IDF por corpus e por cluster
  -> duplicatas exatas e semânticas

Camada 4 - Retrieval
  query
  -> embedding da query
  -> busca FAISS
  -> busca BM25
  -> fusão RRF

Camada 5 - Reranking
  candidatos recuperados
  -> CrossEncoder(query, documento)
  -> top documentos finais

Camada 6 - Geração
  contexto com [ID: n]
  -> prompt com regra de citação, honestidade e idioma
  -> LLM via OpenRouter
  -> resposta final citada

Por que o RAG é híbrido

Recuperador Força Fraqueza
FAISS/embeddings Sinônimos, paráfrases e similaridade semântica Pode errar nomes próprios, códigos e termos exatos
BM25 Termos raros, nomes, siglas e correspondência literal Pode falhar com sinônimos e perguntas parafraseadas
RRF Combina rankings sem depender de escala comum de score Depende da qualidade dos candidatos iniciais
CrossEncoder Reordena olhando o par query-documento Custa mais latência que retrieval puro

A arquitetura favorece recall amplo primeiro, precisão no topo depois. FAISS e BM25 trazem candidatos por sinais diferentes; RRF junta os rankings; o CrossEncoder decide a ordem final dos melhores documentos.

Query expansion

O modo full ativa a expansão de query via LLM antes da recuperação. A ideia é aumentar o recall, mas ela tem custo: aumenta a latência e pode introduzir ruído. No snapshot principal, full empatou com hybrid_rerank em qualidade e foi mais lento; portanto, a recomendação operacional é usar hybrid_rerank.

4. Endpoints principais

Endpoint Papel
GET / Verificação de integridade simples
POST /csv_columns/ Lista colunas de um CSV antes do processamento
POST /process/ Processa corpus, gera embeddings, clusters, índices e métricas
POST /search/ Executa RAG com ablation_mode configurável
POST /describe_clusters/ Usa LLM para descrever clusters
POST /search_web/ Busca web via Tavily e cria corpus a partir dos resultados
POST /entity_graph/ Extrai entidades e cria grafo
POST /analyze_graph/ Análise LLM do grafo de entidades
GET /metrics Métricas Prometheus

5. Modos de ablação

Modo Componentes ativos Uso recomendado
faiss_only FAISS Baseline semântico
bm25_only BM25 Baseline lexical
hybrid FAISS + BM25 + RRF Modo rápido com recuperação híbrida
hybrid_rerank FAISS + BM25 + RRF + CrossEncoder Melhor compromisso atual entre qualidade e latência
full Hybrid rerank + query expansion Teto experimental de qualidade, mais lento

Configuração atual de busca:

Parâmetro Normal turbo_mode=true
top_k_retrieval até 100 candidatos até 30 candidatos
final_top_k 10 documentos 5 documentos
RRF k 60 60
Query expansion apenas em full desativada

6. Métricas usadas

As métricas foram separadas em retrieval, geração, citação, abstenção e latência. Isso evita conclusões erradas: um sistema pode recuperar o documento certo e gerar mal, ou recuperar mal e ainda parecer plausível.

Área Métricas
Retrieval Hit@1, Hit@3, Hit@5, Hit@10, MRR
Geração literal Generation success, Strict answer contains gold
Geração semântica Semantic answer quality, Correctness pass rate
Citação Citation valid, Citation supports gold doc
Abstenção Refusal accuracy
LLM-as-judge correctness, faithfulness, citation_quality, refusal_quality, verdict_pass_rate
Latência média, mediana, p95, llm_api_latency_seconds
Corpus documentos, clusters, ruído, riqueza lexical, entropia, duplicatas

Definições importantes:

Métrica Definição
Hit@k Documento correto apareceu entre os k primeiros resultados
MRR Média de 1/rank do primeiro documento correto
Generation success A resposta foi gerada, sem cair no fallback
Strict answer contains gold A resposta contém literalmente a resposta esperada após normalização
Semantic answer quality Correctness avg / 5.0 no juiz Mistral; aproxima qualidade semântica da resposta
Correctness pass rate Proporção de respostas com correctness >= 4 no juiz Mistral
Citation valid Toda citação [ID: n] aponta para um resultado existente
Citation supports gold doc A citação aponta para o documento correto
Refusal accuracy O sistema recusa quando o tópico correto não foi fornecido ao corpus

7. Observabilidade implementada

O backend expõe /metrics via Prometheus FastAPI Instrumentator:

curl https://madras1-aethermap.hf.space/metrics

Além das métricas HTTP automáticas, o backend agora expõe métricas customizadas para latência do LLM e para as etapas internas do /search/.

Métrica Tipo Mede
llm_api_latency_seconds Histograma Tempo da chamada externa ao LLM
aethermap_search_total_latency_seconds Histograma Latência total do endpoint /search/, por ablation_mode
aethermap_search_stage_latency_seconds Histograma Latência por etapa interna, com os rótulos stage e ablation_mode

Etapas instrumentadas em aethermap_search_stage_latency_seconds:

Stage O que mede
model_load Carregamento de modelos sob demanda
query_expansion Expansão de query do modo full
query_embedding Embedding da pergunta
faiss_search Busca vetorial no índice FAISS
bm25_search Busca lexical BM25
rank_fusion Fusão RRF
candidate_prep Preparação dos documentos candidatos
rerank CrossEncoder reranking
prompt_assembly Montagem do contexto e prompt
llm_call Chamada externa ao LLM

O endpoint /search/ também devolve timings_ms na resposta JSON. Isso foi usado para medir o gargalo real do Space sem instalar FAISS localmente.

Buckets do histograma legado do LLM:

0.1, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0 segundos

8. Benchmark principal: SQuAD-PT estrito

Este é o benchmark principal para portfólio porque está em português, usa um dataset real e mede o caminho completo da API do Space: processamento, recuperação e resposta LLM citada.

Item Valor
Dataset nunorc/squad_v1_pt
Split validation
API medida https://madras1-aethermap.hf.space
Embedding no Space Madras1/minilm-gooaq-mnr-v5
Contextos enviados 120
Perguntas respondíveis 40
Perguntas controladas sem resposta 10
Total por modo 50 queries
Total de buscas 250
Modos testados faiss_only, bm25_only, hybrid, hybrid_rerank, full
Script metrics/run_aethermap_squad_llm_metrics.py
Resultado bruto metrics/results/squad_llm_metrics_squadpt-50q-strict-specific-fullanswer-minilm-gooaq-mnr-v5_20260511T025206Z.json

Como o SQuAD-PT não possui exemplos nativos sem resposta, essas perguntas foram criadas de forma controlada. Na versão final do benchmark, os controles sem resposta usam três filtros:

Filtro Motivo
source_title ausente do corpus Evita vazamento por outros trechos do mesmo artigo
título específico, não tópico genérico Evita controles amplos como Construction, que podem ser parcialmente respondidos por documentos relacionados
pergunta com âncora nominal Prefere perguntas com entidades como BSkyB, Sky, Virgin ou Microsoft

Na rodada final, os controles sem resposta vieram de Sky_(United_Kingdom), título ausente do corpus enviado.

Resultados automáticos

Modo Hit@1 Hit@3 MRR Generation success Strict answer contains gold Citation valid Citation supports gold doc Refusal accuracy Mean latency p95 latency
faiss_only 0.57 0.80 0.69 1.00 0.60 0.88 0.88 1.00 9.31s 24.18s
bm25_only 0.80 0.82 0.83 1.00 0.72 0.93 0.93 1.00 10.30s 26.94s
hybrid 0.72 0.90 0.82 1.00 0.70 0.88 0.88 1.00 8.11s 18.45s
hybrid_rerank 0.97 1.00 0.99 1.00 0.68 0.97 0.97 1.00 22.34s 36.26s
full 0.97 1.00 0.99 1.00 0.70 1.00 1.00 1.00 24.82s 35.61s

Qualidade semântica da resposta

O Strict answer contains gold é uma métrica propositalmente dura: ela procura a string do gabarito dentro da resposta. Isso é bom para detectar falhas óbvias, mas subestima respostas corretas que parafraseiam, traduzem, flexionam ou respondem com uma formulação equivalente. Por isso a métrica de portfólio para qualidade de resposta deve ser a qualidade semântica julgada pelo Mistral, junto com fidelidade e citação.

Artefato:

metrics/results/answer_quality_summary_squadpt_50q_strict_specific_20260511T191047Z.md
Modo Strict answer contains gold Semantic answer quality Mistral correctness avg Correctness pass rate Faithfulness avg Verdict pass rate
faiss_only 0.60 0.92 4.60 0.88 4.82 0.90
bm25_only 0.72 0.97 4.86 0.97 4.92 0.98
hybrid 0.70 0.95 4.76 0.93 4.80 0.94
hybrid_rerank 0.68 0.98 4.88 0.97 4.98 0.98
full 0.70 0.98 4.92 0.97 5.00 0.96

Leitura: o número literal 0.68 do hybrid_rerank não significa que o sistema só acerta 68% das respostas. Ele significa que 68% das respostas continham literalmente a string esperada do SQuAD-PT. Quando o julgamento considera equivalência semântica, o mesmo modo chega a Semantic answer quality = 0.98, Faithfulness avg = 4.98 e Verdict pass rate = 0.98.

Leitura técnica

O melhor modo medido é hybrid_rerank.

Ele empatou com full em retrieval (Hit@1 = 0.97, Hit@3 = 1.00, MRR = 0.99, refusal 1.00), mas teve menor latência média (22.34s contra 24.82s) e melhor pass rate no juiz Mistral. O full continua útil como teto experimental, mas não é o modo recomendado para portfólio porque a query expansion não trouxe ganho de retrieval nessa rodada.

O hybrid foi o modo rápido mais interessante: Hit@3 = 0.90, MRR = 0.82, Refusal accuracy = 1.00 e p95 = 18.45s. Ele é bom para uma demonstração rápida, mas o modo de portfólio deve ser hybrid_rerank, porque ele estabiliza muito melhor o top 1.

Strict answer contains gold ficou abaixo das demais métricas porque é uma métrica literal. Ela penaliza respostas semanticamente corretas que parafraseiam o gabarito, mudam flexão, usam tradução alternativa ou incluem uma forma equivalente da entidade. Por isso ela deve ser lida junto com citação e LLM-as-judge, não isoladamente.

9. Avaliação LLM-as-judge com Mistral

Foi rodada uma avaliação LLM-as-judge usando a API da Mistral sobre a rodada SQuAD-PT estrita. O juiz avaliou todos os cinco modos de ablação, com 50 respostas por modo (40 respondíveis e 10 controladas sem resposta), totalizando 250 julgamentos.

Arquivos:

Artefato Caminho
Script metrics/run_mistral_llm_judge.py
Resultado JSON metrics/results/mistral_llm_judge_squadpt_50q_strict_specific_fullanswer_all_modes_20260511T025836Z.json
Relatório Markdown metrics/results/mistral_llm_judge_squadpt_50q_strict_specific_fullanswer_all_modes_20260511T025836Z.md

Correção metodológica importante: a versão anterior do juiz lia summary_preview, truncado em 700 caracteres. Isso foi corrigido. O benchmark agora salva generated_answer com a resposta completa, e o juiz usa answer_text_source = generated_answer. Respostas muito longas ainda podem ser recortadas apenas pelo limite de prompt do juiz (max_answer_chars = 6000), o que não ocorreu de forma relevante nesta rodada.

Modo Julgados Correctness avg Faithfulness avg Citation avg Refusal avg Verdict pass rate
faiss_only 50 4.60 4.82 4.64 5.00 0.90
bm25_only 50 4.86 4.92 4.70 5.00 0.98
hybrid 50 4.76 4.80 4.56 5.00 0.94
hybrid_rerank 50 4.88 4.98 4.92 5.00 0.98
full 50 4.92 5.00 4.94 5.00 0.96

Leitura: o juiz confirma que hybrid_rerank é o melhor modo de apresentação. Ele empatou com o melhor pass rate (0.98), teve Faithfulness avg = 4.98, Citation avg = 4.92 e manteve Refusal avg = 5.00. O full teve médias ligeiramente maiores em correctness/faithfulness, mas menor pass rate (0.96) e maior latência média.

Os casos reprovados restantes foram todos em perguntas respondíveis; nenhum modo falhou nos controles sem resposta:

Modo Casos reprovados Padrão observado
faiss_only 5 Recuperação/citação mais fraca e algumas recusas indevidas
bm25_only 1 Recusa indevida em uma pergunta sobre Victoria/Koori
hybrid 3 Recusas indevidas e uma citação parcial
hybrid_rerank 1 Resposta parcial sobre oxigênio livre
full 2 Respostas semanticamente válidas, mas parciais frente ao gabarito literal

10. Investigação da precisão de recusa

A rodada ampliada anterior tinha Refusal accuracy = 0.83. A investigação mostrou que isso era um artefato do benchmark, não uma conclusão limpa sobre o sistema.

O problema era este: os exemplos controlados sem resposta vinham do título Super_Bowl_50. O benchmark removia o contexto exato da pergunta, mas ainda permitia que outros trechos do mesmo artigo entrassem no corpus. Assim, a pergunta era marcada como "sem resposta", mas o retriever encontrava documentos semanticamente próximos sobre Broncos, Panthers, MVP e evento do Super Bowl.

Caso que falhou na rodada antiga:

Qual time da NFL representou o AFC no Super Bowl 50?

O sistema recuperava trechos sobre Denver Broncos e Super Bowl 50. O texto citado não dizia literalmente "representou a AFC", mas o LLM completava a lacuna por inferência/conhecimento externo. Havia dois problemas:

Problema Efeito
Controle sem resposta por contexto, não por título O corpus ainda tinha documentos do mesmo tema
Prompt permissivo a inferência indireta O LLM respondia a partir de pista parcial

Correções aplicadas:

Arquivo Correção
metrics/run_aethermap_squad_llm_metrics.py Controles sem resposta agora exigem que o source_title da pergunta esteja ausente do corpus enviado
app.py Prompt do RAG foi endurecido contra conhecimento externo, inferência indireta e citação apenas relacionada

Depois disso, uma rodada com 200 contextos revelou um segundo problema: ao aumentar demais o corpus, sobravam controles sem resposta vindos de Construction, que é um tópico genérico. Mesmo removendo o título Construction, outros documentos relacionados a prédios, reformas e infraestrutura podiam sustentar respostas parciais. Isso não era um bom controle de abstenção.

A versão final do script filtra esses casos: controles sem resposta precisam vir de títulos específicos e perguntas com âncora nominal. Na rodada final, os controles vieram de Sky_(United_Kingdom), ausente do corpus. Resultado: Refusal accuracy = 1.00 em todos os modos e Refusal avg = 5.00 no juiz Mistral em todos os modos.

Observação: essas medições foram feitas contra o Space em 2026-05-11. O app.py local também foi endurecido para proibir conhecimento externo, inferência indireta e vazamento de raciocínio interno em respostas finais. Em 2026-08-22, a configuração local do build foi corrigida para fixar versões compatíveis do PyTorch e permitir que o instalador resolva dependências no PyPI. Até a nova versão ser publicada e os testes serem executados novamente, os números desta seção devem ser tratados como um snapshot histórico, não como validação do commit atual.

11. Análise arquitetural: escala, RAM e Space

O AetherMap foi desenhado como um sistema de processamento de corpus inteiro antes da busca. Isso muda o comportamento do RAG: em vez de calcular tudo a cada pergunta, ele faz uma fase pesada de ingestão e depois consulta estruturas já materializadas.

Fluxo real:

/process/
  CSV/TXT completo
  -> extração da coluna textual
  -> embeddings em batch
  -> normalização vetorial
  -> índice FAISS
  -> índice BM25
  -> mapa 3D
  -> clusters HDBSCAN
  -> TF-IDF global/por cluster
  -> detecção de duplicatas
  -> cache por job_id

/search/
  query
  -> consulta o job_id já indexado
  -> recupera candidatos
  -> funde rankings
  -> rerankeia
  -> envia apenas top documentos ao LLM

Por isso a arquitetura aguenta melhor datasets reais do que uma demo que faz retrieval improvisado a cada pergunta:

Mecanismo Por que ajuda
Indexação uma vez por job_id O custo pesado fica no /process/; várias perguntas reaproveitam embeddings, FAISS, BM25 e DataFrame
Embeddings em batch Vetoriza o corpus de forma mais eficiente do que documento a documento
FAISS com vetores normalizados Busca semântica sobre índice pronto por produto interno/cosseno
Troca para HNSW em corpus grande Acima de FAISS_HNSW_MIN_SIZE, usa IndexHNSWFlat, reduzindo custo de busca em bases maiores
Reranker sobre os principais candidatos O CrossEncoder não vê o corpus inteiro; ele reordena até 100 candidatos recuperados

Trade-offs atuais da demo no Hugging Face Space:

Trade-off Leitura técnica
Cache em RAM Decisão adequada para demo em Space: evita depender de armazenamento persistente
Sem persistência de jobs/índices Escolha pragmática, porque o armazenamento local do Space não deve ser tratado como banco durável
BM25 custom atual Percorre documentos para pontuar; em corpus muito grande pode virar gargalo por query
plot_data retorna todos os textos Bom para demo visual imediata, mas respostas de /process/ podem ficar pesadas com datasets inteiros
UMAP fora do fast_mode Pode ficar caro em datasets grandes; fast_mode com PCA é o caminho prático
Query expansion do full Aumenta latência e, nas medições, não melhorou qualidade sobre hybrid_rerank

Perfil de latência do /search/ no Space

Para investigar a latência sem instalar FAISS localmente, foi criado um profiler que usa a própria API hospedada no Hugging Face Space. O script envia o corpus pelo /process/, executa buscas no /search/ e coleta o campo timings_ms devolvido pelo backend instrumentado.

Artefatos:

Artefato Caminho
Script metrics/profile_space_search_timings.py
Resultado JSON metrics/results/space_search_timing_profile_squadpt_5q_20260511T204758Z.json
Relatório Markdown metrics/results/space_search_timing_profile_squadpt_5q_20260511T204758Z.md

Estas medições incluem efeitos reais do Space: CPU compartilhada, poucos workers/threads, fila, rede e chamada externa ao LLM. Elas descrevem a latência da demo pública, não uma implantação de produção otimizada.

Latência ponta a ponta do endpoint:

Modo Queries Mean Median p95
faiss_only 5 10.62s 10.97s 16.45s
bm25_only 5 5.19s 3.00s 11.17s
hybrid 5 4.82s 3.66s 8.11s
hybrid_rerank 5 16.47s 15.24s 22.48s
full 5 18.94s 18.47s 25.73s

Média das etapas internas mais relevantes:

Modo Query embedding FAISS BM25 RRF Rerank Query expansion LLM call Total backend
faiss_only 12.29ms 0.28ms 0.00ms 0.02ms 0.10ms 0.00ms 10223.82ms 10240.65ms
bm25_only 15.63ms 0.00ms 2.90ms 0.02ms 0.10ms 0.00ms 4999.80ms 5022.49ms
hybrid 13.95ms 0.30ms 2.69ms 0.17ms 0.10ms 0.00ms 4639.20ms 4660.91ms
hybrid_rerank 13.56ms 0.29ms 2.41ms 0.15ms 8018.13ms 0.00ms 8076.30ms 16115.11ms
full 24.20ms 0.29ms 2.81ms 0.14ms 7937.09ms 3252.05ms 7552.86ms 18773.65ms

Leitura técnica: no corpus medido (120 documentos), FAISS e BM25 são baratos. FAISS fica abaixo de 0.4ms; BM25 fica em torno de 2.4ms a 2.9ms; RRF fica abaixo de 0.2ms. Portanto, a latência alta do portfólio não vem do retrieval base. O custo vem principalmente de três pontos:

Gargalo Evidência
Chamada externa ao LLM De 4.6s a 10.2s de média nos modos sem rerank/full
CrossEncoder reranking Aproximadamente 8.0s em hybrid_rerank e full
Query expansion Aproximadamente 3.25s adicionais no full

O BM25 customizado merece ser substituído em um corpus grande, mas ele não é o gargalo desta demonstração. A decisão correta é manter a medição por etapa e revisitar o BM25 quando o corpus passar para milhares/dezenas de milhares de documentos ou quando o perfil mostrar bm25_search como componente dominante.

Portanto, o AetherMap já é mais do que um protótipo simples: ele tem arquitetura de RAG de corpus real, com ingestão, indexação, cache por job, recuperação híbrida, reranking e resposta citada. No formato atual, ele está corretamente otimizado para uma demo interativa no Space. Para produção fora do Space, os próximos passos seriam persistir índices/jobs em armazenamento durável, paginar plot_data, otimizar reranking/LLM e trocar o BM25 manual por uma estrutura lexical mais eficiente quando a escala justificar.

12. Ablação do embedding: customizado com GooAQ vs. all-MiniLM-L6-v2

Foi rodada uma comparação A/B com o mesmo dataset, mesma seed, mesmas queries e mesmos modos. O baseline foi sentence-transformers/all-MiniLM-L6-v2; a rodada customizada usa o embedding fine-tunado no GooAQ.

Arquivo de comparação:

metrics/results/embedding_ablation_custom_vs_minilm.md

Resumo dos deltas do embedding customizado contra o baseline:

Modo Delta Hit@1 Delta Hit@3 Delta MRR Delta citation support Delta p95 latency
faiss_only +0.125 +0.125 +0.135 +0.000 +6.097s
bm25_only +0.000 +0.000 +0.000 +0.000 +8.452s
hybrid +0.125 +0.000 +0.083 +0.000 +4.684s
hybrid_rerank +0.000 +0.000 +0.000 +0.000 +3.887s
full +0.000 +0.000 +0.000 +0.000 +6.908s

Leitura: nesse benchmark controlado pequeno, o embedding customizado melhorou os modos em que o recuperador semântico aparece com menos compensação do reranker, especialmente faiss_only e hybrid. Nos modos com CrossEncoder (hybrid_rerank e full), a qualidade empatou porque o reranker corrigiu o topo do ranking nos dois casos.

O ganho não deve ser generalizado para qualquer dataset. Em uma rodada pequena separada sobre SQuAD em inglês, o baseline foi melhor no faiss_only, enquanto os modos com reranker empataram em recuperação. A conclusão sustentada é que o projeto implementa um embedding próprio e uma medição A/B capaz de revelar onde ele ajuda ou regride. Para afirmar superioridade por domínio, a comparação deve ser repetida de forma pareada em SQuAD-PT com 50 a 100 perguntas por modo.

13. Nota sobre benchmark controlado antigo

Existe um benchmark sintético pequeno em metrics/run_aethermap_portfolio_metrics.py. Ele não é usado como evidência principal neste documento, porque a rodada antiga misturava um dataset toy com comportamento de geração/fallback do full que não representa o benchmark real em SQuAD-PT. Para apresentação pública, a evidência principal deve ser a rodada SQuAD-PT estrita com 50 queries por modo e LLM-as-judge sobre resposta completa.

O benchmark controlado continua útil como teste de sanidade do harness, mas deve ser executado novamente depois que a correção do build e o prompt endurecido forem publicados no Space.

14. Como reproduzir

Benchmark principal em português, modo estrito:

python metrics/run_aethermap_squad_llm_metrics.py --dataset-id nunorc/squad_v1_pt --split validation --n-contexts 120 --n-answerable 40 --n-unanswerable 10 --modes faiss_only bm25_only hybrid hybrid_rerank full --run-label squadpt-50q-strict-specific-fullanswer --embedding-label minilm-gooaq-mnr-v5 --timeout 300 --pause 0.15

LLM-as-judge com Mistral:

python metrics/run_mistral_llm_judge.py --input-json metrics/results/squad_llm_metrics_squadpt-50q-strict-specific-fullanswer-minilm-gooaq-mnr-v5_20260511T025206Z.json --modes faiss_only bm25_only hybrid hybrid_rerank full --model mistral-small-latest --max-answer-chars 6000 --output-prefix metrics/results/mistral_llm_judge_squadpt_50q_strict_specific_fullanswer_all_modes

Resumo de qualidade semântica da resposta:

python metrics/summarize_answer_quality.py --benchmark-json metrics/results/squad_llm_metrics_squadpt-50q-strict-specific-fullanswer-minilm-gooaq-mnr-v5_20260511T025206Z.json --judge-json metrics/results/mistral_llm_judge_squadpt_50q_strict_specific_fullanswer_all_modes_20260511T025836Z.json --output-prefix metrics/results/answer_quality_summary_squadpt_50q_strict_specific

Perfil de latência por etapa no Space, sem instalar FAISS local:

python metrics/profile_space_search_timings.py --api-url https://madras1-aethermap.hf.space --contexts-csv metrics/results/squad_contexts_latest.csv --queries-json metrics/results/squad_queries_latest.json --modes faiss_only bm25_only hybrid hybrid_rerank full --max-queries 5 --timeout 300 --pause 0.15 --output-prefix metrics/results/space_search_timing_profile_squadpt_5q

Benchmark controlado:

python metrics/run_aethermap_portfolio_metrics.py

Ablação do embedding:

python metrics/compare_embedding_runs.py --baseline-json metrics/results/<baseline>.json --custom-json metrics/results/<custom>.json