<?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: Abhishek Kumar Yadav</title>
    <description>The latest articles on DEV Community by Abhishek Kumar Yadav (@abhishekyadav26).</description>
    <link>https://dev.to/abhishekyadav26</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%2F2223418%2F410db789-fe0a-4721-ba53-a6669fc0cb85.jpg</url>
      <title>DEV Community: Abhishek Kumar Yadav</title>
      <link>https://dev.to/abhishekyadav26</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhishekyadav26"/>
    <language>en</language>
    <item>
      <title>GraphSentinel- Agentic fraud investigation</title>
      <dc:creator>Abhishek Kumar Yadav</dc:creator>
      <pubDate>Thu, 24 Sep 2026 22:09:51 +0000</pubDate>
      <link>https://dev.to/abhishekyadav26/graphsentinel-agentic-fraud-investigation-47mj</link>
      <guid>https://dev.to/abhishekyadav26/graphsentinel-agentic-fraud-investigation-47mj</guid>
      <description>&lt;h2&gt;
  
  
  GraphSentinel: Building an Agentic Fraud Investigation System on TigerGraph
&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%2Fzeqz0bxfvc6ye291bf9x.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%2Fzeqz0bxfvc6ye291bf9x.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fraud investigation is not only a classification problem.&lt;/p&gt;

&lt;p&gt;An analyst needs to understand &lt;strong&gt;why&lt;/strong&gt; a transaction is risky, gather the right evidence, follow policy, choose an action, document approvals, and preserve the investigation for the next case.&lt;/p&gt;

&lt;p&gt;GraphSentinel was built to explore that complete workflow.&lt;/p&gt;

&lt;p&gt;It is an &lt;strong&gt;agentic fraud-investigation and next-best-action system&lt;/strong&gt; for the TigerGraph Agentic Fraud Investigation challenge. The system starts from a risk alert, customer report, or analyst request and produces a traceable investigation record containing graph evidence, model belief, policy citations, evidence requests, actions, approvals, SAR decisions, and case memory.&lt;/p&gt;

&lt;p&gt;The central idea is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Use the graph to gather structured evidence, use a model to estimate risk, use policy to constrain actions, and use an agent to decide what investigation should happen next.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  What we built
&lt;/h1&gt;

&lt;p&gt;GraphSentinel combines five main ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A temporal graph investigation layer for transactions, customers, devices, cards, addresses, prior cases, and relationships.&lt;/li&gt;
&lt;li&gt;A risk model trained on closed investigations rather than relying only on a hard-coded fraud score.&lt;/li&gt;
&lt;li&gt;GraphRAG retrieval for policies, fraud patterns, and regulatory references.&lt;/li&gt;
&lt;li&gt;A policy engine that determines what is permitted, what requires approval, and when customer contact is restricted.&lt;/li&gt;
&lt;li&gt;A bounded agent workflow that can select follow-up tools, request evidence, update its belief, and produce an auditable explanation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interesting part is that the workflow records a &lt;strong&gt;next-best action before requesting additional evidence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the case is still uncertain, the system chooses an allowed evidence request using value-of-information scoring. After the response, it updates its belief and records another next-best action.&lt;/p&gt;

&lt;p&gt;This makes the effect of evidence visible instead of hiding everything inside a final classification.&lt;/p&gt;

&lt;p&gt;The application supports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local graph store
       │
       ├── Offline development
       │
       ├── TigerGraph REST
       │
       └── TigerGraph MCP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local graph implementation follows the same query contract as the TigerGraph backends, allowing the investigation workflow to be tested without requiring a live TigerGraph instance.&lt;/p&gt;




&lt;h1&gt;
  
  
  Architecture
&lt;/h1&gt;

&lt;p&gt;At a high level, an investigation follows this path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trigger
  │
  ▼
Intake
  │
  ▼
Baseline Graph Evidence
  │
  ▼
Agent-selected Follow-up Queries
  │
  ├───────────────┐
  ▼               ▼
Similar Cases    GraphRAG
  │               │
  └───────┬───────┘
          ▼
Risk Model + Fraud Classification
          │
          ▼
Policy Decision
          │
          ▼
