<?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: Ashutosh Kumar Rai</title>
    <description>The latest articles on DEV Community by Ashutosh Kumar Rai (@ashutosh_kumarrai_6335bf).</description>
    <link>https://dev.to/ashutosh_kumarrai_6335bf</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%2F4139873%2F67d91e61-8780-4860-a181-933f4a2c37a6.png</url>
      <title>DEV Community: Ashutosh Kumar Rai</title>
      <link>https://dev.to/ashutosh_kumarrai_6335bf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ashutosh_kumarrai_6335bf"/>
    <language>en</language>
    <item>
      <title>Our fraud classifier scored 0.963 AUC. We threw it away.</title>
      <dc:creator>Ashutosh Kumar Rai</dc:creator>
      <pubDate>Wed, 23 Sep 2026 17:19:09 +0000</pubDate>
      <link>https://dev.to/ashutosh_kumarrai_6335bf/our-fraud-classifier-scored-0963-auc-we-threw-it-away-1cl5</link>
      <guid>https://dev.to/ashutosh_kumarrai_6335bf/our-fraud-classifier-scored-0963-auc-we-threw-it-away-1cl5</guid>
      <description>&lt;p&gt;Code: &lt;a href="https://github.com/AKRai-2005/fraudgraph" rel="noopener noreferrer"&gt;https://github.com/AKRai-2005/fraudgraph&lt;/a&gt; ·&lt;br&gt;
Console: &lt;a href="https://akrai-2005.github.io/fraudgraph/" rel="noopener noreferrer"&gt;https://akrai-2005.github.io/fraudgraph/&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;The challenge gives you 590,742 card transactions, six months, no fraud&lt;br&gt;
labels, 5,565 historical investigations that &lt;em&gt;do&lt;/em&gt; have outcomes, and twenty&lt;br&gt;
alerts to judge. Each alert carries a risk score from the bank's model.&lt;/p&gt;

&lt;p&gt;The obvious first move is to fit a classifier on those 5,565 closed cases. We&lt;br&gt;
did, on day two. &lt;strong&gt;ROC AUC 0.963.&lt;/strong&gt; Then we looked at the coefficients:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;feature&lt;/th&gt;
&lt;th&gt;fitted weight&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bank_risk_score&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−5.52&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;device_marked_new&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−2.65&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;consistent_with_history&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+1.40&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every sign is backwards. A model carrying those numbers clears a device ring&lt;br&gt;
and blocks someone's holiday. The 0.963 is real, reproducible, and completely&lt;br&gt;
useless — it is measuring &lt;em&gt;which alerts a bank chose to investigate&lt;/em&gt;, not&lt;br&gt;
fraud.&lt;/p&gt;

&lt;p&gt;Here is why, and it is the single most useful thing we found in this dataset.&lt;br&gt;
The closed cases are not a sample of alerts. They are a sample of&lt;br&gt;
investigations somebody opened. All 900 cleared cases began as high-scoring&lt;br&gt;
model alerts that turned out to be travel (716), a new phone (158), or a large&lt;br&gt;
but intended purchase (26). So the "legitimate" class is &lt;em&gt;deliberately&lt;br&gt;
enriched&lt;/em&gt; with exactly the anomaly signals that indicate fraud:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;population&lt;/th&gt;
&lt;th&gt;fires at least one strong detector&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;confirmed fraud&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11.8%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cleared alerts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31.1%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ordinary unalerted transactions&lt;/td&gt;
&lt;td&gt;7.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cleared alerts are nearly three times more anomalous than confirmed frauds. If&lt;br&gt;
you train on this and ship it, you have built a machine that blocks careful&lt;br&gt;
customers.&lt;/p&gt;

&lt;p&gt;That finding set the design: &lt;strong&gt;graph evidence cannot be the classifier. It is&lt;br&gt;
the thing that is decisive when it fires.&lt;/strong&gt; Everything below follows from it.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the agent actually is
&lt;/h2&gt;

