FMFelipe MiillerNotes on software & systems
HomeBlogAbout
GitHub

Keep building.

Felipe Miiller · © 2026

MailGitHubGitHubLinkedinGitHub
View source on GitHub
Back to blog

GraphRAG Mão na Massa: Do Zero à Consulta Multi-hop

29/09/2026
7 min de leitura
1968 palavras
RAGPythonTutorial
  • GraphRAG Mão na Massa: Do Zero à Consulta Multi-hop
  • 1. Antes de começar: o que essa coisa custa
  • 2. Caminho 1 — Microsoft GraphRAG pelo CLI
  • 2.1 Instalação e inicialização
  • 2.2 O `settings.yaml` — só o que importa
  • models
  • chunking — o parâmetro que mais afeta a qualidade do grafo
  • detecção de comunidades
  • saída
  • 2.3 Indexar — e testar antes de gastar
  • valida a configuração sem executar nada
  • executa de verdade, com log verboso
  • 2.4 Consultar
  • 2.5 Os quatro métodos, e o que cada um exige
  • 2.6 Ajustes que mudam a resposta
  • nível da hierarquia Leiden — padrão 2. MAIOR = comunidades MENORES = mais específico
  • seleção dinâmica de comunidades: só carrega os relatórios relevantes
  • formato da resposta
  • streaming
  • 2.7 Ajustar os prompts ao seu domínio
  • 3. Caminho 2 — Neo4j com `neo4j-graphrag`
  • 3.1 Instalação
  • 3.2 Índice vetorial e carga
  • 3.3 Pipeline de geração
  • 3.4 O que faz ser GraphRAG: o retriever com Cypher
  • 3.5 Busca híbrida (vetor + full-text)
  • 3.6 Construir o grafo a partir de texto solto
  • 4. Escolhendo o método: tabela de decisão
  • 5. Os 6 erros que só aparecem em produção
  • 6. Checklist de implantação
  • Referências

GraphRAG Mão na Massa: Do Zero à Consulta Multi-hop

Lede: O artigo anterior explicou por que GraphRAG. Este é o guia de campo: instalar, indexar, escolher o método de busca e depurar quando a resposta sai ruim. Dois caminhos — o CLI da Microsoft, que resolve em três comandos, e o Python do Neo4j, que te dá controle total. Com os custos reais, os --dry-run que você deveria usar e os erros que só aparecem em produção.

🧰 O que você leva daqui:

  • Microsoft GraphRAG do zero: init → index → query, com o que cada método exige

  • Neo4j + neo4j-graphrag em código: índice vetorial, retrievers e pipeline

  • Tabela de decisão: qual método de busca para qual pergunta

  • Custo real de indexação e os 6 erros mais comuns


1. Antes de começar: o que essa coisa custa

GraphRAG não é "RAG com mais poder". O preço vem na indexação: um LLM é chamado para extrair entidades, relações e claims de cada chunk, e depois chamado de novo para escrever um relatório por comunidade. O corpus não é consultado uma vez — é processado com custo de geração.

EtapaChamadas de LLMEscala com
Chunking e embedding0 (embeddings são baratos)Nº de chunks
Extração de grafo~1 por chunkNº de chunks
Relatórios de comunidade1 por comunidadeNº de comunidades
Busca local1 por pergunta—
Busca globalN por pergunta (map-reduce)Nº de relatórios relevantes

💸 A causa nº 1 de surpresa na fatura: indexar 50.000 documentos não é uma tarefa de background, é uma despesa. Rode o --dry-run antes, comece com 200 documentos e só escale quando a qualidade do grafo estiver aceitável. Índice mal feito é mais caro que refazer.

Requisitos: Python 3.10, 3.11 ou 3.12 (o Microsoft GraphRAG não suporta 3.13+), um provedor de LLM com completion e embeddings, e um lugar para guardar o grafo.


