Task #4 Β· TigerGraph problem statement Β· Hacker House Goa 2026
A risk score pings. Is it fraud, a holiday, or a new phone?
We built SentinelGraph, an AI agent that works the alert like a sharp analyst: it digs through the graph, admits what it doesn't know, asks for the one piece of evidence that would settle it, and recommends the next move with the right person signing off.
590,742 transactions in the graph Β· 5,565 closed cases as memory Β· 16 GSQL queries via MCP Β· 0.914 memory-model AUC (bank score: 0.866) Β· 28 cards in the biggest ring we found
π Code: https://github.com/nirmaljosephukken/HH_GOA_Task_4
Fraud teams are never short of alerts. They're short of time to turn each one into a call they can defend. For every ping an analyst pulls the card history, checks the device, hunts for other victims, rereads the policy, decides whether to phone the customer, and writes it all up. Usually after the money has moved.
SentinelGraph runs that loop end to end:
We ran it on the 20 benchmark alerts: 10 legitimate, 9 fraud, 1 uncertain, 6 SARs. Re-running all 20 gives the same decisions every time.
The dataset is the IEEE-CIS card data with the fraud label taken away. In its place: a bank risk score, 5,565 closed investigations (July to October), a fraud policy, five documented patterns, and 20 alerts from November and December. Three findings shaped everything:
Client vertex: a latent cardholder built from card + billing region + account-open day. In closed cases, 85% of a fraud client's transactions are fraud.card6 values reproduces every card ID in the closed cases and the case pack.
Every layer has one job. Graph queries find facts. A deterministic evidence layer weighs them. A policy engine picks the actions. The LLM probes open questions and writes the story, and it never sets a probability or an action.
Trigger (score / customer / analyst)
β
βΌ
Case orchestrator ββ state machine + audit trail βββββββββββββββ
β MCP: tigergraph__run_installed_query GraphRAG: vectorSearch
βΌ β
TigerGraph Savanna (FraudGraph) β
β β
βΌ β
Evidence layer ββ detectors β likelihood ratios by family β
βΌ β
Uncertainty engine ββ log-odds fusion, confidence, open questions
βΌ β
Policy engine ββ R1βR10, case vs report, stop rule, routes, guardrails
β β β
βΌ βΌ β
ask for evidence next best action (auto runs, L1/L2 wait) β
(verify / step-up) β β
ββββΊ re-assess βΌ β
FraudCase + CaseEvents written back ββββββββ
LLM (Gemini): bounded extra tool calls + narrative/SAR writer, output validated
One alert, six moves:
One graph, three layers:
CustomerβCardβClientβTxn, with Txn linked to DeviceProfile, EmailDomain (purchaser and recipient) and BillingRegion, plus a NEXT_TXN chain per card.ClosedCase vertices linked to their transactions, cards, connected cards and pattern. Every FraudCase the agent opens is stored too, with edges to the transactions, cards, devices and closed cases it used, and a CaseEvent chain as its audit trail.PolicyChunk vertices, one per policy rule, plus fraud typologies, a FinCEN/FATF/FFIEC digest and lessons mined from closed cases.
ClosedCase, FraudCase and PolicyChunk carry TigerGraph's native 256-dimension vector attribute, so memory lookups are a vectorSearch() inside an installed query, right next to the graph hops.
Sixteen installed GSQL queries, each exposed to the agent as a tool. Highlights:
peer_txns: two hops, flagged transaction β its device or email β purchases on card_profile: a behavioural baseline in one pass with MapAccum s over products, regions, devices and emails.ring_components: tg_wcc) over the clientβdevice co-usage graph. It isolates the 28-card ring as one component.device_ring_scan and amount_band_scan: sweep the whole exam period, so the agent raises alerts nobody reported (monitor.py). prior_cases and device_cases: memory by adjacency. Which closed cases touched this device?
TigerGraph MCP. The agent launches the official tigergraph-mcp server over stdio, discovers its tools, and calls tigergraph__run_installed_query for every read, vector searches included. Argument names come from the tool schema at runtime. If MCP is unavailable it falls back to RESTPP, and every call in the trace records which transport served it.
. Streamed parallel RESTPP upserts loaded 590k transactions and about 3M edges into Savanna in under two minutes.
Explicit uncertainty. Every detector returns a claim, the query it came from, the entity IDs it rests on, a likelihood ratio and a family. The case records the prior, the posterior, each family's contribution, which families argue each way, and the open questions ("device ownership not established", "evidence conflicts").
A calibrated prior learned from case memory. The closed cases cover about 3.4% of July to October transactions, close to the true fraud rate, so we trained a gradient-boosted model on them. Trained on July to September and tested on October: AUC 0.914 vs 0.866 for the bank's score, with nearly double the average precision.
Knowing when to stop. Policy section 6, as code: stop at β₯ 0.85 or β€ 0.15 with two independent families agreeing. Otherwise the agent picks the evidence request that best resolves the case: step-up authentication for an online score alert, customer verification for a dispute, R7 verification for a disputed charge that matches the customer's own recurring pattern. Replies aren't provided in this round, so a simulator assumes the reply the evidence supports, records the assumption and its basis, and the agent re-assesses. The next best action is recorded before and after the evidence, with what changed.
Policy as code, with permissions. R1βR10, the case-vs-report rule, exposure-based routing, action ordering, and a guard that refuses breaches (blocking on a single weak signal, BLOCK_ALL_CARDS without two compromised cards, blocking a recurring dispute). Only auto actions execute. L1 and L2 actions wait in the console's approval queue, and every approval is written to the case's audit trail in TigerGraph.
Memory that changes the answer.
The LLM where language is the product. Gemini gets a bounded budget of extra tool calls to probe open questions, then writes the summary, pattern description and SAR narrative. Any text that cites an ID not present in the evidence is rejected. Its notes carry zero weight in the probability, which is why decisions are reproducible.
| Case | Outcome | P(fraud) | Pattern | Exposure | Linked cards | SAR | Evidence asked | Final next best action |
|---|---|---|---|---|---|---|---|---|
| HHG-001 | legitimate | 0.01 | none | $0.00 | 0 | no | none | ALLOW_TRANSACTION βGENERATE_REPORT βCLOSE_NO_FRAUD |
| HHG-002 | legitimate | 0.01 | none | $0.00 | 0 | no | step up auth | ALLOW_TRANSACTION βCREATE_CASE βCLOSE_NO_FRAUD |
| HHG-003 | uncertain | 0.63 | out of region use | $49.00 | 0 | no | customer validation | BLOCK_CARD βCREATE_CASE βESCALATE_TO_ANALYST |
| HHG-004 | fraud | 0.73 | card not present new device | $128.33 | 0 | no | customer validation | BLOCK_CARD βCREATE_CASE |
| HHG-005 | legitimate | 0.01 | none | $0.00 | 0 | no | step up auth | ALLOW_TRANSACTION βCREATE_CASE βCLOSE_NO_FRAUD |
| HHG-006 | fraud | 0.99 | undocumented | $1,906.07 | 11 | yes | none | BLOCK_CARD βCREATE_CASE βFILE_REPORT βMONITOR_CONNECTED_CARDS βESCALATE_TO_ANALYST |
| HHG-007 | legitimate | 0.01 | none | $0.00 | 0 | no | none | ALLOW_TRANSACTION βGENERATE_REPORT βCLOSE_NO_FRAUD |
| HHG-008 | fraud | 0.99 | card not present fraud | $166.97 | 1 | yes | none | BLOCK_CARD βCREATE_CASE βFILE_REPORT βMONITOR_CONNECTED_CARDS |
| HHG-009 | fraud | 0.98 | card not present fraud | $30.02 | 0 | no | none | BLOCK_CARD βCREATE_CASE |
| HHG-010 | legitimate | 0.01 | none | $0.00 | 0 | no | step up auth | ALLOW_TRANSACTION βCREATE_CASE βCLOSE_NO_FRAUD |
| HHG-011 | fraud | 0.95 | card not present new device | $131.30 | 5 | yes | none | BLOCK_CARD βCREATE_CASE βFILE_REPORT βMONITOR_CONNECTED_CARDS |
| HHG-012 | legitimate | 0.01 | none | $0.00 | 0 | no | none | ALLOW_TRANSACTION βGENERATE_REPORT βCLOSE_NO_FRAUD |
| HHG-013 | legitimate | 0.01 | none | $0.00 | 0 | no | step up auth | ALLOW_TRANSACTION βCREATE_CASE βCLOSE_NO_FRAUD |
| HHG-014 | fraud | 0.89 | undocumented | $439.61 | 27 | yes | none | BLOCK_CARD βCREATE_CASE βFILE_REPORT βMONITOR_CONNECTED_CARDS βESCALATE_TO_ANALYST |
| HHG-015 | legitimate | 0.01 | none | $0.00 | 0 | no | step up auth | ALLOW_TRANSACTION βCREATE_CASE βCLOSE_NO_FRAUD |
| HHG-016 | fraud | 0.99 | card not present new device | $59.67 | 3 | yes | none | BLOCK_CARD βCREATE_CASE βFILE_REPORT βMONITOR_CONNECTED_CARDS |
| HHG-017 | legitimate | 0.03 | none | $0.00 | 0 | no | none | ALLOW_TRANSACTION βGENERATE_REPORT βCLOSE_NO_FRAUD |
| HHG-018 | legitimate | 0.02 | none | $0.00 | 0 | no | customer validation | CREATE_CASE βWARN_CUSTOMER βCLOSE_NO_FRAUD |
| HHG-019 | fraud | 0.99 | card not present new device | $99.92 | 4 | yes | none | BLOCK_CARD βCREATE_CASE βFILE_REPORT βMONITOR_CONNECTED_CARDS |
| HHG-020 | legitimate | 0.01 | none | $0.00 | 0 | no | step up auth | ALLOW_TRANSACTION βCREATE_CASE βCLOSE_NO_FRAUD |
Stack: TigerGraph Savanna Β· GSQL Β· TigerGraph MCP Β· native vector search Β· Python Β· Gemini Β· Streamlit
Built for Hacker House Goa 2026 with @TigerGraphDB. Code, the 20 answer files and full investigation traces: https://github.com/nirmaljosephukken/HH_GOA_Task_4