Preparação para entrevista
PadrãoContexto e conhecimento

RAG com Grafo

GraphRAG

RAG com Grafo (GraphRAG) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Contexto e conhecimento. Navegar relações entre entidades quando a resposta depende de conexões, não de um trecho.

Elemento
15/70
Nível
Avançado
Leitura
8 min
Atualizado
Nesta página 17
  1. 01Intenção
  2. 02Problema
  3. 03Solução
  4. 04Estrutura
  5. 05Pseudocódigo
  6. 06Cenários reais
  7. 07Aplicabilidade
  8. 08Quando não usar
  9. 09Como implementar
  10. 10Variações
  11. 11Custo, latência e falhas
  12. 12Como avaliar
  13. 13Em produção
  14. 14Segurança
  15. 15Prós e contras
  16. 16Código
  17. 17Teste seu entendimento
  18. ··Perguntas rápidas

Intenção

Representar entidades e suas relações num grafo e percorrer essas relações para responder perguntas que dependem de conexões, não de um trecho isolado. Serve para domínios em que a resposta está espalhada por vários documentos ligados entre si.

Em uma frase
O detetive que não procura uma pista, procura o fio que liga as pistas.

Problema

Bruna é engenheira de confiabilidade numa empresa com 300 microsserviços. Quando um alerta dispara no serviço de pagamentos, a pergunta certa raramente é “o que diz a documentação de pagamentos?”. É “quais serviços pagamentos chama, quais desses mudaram hoje, e quem é o dono de cada um?”.

O RAG da empresa busca trechos parecidos com a pergunta. Ele acha a página de pagamentos, mas não segue as dependências. A informação de que o serviço de antifraude (chamado por pagamentos) teve deploy às 14h está em outro documento, que não se parece com a pergunta.

Esse tipo de resposta não está em nenhum trecho. Está na ligação entre trechos.

O mesmo buraco aparece em contratos e NF-e: “esta cláusula ainda vale?” depende de quem revogou o quê, não de um parágrafo parecido com a pergunta. RAG por similaridade não caminha.

A pergunta que este padrão responde: como responder perguntas cuja resposta depende de relações entre várias coisas, espalhadas por várias fontes?

Solução

Construa um grafo com as entidades do domínio (serviços, pessoas, contratos, cláusulas, leis) e as relações entre elas (chama, é dono de, cita, revoga, depende de). As relações vêm de fontes estruturadas sempre que possível (catálogo de serviços, banco de dados) e, quando necessário, são extraídas de texto por um modelo.

Na hora da pergunta:

  1. Identifique as entidades citadas.
  2. Percorra o grafo a partir delas, com uma consulta (vizinhos a até N passos, filtros por tipo de relação).
  3. Junte os fatos do caminho com os trechos de texto ligados a cada entidade.
  4. O modelo responde com base nesse subgrafo, citando as relações.

Microsoft Research publicou GraphRAG (Edge et al., 2024) como uma abordagem para perguntas que pedem visão de um corpus inteiro: o grafo é extraído dos documentos e grupos densos ganham resumos. A lição que vale para produção, mesmo fora daquele paper, é a mesma: extraia relações de fonte confiável primeiro; extração por modelo entra só onde a fonte não existe, com revisão.

Estrutura

flowchart LR
  Q(["1. Pergunta"]):::io --> E(["2. Identificar entidades"]):::llm
  E --> T["3. Percorrer o grafo"]:::tool
  G[("Grafo: entidades e relações")]:::store --> T
  T --> C["4. Subgrafo e trechos ligados"]:::tool
  D[("Documentos")]:::store --> C
  C --> M(["5. Responder citando o caminho"]):::llm
  1. A pergunta chega.
  2. Um modelo pequeno, ou um dicionário de nomes, identifica as entidades citadas.
  3. Uma consulta ao grafo traz as entidades ligadas, no raio e nos tipos de relação relevantes.
  4. Os fatos do subgrafo se juntam aos trechos de documentos ligados a cada entidade.
  5. O modelo responde mostrando o caminho (“pagamentos chama antifraude, que teve deploy às 14h”).

Exemplo trabalhado

Pergunta: “Quais serviços chamados por pagamentos tiveram deploy nas últimas 6 horas?”.

O resolvedor de nomes mapeia “pagamentos” para o nó payments. A consulta anda no máximo 2 saltos em CALLS, cruza com DEPLOYED recente e OWNS:

payments -CALLS-> antifraud   dono: time-risco    deploy: r-8812 às 14:05
payments -CALLS-> ledger      dono: time-finanças deploy: (nenhum nas 6h)
payments -CALLS-> notify      dono: time-msg      deploy: r-8801 às 09:12

