# graph_agent.py — GraphRAG Agent: GPT-4o-mini + Neo4j Cypher from openai import OpenAI import re SYSTEM_PROMPT = """Você é um agente especialista em Graph Neural Networks para detecção de fraude. Você tem acesso a uma base de conhecimento em grafo Neo4j com 5 projetos de GNN. PROJETOS DISPONÍVEIS: 1. Sistema Imune Digital — Deep RL (DQN Dueling), 3 agentes especialistas 2. HetGNN Fraud — Grafo heterogêneo, HGTConv, 5 tipos de nó 3. TGN Fraud Detection — Temporal GNN, memória GRU, stream e-commerce 4. DOMINANT — Anomaly detection sem labels (IJCAI 2019) 5. GraphSAGE Elliptic — Dataset real Bitcoin, inductive learning SCHEMA DO GRAFO: Nós: Projeto, Tecnologia, Conceito, Paper, Metrica Arestas: (Projeto)-[:USA]->(Tecnologia) (Projeto)-[:IMPLEMENTA]->(Conceito) (Projeto)-[:REFERENCIA]->(Paper) (Projeto)-[:TEM_METRICA]->(Metrica) (Projeto)-[:DIFERENTE_DE]->(Projeto) PROPRIEDADES: Projeto: nome, descricao, paradigma, dado, url, emoji, ano Tecnologia: nome Conceito: nome, descricao Paper: titulo, autores, venue, modelo Metrica: projeto, tipo, valor, dataset Sua tarefa: 1. Gerar uma query Cypher para buscar informação relevante no grafo 2. A query deve ser eficiente e específica à pergunta 3. Retornar APENAS o Cypher, sem explicação, dentro de ```cypher ... ``` Exemplos: Pergunta: "Quais projetos usam PyTorch Geometric?" ```cypher MATCH (p:Projeto)-[:USA]->(t:Tecnologia {nome: 'PyTorch Geometric'}) RETURN p.nome, p.descricao, p.url ``` Pergunta: "Qual projeto tem maior AUC?" ```cypher MATCH (p:Projeto)-[:TEM_METRICA]->(m:Metrica {tipo: 'AUC'}) RETURN p.nome, m.valor, m.dataset ORDER BY m.valor DESC ``` Pergunta: "Me explique o conceito de Inductive Learning" ```cypher MATCH (c:Conceito {nome: 'Inductive Learning'})<-[:IMPLEMENTA]-(p:Projeto) RETURN c.nome, c.descricao, collect(p.nome) AS projetos ```""" ANSWER_PROMPT = """Você é Daniel Fonseca, ML Engineer especialista em Graph Neural Networks para detecção de fraude. Responda de forma técnica, clara e entusiasmada sobre seus projetos. Contexto do grafo Neo4j: {context} Pergunta do usuário: {question} Instruções: - Responda em português - Seja específico e técnico - Cite os projetos relevantes com seus emojis - Se tiver URL de projeto, mencione que pode ser acessado no Hugging Face - Máximo 4 parágrafos - Finalize com uma frase que convide o usuário a explorar mais""" class GraphRAGAgent: def __init__(self, openai_api_key: str, neo4j_driver, neo4j_database: str): self.client = OpenAI(api_key=openai_api_key) self.driver = neo4j_driver self.database = neo4j_database self.model = "gpt-4o-mini" def gerar_cypher(self, pergunta: str) -> str: """GPT gera Cypher a partir da pergunta em linguagem natural.""" resp = self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": pergunta} ], temperature=0.1, max_tokens=300, ) texto = resp.choices[0].message.content # Extrai o Cypher do bloco de código match = re.search(r'```cypher\s*(.*?)\s*```', texto, re.DOTALL) if match: return match.group(1).strip() # Fallback: tenta extrair qualquer bloco de código match = re.search(r'```\s*(.*?)\s*```', texto, re.DOTALL) if match: return match.group(1).strip() return texto.strip() def executar_cypher(self, cypher: str) -> list: """Executa Cypher no Neo4j e retorna resultados.""" try: with self.driver.session(database=self.database) as session: result = session.run(cypher) return [dict(record) for record in result] except Exception as e: return [{"erro": str(e)}] def formatar_contexto(self, resultados: list) -> str: """Formata resultados do Neo4j em texto para o LLM.""" if not resultados: return "Nenhum resultado encontrado no grafo." if len(resultados) == 1 and "erro" in resultados[0]: return f"Erro na query: {resultados[0]['erro']}" linhas = [] for r in resultados[:10]: # max 10 resultados linha = " | ".join(f"{k}: {v}" for k, v in r.items() if v is not None) linhas.append(linha) return "\n".join(linhas) def gerar_resposta(self, pergunta: str, contexto: str) -> str: """GPT gera resposta final com base no contexto do grafo.""" prompt = ANSWER_PROMPT.format(context=contexto, question=pergunta) resp = self.client.chat.completions.create( model=self.model, messages=[{"role": "user", "content": prompt}], temperature=0.7, max_tokens=600, ) return resp.choices[0].message.content def responder(self, pergunta: str) -> dict: """ Pipeline completo: 1. Gera Cypher 2. Executa no Neo4j 3. Formata contexto 4. Gera resposta """ cypher = self.gerar_cypher(pergunta) resultados = self.executar_cypher(cypher) contexto = self.formatar_contexto(resultados) resposta = self.gerar_resposta(pergunta, contexto) return { "cypher": cypher, "resultados": resultados, "contexto": contexto, "resposta": resposta, }