NBA Before Evidence
          │
          ▼
   Is the case uncertain?
       /          \
     No            Yes
     │              │
     │              ▼
     │        Select Evidence
     │              │
     │              ▼
     │        Apply Response
     │              │
     │              ▼
     │        Update Belief
     │              │
     │              ▼
     │        NBA After Evidence
     │              │
     └──────┬───────┘
            ▼
 Actions / Approvals / SAR
            │
            ▼
      Explanation
            │
            ▼
      Graph Write-back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main runtime is assembled by &lt;code&gt;services/runtime.py&lt;/code&gt;. It loads the dataset, graph store, policy configuration, pattern library, risk model, likelihood tables, case repository, evidence provider, and optional CrewAI client.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;agent/workflow.py&lt;/code&gt; module builds the LangGraph state machine with explicit nodes for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;intake
baseline evidence
follow-ups
memory / RAG
assessment
decision
evidence
finalization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This explicit state-machine approach makes the investigation path easier to test and reason about than putting the entire workflow inside a single agent prompt.&lt;/p&gt;




&lt;h1&gt;
  
  
  Separation of powers
&lt;/h1&gt;

&lt;p&gt;One of the most important architectural decisions was deliberately separating responsibilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────┐
│        TigerGraph         │
│                           │
│ Evidence + Relationships  │
└─────────────┬─────────────┘
              │
              ▼
┌───────────────────────────┐
│        Risk Model         │
│                           │
│ Fraud probability         │
└─────────────┬─────────────┘
              │
              ▼
┌───────────────────────────┐
│       Policy Engine       │
│                           │
│ Permissions + Approvals   │
└─────────────┬─────────────┘
              │
              ▼
┌───────────────────────────┐
│       Agent / LLM         │
│                           │
│ Follow-up + Explanation   │
└─────────────┬─────────────┘
              │
              ▼
┌───────────────────────────┐
│       Action Gateway      │
│                           │
│ Approved actions only     │
└───────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The graph supplies evidence.&lt;/p&gt;

&lt;p&gt;The risk model estimates fraud probability.&lt;/p&gt;

&lt;p&gt;The policy engine determines permissions and approval routes.&lt;/p&gt;

&lt;p&gt;The LLM proposes optional follow-up work and generates language.&lt;/p&gt;

&lt;p&gt;The action gateway executes only policy-approved actions.&lt;/p&gt;

&lt;p&gt;The LLM is &lt;strong&gt;never allowed to authorize or execute a protective action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Its JSON output is validated, unknown tool names are discarded, citations are filtered against retrieved policy clauses, and failures fall back to deterministic templates.&lt;/p&gt;




&lt;h1&gt;
  
  
  The investigation workflow
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Baseline evidence
&lt;/h2&gt;

&lt;p&gt;Every investigation begins with a focal transaction.&lt;/p&gt;

&lt;p&gt;The system gathers a bounded set of temporal graph queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction details + owner
        │
        ├── Customer history
        ├── Card activity
        ├── Shared devices
        ├── Address peers
        ├── Customer case history
        └── Linked cases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Representative queries include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gs_txn_detail
gs_customer_history
gs_device_customers
gs_address_peers
gs_linked_cases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These queries are transformed into signals such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;signals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount_ratio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;amount_ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;device_novelty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;device_novelty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;account_age&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;account_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;card_velocity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;card_velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email_mismatch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;email_mismatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linked_confirmed_cases&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linked_confirmed_cases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph_fraud_proximity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fraud_proximity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address_cluster_size&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address_cluster_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A critical constraint is that behavioral queries are evaluated strictly &lt;strong&gt;before the focal event&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If a transaction occurred on January 10, information that only became available on January 20 cannot influence the January 10 decision.&lt;/p&gt;

&lt;p&gt;A simplified interface therefore looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_customer_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;as_of&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gs_customer_history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;as_of&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;as_of&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;as_of&lt;/code&gt; boundary is part of the graph-access layer rather than an assumption made by the analyst.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Follow-up planning
&lt;/h1&gt;

&lt;p&gt;Baseline evidence is not always enough.&lt;/p&gt;

&lt;p&gt;The agent can select additional graph investigations, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Device ring expansion
Card activity
Address-cluster transactions
Community statistics
Spending trajectory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent can select up to three additional tools.&lt;/p&gt;

&lt;p&gt;CrewAI acts as a bounded investigation assistant for this stage.&lt;/p&gt;

&lt;p&gt;It receives the available signals and a menu of installed tools and returns structured output such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"device_ring"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Device is shared with multiple high-risk customers."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"address_cluster"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Several recently created accounts share this address."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the model doesn't receive arbitrary database access.&lt;/p&gt;