&lt;p&gt;An investigation is a state machine, not a prompt. Twelve steps, each one&lt;br&gt;
appending to a timeline and a query ledger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alert → baseline retrieval → targeted retrieval → 9 pattern detectors
                                   │                       │
                        (the LLM may propose queries)  risk + uncertainty
                                                           │
   case memory ← write case ← policy engine ← evidence request ←┘
   (TigerGraph)              (R1–R10, routes)   (simulated, and labelled)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The probability is not fitted. It is stated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;log-odds(fraud) = trigger prior + Σ log(likelihood ratio) over detectors that fired
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No global intercept, no term for the absence of a signal, every term clipped&lt;br&gt;
at ±2.6 so no single detector can carry a case alone, and each likelihood&lt;br&gt;
ratio measured on the history with its sample size recorded beside it. The&lt;br&gt;
bank's score is reported next to every assessment and never folded in — a test&lt;br&gt;
asserts that identical evidence at score 0.05 and at 0.95 produces an&lt;br&gt;
identical probability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where the LLM is, and is not.&lt;/strong&gt; Gemini (&lt;code&gt;gemini-flash-lite-latest&lt;/code&gt;, free&lt;br&gt;
tier) may propose extra retrieval — validated against the query catalogue&lt;br&gt;
before anything executes — and it writes the case summary and the SAR&lt;br&gt;
narrative. It does not decide the verdict, the probability, the pattern, the&lt;br&gt;
actions, the routes, or whether a report is due. If a rewrite introduces an&lt;br&gt;
id, an amount or a date that is not already in the retrieved evidence, it is&lt;br&gt;
thrown away and the deterministic template stands. With no API key the system&lt;br&gt;
runs identically and reports &lt;code&gt;tokens: 0&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  One case, end to end
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;HHG-014.&lt;/strong&gt; An analyst flagged a $74.96 online purchase by hand. The bank's&lt;br&gt;
model scored it &lt;strong&gt;0.05&lt;/strong&gt; — near zero. Nothing about the transaction is&lt;br&gt;
remarkable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkuxgnomuvrwfnvcvalai.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%2Fkuxgnomuvrwfnvcvalai.png" alt="The case as the console shows it: verdict, the agent's probability beside the bank's score, and what remains uncertain." width="800" height="463"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The case as the console shows it: verdict, the agent's probability beside the bank's score, and what remains uncertain.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two hops in the graph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction ──FROM_DEVICE──▶ DeviceProfile ──▶ Transaction ──MADE_BY──▶ PaymentCard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Straight from the published answer file:&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;"claim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Shared device ring: device profile shared by 28 unrelated cards within 30 days, marked New for 100% of the transactions it ever appears on, and consistently behind IP_PROXY:ANONYMOUS."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"query:device_neighbors(device_profile=SM-G935F Build/NRD90M | Android 7.0 | chrome 62.0 for android | 1920x1080)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entity_ids"&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="s2"&gt;"3460634"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3478561"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3489320"&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;Every claim in every answer file carries the query that produced it and the&lt;br&gt;
ids it rests on. &lt;code&gt;query:device_neighbors(...)&lt;/code&gt; is a query that actually ran; a&lt;br&gt;
test asserts that every &lt;code&gt;ref&lt;/code&gt; names a real query in the catalogue.&lt;/p&gt;

