Accueil / Articles

AIOps agnostique avec un graphe de contexte : Neocarta, Neo4j, LangGraph et Gemini

Une conception de réponse aux incidents en boucle fermée : intégrer ServiceNow, Dynatrace et GitHub dans Neo4j, analyser les données à l’aide de LangGraph et Gemini derrière une barrière de sécurité, puis enregistrer chaque action effectuée.

Éditeur : REACTAPP.TOP10 min de lecture2402 mots

Sources: Building Agentic AIOps: A Closed-Loop Platform with LangGraph, Gemini, and a Native Graph Database

Lors d’une panne sur une plateforme ou au sein d’une équipe SRE, le même schéma se reproduit. Un ingénieur suit les traces fournies par Dynatrace, un autre examine l’historique des déploiements sur GitHub pour identifier ce qui a changé, et un troisième modifie le ticket ServiceNow afin que la direction cesse de demander des mises à jour. Personne ne dispose de l’ensemble de l’information, car celle-ci n’existe nulle part en particulier. Elle est dispersée entre trois outils, voire dix.

Cette fragmentation, plus que le bug ou un mauvais déploiement, est la cause principale des retards dans la récupération. Le retard provient du fait que les humains doivent reconstruire un contexte qui existait déjà quelque part dans l’ensemble des outils, mais pas en un seul endroit.

Un plan pour résoudre ce problème combine trois technologies sélectionnées afin de créer une plateforme AIOps basée sur des agents. Neocarta automatise l’ingestion des données dans un graphe Neo4j et met ce graphe à la disposition des outils d’IA via MCP. LangGraph gère l’orchestration déterministe des agents, tandis que le SDK Google Gen AI avec Gemini fournit les capacités de raisonnement. Il n’y a ni lakehouse, ni traitement ETL par lots, ni configuration manuelle de webhooks, ni tableau de bord affichant des métriques obsolètes. Ce qui reste, c’est un graphe en temps réel et consultable de la réalité opérationnelle, auquel les agents peuvent accéder directement pour y agir.

Un graphe contextuel à la place des tableaux de bord

De nombreux outils AIOps ajoutent des fonctionnalités intelligentes à un stockage de données analytiques conçu pour l’intelligence d’affaires plutôt que pour le raisonnement en temps réel, ce qui constitue une base inadaptée. La conception retenue considère l’état opérationnel comme un problème de graphe : chaque service, déploiement, incident, équipe et action correctrice devient un nœud, tandis que les relations entre eux — telles que DEPLOYED_TO, ALERTS_ON, TRIGGERED_BY et OWNER_OF — constituent des arêtes de première classe.

Lorsque les événements d’infrastructure forment un graphe connecté plutôt que des journaux isolés, l’analyse des causes racines change de nature. Elle cesse d’être une démarche archéologique manuelle pour devenir un parcours dans le graphe, une requête à plusieurs étapes que les LLM peuvent traiter directement grâce à Graph RAG.

Quatre couches qui ferment le cycle