&lt;p&gt;The returned tool names are validated against the installed tool registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;TOOLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;device_ring&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;investigate_device_ring&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;card_activity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;investigate_card_activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address_cluster&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;investigate_address_cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;community_stats&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;investigate_community&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spending_trajectory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;investigate_spending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;tool&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;requested&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TOOLS&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unknown tools are discarded.&lt;/p&gt;

&lt;p&gt;If the model produces malformed output or fails completely, the workflow falls back to deterministic planning.&lt;/p&gt;

&lt;p&gt;This creates a controlled boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM
 │
 │ proposes
 ▼
Tool Registry
 │
 │ validates
 ▼
Installed Graph Queries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM ─────────────► arbitrary database access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  3. Memory and GraphRAG
&lt;/h1&gt;

&lt;p&gt;Fraud investigation requires more than transaction data.&lt;/p&gt;

&lt;p&gt;The agent may need to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;previous investigations,&lt;/li&gt;
&lt;li&gt;internal policies,&lt;/li&gt;
&lt;li&gt;known fraud patterns,&lt;/li&gt;
&lt;li&gt;regulatory requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GraphSentinel therefore uses GraphRAG.&lt;/p&gt;

&lt;p&gt;The case is embedded and compared with previous cases using both vector similarity and structural relationships such as shared devices and cards.&lt;/p&gt;

&lt;p&gt;Policy documents are parsed into a document graph containing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DocumentChunk
     │
     ├── PolicyClause
     ├── Pattern
     └── Regulation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieval combines vector search with graph expansion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User / Case Question
        │
        ▼
Vector Search
        │
        ▼
Relevant Document Chunks
        │
        ▼
Graph Expansion
        │
        ▼
Policy / Pattern / Regulation Context
        │
        ▼
Cited Investigation Context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Representative queries include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gs_similar_cases_vec
gs_doc_search_vec
gs_policy_context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the system to attach policy citations to evidence requests, actions, explanations, and SAR decisions.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Similar-case retrieval
&lt;/h1&gt;

&lt;p&gt;Vector similarity alone is not enough for fraud investigations.&lt;/p&gt;

&lt;p&gt;Two cases may have similar descriptions but completely different graph structures.&lt;/p&gt;

&lt;p&gt;Therefore case retrieval combines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vector similarity
       +
Shared devices
       +
Shared cards
       +
Structural relationships
       +
Previous case outcomes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;similar_cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;retrieve_similar_cases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_case_embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;graph_links&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;device_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;card_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the risk model to use historical cases without reducing the investigation to a simple nearest-neighbor search.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Assessment
&lt;/h1&gt;

&lt;p&gt;After graph evidence, follow-up investigation, and memory retrieval, the system estimates fraud probability.&lt;/p&gt;

&lt;p&gt;The risk model is a regularized logistic model.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;bank_risk_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;amount_ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device_novelty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;account_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;card_velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;graph_fraud_proximity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;confirmed_case_links&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;similar_case_outcomes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pattern_strength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trigger_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;fraud_probability&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict_proba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting belief is separated into fraud hypotheses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Legitimate
Third-party fraud
First-party fraud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Evidence can later update those hypotheses.&lt;/p&gt;

&lt;p&gt;For example, the supplied likelihood tables can make a failed step-up authentication increase the probability of third-party fraud, while a passed authentication shifts belief in the other direction.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Policy decision and next-best action
&lt;/h1&gt;

&lt;p&gt;This is one of the most important parts of the architecture.&lt;/p&gt;

&lt;p&gt;The policy engine applies configurable thresholds.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;probability&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;CLEAR_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clear&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;probability&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ACTION_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uncertain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For uncertain cases, the system evaluates potential evidence requests.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;policy is applied before optimization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer validation
Step-up authentication
Analyst review
Device investigation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A naive implementation might calculate information gain first.&lt;/p&gt;

&lt;p&gt;GraphSentinel instead does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Candidate Evidence
       │
       ▼
Policy Filter
       │
       ▼
Allowed Evidence
       │
       ▼
Value of Information
       │
       ▼
Selected Evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;evidence_requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;policy_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;policy_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;best_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;expected_information_gain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;request_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters because a request can be statistically useful while still being impermissible.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;customer validation can be blocked for customer-reported cases;&lt;/li&gt;
&lt;li&gt;contact can be restricted when first-party fraud is likely;&lt;/li&gt;
&lt;li&gt;requests that could create SAR tipping-off risk can be excluded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The optimizer never sees those prohibited requests.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. NBA before evidence
&lt;/h1&gt;