&lt;p&gt;Exactly &lt;strong&gt;one device profile out of 9,706&lt;/strong&gt; in this dataset meets that test —&lt;br&gt;
shared across many unrelated cards, marked &lt;em&gt;New&lt;/em&gt; every time, always behind an&lt;br&gt;
anonymising proxy. Case memory then pulled four closed investigations from the&lt;br&gt;
same device, all labelled by the bank's own analysts as matching no documented&lt;br&gt;
typology.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftrn52ou61gsvyn6amzpe.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%2Ftrn52ou61gsvyn6amzpe.png" alt="Two hops from a $74.96 payment: the device profile (centre) and the cards it touched. The purple nodes are prior closed cases found through the same device." width="800" height="448"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Two hops from a $74.96 payment: the device profile (centre) and the cards it touched. The purple nodes are prior closed cases found through the same device.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The agent stopped with a reason, not a threshold:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fraud probability 0.98 is at or above 0.85 with 3 independent pieces of evidence (policy 6).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and produced actions with routes attached: &lt;code&gt;BLOCK_CARD&lt;/code&gt; (&lt;strong&gt;L1&lt;/strong&gt;),&lt;br&gt;
&lt;code&gt;FILE_REPORT&lt;/code&gt; (&lt;strong&gt;L2&lt;/strong&gt;), and four the agent may carry out itself. Neither L1 nor&lt;br&gt;
L2 has happened. They wait for a named human, and when a human approves one,&lt;br&gt;
what gets recorded is a &lt;em&gt;simulated&lt;/em&gt; execution that states what a real&lt;br&gt;
integration would have done.&lt;/p&gt;

&lt;p&gt;Across the twenty alerts, graph evidence moved the verdict away from the&lt;br&gt;
bank's score &lt;strong&gt;eighteen times — nine escalated, nine cleared.&lt;/strong&gt; That is the&lt;br&gt;
product in one sentence: it argues with the score in both directions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzkyth63jjnh23si2enx5.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%2Fzkyth63jjnh23si2enx5.png" alt="The console opens on this: the bank's score along the bottom, the agent's assessment up the side. Points off the diagonal are disagreements." width="800" height="281"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The console opens on this: the bank's score along the bottom, the agent's assessment up the side. Points off the diagonal are disagreements.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The challenge documents five fraud patterns. Nine closed cases match none of&lt;br&gt;
them&lt;/strong&gt;, labelled only &lt;code&gt;undocumented&lt;/code&gt;. Reading their analyst notes, they are&lt;br&gt;
two distinct typologies, and we wrote detectors that key on behaviour rather&lt;br&gt;
than on case ids. One is the device ring above. The other is &lt;strong&gt;sub-threshold&lt;br&gt;
structuring&lt;/strong&gt;: several online purchases inside one short window, each priced&lt;br&gt;
just under the $500 authorisation threshold so no single charge triggers&lt;br&gt;
review, totalling far more than it. In the exam that is HHG-006 — four&lt;br&gt;
purchases, $1,906.07 — and the agent describes the pattern in its own words&lt;br&gt;
because there is no label to reach for.&lt;/p&gt;
&lt;h2&gt;
  
  
  The graph
&lt;/h2&gt;

&lt;p&gt;11 vertex types, 18 edge types, on a TigerGraph Savanna workspace (4.2.5):&lt;br&gt;
590,742 &lt;code&gt;Transaction&lt;/code&gt;, 14,318 &lt;code&gt;PaymentCard&lt;/code&gt;, 13,553 &lt;code&gt;Customer&lt;/code&gt;, 9,706&lt;br&gt;
&lt;code&gt;DeviceProfile&lt;/code&gt;, 5,565 &lt;code&gt;ClosedCase&lt;/code&gt;. Historical cases and the agent's own&lt;br&gt;
cases are separate vertex types on purpose — ground truth and conclusions must&lt;br&gt;
never be confusable.&lt;/p&gt;

&lt;p&gt;One catalogue of &lt;strong&gt;17 named GSQL queries&lt;/strong&gt; is the agent's entire tool surface,&lt;br&gt;
and it is implemented three times: installed GSQL over pyTigerGraph, the same&lt;br&gt;
queries through the official &lt;code&gt;tigergraph-mcp&lt;/code&gt; server, and a pandas mirror for&lt;br&gt;
offline testing. All three answer the same 20 cases.&lt;/p&gt;

&lt;p&gt;That redundancy is not belt-and-braces. It found bugs nothing else would have:&lt;br&gt;
&lt;strong&gt;the three backends agreed on every verdict while disagreeing on the evidence&lt;br&gt;
underneath&lt;/strong&gt; — different quantile conventions, different tie-breaking between&lt;br&gt;
two equally-scored prior cases, an unsorted subset of a device ring. If we had&lt;br&gt;
compared conclusions instead of evidence, all three would still be there.&lt;/p&gt;
&lt;h2&gt;
  
  
  Does it actually work?