2. Caminho 1 — Microsoft GraphRAG pelo CLI

É o caminho mais rápido: três comandos e você tem um sistema funcional.

2.1 Instalação e inicialização

pip install graphrag

graphrag init --root ./meu-projeto

O init é interativo: pergunta o modelo de completion e o de embeddings. Ele gera a estrutura:

meu-projeto/
├── settings.yaml    # configuração do pipeline
├── .env             # chaves de API
└── input/           # coloque seus documentos aqui

🧩 Reexecute o init a cada upgrade de versão. O formato do settings.yaml evoluiu entre minors e o init é o que te dá o template novo. Rodar graphrag init --root ./meu-projeto --force depois de um bump de versão é o passo que quase todo mundo pula.

Coloque os documentos em input/. O padrão do loader aceita texto, CSV e JSON, e você configura o padrão de arquivo.

2.2 O settings.yaml — só o que importa

O arquivo gerado tem dezenas de campos. Estes são os que você realmente mexe:

# models
models:
  completion:
    type: openai_chat
    model: gpt-4o
    api_key: ${OPENAI_API_KEY}
    max_tokens: 4000
    temperature: 0.0
  embeddings:
    type: openai_embeddings
    model: text-embedding-3-large
    api_key: ${OPENAI_API_KEY}
    dimensions: 1536

# chunking — o parâmetro que mais afeta a qualidade do grafo
chunks:
  size: 1200
  overlap: 100

# detecção de comunidades
community_detection:
  max_cluster_size: 10
  use_lcc: true
  seed: 12345

# saída
output_storage:
  type: file
  base_dir: ./output

O chunks.size é a alavanca mais subestimada: chunk pequeno demais quebra entidades ao meio e fragmenta o grafo; chunk grande demais junta contextos diferentes e cria arestas falsas. Comece em 1200 com overlap de 100.

2.3 Indexar — e testar antes de gastar

# valida a configuração sem executar nada
graphrag index --root ./meu-projeto --dry-run

# executa de verdade, com log verboso
graphrag index --root ./meu-projeto -m standard -v

Métodos de indexação:

MétodoQuando usar
standardIndexação completa. Padrão. Use na primeira vez
fastSem extração de claims nem embeddings de comunidade. ~metade do custo, menos qualidade
standard-updateReindexa incrementalmente documentos novos
fast-updateVariante incremental enxuta

Ao terminar, ./output/ fica cheio de arquivos Parquet — é isso que as buscas leem.

2.4 Consultar

graphrag query "Quais são os principais riscos operacionais?" \
  --root ./meu-projeto \
  --method global

2.5 Os quatro métodos, e o que cada um exige

MétodoPara que perguntaTabelas Parquet exigidas
basicBaseline de similaridade vetorial, sem grafotext_units
localFoco em entidade e vizinhança: "Quem são os dependentes do serviço X?"entities, communities, community_reports, text_units, relationships (+ covariates se existir)
globalSíntese do corpus inteiro: "Quais são os temas recorrentes?"entities, communities, community_reports
driftComeça na entidade, sobe para a comunidade: "Por que o componente Y falha?"entities, communities, community_reports, text_units, relationships

2.6 Ajustes que mudam a resposta

# nível da hierarquia Leiden — padrão 2. MAIOR = comunidades MENORES = mais específico
graphrag query "..." --method global --community-level 1   # panorâmico
graphrag query "..." --method global --community-level 3   # detalhado

# seleção dinâmica de comunidades: só carrega os relatórios relevantes
graphrag query "..." --method global --dynamic-community-selection

# formato da resposta
graphrag query "..." --method local --response-type "List of 3-5 Points"

# streaming
graphrag query "..." --method local --streaming

🎛️ --community-level é o botão de dial mais útil que existe no GraphRAG. Padrão 2. Valor menor = comunidades maiores = síntese mais panorâmica e barata. Valor maior = comunidades menores = mais detalhe, mais custo. Se a resposta está rasa demais, suba o nível antes de culpar o modelo.