&lt;p&gt;Before any additional evidence is requested, the system records the current next-best action.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;nba_before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;escalate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;probability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk_level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fraud_class&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;third_party&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hold_transaction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;permission&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approval&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approval_route&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fraud_analyst&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;policy_clause&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POL-2.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates an important property:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We know what the system would have done before seeing the additional evidence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each NBA records information such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;decision
probability
risk level
fraud class
selected evidence request
excluded requests
policy reasons
actions
permission type
approval route
policy clause
received evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  8. Evidence request and belief update
&lt;/h1&gt;

&lt;p&gt;If the case remains uncertain, the selected evidence request is executed.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;evidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step_up_auth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result updates the fraud hypotheses.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;posterior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bayesian_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;prior&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;belief&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;likelihoods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;likelihood_tables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important architectural property is that evidence is represented as an explicit transition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Belief Before
     │
     ▼
NBA Before
     │
     ▼
Evidence Request
     │
     ▼
Evidence Response
     │
     ▼
Belief Update
     │
     ▼
NBA After
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it possible to inspect exactly how new evidence changed the investigation.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. NBA after evidence
&lt;/h1&gt;

&lt;p&gt;After updating the belief, the system records a second next-best action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;nba_after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;protect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;probability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;posterior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fraud_probability&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fraud_class&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;posterior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fraud_class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;block_transaction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;permission&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approval&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approval_route&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fraud_analyst&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;policy_clause&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POL-2.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The case now contains a complete decision timeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initial Evidence
      │
      ▼
Initial Belief
      │
      ▼
NBA Before Evidence
      │
      ▼
Evidence Request
      │
      ▼
Evidence Response
      │
      ▼
Updated Belief
      │
      ▼
NBA After Evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is more informative than simply returning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fraud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  10. LangGraph workflow
&lt;/h1&gt;

&lt;p&gt;The complete investigation is represented as a state machine.&lt;/p&gt;

&lt;p&gt;A simplified version looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.graph&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;


&lt;span class="n"&gt;workflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InvestigationState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intake&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;baseline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baseline_evidence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;followups&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;followup_planning&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retrieve_memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assessment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assess_risk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy_decision&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_evidence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;finalize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;finalize_case&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intake&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intake&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;baseline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;baseline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;followups&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;followups&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assessment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assessment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_conditional_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;route_after_decision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;finalize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;finalize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assessment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;finalize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important property is that an evidence response can send the case back through assessment.&lt;/p&gt;

&lt;p&gt;The system is therefore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trigger
   ↓
Investigate
   ↓
Assess
   ↓
Decide
   ↓
Need evidence?
   ├── No ───────► Finalize
   │
   └── Yes
        ↓
     Evidence
        ↓
     Reassess
        ↓
     Decide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the core agentic loop.&lt;/p&gt;




&lt;h1&gt;
  
  
  How TigerGraph is used
&lt;/h1&gt;

&lt;p&gt;TigerGraph is the graph system of record for investigation evidence and case memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graph model
&lt;/h2&gt;

&lt;p&gt;The graph contains vertices for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction
Customer
Device
Card
Address
FraudCase
Finding
Action
DocumentChunk
PolicyClause
Pattern
Regulation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edges represent relationships such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer ──owns──────► Card
Customer ──uses──────► Device
Customer ──lives_at──► Address
Transaction ──belongs_to──► Customer
Case ──has_finding──► Finding
Case ──has_action────► Action
Case ──similar_to────► Case
Document ──references─► PolicyClause
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns the fraud investigation into a connected evidence problem rather than a flat feature table.&lt;/p&gt;




&lt;h1&gt;
  
  
  Installed GSQL query contract
&lt;/h1&gt;

&lt;p&gt;Every graph read is a named installed query.&lt;/p&gt;

&lt;p&gt;The contract in &lt;code&gt;graph/contract.py&lt;/code&gt; defines query names, parameters, and result parsing.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;QUERY_CONTRACT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gs_txn_detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;txn_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gs_customer_history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cust&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;as_of&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gs_address_peers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cust&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;window_sec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;as_of&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min_first_seen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gs_similar_cases_vec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query_vec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local graph store, TigerGraph REST store, and MCP store all implement the same interface.&lt;/p&gt;

&lt;p&gt;This gives us two major advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Investigation code isn't coupled to one TigerGraph client mechanism.&lt;/li&gt;
&lt;li&gt;Local, REST, and MCP implementations can be tested against the same query names and response shapes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Representative installed queries include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gs_txn_detail
gs_customer_history
gs_device_customers
gs_address_peers
gs_linked_cases
gs_similar_cases_vec
gs_doc_search_vec
gs_policy_context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Graph algorithms
&lt;/h1&gt;

