<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yashas M</title>
    <description>The latest articles on DEV Community by Yashas M (@yashasm18).</description>
    <link>https://dev.to/yashasm18</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4192445%2Ff05d00e4-a97d-412c-bf33-a6c5dc7bc59c.jpg</url>
      <title>DEV Community: Yashas M</title>
      <link>https://dev.to/yashasm18</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yashasm18"/>
    <language>en</language>
    <item>
      <title>I gave my on-call agents a graph instead of a bigger prompt</title>
      <dc:creator>Yashas M</dc:creator>
      <pubDate>Sun, 18 Oct 2026 16:44:17 +0000</pubDate>
      <link>https://dev.to/yashasm18/i-gave-my-on-call-agents-a-graph-instead-of-a-bigger-prompt-14m</link>
      <guid>https://dev.to/yashasm18/i-gave-my-on-call-agents-a-graph-instead-of-a-bigger-prompt-14m</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgw71pkp78nzae7gtl51c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgw71pkp78nzae7gtl51c.png" alt="GraphOps: the decision brief, the live FalkorDB graph and the evidence trail" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is 2 a.m. and checkout is down.&lt;/p&gt;

&lt;p&gt;Five alerts are firing across four services. &lt;code&gt;checkout-service&lt;/code&gt; is throwing 500s, &lt;code&gt;payments-service&lt;/code&gt; is slow, &lt;code&gt;fraud-service&lt;/code&gt; is timing out and &lt;code&gt;payments-db&lt;/code&gt; is almost out of connections. The on-call engineer opens Grafana, then the deploy log, then the service catalog, then Slack search ("didn't this happen before?"), then the wiki. Twenty minutes later they have rebuilt, in their head, a picture that already existed. It was just spread across ten tools.&lt;/p&gt;

&lt;p&gt;I kept coming back to one thought while building for &lt;strong&gt;Graph Hacks&lt;/strong&gt; (WeMakeDevs x FalkorDB): every question in that story is about &lt;em&gt;how things connect&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which alerting service is the cause and which ones are just victims? Follow the dependencies until the failures stop.&lt;/li&gt;
&lt;li&gt;What changed recently? Look at deploys next to the failing node.&lt;/li&gt;
&lt;li&gt;What breaks next? Walk the dependencies backwards.&lt;/li&gt;
&lt;li&gt;Has this happened before, and who fixed it? Find incidents with the same root cause, then follow them to the person who resolved them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A vector store gives you chunks that look similar. A graph gives you the relationships, and it can show you the path. So I built &lt;strong&gt;GraphOps&lt;/strong&gt;, an incident commander where three AI agents work an outage through one FalkorDB graph.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://graphops-gr4u.onrender.com" rel="noopener noreferrer"&gt;graphops-gr4u.onrender.com&lt;/a&gt; (free hosting, so the first load after a quiet spell takes about 50 seconds)&lt;br&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Yashasm18/graphops" rel="noopener noreferrer"&gt;github.com/Yashasm18/graphops&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What it does in 20 seconds
&lt;/h2&gt;

&lt;p&gt;You type "Checkout is throwing 500s and customers can't pay" and click Investigate. About 20 seconds later GraphOps tells you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The root cause is &lt;strong&gt;payments-db&lt;/strong&gt; connection exhaustion, triggered by deploy &lt;strong&gt;DEP-981&lt;/strong&gt; that raised retry attempts from 3 to 10, 23 minutes ago. This is the same failure as incident &lt;strong&gt;INC-2041&lt;/strong&gt;. Roll back and follow runbook &lt;strong&gt;RB-PAY-01&lt;/strong&gt;. Page &lt;strong&gt;Diego&lt;/strong&gt; (on call) and &lt;strong&gt;Nina&lt;/strong&gt; (she fixed it last time). By the way, Diego warned against this exact change in &lt;code&gt;#payments-eng&lt;/code&gt;, and the follow-up ticket from the last incident is still open.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That last sentence is my favourite part, and I will come back to it.&lt;/p&gt;
&lt;h2&gt;
  
  
  One graph for the whole company
&lt;/h2&gt;