&lt;/h2&gt;

&lt;p&gt;We publish &lt;strong&gt;no accuracy figure for the twenty exam cases.&lt;/strong&gt; The answer key is&lt;br&gt;
withheld; any number would be a guess, and none appears anywhere in the repo.&lt;/p&gt;

&lt;p&gt;What we could measure is the 5,565 closed investigations that carry real&lt;br&gt;
outcomes. The agent is replayed over them as if each were a live alert. Three&lt;br&gt;
things had to be controlled or the result would have been a lie:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Time.&lt;/strong&gt; Case memory is filtered by date, every forward-looking
transaction window is clamped to the moment the case opened, and the replay
re-reads the bounds &lt;em&gt;actually sent&lt;/em&gt; from the query ledger rather than
trusting the clamp. Leakage is a failure, not a warning: the runner exits
non-zero. Zero violations across every run reported here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The trigger prior.&lt;/strong&gt; In this history every cleared case began as a model
score and every investigated dispute was fraud, so replaying under real
triggers mostly scores the prior. The headline run gives every case a
neutral &lt;code&gt;analyst_request&lt;/code&gt; prior of 0.50 — nothing on the scale, so anything
that separates the classes came from the graph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The sample.&lt;/strong&gt; Every cleared case is a high-scoring alert that turned out
fine: the hardest negatives in the data. Specificity measured here is a
lower bound, not a typical-traffic number.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Results, neutral prior, graph evidence alone, run over the entire population&lt;br&gt;
of both relational typologies against 300 cleared alerts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;true typology&lt;/th&gt;
&lt;th&gt;caught&lt;/th&gt;
&lt;th&gt;recall&lt;/th&gt;
&lt;th&gt;mean p&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;undocumented&lt;/strong&gt; (device ring, structuring)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9 / 9&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;card testing&lt;/td&gt;
&lt;td&gt;5 / 16&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;td&gt;0.71&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;false fraud calls on 300 cleared alerts&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;97.7% specificity&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ROC AUC &lt;strong&gt;0.898&lt;/strong&gt;. At the exam's own operating point: &lt;strong&gt;zero&lt;/strong&gt; false fraud&lt;br&gt;
verdicts on those 300.&lt;/p&gt;

&lt;p&gt;And the part that matters more than any of it — on a random sample of 300&lt;br&gt;
frauds the same neutral run reaches &lt;strong&gt;2.7% recall&lt;/strong&gt;. Most fraud in this&lt;br&gt;
history is a single unremarkable transaction that a cardholder disputed. There&lt;br&gt;
is no relational structure to find, and the agent says &lt;em&gt;uncertain&lt;/em&gt; rather than&lt;br&gt;
pretend. Its edge is narrow, specific, and real: &lt;strong&gt;relational fraud, found by&lt;br&gt;
traversal.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Four things that were wrong, and how we know
&lt;/h2&gt;