&lt;p&gt;The repository also includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Weakly Connected Components
Louvain Communities
Personalized PageRank
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Personalized PageRank is seeded from confirmed-fraud customers to create a graph-based fraud-proximity feature.&lt;/p&gt;

&lt;p&gt;The setup also computes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;device degrees
customer links
connected components
communities
fraud proximity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Highly connected hub devices are excluded from useful proximity signals because shared corporate or public devices can otherwise create misleading fraud relationships.&lt;/p&gt;




&lt;h1&gt;
  
  
  TigerGraph MCP
&lt;/h1&gt;

&lt;p&gt;The agent-plane MCP adapter exposes four operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run_installed_query
add_nodes
add_edges
get_node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LangGraph Agent
      │
      ▼
TigerGraph MCP
      │
      ├── run_installed_query
      ├── get_node
      ├── add_nodes
      └── add_edges
      │
      ▼
TigerGraph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server is launched over stdio using the same TigerGraph configuration.&lt;/p&gt;

&lt;p&gt;The repository also contains an MCP emulator backed by the local graph store.&lt;/p&gt;

&lt;p&gt;This made it possible to test the complete MCP path without requiring a live TigerGraph deployment.&lt;/p&gt;




&lt;h1&gt;
  
  
  Precomputation
&lt;/h1&gt;

&lt;p&gt;Before investigations run, graph-derived features are precomputed.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute_device_degrees&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute_customer_links&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute_wcc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute_louvain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute_fraud_pagerank&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values can then be used during investigation instead of repeatedly traversing the entire graph.&lt;/p&gt;




&lt;h1&gt;
  
  
  Temporal correctness
&lt;/h1&gt;

&lt;p&gt;One of the less obvious challenges was preventing future information from leaking into the investigation.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;January 10
   │
   └── suspicious transaction

January 15
   │
   └── investigation starts

January 20
   │
   └── case confirmed as fraud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The January 20 outcome must not become a feature for the January 10 transaction.&lt;/p&gt;

&lt;p&gt;Therefore graph queries use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;as_of&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;case_opened_at&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and only retrieve information available before the relevant event.&lt;/p&gt;

&lt;p&gt;The same principle applies to customer history, account age, devices, cards, and linked cases.&lt;/p&gt;

&lt;p&gt;Left-censored accounts also need special handling. If the available dataset starts after the account was created, we shouldn't automatically classify that account as "new."&lt;/p&gt;

&lt;p&gt;These rules belong in the graph access layer and tests, not only in analyst convention.&lt;/p&gt;




&lt;h1&gt;
  
  
  Case memory
&lt;/h1&gt;

&lt;p&gt;The investigation does not disappear after the final API response.&lt;/p&gt;

&lt;p&gt;The case is written back to graph memory as a &lt;code&gt;FraudCase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FraudCase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;embedding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Findings and actions are then connected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Finding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;finding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;finding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;finding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HAS_FINDING&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;finding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The case can be connected to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer
Focal Transaction
Related Transactions
Findings
Actions
Similar Cases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a continuous memory loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Past Investigations
        │
        ▼
    Case Memory
        │
        ▼
 New Investigation
        │
        ▼
New Findings
        │
        ▼
Updated Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But agent outcomes and analyst-confirmed outcomes are deliberately kept separate.&lt;/p&gt;

&lt;p&gt;An agent prediction should not automatically become training ground truth.&lt;/p&gt;

&lt;p&gt;Only analyst-confirmed outcomes should become authoritative learning data.&lt;/p&gt;




&lt;h1&gt;
  
  
  SAR handling
&lt;/h1&gt;

&lt;p&gt;SAR eligibility is evaluated by policy.&lt;/p&gt;

&lt;p&gt;The system considers factors such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Posterior probability
Aggregate amount
Suspect identification
Money-laundering indicators
Applicable thresholds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a SAR is required, the agent can draft the narrative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requires_sar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;sar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;draft_sar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;citations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;policy_context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;approval_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;sar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bsa_officer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM can help write the narrative, but it does not independently authorize the SAR.&lt;/p&gt;

&lt;p&gt;The policy engine controls eligibility and the required approval route.&lt;/p&gt;




&lt;h1&gt;
  
  
  Agentic capabilities
&lt;/h1&gt;

&lt;p&gt;The system is agentic in a constrained, auditable sense.&lt;/p&gt;

