Back to Archive

AI-Assisted Fraud Review

TLP:AMBER August 2026
Anti Fraud LLM Kubernetes

An analyst reviewing a blocked signup answers one question — is this fraud? — and the evidence lives in about a dozen places. Identity, billing, domain reputation, the rules that fired, related accounts, payment attempts. Every review meant opening five systems and rebuilding the same picture by hand.

So: a scheduled job every fifteen minutes. Read the queue, decide who needs a look, pull every source into one report, ask a model for a verdict, write down that the run happened. The verdict is advisory — nothing here blocks or approves an account. A human still decides.

$1.43 a user, and none of the obvious fixes worked

The activity data is the expensive part. One review scanned 234 GiB of warehouse data and cost $1.43 — against an analyst cost of roughly $0.50 a case, which makes the assistant worse than the problem.

The obvious fixes don't work. Asking for less data, or asking for one specific user, both bill exactly the same — the way these tables are laid out, a query for one person reads everything anyway.

Once you know the scan is fixed, the answer inverts. If one user costs the same as twenty-five, stop querying per user. One batched query per cycle is byte-identical at every batch size — so the cost just divides.

$1.43 at batch 1, $0.286 at 5, $0.057 at 25. Measured by dry run, not estimated — on-demand BigQuery bills bytes processed, so a dry run is the invoice. A floor of five users means a thin cycle issues no job at all rather than paying $1.43 to review one person.

The neat part is what it doesn't touch. The query runs once per cycle before the fan-out, so concurrency moves the bill by exactly zero, and the whole fix needed no upstream team and no re-clustered copy of a 270 GiB table.

The metric that lied

A subtler one. Each run writes a log at the end, and that log is both the only record the run happened and the denominator of the coverage metric. Runs have a hard kill deadline. A killed run writes no log, so its cases never enter the total, so coverage goes up. The worse it performed, the healthier the dashboard looked.

Now a stopwatch checks whether there's time to finish one more account before starting it, and marks the rest deferred if not. Deferring is recorded honestly and self-corrects — the next run picks them up fifteen minutes later. It stops new work only; reports are write-once, and killing one halfway strands an orphaned file that nothing explains, since a retry gets a new ID and can't overwrite it.

One more, same instinct

Unknown is not false. A source that fails returns "unknown", and a source that's switched off records "not collected" — never empty. Collapse those and you've told the model a real person has no activity, which is a confidently wrong claim, or turned a broken integration into evidence of innocence.

All three are the same lesson: the failures worth engineering against are the ones that look like success.