O contexto que o modelo recebe não é “os 5 trechos mais parecidos com a pergunta”. É essa lista de fatos, mais um parágrafo da runbook de cada serviço que mudou. A resposta cita o caminho. Se antifraud não estiver no grafo, o sistema diz que a entidade não resolveu, em vez de inventar um dono.

Compare com o RAG por trechos na mesma pergunta:

Desenho O que entra no contexto O que costuma sair
RAG por similaridade Página de pagamentos, às vezes a de deploy genérico Uma descrição de pagamentos, sem o deploy das 14h
RAG com grafo Arestas CALLS + deploys recentes + donos “antifraud (r-8812, 14:05), notify (r-8801, 09:12)”
Checagem +20 XP de primeiraO RAG por trechos acha a página de pagamentos e não menciona o deploy das 14h no antifraude. Por que o grafo acerta e a similaridade não?

Pseudocódigo

schema Entities { names: list[string], intent: "dependencies" | "ownership" | "impact" | "other" }

function answer(question):
    ents = llm(ENTITY_PROMPT, question, model="small") -> Entities            # 2
    nodes = graph.resolve(ents.names)                                         # nome para nó
    sub = graph.query("""
        MATCH (s:Service)-[:CALLS*1..2]->(d:Service)
        WHERE s.id IN $ids
        OPTIONAL MATCH (d)<-[:OWNS]-(t:Team)
        OPTIONAL MATCH (d)-[:DEPLOYED]->(r:Release) WHERE r.at > now() - duration('PT6H')
        RETURN s, d, t, r""", ids=nodes)                                      # 3
    passages = docs.for_entities(sub.nodes, k_per_entity=2)                   # 4
    trace.span("graph_rag", nodes=len(sub.nodes), edges=len(sub.edges))
    return llm(ANSWER_PROMPT, question, render(sub), passages)               # 5

Cenários reais

  • Assistente jurídico. Leis, artigos, súmulas e contratos formam um grafo de citações e revogações. “Esta cláusula ainda vale?” depende de seguir quem revogou o quê.
  • Agente de incidentes (on-call). O grafo de dependências entre serviços, donos e deploys recentes estreita as causas prováveis de um alerta.
  • Extração de NF-e e contratos. CNPJ, contrato, cláusula de reajuste e notas ligadas ao mesmo fornecedor formam um grafo pequeno e estável, útil quando a pergunta cruza documento.

Aplicabilidade

  • As perguntas envolvem relações (“quem depende de”, “o que mudou entre”, “quais afetados por”).
  • As entidades e relações do domínio são relativamente estáveis e identificáveis.
  • Já existem fontes estruturadas das relações (catálogo, CMDB, banco).
  • O RAG por trechos falha em perguntas de vários saltos.

Quando não usar

  • As perguntas são sobre um documento ou um trecho. RAG resolve com menos custo.
  • Não existe fonte confiável das relações, e extraí-las por modelo traria muito ruído. O grafo errado é pior que nenhum.
  • O domínio muda rápido demais para manter o grafo atualizado.
  • Um agente com ferramentas de consulta resolve buscando em etapas. Veja RAG Agêntico.

Grafo extraído só de texto, sem revisão, costuma parecer inteligente na demo e mentir em produção. Se a única fonte for PDF de contrato, extraia poucas relações (partes, vigência, índice) com schema e humano no circuito, como no cenário de NF-e.

Checagem +20 XP de primeiraO time não tem CMDB. A proposta é mandar os 6 mil documentos da wiki para um modelo extrair triplas e montar o grafo neste fim de semana. Qual é o risco que o padrão pede para levar a sério?

Como implementar

  1. Liste as perguntas que o RAG atual não responde e os tipos de relação que elas exigem.
  2. Defina o esquema do grafo: tipos de entidade, tipos de relação, atributos.
  3. Popule a partir de fontes estruturadas primeiro; use extração por modelo só onde não houver fonte, com revisão.
  4. Ligue cada entidade aos documentos em que ela aparece.
  5. Escreva consultas de travessia para os tipos de pergunta mais comuns, com limite de profundidade.
  6. Monte o contexto com o subgrafo em texto legível e os trechos ligados.
  7. Avalie com perguntas de vários saltos, comparando com o RAG simples.
  8. Mantenha o grafo atualizado com os mesmos eventos que mudam as fontes (deploys, cadastros).