&lt;p&gt;It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;choose which optional graph tools to run;&lt;/li&gt;
&lt;li&gt;decide that additional evidence is needed;&lt;/li&gt;
&lt;li&gt;select evidence using expected information gain and cost;&lt;/li&gt;
&lt;li&gt;update beliefs after evidence responses;&lt;/li&gt;
&lt;li&gt;produce separate pre-evidence and post-evidence decisions;&lt;/li&gt;
&lt;li&gt;request human approval through policy-defined routes;&lt;/li&gt;
&lt;li&gt;write structured explanations;&lt;/li&gt;
&lt;li&gt;preserve the complete investigation timeline;&lt;/li&gt;
&lt;li&gt;discover residual patterns in closed cases and flag undocumented patterns for policy review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important design choice is that agency is bounded by contracts and policy.&lt;/p&gt;

&lt;p&gt;The agent can explore and explain.&lt;/p&gt;

&lt;p&gt;It cannot silently bypass an approval route or turn an uncertain case into an automatic protective action.&lt;/p&gt;




&lt;h1&gt;
  
  
  Undocumented pattern discovery
&lt;/h1&gt;

&lt;p&gt;GraphSentinel also contains a discovery loop for closed investigations.&lt;/p&gt;

&lt;p&gt;The idea is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Closed Cases
     │
     ▼
Residual Analysis
     │
     ▼
Unexpected Pattern
     │
     ▼
Candidate Rule
     │
     ▼
Policy Review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, the discovery system might identify an unexplained cluster of newly created accounts sharing the same address.&lt;/p&gt;

&lt;p&gt;The important part is that discovery does &lt;strong&gt;not&lt;/strong&gt; automatically become policy.&lt;/p&gt;

&lt;p&gt;The candidate is flagged for review:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;candidate_pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pattern&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;documented&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requires_policy_review&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps pattern discovery separate from authorization.&lt;/p&gt;




&lt;h1&gt;
  
  
  Finalization
&lt;/h1&gt;

&lt;p&gt;Once the investigation is complete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Automatic actions
        │
        ▼
Mock Action Gateway

Approval actions
        │
        ▼
Required Approval Route

SAR required
        │
        ▼
BSA Officer Approval

All paths
        │
        ▼
Explanation
        │
        ▼
Graph Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automatic actions are sent to the mock gateway.&lt;/p&gt;

&lt;p&gt;Approval actions are queued for the required role.&lt;/p&gt;

&lt;p&gt;SAR eligibility is evaluated using the policy rules.&lt;/p&gt;

&lt;p&gt;Finally, the complete case is written back to graph memory.&lt;/p&gt;




&lt;h1&gt;
  
  
  Running the system locally
&lt;/h1&gt;

&lt;p&gt;The project can run without TigerGraph for the initial development loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;

graphsentinel synth

graphsentinel build

graphsentinel run-benchmark

graphsentinel &lt;span class="nb"&gt;eval

&lt;/span&gt;graphsentinel serve

pytest &lt;span class="nt"&gt;-q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local graph store follows the same query contract as the TigerGraph implementation.&lt;/p&gt;

&lt;p&gt;For TigerGraph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env

&lt;span class="c"&gt;# Configure:&lt;/span&gt;
&lt;span class="c"&gt;# GS_MODE=tigergraph&lt;/span&gt;
&lt;span class="c"&gt;# TG_* variables&lt;/span&gt;

graphsentinel tg-setup

graphsentinel tg-check

graphsentinel serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default agent access path is MCP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GS_TG_ACCESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;REST access is also supported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GS_TG_ACCESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Administrative operations such as DDL, bulk loading, and algorithm setup use the REST path.&lt;/p&gt;




&lt;h1&gt;
  
  
  Using the real HHGOA dataset
&lt;/h1&gt;

&lt;p&gt;The application also supports the actual HHGOA/IEEE-style dataset through configurable column mappings.&lt;/p&gt;

&lt;p&gt;The dataset directory is configured with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GS_DATA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/dataset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loader resolves file and column names using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config/dataset_mapping.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the provided benchmark case pack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;case_pack.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is placed alongside the dataset files.&lt;/p&gt;

&lt;p&gt;The benchmark can then be run with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GS_DATA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/case-pack &lt;span class="se"&gt;\&lt;/span&gt;
graphsentinel run-benchmark
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated cases are written as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cases/
├── HHG-001.json
├── HHG-002.json
├── ...
└── HHG-020.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each investigation record contains the investigation evidence, findings, decisions, actions, graph write-back status, SAR details where applicable, and the next-best action before and after evidence.&lt;/p&gt;