&lt;p&gt;Everything lives in a single FalkorDB graph per company:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(:Service)-[:DEPENDS_ON]-&amp;gt;(:Service)
(:Service)-[:OWNED_BY]-&amp;gt;(:Team)&amp;lt;-[:ON_CALL_FOR]-(:Person)
(:Service)-[:POWERS]-&amp;gt;(:Capability {revenue_per_min})
(:Deploy)-[:CHANGED]-&amp;gt;(:Service), (:Deploy)-[:AUTHORED_BY]-&amp;gt;(:Person)
(:Alert)-[:FIRED_ON]-&amp;gt;(:Service)
(:Incident)-[:ROOT_CAUSE|AFFECTED]-&amp;gt;(:Service), -[:RESOLVED_BY]-&amp;gt;(:Person)
(:Runbook {success_count, failure_count})-[:FOR]-&amp;gt;(:Service)
(:Decision)-[:ABOUT]-&amp;gt;(:Service), -[:MOTIVATED_BY]-&amp;gt;(:Incident)
(:Deploy)-[:IMPLEMENTS]-&amp;gt;(:Ticket)-[:PART_OF]-&amp;gt;(:Project)
(:Message {channel})-[:MENTIONS]-&amp;gt;(:Ticket|:Incident|:Decision)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Capability&lt;/code&gt; nodes are a small idea that paid off a lot. Each one carries a revenue per minute, so "which services are affected" becomes "how much money are we losing right now".&lt;/p&gt;

&lt;h2&gt;
  
  
  Three agents that never talk to each other
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhghrlz16en4f02qbde0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhghrlz16en4f02qbde0j.png" alt="GraphOps architecture" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Triage&lt;/strong&gt; finds the root cause and the blast radius.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Historian&lt;/strong&gt; recalls past incidents, runbooks and decisions, and traces why the suspect change was shipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commander&lt;/strong&gt; decides what to do and who to page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Triage and Historian run at the same time. The design choice I am proudest of is that &lt;strong&gt;the agents never pass messages to each other&lt;/strong&gt;. Each one writes &lt;code&gt;Finding&lt;/code&gt; nodes into a shared investigation, linked to the evidence they are based on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(:Investigation)-[:HAS_FINDING]-&amp;gt;(:Finding)-[:EVIDENCE]-&amp;gt;(payments-db, DEP-981, INC-2041)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Commander starts by reading that subgraph back, and its decision is linked to every finding with &lt;code&gt;BASED_ON&lt;/code&gt; edges. That one choice gave me three things for free:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shared state&lt;/strong&gt; without a message bus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An audit trail.&lt;/strong&gt; "Why did you page Nina?" is a two hop query from the decision to its findings to the evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory.&lt;/strong&gt; When the incident is resolved, the investigation turns into a new &lt;code&gt;Incident&lt;/code&gt; node, and the next session finds it like any other incident.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tools, not a text-to-Cypher free for all
&lt;/h2&gt;

&lt;p&gt;My first instinct was to let Gemini write Cypher. It worked in the happy path and fell apart under pressure. So instead each agent gets &lt;strong&gt;small, named graph tools&lt;/strong&gt;, and each tool is one tested query or FalkorDB algorithm. The model only decides &lt;em&gt;which&lt;/em&gt; traversal to run.&lt;/p&gt;

&lt;p&gt;Here is the heart of the root cause ranking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;:Alert&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;status:&lt;/span&gt; &lt;span class="s1"&gt;'firing'&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:FIRED_ON&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;a:&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;alerting&lt;/span&gt;
&lt;span class="k"&gt;UNWIND&lt;/span&gt; &lt;span class="n"&gt;alerting&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:DEPENDS_ON&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;a.name&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;explains&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;c.name&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;explains&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every candidate is scored. The biggest weight goes to how many alerts it explains, because the simplest explanation that covers everything usually wins. After that come a few tie breakers: is it alerting itself, are its direct callers failing, is it the deepest failing node, and did a deploy land on it or right next to it.&lt;/p&gt;

&lt;p&gt;FalkorDB's algorithms are tools too. PageRank and betweenness find the chokepoints, label propagation finds clusters of tightly coupled services, and &lt;code&gt;shortestPath&lt;/code&gt; shows how a failure travelled from &lt;code&gt;mobile-bff&lt;/code&gt; all the way down to &lt;code&gt;payments-db&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not trust the model, ask the graph
&lt;/h2&gt;

&lt;p&gt;LLMs are confident even when they are wrong. In early runs a weaker fallback model happily blamed &lt;code&gt;payments-service&lt;/code&gt; because that is where the deploy happened, even though the thing actually failing was the database underneath it.&lt;/p&gt;