Comece com um grafo que cabe em memória, alimentado pelo catálogo que você já tem. Banco de grafos entra quando as consultas ficam mais ricas que uma vizinhança de 2 saltos, não no primeiro dia.

Erros comuns

  • Travessia sem limite. Dois saltos em CALLS numa malha densa explodem o subgrafo. LIMIT e profundidade máxima são parte do padrão, não otimização.
  • Nome que não resolve. “pagamentos”, “Payments”, “svc-pay”. Sem apelidos e um resolvedor, o subgrafo volta vazio e o modelo improvisa.
  • Grafo atrasado. O deploy das 14h entrou no Git e não no grafo. A resposta explicável fica errada até o lag acabar. Trate defasagem como incidente.
  • Mandar o subgrafo cru em JSON ilegível. O modelo erra menos quando cada aresta vira uma frase: payments CALLS antifraud.

Variações

  • Grafo de conhecimento curado. Entidades e relações vindas de sistemas oficiais. Mais confiável.
  • Grafo extraído por modelo. O modelo lê documentos e extrai triplas (entidade, relação, entidade). Rápido de montar, exige limpeza.
  • Resumo de comunidades. Grupos densos do grafo ganham resumos, usados para perguntas amplas (“quais os principais temas destes contratos?”). É a peça central do GraphRAG da Microsoft (2024).
  • Grafo como ferramenta do agente. O agente decide quando consultar o grafo, com consultas parametrizadas.

Custo, latência e falhas

Aspecto O que acontece Como aparece no trace
Chamadas Identificação de entidades, uma consulta ao grafo e a resposta span graph_rag com nós e arestas
Custo Manter o grafo atualizado; extração por modelo custa por documento custo de ingestão
Latência A consulta ao grafo costuma ser rápida; travessias profundas não duração da consulta
Falha: entidade não resolvida Nome escrito de outro jeito não acha o nó subgrafo vazio
Falha: grafo desatualizado Relação que já não existe resposta com dependência antiga
Falha: subgrafo grande demais Travessia sem limite enche o contexto número de nós acima do esperado

Como avaliar

  • Perguntas de vários saltos, com a resposta certa e o caminho esperado.
  • Comparação com RAG por trechos nas mesmas perguntas.
  • Qualidade do grafo: amostras de relações conferidas contra a fonte.
{ "question": "Quais serviços chamados por pagamentos tiveram deploy nas últimas 6 horas?", "expected_path": ["payments -CALLS-> antifraud", "antifraud -DEPLOYED-> r-8812"], "expected_answer_contains": ["antifraud"] }

Avalie também o caso negativo: um serviço que pagamentos não chama não pode aparecer no caminho. Falso positivo de aresta é pior que um “não sei”.

Em produção

  • Span com as entidades resolvidas, o tamanho do subgrafo e a consulta usada.
  • Alerta para entidades frequentes que não são resolvidas.
  • Monitoramento da defasagem entre as fontes e o grafo.

Se nodes no span passa de algumas dezenas, a consulta está larga demais para o tipo da pergunta. Ajuste profundidade e tipo de relação, não o prompt da resposta.

Segurança

O grafo concentra mapa da organização: quem chama quem, quem é dono, o que mudou. Permissão entra na travessia. Um estagiário que pergunta sobre pagamentos não deve receber o subgrafo da folha. Filtrar nós depois de o modelo responder é tarde.

Relações extraídas de documento externo (PDF de fornecedor, wiki aberta) são conteúdo não confiável persistido. Uma cláusula que “estabelece que o fornecedor é dono do serviço de pagamentos” não pode virar aresta OWNS sem revisão. Veja Ordem Vinda do Dado.

Prós e contras

Prós Contras
Responde perguntas de relação e vários saltos Construir e manter o grafo custa
Respostas explicáveis pelo caminho Grafo errado gera respostas confiantes e erradas
Usa fontes estruturadas que já existem Resolver nomes para entidades é difícil
Combina bem com trechos de texto Mais uma infraestrutura (banco de grafo)

Código

As duas abas resolvem as entidades da pergunta, percorrem as relações e montam o contexto para a resposta.

Python. Use quando o grafo de dependências cabe em memória, carregado do catálogo de serviços.

import anthropic
import networkx as nx

client = anthropic.Anthropic()


def build_graph(catalog: list[dict], deploys: list[dict]) -> nx.DiGraph:
    g = nx.DiGraph()
    for svc in catalog:  # fonte estruturada: catálogo de serviços
        g.add_node(svc["name"], kind="service", owner=svc["owner"])
        for dep in svc["calls"]:
            g.add_edge(svc["name"], dep, rel="CALLS")
    for d in deploys:  # eventos recentes
        g.nodes[d["service"]].setdefault("deploys", []).append(d)
    return g