&lt;h1&gt;
  
  
  Evaluation
&lt;/h1&gt;

&lt;p&gt;The project contains two different evaluation modes.&lt;/p&gt;

&lt;p&gt;The first is the generated benchmark.&lt;/p&gt;

&lt;p&gt;The second is temporal replay.&lt;/p&gt;

&lt;p&gt;This distinction is important because the benchmark uses synthetic data where the generator deliberately plants patterns.&lt;/p&gt;

&lt;p&gt;The benchmark therefore demonstrates that the system behaves correctly against the generated ground truth.&lt;/p&gt;

&lt;p&gt;It should not be interpreted as a production fraud-detection accuracy estimate.&lt;/p&gt;

&lt;p&gt;The temporal replay is a more realistic test because the model trains on earlier cases and investigates later cases.&lt;/p&gt;

&lt;p&gt;The repository reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Training:
Months 1–3
69 closed cases

Replay:
37 later cases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The replay produced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;21 cases decided directly
20 correct decisions
16 escalations
Precision: 1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important limitation is that many escalations came from evidence requests for which no response was recorded in the closed-case data.&lt;/p&gt;

&lt;p&gt;This means the system sometimes correctly identifies uncertainty but does not have enough historical evidence to resolve it automatically.&lt;/p&gt;

&lt;p&gt;That is an important difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"I don't know"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"I am confident this is legitimate."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A production system should preserve that distinction.&lt;/p&gt;




&lt;h1&gt;
  
  
  What we learned
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Graphs are most valuable when they change an action
&lt;/h2&gt;

&lt;p&gt;A large graph is not automatically useful.&lt;/p&gt;

&lt;p&gt;Device, card, and address relationships matter when they:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change the posterior;&lt;/li&gt;
&lt;li&gt;explain a fraud class;&lt;/li&gt;
&lt;li&gt;justify an action;&lt;/li&gt;
&lt;li&gt;identify useful evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The query contract helped keep graph investigation focused on decisions rather than graph traversal for its own sake.&lt;/p&gt;




&lt;h2&gt;
  
  
  Time correctness is harder than it looks
&lt;/h2&gt;

&lt;p&gt;Fraud data contains future outcomes, later cases, and accounts that may predate the available dataset.&lt;/p&gt;

&lt;p&gt;Every query therefore needs an &lt;code&gt;as_of&lt;/code&gt; boundary.&lt;/p&gt;

&lt;p&gt;Left-censored accounts must also be handled correctly.&lt;/p&gt;

&lt;p&gt;Otherwise a seemingly good model can quietly learn from information that would not have been available at decision time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Evidence selection needs policy before optimization
&lt;/h2&gt;

&lt;p&gt;A request can be statistically informative and still be impermissible.&lt;/p&gt;

&lt;p&gt;Therefore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Evidence candidates
       ↓
Policy constraints
       ↓
Allowed candidates
       ↓
Value-of-information
       ↓
Selected evidence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ordering prevents an optimizer from selecting a request that creates customer-contact or tipping-off risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Human outcomes must remain authoritative
&lt;/h2&gt;

&lt;p&gt;Agent-confirmed and agent-cleared cases are useful memory.&lt;/p&gt;

&lt;p&gt;But they are not automatically analyst ground truth.&lt;/p&gt;

&lt;p&gt;Keeping those outcomes separate prevents feedback loops where the model starts training on its own previous decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deterministic fallbacks are essential
&lt;/h2&gt;

&lt;p&gt;LLM calls can fail because of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Credentials
Rate limits
Provider changes
Malformed JSON
Network failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The investigation should still continue.&lt;/p&gt;

&lt;p&gt;That's why GraphSentinel has deterministic planning and explanation fallbacks.&lt;/p&gt;

&lt;p&gt;The LLM is an optional reasoning and language layer, not a single point of failure.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  1. Live TigerGraph validation
&lt;/h3&gt;

&lt;p&gt;Execute every GSQL query against a real TigerGraph Savanna or Community Edition deployment and add stronger deployment/version checks.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Real integrations
&lt;/h3&gt;

&lt;p&gt;Replace the mock action gateway with authenticated banking, notification, evidence-provider, and e-filing integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Production identity and authorization
&lt;/h3&gt;

&lt;p&gt;Replace the development &lt;code&gt;X-Role&lt;/code&gt; header with an identity-provider integration and enforce role claims at a trusted proxy boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Model calibration
&lt;/h3&gt;