2.7 Ajustar os prompts ao seu domínio

O prompt de extração de entidades é genérico e erra em jargão da sua área.

graphrag prompt-tune --root ./meu-projeto

Isso usa uma amostra dos seus documentos para reescrever os prompts de extração. Em domínio técnico, é o passo que mais melhora a qualidade do grafo por hora de trabalho investida.


3. Caminho 2 — Neo4j com neo4j-graphrag

Quando você quer o grafo consultável com Cypher, versionado, e integrado ao resto da sua stack.

3.1 Instalação

pip install "neo4j-graphrag[openai]"

O pacote suporta OpenAI, Azure OpenAI, Anthropic, Google Vertex, Cohere, Mistral e Ollama. Python >= 3.10.

3.2 Índice vetorial e carga

from neo4j import GraphDatabase
from neo4j_graphrag.indexes import create_vector_index, upsert_vectors
from neo4j_graphrag.types import EntityType

URI = "neo4j://localhost:7687"
AUTH = ("neo4j", "sua-senha")
driver = GraphDatabase.driver(URI, auth=AUTH)

create_vector_index(
    driver,
    "artigo-embedding",
    label="Artigo",
    embedding_property="vetor",
    dimensions=1536,          # tem que bater com o modelo de embedding
    similarity_fn="cosine",
)

upsert_vectors(
    driver,
    ids=["art-001", "art-002"],
    embedding_property="vetor",
    embeddings=[v1, v2],
    entity_type=EntityType.NODE,
)

🔢 O erro mais silencioso dessa biblioteca: dimensions divergente do modelo. O índice aceita, a consulta retorna resultado errado, e não avisa nada. Confira antes: text-embedding-3-small e 3-large são 1536 dimensões, text-embedding-3-large com dimensions=256 é outra configuração válida — mas tem que ser a mesma na criação e na consulta.

3.3 Pipeline de geração

from neo4j_graphrag.retrievers import VectorRetriever
from neo4j_graphrag.embeddings.openai import OpenAIEmbeddings
from neo4j_graphrag.llm.openai_llm import OpenAILLM
from neo4j_graphrag.generation import GraphRAG

embedder = OpenAIEmbeddings(model="text-embedding-3-large")
llm = OpenAILLM(model_name="gpt-4o", model_params={"temperature": 0})

retriever = VectorRetriever(
    driver,
    index_name="artigo-embedding",
    embedder=embedder,
    return_properties=["titulo", "texto"],
)

rag = GraphRAG(retriever=retriever, llm=llm)
resposta = rag.search(query_text="Como indexar no PostgreSQL?", retriever_config={"top_k": 5})
print(resposta.answer)

3.4 O que faz ser GraphRAG: o retriever com Cypher

Aqui é a virada. Você recupera os nós por similaridade e depois percorre o grafo:

from neo4j_graphrag.retrievers import VectorCypherRetriever

retrieval_query = """
MATCH (artigo:Artigo)<-[:ESCRITO_POR]-(autor:Pessoa)
WHERE artigo.suporte >= 0.8
RETURN autor.nome, autor.afiliacao, artigo.titulo
ORDER BY artigo.suporte DESC
"""

retriever = VectorCypherRetriever(
    driver,
    index_name="artigo-embedding",
    retrieval_query=retrieval_query,
    embedder=embedder,
)

Esse é o padrão real: a busca vetorial acha o documento, o Cypher traz o contexto relacional que o texto sozinho não carrega.

3.5 Busca híbrida (vetor + full-text)

from neo4j_graphrag.retrievers import HybridRetriever

retriever = HybridRetriever(
    driver,
    vector_index_name="artigo-embedding",
    fulltext_index_name="artigo-fulltext",
    embedder=embedder,
)
resultado = retriever.search(query_text="índice invertido", top_k=5)