&lt;p&gt;An honest answer to "does it work" is mostly a list of things that were&lt;br&gt;
broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The backtest leaked.&lt;/strong&gt; The first version time-boxed case memory but not&lt;br&gt;
transactions — a replayed alert could count cards compromised &lt;em&gt;after&lt;/em&gt; its own&lt;br&gt;
investigation opened. Clamping every window moved the general-sample AUC from&lt;br&gt;
&lt;strong&gt;0.718 to 0.691&lt;/strong&gt;. The 0.718 had already been written down, so it is marked&lt;br&gt;
withdrawn in &lt;code&gt;docs/BACKTEST.md&lt;/code&gt;. The headline (9 of 9) did not move.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A detector caught nothing.&lt;/strong&gt; &lt;code&gt;card_testing&lt;/code&gt; scored &lt;strong&gt;0 of 16&lt;/strong&gt;. Its&lt;br&gt;
definition took the README literally — three small authorisations within an&lt;br&gt;
hour, then a larger purchase — and the real runs break all three assumptions:&lt;br&gt;
probes and purchases interleave, probes are sparse, runs last up to eleven&lt;br&gt;
days. Rebuilt around probes under $2, with the threshold fixed by a rule&lt;br&gt;
stated &lt;em&gt;before&lt;/em&gt; recall was looked at (the variant closest to the README whose&lt;br&gt;
firing rate on cleared alerts is ≤1%, selected on the negative class only):&lt;br&gt;
0/16 → 5/16, no added false positives. On the 13 cases nobody had inspected by&lt;br&gt;
hand it catches 3 — &lt;strong&gt;23% out of sample&lt;/strong&gt;, and that is the number we quote.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Its weight still measures negative.&lt;/strong&gt; The rebuilt detector fires on 31 of&lt;br&gt;
4,665 confirmed frauds and 39 of 3,400 legitimate ones — a likelihood ratio of&lt;br&gt;
0.58, which taken at face value argues for innocence. Traced on the agent's&lt;br&gt;
own code path: all 39 legitimate firings fall on 5 cards, all 5 have a&lt;br&gt;
confirmed-fraud case, and the detector fires on no card without fraud history.&lt;br&gt;
The negative class is contaminated exactly where this detector looks, so the&lt;br&gt;
measured ratio is not believed and the weight stays at its documented floor —&lt;br&gt;
with the trace recorded as its basis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our latency was understated.&lt;/strong&gt; The console said an investigation took&lt;br&gt;
0.19 seconds, because the timer stopped before the LLM wrote the narrative. On&lt;br&gt;
the free tier that narration has run from 7 to 58 seconds for the same case.&lt;br&gt;
It is now measured over the whole run, and the live feed announces the wait&lt;br&gt;
and reports it afterwards.&lt;/p&gt;
&lt;h2&gt;
  
  
  Verify any of this in about two minutes
&lt;/h2&gt;

&lt;p&gt;Nothing above needs to be taken on trust. The repository runs with no&lt;br&gt;
credentials at all — the local mirror serves the same query catalogue:&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;-r&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; fraudgraph.benchmark.validate    &lt;span class="c"&gt;# checks all 20 against the answer format&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt;                        &lt;span class="c"&gt;# 310 tests, 45 of them in a browser&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; fraudgraph.analysis.backtest &lt;span class="nt"&gt;--all-modes&lt;/span&gt;   &lt;span class="c"&gt;# the numbers above&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 20 answer files are in &lt;code&gt;cases/&lt;/code&gt;. The backtest artefact, including leakage&lt;br&gt;
counters, is &lt;code&gt;build/backtest.json&lt;/code&gt;. The model card with every weight's sample&lt;br&gt;
size is &lt;code&gt;docs/RISK_MODEL.md&lt;/code&gt;. What the system cannot do is&lt;br&gt;
&lt;code&gt;docs/LIMITATIONS.md&lt;/code&gt;, and it is the document we would read first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we would tell the next team
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fit the obvious model early&lt;/strong&gt; — not to ship it, but because its failure
tells you what your data actually is. Ours took a day and redirected the
whole project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare evidence, not conclusions.&lt;/strong&gt; Three backends agreeing on verdicts
hid three bugs in what the evidence said.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat leakage as a build failure.&lt;/strong&gt; If a replay can see the future, every
number after it is decoration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write down what you refuse to claim.&lt;/strong&gt; No accuracy on the twenty, a
withdrawn AUC, 23% out-of-sample on card testing, and the plain fact that
most fraud here is invisible to a graph.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A risk score is a reason to look, never a verdict. It turns out the same&lt;br&gt;
applies to an AUC.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built on TigerGraph Savanna and Gemini's free tier — ₹0 of infrastructure.&lt;br&gt;
The console, the backtest and all 20 answers:&lt;br&gt;
&lt;a href="https://github.com/AKRai-2005/fraudgraph" rel="noopener noreferrer"&gt;https://github.com/AKRai-2005/fraudgraph&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tigergraph</category>
      <category>ai</category>
      <category>python</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