&lt;p&gt;Train and calibrate thresholds on real closed investigations, monitor drift, and add confidence intervals and champion/challenger evaluation.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Richer evidence providers
&lt;/h3&gt;

&lt;p&gt;Connect real authentication, customer-validation, and analyst-review systems with asynchronous response handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Evaluation against the actual case pack
&lt;/h3&gt;

&lt;p&gt;Run the complete &lt;code&gt;HHG-001&lt;/code&gt; through &lt;code&gt;HHG-020&lt;/code&gt; case pack and compare decisions with independent review.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Frontend modernization
&lt;/h3&gt;

&lt;p&gt;The current console is a lightweight static analyst UI. A production version would use richer graph interactions, accessibility improvements, and durable event streaming.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Operational observability
&lt;/h3&gt;

&lt;p&gt;Add structured traces for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Graph latency
Model versions
LLM calls
Policy decisions
Approval turnaround
Action outcomes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These would be essential for production monitoring.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final architecture
&lt;/h1&gt;

&lt;p&gt;The entire system can ultimately be reduced to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  ┌───────────────────┐
                  │      TRIGGER      │
                  │                   │
                  │ Risk alert        │
                  │ Customer report   │
                  │ Analyst request   │
                  └─────────┬─────────┘
                            │
                            ▼
                  ┌───────────────────┐
                  │    LANGGRAPH      │
                  │      AGENT        │
                  └─────────┬─────────┘
                            │
              ┌─────────────┼─────────────┐
              │             │             │
              ▼             ▼             ▼
        ┌──────────┐  ┌──────────┐  ┌───────────┐
        │TigerGraph│  │ GraphRAG │  │   Case    │
        │          │  │          │  │  Memory   │
        │ Evidence │  │ Policies │  │  Similar  │
        │ Relations│  │ Patterns │  │  Cases    │
        └────┬─────┘  └────┬─────┘  └─────┬─────┘
             │             │              │
             └─────────────┼──────────────┘
                           ▼
                  ┌───────────────────┐
                  │    RISK MODEL     │
                  │                   │
                  │ Probability       │
                  │ Fraud class       │
                  └─────────┬─────────┘
                            │
                            ▼
                  ┌───────────────────┐
                  │   POLICY ENGINE   │
                  │                   │
                  │ Permissions       │
                  │ Approval routes   │
                  │ SAR rules         │
                  └─────────┬─────────┘
                            │
                            ▼
                  ┌───────────────────┐
                  │ NBA BEFORE        │
                  │ EVIDENCE          │
                  └─────────┬─────────┘
                            │
                      uncertain?
                       /          \
                     no            yes
                     │              │
                     │              ▼
                     │       ┌──────────────┐
                     │       │   EVIDENCE   │
                     │       │   SELECTION  │
                     │       └──────┬───────┘
                     │              │
                     │              ▼
                     │       ┌──────────────┐
                     │       │ BELIEF UPDATE│
                     │       └──────┬───────┘
                     │              │
                     │              ▼
                     │       ┌──────────────┐
                     │       │ NBA AFTER    │
                     │       │ EVIDENCE     │
                     │       └──────┬───────┘
                     │              │
                     └──────┬───────┘
                            ▼
                  ┌───────────────────┐
                  │ ACTION / APPROVAL │
                  │ SAR / EXPLANATION│
                  └─────────┬─────────┘
                            │
                            ▼
                  ┌───────────────────┐
                  │   GRAPH MEMORY    │
                  │                   │
                  │ Case              │
                  │ Findings          │
                  │ Actions           │
                  │ Evidence          │
                  └───────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final system is not an unconstrained chatbot making banking decisions.&lt;/p&gt;

&lt;p&gt;It is a &lt;strong&gt;traceable investigation workflow&lt;/strong&gt; in which:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Graph
  → provides evidence

Risk Model
  → estimates risk

Policy
  → controls permissions

Agent
  → chooses useful investigation work

Human
  → provides required approvals

Action Gateway
  → executes approved actions

Graph Memory
  → preserves the investigation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That separation is the central design principle behind GraphSentinel.&lt;/p&gt;

&lt;p&gt;The goal is not simply to predict fraud.&lt;/p&gt;

&lt;p&gt;The goal is to build an investigation system where &lt;strong&gt;evidence, reasoning, policy, actions, approvals, and outcomes remain connected and auditable&lt;/strong&gt;.&lt;/p&gt;

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