É o ponto de partida mais barato: você tem busca semântica e textual sem custo de LLM na indexação.

3.6 Construir o grafo a partir de texto solto

from neo4j_graphrag.kg_builder import KGBuilder

kg_builder = KGBuilder(
    driver=driver,
    llm=llm,
    embedder=embedder,
)

kg_builder.add_entity_types(
    entity_types=[
        ("Pessoa", ["nome", "cargo"]),
        ("Organizacao", ["nome", "setor"]),
    ],
    possible_relations=[
        ("Pessoa", "TRABALHA_NA", "Organizacao"),
        ("Pessoa", "RELACIONADA_COM", "Pessoa"),
    ],
)

await kg_builder.run_async(text=texto_do_documento)

⚠️ O KGBuilder exige a biblioteca APOC core instalada no seu Neo4j. Sem ela o build falha com erro de plugin. É o erro mais comum de quem vem do RAG vetorial e não sabe que existe essa dependência.


4. Escolhendo o método: tabela de decisão

A pergunta é…MétodoPor quê
"O que a doc do módulo 3 diz sobre cache?"basicResposta está em um trecho. Grafo é desperdício
"Quem é o responsável pelo serviço de pagamento?"localEntidade específica, vizinhança pequena
"Quais são os temas dominantes da base?"globalNão há entidade-âncora; é síntese
"Por que a autenticação falha em produção?"driftComeça na entidade, sobe para o contexto da comunidade

5. Os 6 erros que só aparecem em produção

1. Índice vetorial com dimensão errada. Aceita na criação, retorna lixo na consulta. Confira dimensions contra o modelo.

2. APOC ausente. KGBuilder e várias funções de community falham sem a biblioteca instalada.

3. graphrag init não re-executado após upgrade. O settings.yaml antigo não tem os campos novos e o pipeline falha em silêncio, usando defaults.

4. Esquecer o --dry-run****. Descobre config inválida só depois de pagar metade do indexamento.

5. community_reports ausente. Se você pula a etapa 8-9 do pipeline ou indexa com um método que não a gera, a busca global retorna vazio sem erro. O CLI chega a reclamar das tabelas faltantes, mas a API Python não.

6. Grafo ruidoso. Entidades duplicadas, arestas espúrias, entidades que são na verdade a mesma. A resposta fica pior que RAG vetorial. A cure é revisar uma amostra de arestas manualmente antes de indexar o corpus inteiro.


6. Checklist de implantação

  • Python 3.10–3.12 em ambiente isolado
  • Rodar graphrag init --root e editar settings.yaml (chunk size, modelo, embeddings)
  • Testar o pipeline com 100–200 documentos e --dry-run antes
  • Revisar manualmente ~50 arestas extraídas: qualidade do grafo antes de escalar
  • graphrag prompt-tune para adaptar os prompts ao domínio
  • Indexar o corpus completo só depois de validar qualidade
  • Definir o --community-level padrão por tipo de pergunta
  • Guardar o cache do LLM: reindexar sem cache custa o mesmo preço
  • No Neo4j: instalar APOC antes de usar KGBuilder
  • No Neo4j: validar dimensions do índice vetorial contra o modelo

Referências

  • 📚 Microsoft GraphRAG — Getting Started — init, index, query
  • 📚 Microsoft GraphRAG — CLI — todas as flags de index e query
  • 📚 Microsoft GraphRAG — Query Overview — os 4 métodos
  • 📚 Microsoft GraphRAG — DRIFT Search — busca híbrida
  • 🔧 neo4j-graphrag-python (GitHub) — pacote oficial, exemplos
  • 🔧 Neo4j GraphRAG — Documentação Python — retrievers, índices, KGBuilder
  • 🔧 Neo4j GraphRAG — API Reference — assinatura de cada classe
  • 🔧 Getting started with the Neo4j GraphRAG package — tutorial com banco de demonstração