← Voltar ao Blog

GraphRAG perdeu da busca vetorial: 71,7 vs 60,8%

Montei o GraphRAG descrito no capítulo 5 do meu livro, apontei para 120 perguntas de multi-hop em japonês, e perdi para a busca vetorial clássica. 71,7% contra 60,8% de acerto exato. O grafo custou 3,2 horas de extração de triplas; o vetor custou 43 segundos de embedding.

A parte interessante não é a derrota. É onde ela aconteceu.

GraphRAG vs vetor em 120 perguntas: comparação de acerto exato e custo de construção

O que eu estava testando

GraphRAG é a técnica do Microsoft Research (2024-02) que indexa um corpus construindo um grafo de entidades e relações antes da consulta. A promessa é que, em perguntas multi-hop ("onde morreu o general que assinou X?"), a travessia do grafo encontra o caminho que a busca vetorial perde.

Eu descrevi essa mecânica no GraphRAG vs RAG clássico, em 4 projetos e ainda acho que o conceito faz sentido. Mas conceito não é número.

Então peguei o JEMHopQA (dev ver1.2, CC BY-SA 4.0), um benchmark japonês de multi-hop QA com 120 perguntas: 47 composicionais (duas inferências encadeadas) e 73 comparativas (A é maior que B?). Baixei os 219 artigos de Wikipedia referenciados, cortei cada um em 4.000 caracteres, dividi em 2.382 chunks de até 400 caracteres.

Para a camada LLM, usei qwen3.5:9b local via Ollama. Mesmo modelo para extração de triplas, extração de entidades da pergunta, e geração da resposta final. Temperature 0. Embeddings via bge-m3.

A implementação do GraphRAG foi o graphrag.py que escrevi para este teste, seguindo a mecânica descrita no capítulo 5 do livro: extração de triplas por chunk com schema JSON, grafo com networkx MultiDiGraph, busca por entidades da pergunta + vizinhança 1-2 hops. Sem resolução de identidade, sem detecção de comunidade, sem resumos. GraphRAG “cru”, do jeito que você escreve na primeira iteração.

Os resultados

Rodei 6 condições nas 120 perguntas, 1 passada cada, mesmo orçamento de contexto (3.000 caracteres).

CondiçãoAcerto exatoInclusãoResposta tem o alvo
none (só memória)46 (38,3%)47 (39,2%)—
vector86 (71,7%)88 (73,3%)49/75 (65,3%)
graph73 (60,8%)74 (61,7%)38/75 (50,7%)
hybrid78 (65,0%)79 (65,8%)45/75 (60,0%)
graph_rk69 (57,5%)71 (59,2%)36/75 (48,0%)
hybrid_rk76 (63,3%)78 (65,0%)42/75 (56,0%)

A busca vetorial pura ganhou. O híbrido (trechos de grafo + chunks por vetor intercalados) chegou mais perto, mas ainda ficou atrás. O _rk é uma variante que reordena as triplas pela sobreposição de bigramas de caracteres com a pergunta — pensei que fosse ajudar e piorou.

Separando por tipo:

  • Composicionais (47): vector 44,7%, graph 29,8%. Diferença de 15 pontos.
  • Comparativas (73): vector 89,0%, graph 80,8%. Diferença de 8 pontos.

As comparativas são "X é maior que Y?" — 60% já é resolvido só com a memória do modelo. É nas composicionais, exatamente onde o grafo deveria brilhar, que ele mais perdeu.

Onde o grafo salvou (e onde quebrou)

Cruzando as 120 respostas com a métrica de inclusão:

  • Vector e graph acertaram: 64 perguntas
  • Só vector: 24 perguntas (quase sempre composicionais)
  • Só graph: 10 perguntas
  • Nenhum acertou: 22 perguntas

