<?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: JOSHUA _231125</title>
    <description>The latest articles on DEV Community by JOSHUA _231125 (@josh99).</description>
    <link>https://dev.to/josh99</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%2F4141518%2Fadb7e07e-c176-404d-9c01-7c75af142e8a.png</url>
      <title>DEV Community: JOSHUA _231125</title>
      <link>https://dev.to/josh99</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/josh99"/>
    <language>en</language>
    <item>
      <title>Building an Agentic Fraud Investigation System on TigerGraph</title>
      <dc:creator>JOSHUA _231125</dc:creator>
      <pubDate>Thu, 24 Sep 2026 15:23:08 +0000</pubDate>
      <link>https://dev.to/josh99/building-an-agentic-fraud-investigation-system-on-tigergraph-4nap</link>
      <guid>https://dev.to/josh99/building-an-agentic-fraud-investigation-system-on-tigergraph-4nap</guid>
      <description>&lt;p&gt;What we built&lt;/p&gt;

&lt;p&gt;We built an agent that investigates fraud alerts the way a bank analyst would: gather evidence, weigh it against a written policy, decide what to do next, ask for more information when genuinely unsure, and write the finished case back into permanent memory. It runs against the HHGOA benchmark, a set of twenty fraud alerts drawn from the IEEE CIS transaction dataset, each one triggered by a risk score, a customer complaint, or an analyst request.&lt;/p&gt;

&lt;p&gt;Every case produces a structured answer: a verdict, a probability, the evidence behind it, the recommended action with the correct approval route, and, where required, a suspicious activity report written in plain language. All twenty cases pass through the same pipeline, get validated against the required answer format, and get written into the graph as a permanent case record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The pipeline has five stages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence gathering:&lt;/strong&gt; For each alert, the agent pulls the card's transaction history, checks whether the billing region is routine or new, looks for a burst pattern consistent with card testing, and checks the device fingerprint on the transaction. Four of these checks run as real GSQL queries against TigerGraph, called through the TigerGraph MCP server rather than a raw database driver, so every graph interaction is a tool call the agent makes deliberately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assessment:&lt;/strong&gt; The evidence gets weighed using a decision table, not a machine learning model. Strong evidence (an event sequence, a link to other cards) and weak evidence (a deviation from the card's own baseline, a device anomaly) are counted separately, and the probability comes from how many independent pieces point the same direction, following the same stop rule the policy itself defines: stop once the probability crosses 0.85 or falls under 0.15 with at least two independent pieces of evidence behind it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory:&lt;/strong&gt; Every one of the 5,565 closed historical cases is embedded and loaded into a TigerGraph vector attribute. When the agent needs precedent, it runs a similarity search directly against that vector index and gets back real prior cases, their outcomes, and their patterns, grounding the current investigation in what actually happened before rather than guessing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action and explanation:&lt;/strong&gt; The policy itself is written as code: which actions exist, which approval route each one takes (automatic, level one, or level two), and the ten numbered rules that govern when a case must be opened, when a report must be filed, and when a block requires human sign off. The agent's recommendations are derived from this code, never from the language model. A Groq hosted model is then used, but only to turn the already computed facts into readable prose for the case summary and the report narrative. It never decides a verdict, a probability, or an action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write back:&lt;/strong&gt; Once a case is finished, it is written into TigerGraph as its own vertex, linked to the customer, the card, the affected transactions, and any precedent cases it cited. The investigation becomes part of the graph, available to the next investigation that touches the same customer or device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How TigerGraph is used&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TigerGraph is not a passive data store here, it is where the actual investigation happens. The graph holds customers, cards, transactions, device fingerprints, billing regions, closed historical cases, and the fraud cases the agent itself creates. Four custom GSQL queries traverse this graph to compute a card's baseline behavior, check whether a billing region is routine, detect a testing burst, and find other cards sharing a device fingerprint.&lt;/p&gt;