Chacun des quatre niveaux a une responsabilité bien définie, et le cycle se boucle car les actions mêmes de l’agent sont réécrites dans le graphe. Ainsi, lorsqu’il gère un incident suivant, il sait déjà ce qui a été tenté.

  1. Récupération automatisée à l’aide de la CLI d’ingestion Neocarta. Au lieu d’écrire un récepteur webhook pour chaque outil, les connecteurs Neocarta extraitent les métadonnées et les journaux de ServiceNow, Dynatrace et GitHub pour les charger dans le graphe. Aucun service d’ingestion personnalisé n’a besoin d’être maintenu pour chaque source.
  • Contexte systémique dans Neo4j, géré par le serveur MCP Neocarta. Il s’agit du cerveau opérationnel. Les dépôts, les services, les déploiements, les incidents, les équipes et les actions correctives sont des nœuds, tandis que chaque relation représente une arête orientée et de type défini. Neocarta place un serveur MCP devant le graphe, permettant ainsi à tout outil prenant en charge MCP, y compris l’agent LangGraph, de le interroger sans avoir besoin de code de base de données personnalisé.
  • Raisonnement avec LangGraph et Gemini. Une machine à états LangGraph met en œuvre un cycle d’observation, d’orientation, de décision et d’action. Elle récupère un sous-graphe contextuel via MCP, le transforme en prompt et propose à Gemini un choix limité : résoudre le problème ou le transmettre à un humain.
  • Réparation et écriture des modifications. Si l’agent décide d’intervenir, il lance la correction, par exemple un rollback via GitHub Actions, puis enregistre le résultat dans le graphe sous forme de nœud nouveau. La plupart des démonstrations AIOps omettent précisément cette étape, et c’est elle qui permet au cycle de se clore plutôt que d’être ponctuel.
  • Neocarta remplace le service d’ingestion personnalisé ainsi que les appels directs aux drivers de base de données. Sa CLI automatise la collecte dans Neo4j, et son serveur MCP est celui avec lequel l’agent communique réellement pour obtenir le contexte du graphe.

    Les couches 1 et 2 : ingestion via Neocarta

    Le schéma vient en premier, car c’est l’ontologie sur laquelle repose toute la plateforme. Les étiquettes des nœuds et les types de relations sont ce que chaque connecteur ainsi que chaque requête Cypher lisent et écrivent finalement. Chaque entité est un nœud de type défini, et chaque dépendance, lien de propriété ou alerte est un arête dirigée de type défini. C’est cette structure qui transforme une investigation à travers plusieurs outils en un parcours à travers le graphe.

    Auparavant, l’ingestion signifiait exécuter un récepteur webhook pour chaque source et écrire manuellement du Cypher pour chaque format de charge. Neocarta réduit cela à une simple configuration : il suffit d’orienter sa CLI vers ServiceNow, Dynatrace et GitHub pour qu’il s’occupe de la découverte du schéma, des chargements incrémentaux et du mappage vers les types de nœuds et de relations du graphe.

    # Register each source once — Neocarta handles auth, schema inference, and scheduling
    neocarta connect servicenow --instance your-org.service-now.com --scope incident
    neocarta connect dynatrace --tenant your-tenant-id --scope metrics,problems
    neocarta connect github --org your-org --scope deployments,repos
    
    # Point the loader at your graph and let it run
    neocarta load --target neo4j://your-instance:7687 --database aiops-graph
    

    Au fond, les mêmes schémas MERGE et CREATE que l’on écrirait manuellement sont utilisés. Les entités persistantes telles que les repositories, les services et les équipes sont fusionnées, de sorte qu’aucun nœud dupliqué ne apparaît. Les événements liés à un moment donné, comme une déploiement ou un incident, sont insérés en tant que nouveaux nœuds à chaque fois, ce qui garantit l’exactitude de la chronologie tout en préservant l’historique antérieur. La différence réside dans le fait que personne n’a besoin de gérer le code de mappage entre trois API qui modifient la structure des données selon leurs propres calendriers.

    En plus de l’ingestion, Neocarta ajoute son serveur MCP. Une fois le graphe rempli, il est mis à disposition en tant qu’endpoint MCP, et un client capable de communiquer via MCP, comme LangGraph par exemple, peut effectuer des requêtes graphiques multi-nœuds en les traitant comme de simples appels d’outil. Le code de l’agent n’a pas besoin d’une couche de pilote de base de données pour être écrit ou maintenu.

    # LangGraph's context-fetching node talks to Neocarta over MCP,
    # not to a Neo4j driver directly
    from langchain_mcp_adapters.client import MCPClient
    
    neocarta_client = MCPClient(server_url="http://neocarta-mcp:8080")
    tools = await neocarta_client.get_tools()   # graph_query, graph_traverse, etc.
    

    Le contrôle du schéma reste entre les mains des ingénieurs. Les contraintes et les index sont définis, comme toujours, directement dans Neo4j.

    CREATE CONSTRAINT unique_service_id IF NOT EXISTS
    FOR (s:Service) REQUIRE s.id IS UNIQUE;
    
    CREATE INDEX incident_state_index IF NOT EXISTS
    FOR (i:Incident) ON (i.state);
    

    Niveau 3 : la boucle LangGraph

    Ce niveau mérite le qualificatif d’« agentic ». Au lieu d’une seule appel à un LLM, le raisonnement s’effectue comme une machine à états dotée de nœuds et d’arêtes explicites, ce qui permet d’examiner, de tester et d’analyser chaque chemin de décision, contrairement à la boucle opaque propre à de nombreux agents.

    L’état est un dictionnaire typé qui circule à travers le graphe.

    class AIOpsAgentState(TypedDict):
        incident_id: str
        failing_service_id: str
        graph_context_summary: Optional[str] = None
        safety_gate_passed: Optional[bool] = None
        chosen_action: Optional[str] = None
        target_repository: Optional[str] = None
        execution_result: Optional[str] = None
    

    Quatre nœuds pilotent ce cycle :

    • FetchGraphContext appelle l’outil graph_traverse sur le serveur MCP de Neocarta afin de remonter jusqu’au service concerné par un incident, puis jusqu’à la déploiement qui a modifié ce service, au répertoire associé ainsi qu’à l’équipe en charge. Le code de l’agent ne maintient jamais de connexion directe à la base de données.
    • VerifySafetyGate vérifie, via la même interface de requête MCP, s’une action corrective a déjà été déclenchée pour ce service au cours de la dernière heure. Ce mécanisme de protection empêche un agent de tourner indéfiniment sur un service instable.
    • GeminiReasoning s’exécute uniquement si le contrôle de sécurité est réussi. Gemini 2.5 Pro reçoit le résumé du contexte du graphe ainsi qu’une instruction système précise, et la seule réponse acceptable est l’un de deux tokens prédéfinis.
  • ExecuteRemediationTool lance la correction et écrit un nœud RemediationAction à nouveau dans Neo4j via Neocarta, ce qui ferme le cycle.
  • Si un composant mérite toute l’attention, c’est bien la barrière de sécurité. Elle est représentée ici comme une appel d’outil MCP plutôt que comme une session de pilote brute.

    async def evaluate_remediation_safety_gate(state: AIOpsAgentState) -> Dict:
        result = await neocarta_client.call_tool(
            "graph_query",
            cypher="""
                MATCH (s:Service {id: $service_id})<-[:EXECUTED_AGAINST]-(a:RemediationAction)
                WHERE a.timestamp >= datetime() - duration('PT1H')
                RETURN count(a) as active_actions
            """,
            params={"service_id": state["failing_service_id"]},
        )
        return {"safety_gate_passed": (result["active_actions"] == 0)}
    

    Un cycle d’action autonome sans mécanisme de sécurité ne devrait jamais être mis en production. C’est ce mécanisme qui empêche l’agent de déclencher à nouveau un rollback toutes les trente secondes, car l’anomalie sous-jacente n’a pas encore disparu.

    Le nœud Gemini est conçu pour être ennuyeux, et c’est là sa force.

    system_instruction = (
        "You are an automated Agentic AIOps architect. "
        "Analyze the provided graph context summary. "
        "If a deployment recently modified the service state, "
        "output EXACTLY the word TRIGGER_ROLLBACK. "
        "If the context is clear but safe guidelines are violated, "
        "output HALT_ESCALATE. Do not append any other characters."
    )
    

    En limitant la sortie du modèle à deux tokens exacts, l’appel se comporte davantage comme une fonction programmée qu’en tant que réponse de chat, ce qui est la propriété souhaitée lorsque le résultat déclenche un rollback en production.

    Niveau 4 : fermeture du cycle

    Le dernier nœud soit déclenche un rollback via l’API GitHub Actions, soit ne fait rien d’autre que de consigner cet arrêt. Dans les deux cas, le graphe est mis à jour par le même chemin d’écriture MCP que pour tout le reste, de sorte qu’aucunes identifiants de base de données supplémentaires ne sont nécessaires pour l’écriture des données.

    await neocarta_client.call_tool(
        "graph_write",
        cypher="""
            MATCH (s:Service {id: $service_id}), (i:Incident {id: $incident_id})
            CREATE (a:RemediationAction {id: $action_id, timestamp: datetime(), status: 'SUCCESS_ROLLBACK'})
            CREATE (a)-[:EXECUTED_AGAINST]->(s)
            CREATE (a)-[:ATTEMPTED_TO_FIX]->(i)
        """,
        params={"service_id": service_id, "incident_id": incident_id, "action_id": action_id},
    )
    

    Ce qui fait de ce système un système à boucle fermée et non un simple script d’automatisation jetable réside dans un détail simple : le résultat de l’action menée par l’agent devient le contexte pour le prochain incident qu’il évalue, de sorte que la plateforme devient progressivement plus consciente de la situation au fil du temps plutôt que moins.

    Un incident du début à la fin

    Le plan de conception inclut une simulation en mode essai qui montre comment les différents éléments coopèrent, et un aperçu de celle-ci est très instructif.

    1. 14:00:00. Le service de passerelle de paiement termine un déploiement, ce qui ajoute un nœud de Déploiement connecté à ce service.
    2. 14:05:00. Dynatrace détecte une anomalie critique sur ce même service.
    3. 14:06:00. Un incident de niveau P1 apparaît dans ServiceNow et devient un nœud d’Incident, lié au service par une liaison ALERTS_ON.
  • L’agent LangGraph s’éveille sur le nouveau nœud d’incident. FetchGraphContext parcourt la chaîne Incident → Service → Déploiement → Répertoire → Équipe et constate que le déploiement a eu lieu cinq minutes avant l’anomalie.
  • VerifySafetyGate ne détecte aucune mesure corrective antérieure au cours de la dernière heure, donc le chemin est libre.
  • Gemini reçoit le résumé du contexte, reconnaît que le déploiement a causé l’incident et renvoie TRIGGER_ROLLBACK.
  • L’exécuteur d’outils lance le flux de travail de rollback dans GitHub Actions, puis enregistre un nouveau nœud RemediationAction dans Neo4j.
  • De l’apparition de l’anomalie à la mise en œuvre de la correction, il ne s’agit que de quelques minutes, sans nécessité d’appel en salle de crise. Chaque étape du raisonnement reste auditable dans le graphe par la suite, ce qui permet de savoir pourquoi l’agent a agi de la sorte.

    Lorsqu’il est cartographié, l’incident forme une structure unique et connectée couvrant l’équipe, le code, le déploiement, le service, l’incident lui-même ainsi que les actions de correction menées par l’agent. C’est précisément ce sous-graphe que Gemini analyse, et c’est l’existence d’une seule structure connectée à la place de trois tableaux de bord distincts qui permet à l’agent de remonter jusqu’à la modification Git à l’origine d’un incident de niveau P1.

    Cette même histoire peut être divisée en trois étapes qui ont permis de construire le graphe : le déploiement, la panne et la correction. Chacune d’elles correspond à une écriture séparée dans la base de données, mais ensemble elles forment un chronologie continue allant d’une fusion Git à une alerte en production, jusqu’à une correction finale, en sept minutes seulement.

    Fournir aux ingénieurs une vue du graphe

    Un système autonome que les ingénieurs ne peuvent pas inspecter représente un risque. Le plan prévoit donc un tableau de bord Streamlit basé sur pyvis, qui affiche en temps réel la topologie du graphe ainsi qu’un registre des actions effectuées.

    net = Network(height="600px", width="100%", bgcolor="#1a1c1e", directed=True)
    color_map = {
        "Team": "#5f3dc4", "Repository": "#24292e", "Deployment": "#007acc",
        "Service": "#11a685", "Incident": "#c41c1c", "RemediationAction": "#cc0088"
    }
    

    Les ingénieurs disposent d’une vue interactive et zoomable de toute la chaîne, allant de la fusion Git à l’incident en production jusqu’à la correction automatique, accompagnée d’un tableau latéral listant dans l’ordre chaque action effectuée par l’agent. Cette visibilité est ce qui rend un système autonome suffisamment fiable pour être mis en production, car rien de ce qu’il fait n’est opaque.

    Un déploiement qui épargne la production

    La méthode recommandée est prudente : elle se déroule en trois phases plutôt qu’en un seul déploiement.

    • Semaine 1 : ingestion et validation. Diriger les connecteurs Neocarta vers des copies en environnement de test de chaque source, définir les contraintes du graphe et vérifier que les nœuds sont correctement mappés et reliés avant d’impliquer des données de production.
  • Seconde semaine : essais en environnement de staging. Construire l’orchestrateur LangGraph sur une base de données de staging en lecture seule et observer les décisions prises par Gemini, avec l’exécution désactivée. C’est ici que se manifestent les problèmes liés aux prompts ou au contexte.
  • Troisième semaine : automatisation en boucle fermée. Connecter la couche 4 aux points de terminaison réels de l’API de staging et vérifier que les transactions d’écriture complètent le cycle, permettant ainsi à la barrière de sécurité d’avoir accès à un historique fiable.
  • Ce n’est qu’après que les trois phases fonctionnent correctement que les outils de correction peuvent être orientés vers les points de terminaison en production.

    Pourquoi cette conception fonctionne

    Plusieurs décisions distinguent une démonstration d’un système fiable en production.

    • Les machines à états sont supérieures aux boucles d’agents libres. LangGraph rend chaque chemin de décision explicite et inspectable, sans aucune chaîne de raisonnement cachée qui ne puisse être reproduite.
    • Une porte de sécurité précède chaque appel à un LLM susceptible de déclencher une action. Si une action corrective a été exécutée récemment, l’agent ne peut pas accéder au nœud de raisonnement. Cette vérification se trouve dans du code déterministe, et non dans un prompt.
    • Le contrat de sortie est restreint. On ne demande pas à Gemini de produire du texte en prose. Le fait de le limiter à l’un des deux types de tokens rend son résultat aussi fiable que la valeur de retour d’une fonction.
    • Le graphe sert de mémoire et non seulement d’emplacement de stockage. Les actions correctives sont enregistrées dans le même graphe que celui que lit l’agent, de sorte que sa conscience de la situation s’accumule et ne se réinitialise pas à chaque incident.
    • MCP est la liaison entre l’agent et le graphe. Avec le serveur MCP de Neocarta situé entre LangGraph et Neo4j, l’agent ne possède jamais de pilote de base de données ni ne gère de pools de connexions. Il appelle simplement des outils. Cette liaison permet également de remplacer Gemini par un autre modèle, ou LangGraph par un autre orchestrateur, sans toucher à la phase d’ingestion ni à la couche de graphe.

    Le contexte stocké nativement dans un graphe, une boucle de raisonnement à étapes fixes ainsi qu’un mécanisme de protection intégré au code permettent de rendre la correction autonome quelque chose qui peut être déployé réellement, et non seulement quelque chose qui a l’air bon sur une diapositive. Des étapes logiques suivantes seraient de déployer l’ensemble avec Docker Compose et d’envoyer des notifications Slack depuis le routeur de niveau 4, afin que tout le monde soit informé de chaque action autonome, même lorsque personne n’a eu besoin d’intervenir.