Então sim, existe um conjunto de 10 perguntas onde o grafo foi o único a chegar. Esse é o ponto que livros e posts de GraphRAG usam para vender a técnica. O problema é que o conjunto do adversário tem 24 perguntas. Para cada ponto que o grafo ganha, o vetor ganha 2,4.

Olhei caso a caso em que o grafo falhou. Dois padrões dominam.

Primeiro: o grafo não atravessa. 97,1% das linhas de triplas que entraram no contexto conectam diretamente a uma entidade da pergunta (seed). O orçamento de 1.000 caracteres de triplas é consumido pelo 1-hop. Para a pergunta “após qual guerra foi criada a instituição onde morreu Tojo Hideki?”, caíram 33 triplas, todas sobre Tojo. A ponte para Sugamo Prison nunca entrou.

Segundo: a normalização de nós é grosseira. No mesmo exemplo, o local de morte de Tojo virou um nó único de 50 caracteres: 日本・東京都豊島区西巣鴨一丁目(巣鴨拘置所) (“Japão, bairro de Nishi-Sugamo, Prisão de Sugamo”). O nó 巣鴨拘置所 extraído de outro artigo nunca se conecta a esse, porque a minha normalização é só NFKC + remoção de símbolos. Resolução de entidades resolveria, mas isso é um projeto à parte.

O custo de ter perdido

Construir o grafo foi caro, mesmo em LLM local.

ItemValor
Chamadas de extração ao LLM2.382 (uma por chunk)
Tempo total de extração11.478,7 s (≈ 3,2 horas)
Montagem do grafo (networkx)0,4 s
Triplas extraídas (sem self-loops)33.762 (33.124 únicas)
Nós / arestas26.988 / 33.762
Embedding dos 2.382 chunks (bge-m3)43,3 s

A extração é 99,7% do custo de construção. O grafo em si é instantâneo. Para o vetor, o indexador inteiro cabe em 43 segundos: duas ordens de magnitude de diferença, e isso considerando que a extração usa Q4_K_M em uma RTX 4070.

A inferência também não ajudou o grafo. Em tempo total das 120 perguntas, graph levou 176,3s contra 132,9s do vector — não é a vizinhança do grafo que custa (0,01s por pergunta), é a chamada extra de LLM para extrair as entidades da pergunta.

O que isso quer dizer

Não é “GraphRAG não funciona”. É “GraphRAG cru, do jeito que a maioria escreve na primeira iteração, não bate uma busca vetorial decente num benchmark multi-hop normal”.

A versão do Microsoft Research em From Local to Global adiciona três coisas que eu deixei de fora: resolução de identidade (nós equivalentes se fundem), detecção de comunidade com algoritmo Leiden, e resumos de comunidade por LLM. Esses três passos são caros — centenas de milhares de tokens de output por indexação — e eles existem porque a versão sem eles perde para a busca vetorial. É literalmente o resultado que eu reproduzi.

Então o próximo experimento vai medir se adicionar resolução de identidade e comunidade tira os 60,8% do lugar. Se não mover, significa que o problema é outro: a extração de triplas de qwen3.5:9b é fraca demais, ou JEMHopQA não é o benchmark certo para a técnica. Preciso descobrir qual.

Por enquanto, se você estiver escolhendo entre GraphRAG cru e busca vetorial pura para QA multi-hop em japonês, a resposta tem um número: use o vetor.

Os dados completos (chunks, triplas extraídas, respostas por condição, falhas rotuladas) estão em experiments/048-graphrag-vs-vector-jemhopqa no iris-lab. A implementação do GraphRAG usada no teste segue a mecânica descrita no capítulo 5 de Manual completo de Knowledge Graph.

ken imoto · WebRTC & Voice AI engineer · kenimoto.dev · TabNews

Manual completo de Knowledge Graph Livro relacionado Manual completo de Knowledge Graph Manual completo de Knowledge Graph | GraphRAG · Neo4j · RDF · Tree-sitter · revisão de código por IA Ver a página do livro →