def neighborhood(g: nx.DiGraph, start: str, depth: int = 2) -> list[str]:  # 3
    lines = []
    for node, dist in nx.single_source_shortest_path_length(g, start, cutoff=depth).items():
        data = g.nodes[node]
        recent = [d["at"] for d in data.get("deploys", [])]
        lines.append(f"{node} (dono: {data.get('owner')}, distância {dist}, deploys recentes: {recent})")
    return lines


def answer(question: str, g: nx.DiGraph) -> str:
    names = [n for n in g.nodes if n in question.lower()]  # 2. resolução simples por nome
    context = "\n".join(line for n in names for line in neighborhood(g, n))  # 4
    reply = client.messages.create(  # 5
        model="claude-sonnet-5-5", max_tokens=2000,
        system="Responda usando só as relações listadas. Mostre o caminho de dependências que sustenta a resposta.",
        messages=[{"role": "user", "content": f"<grafo>\n{context}\n</grafo>\n\n{question}"}],
    )
    return reply.content[-1].text

O que você ganhou: um grafo montado de fontes confiáveis, com travessia limitada e explicável. O que você perdeu: a resolução por nome exato é frágil; use apelidos e um modelo pequeno quando os nomes variarem.

TypeScript. Use quando o grafo vive num banco de grafos e a travessia é uma consulta.

import Anthropic from "@anthropic-ai/sdk";
import neo4j from "neo4j-driver";

const client = new Anthropic();
const driver = neo4j.driver(process.env.NEO4J_URL!, neo4j.auth.basic(process.env.NEO4J_USER!, process.env.NEO4J_PASSWORD!));

export async function answer(question: string, serviceIds: string[]) {
  const session = driver.session({ defaultAccessMode: neo4j.session.READ });
  try {
    const res = await session.run( // 3. travessia limitada a 2 saltos
      `MATCH (s:Service)-[:CALLS*1..2]->(d:Service) WHERE s.id IN $ids
       OPTIONAL MATCH (t:Team)-[:OWNS]->(d)
       OPTIONAL MATCH (d)-[:DEPLOYED]->(r:Release) WHERE r.at > datetime() - duration('PT6H')
       RETURN s.id AS from, d.id AS to, t.name AS owner, collect(r.version) AS releases LIMIT 50`,
      { ids: serviceIds },
    );
    const facts = res.records
      .map((r) => `${r.get("from")} -> ${r.get("to")} (dono: ${r.get("owner")}, releases recentes: ${r.get("releases")})`)
      .join("\n"); // 4
    const reply = await client.messages.create({ // 5
      model: "claude-sonnet-5-5",
      max_tokens: 2000,
      system: "Responda usando só as relações listadas. Mostre o caminho de dependências que sustenta a resposta.",
      messages: [{ role: "user", content: `<grafo>\n${facts}\n</grafo>\n\n${question}` }],
    });
    return reply.content.find((b) => b.type === "text")?.text ?? "";
  } finally {
    await session.close();
  }
}

O que você ganhou: consultas de vários saltos executadas pelo banco, com limite explícito. O que você perdeu: mais uma infraestrutura para manter sincronizada com as fontes.

Teste seu entendimento

Checagem +20 XP de primeiraA pergunta cita 'financeiro', apelido interno do serviço ledger. O resolvedor só conhece o id ledger. O subgrafo volta vazio e o modelo descreve um ledger genérico. O que faltou?

Checagem +20 XP de primeiraO alerta é em pagamentos. A travessia sem LIMIT devolve 400 nós. O modelo resume 'muita coisa mudou'. Qual é o conserto?

Checagem +20 XP de primeiraUma pergunta pontual ('qual o SLA de pagamentos?') acerta no RAG por trechos. O time quer passar todas as perguntas pelo grafo, para o código ficar único. O que você faz?

Checagem +20 XP de primeiraO grafo diz que pagamentos ainda chama legacy-tax, desativado há um mês. A resposta do incidente aponta esse serviço. Qual é o diagnóstico?

Perguntas rápidas

O que é RAG com Grafo?

RAG com Grafo (GraphRAG) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Contexto e conhecimento. Navegar relações entre entidades quando a resposta depende de conexões, não de um trecho.

Onde RAG com Grafo aparece na prática?

Nos cenários reais Assistente jurídico e Agente de incidentes (on-call).

Para ir além

Relações

Buscar