&lt;p&gt;We also installed and ran TigerGraph's built in Weakly Connected Components algorithm across the device sharing edges. The result was informative in an unexpected way: the algorithm collapsed almost the entire graph into one dominant component, a documented phenomenon in device fingerprint graphs where a handful of extremely common device profiles act as hubs connecting otherwise unrelated customers. Rather than treat this as a failed experiment and discard it, we used the finding to justify why our narrower, single profile ring detector is the right tool for this particular graph shape, and we are including that reasoning here rather than hiding it.&lt;/p&gt;

&lt;p&gt;The vector store is the other half of TigerGraph's role. All 5,565 closed case notes are embedded and indexed as a vector attribute on the ClosedCase vertex type, and retrieval happens through TigerGraph's own similarity search rather than an external vector database. This is what let the agent correctly surface the three closed cases most similar to an undocumented device ring pattern, without us ever telling it which cases to look for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agentic capabilities we implemented&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent gathers its own evidence through tool calls rather than being handed a pre packaged summary. It decides, based on the evidence, whether it has enough information to act or whether it needs to ask a question first, and if it asks, the answer changes its final recommendation, not just its confidence number. For every uncertain case we also precomputed both possible replies, so the finished interface lets a reviewer toggle between them and see the investigation actually change its conclusion in front of them, rather than trusting a single frozen outcome.&lt;/p&gt;

&lt;p&gt;The agent also operates under real permission boundaries. Actions are split into three routes: fully automatic, requiring a level one approver, or requiring a level two approver, and the split follows the policy's own thresholds, for instance a card block under a certain exposure needs one level of sign off and above it needs another. The agent recommends, it does not unilaterally execute anything above the automatic tier.&lt;/p&gt;

&lt;p&gt;One case surfaced a pattern that matches none of the five documented fraud typologies: a single device fingerprint, always marked as new, reused across dozens of different cards behind an anonymous proxy, with unusually low individual risk scores. The agent recognized this did not fit any named category, escalated it, and described the pattern in its own words rather than forcing it into the wrong label.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most useful lesson came from something we built and then rejected. We trained a logistic regression on the closed case history and got a 98.5 percent AUC, a number that looked excellent on paper. Before trusting it, we tested it against the handful of cases we had already reasoned through by hand, and it got them backwards: it called our most confidently legitimate cases high probability fraud. The reason turned out to be a selection effect in the historical data itself, every cleared case in that history happened to carry a high risk score, so the model had learned that a high score predicts innocence, which is exactly the opposite of what we needed it to learn. We threw the model out and kept the transparent, rule based evidence counter instead. A strong validation metric is not the same thing as a model that is right for the question you are actually asking, and the only way we caught this was by checking the model against cases we understood deeply enough to know the right answer ourselves.&lt;/p&gt;

&lt;p&gt;We also learned that a graph algorithm can be technically successful and still not be the right tool for a specific question. Weakly connected components is a well established, well documented algorithm, and it ran correctly. It simply told us that almost everything in this dataset is connected to almost everything else through common device models, which is true and also not useful for isolating one specific ring. Recognizing that distinction, between an algorithm working correctly and an algorithm answering the question we actually had, mattered more than any single line of code we wrote.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we would improve with more time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The closed case retrieval currently keys similarity matching on the text of the analyst's note. Extending this to match on the graph's own structural signals directly, rather than through a text embedding of a written description, would likely surface precedent cases the current approach misses.&lt;/p&gt;

&lt;p&gt;We would also extend the device ring detection beyond a single profile threshold into a proper community detection pass, now that we understand why plain connected components collapses on this graph. A small world aware variant, scoped to devices explicitly marked as new rather than every device edge, would likely isolate genuine rings without the collapse we saw.&lt;/p&gt;

&lt;p&gt;Finally, we would build an unprompted monitoring pass that scans the full transaction history on a schedule rather than waiting for an alert, using the same detectors that power the twenty required investigations, so the agent surfaces patterns nobody has flagged yet rather than only responding to ones that have.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>database</category>
      <category>security</category>
    </item>
  </channel>
</rss>