&lt;p&gt;So before any decision is saved, it goes through a &lt;strong&gt;proof gate&lt;/strong&gt;. FalkorDB has to return a causal path from every alerting service to the claimed root cause:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;a:&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;$alerting&lt;/span&gt;&lt;span class="ss"&gt;}),&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;r:&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;$root&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;algo.SPpaths&lt;/span&gt;&lt;span class="ss"&gt;({&lt;/span&gt;&lt;span class="py"&gt;sourceNode:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;targetNode:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;relTypes:&lt;/span&gt; &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'DEPENDS_ON'&lt;/span&gt;&lt;span class="ss"&gt;],&lt;/span&gt; &lt;span class="nl"&gt;pathCount&lt;/span&gt;&lt;span class="dl"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;maxLen&lt;/span&gt;&lt;span class="dl"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;YIELD&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="nf"&gt;nodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n.name&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On top of that, the suspect deploy has to be on the root or one hop from it and recent. A node that is not alerting itself also needs at least one failing caller, otherwise any shared hub like a Kafka cluster would look guilty. &lt;code&gt;algo.maxFlow&lt;/code&gt; with a capacity of 1 on each edge tells us how many links you would have to cut to isolate the node.&lt;/p&gt;

&lt;p&gt;The result is &lt;code&gt;VERIFIED&lt;/code&gt;, &lt;code&gt;WEAK&lt;/code&gt; or &lt;code&gt;REJECTED&lt;/code&gt;. A rejected claim goes back to the Commander to fix. I also check citations: every id an agent mentions, like &lt;code&gt;INC-2041&lt;/code&gt; or &lt;code&gt;DEP-981&lt;/code&gt;, is looked up in the graph, and the UI shows "4/4 cited nodes exist in the graph".&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulate first, on a copy of the graph
&lt;/h2&gt;

&lt;p&gt;Before anyone touches production, you can ask "what if Kafka goes down?" GraphOps copies the whole graph with &lt;code&gt;GRAPH.COPY&lt;/code&gt;, runs the simulation on the copy, and deletes it afterwards. The real graph is never touched.&lt;/p&gt;

&lt;p&gt;The containment planner goes further. Some dependency edges are marked as having a fallback mode, like "write payment events to a local outbox instead of Kafka". The planner tries every combination of up to three fallbacks on the copy, picks the smallest set that protects the most revenue, then actually deletes those edges on the copy and recomputes the impact to prove it.&lt;/p&gt;

&lt;p&gt;The result surprised me the first time. In a Kafka outage, turning on &lt;strong&gt;two&lt;/strong&gt; outbox fallbacks &lt;em&gt;together&lt;/em&gt; keeps checkout up and protects $4,200 a minute. Either one on its own protects nothing, because checkout still reaches Kafka through the other path. That is exactly the kind of thing a human misses at 2 a.m.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GRAPH.COPY&lt;/code&gt; also solved a very practical problem. On the live site, every visitor gets their own private copy of the company graph, so nobody can wipe or pollute anyone else's memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  The company brain moment
&lt;/h2&gt;

&lt;p&gt;This is the part I mentioned at the start. The Historian does not just ask "what broke", it asks "why did this change ship?":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;d:&lt;/span&gt;&lt;span class="n"&gt;Deploy&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt; &lt;span class="s1"&gt;'DEP-981'&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:IMPLEMENTS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;t:&lt;/span&gt;&lt;span class="n"&gt;Ticket&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:PART_OF&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;p:&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;m:&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:MENTIONS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;t.id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p.name&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m.text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It follows the deploy to ticket &lt;strong&gt;PAY-412&lt;/strong&gt; in the "Payments reliability Q4" project, and then to the chat thread about it. In that thread Diego wrote: &lt;em&gt;"Careful: INC-2041 was a retry storm. ADR-022 says jitter first, and PAY-388 is still open."&lt;/em&gt; The deploy shipped anyway. PAY-388, the follow-up from the last outage, is still open.&lt;/p&gt;

&lt;p&gt;No single document contains that story. It only exists in the connections between a deploy, a ticket, a project, a chat message, a design decision and an old incident. That is what a graph is for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real actions, with a human in the loop
&lt;/h2&gt;

&lt;p&gt;After a decision, GraphOps proposes real actions: page the chosen people on Discord, and open a GitHub incident issue whose body is the full evidence trail. Nothing goes out until someone clicks Approve, and every approval is written back to the graph as &lt;code&gt;(:User)-[:APPROVED]-&amp;gt;(:Action)&lt;/code&gt;. I did not want an agent paging people on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  It remembers
&lt;/h2&gt;

&lt;p&gt;GraphOps keeps three kinds of memory, all in the graph:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Episodic:&lt;/strong&gt; incidents, including the ones it learned itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic:&lt;/strong&gt; design decisions, docs and facts people teach it in chat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procedural:&lt;/strong&gt; runbooks with success and failure counts. If a human corrects one, a new version is created with a &lt;code&gt;SUPERSEDES&lt;/code&gt; edge, and every later session uses it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mark an incident as fixed, fire the same alerts again, and the Historian immediately cites the incident it learned a minute earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measured, not just shown
&lt;/h2&gt;

&lt;p&gt;I did not want to only show a nice run, so I wrote a benchmark that needs no LLM and no API key. It injects random faults into &lt;code&gt;GRAPH.COPY&lt;/code&gt; sandboxes, including silent root causes, unrelated noise alerts, and decoy deploys that are often newer than the real culprit.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;200 random fault worlds&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Root cause found first&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;71.5%&lt;/strong&gt; (blaming the newest deploy: 17.5%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root cause in the top 3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;89%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Culprit deploy found&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;85%&lt;/strong&gt; (newest deploy: 53.5%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proof gate verifies an unrelated wrong claim&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.2%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Then I tested it on real data. I wrote an importer that reads the public &lt;a href="https://github.com/open-telemetry/opentelemetry-demo" rel="noopener noreferrer"&gt;OpenTelemetry Demo&lt;/a&gt; compose files and its flagd fault flags, and builds a graph of 19 real services and 35 real dependencies. Every flag that breaks a service is replayed three ways.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OpenTelemetry Demo, 16 real fault flags&lt;/th&gt;
&lt;th&gt;Found first&lt;/th&gt;
&lt;th&gt;Top 3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The broken service alerts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The broken service is silent&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;79%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same fault again, after learning it&lt;/td&gt;
&lt;td&gt;recalled &lt;strong&gt;16 out of 16&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The silent case is honestly hard. In that topology most services have a single caller, so "the caller broke" and "its silent dependency broke" look the same in the graph. GraphOps keeps the true cause in its top three and the gate says WEAK instead of pretending to know. I would rather show that number than hide it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that broke along the way
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free tier quotas.&lt;/strong&gt; Gemini's free tier gives about 20 requests per model per day. I ran out of my main model in the first hour. GraphOps now keeps a pool of models and keys with cooldowns, and if every model is out, each agent falls back to a scripted playbook that calls the same graph tools. The demo never stops working.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FalkorDB quirks.&lt;/strong&gt; &lt;code&gt;shortestPath&lt;/code&gt; has to live in &lt;code&gt;WITH&lt;/code&gt; or &lt;code&gt;RETURN&lt;/code&gt;, not &lt;code&gt;MATCH&lt;/code&gt;. &lt;code&gt;algo.maxFlow&lt;/code&gt; needed &lt;code&gt;capacityProperty&lt;/code&gt; passed explicitly. Pattern checks inside &lt;code&gt;RETURN&lt;/code&gt; gave a type error, so I rewrote them with &lt;code&gt;OPTIONAL MATCH&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A crash I almost shipped.&lt;/strong&gt; A connection timeout made the Node client emit an error event before I had a listener on it, which killed the whole server. I only found it while testing the "what if a judge forgets to start FalkorDB" case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A stale clock.&lt;/strong&gt; Visitor sandboxes copied old timestamps, so a few hours after seeding, the culprit deploy was no longer "recent" and silently disappeared from the answer. New sandboxes now refresh the scenario when they are created.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Make the graph the protocol between agents.&lt;/strong&gt; Shared state, memory and the audit trail all fell out of one design choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small named tools beat raw text-to-Cypher.&lt;/strong&gt; They are reliable, testable and easy to explain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let the graph check the model.&lt;/strong&gt; A causal path is a much better judge than another prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store what happened, not just documents.&lt;/strong&gt; "Nina fixed INC-2041" is an edge, and it is what makes "who should I page?" answerable.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://graphops-gr4u.onrender.com" rel="noopener noreferrer"&gt;the live app&lt;/a&gt;, click Investigate, open the Why? tab, then try What-if, Containment plan on &lt;code&gt;kafka-events&lt;/code&gt;. Or run it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Yashasm18/graphops.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;graphops
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No API key is needed. The agents fall back to their graph playbooks without one.&lt;/p&gt;

&lt;p&gt;Built for Graph Hacks: Context for AI Agents by WeMakeDevs and FalkorDB. I used Claude Code as an AI coding assistant while building it, and the agents use Google Gemini at runtime. Every decision you see is backed by FalkorDB.&lt;/p&gt;

&lt;p&gt;If you have ever been woken up by a pager, I would love to hear what you would want an agent like this to do next.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F39g6imblrzljyrsxovel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F39g6imblrzljyrsxovel.png" alt=" " width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>hackathon</category>
      <category>devops</category>
      <category>graphdatabase</category>
    </item>
  </channel>
</rss>
