-
-
Live run reasoned by Snowflake Cortex: red edges are cited evidence, amber ones were reviewed but not proven. Gemini writes the conclusion.
-
Verdict sheet: $291,606 at risk, every peso backed by 7 cited payments. Gemini answers the auditor using only that evidence.
-
Evidence trail: the 11 edges the agent walked. The 7 in red are cited as proof; the rest were reviewed but prove nothing.
-
Accusations sheet: a 69-B blacklisted supplier, $184,705, four payments as proof. Asked what weighs most, Gemini cites exactly those.
-
Scenario injector: a judge buries one of four fraud schemes in fresh synthetic books, and the agent has to find it without being told where.
Inspiration
The Infosys challenge hit us for a very concrete reason: in Mexico, invoice fraud doesn't show up as a row flagged in red. It hides in relationships: a supplier billing for services never delivered, a kickback routed back through a paper company, money going in circles until the trail goes cold. Mexico's tax authority (SAT) publishes the Article 69-B blacklist of fake-invoice companies, but by the time a company lands on it, the deductions have already been claimed.
And there's a second harm almost nobody talks about: the honest supplier who just "looks odd" and gets accused. So we set out to build something harder than an anomaly detector: an auditor that investigates and proves, and that cannot accuse anyone without evidence. We called it CORPIDE.
What it does
- Generates a company's books: suppliers, CFDI-style invoices, bank accounts and payments, blended with over 14,000 real tax IDs (RFCs) from SAT's 69-B list.
- A judge hides a fraud with the scenario injector: fake billing, kickbacks through a shell company, round-tripping, or inflated sales.
- Snowflake filters the data: SQL detectors, plus Cortex reading each invoice's description. Only the suspects reach the graph.
- An agent investigates live, reasoning with Snowflake Cortex: it runs detectors, traces payments, opens invoices and drops leads that go nowhere.
- A guardrail deletes any accusation that isn't backed by real evidence.
- Gemini writes the case file in plain English for a non-technical reader, and answers the auditor's questions using only the evidence.
How we built it
- Data: a Python generator with four fraud patterns modeled on IBM AMLSim's typologies, on top of SAT's real blacklist.
- Graph: a NetworkX
MultiDiGraph. Companies, accounts and invoices are nodes; payments, invoices and shared attributes are edges. Five deterministic detectors: blacklist match, invoice-vs-payment mismatch, cycles, shared-attribute clusters and betweenness centrality. - Snowflake, REST only, no connector:
- SQL API v2 loads the data and runs three detectors as
JOINs andGROUP BYs over the whole dataset. - Cortex
AI_COMPLETE, inside the warehouse, labels each distinct invoice description as VAGUE or SPECIFIC. "Strategic consulting services" is the language of a phantom company, and neither a SQL query nor a graph can read it. - Cortex REST (
llama3.1-70b) reasons through every agent step, in 2–3 seconds per step. - The reduced graph keeps the audited company and the accounts the money moves through: 21 of 127 nodes.
- SQL API v2 loads the data and runs three detectors as
- Gemini API (
google-genai), two calls per investigation: rewriting the final narrative for a non-technical reader without touching accusations or amounts, and answering questions with the evidence trail as its only context. It retries on 503s and falls back to spare models. - Agent: a hand-written ReAct loop, no framework, so the guardrail has one obvious place to run.
- Guardrail: an accusation survives only if it cites edges that exist, at least one of them is a payment or an invoice ("shares a phone number" is not enough), it doesn't contradict its own rule, and its amount is backed:
$$\text{claimed amount} \le 1.01 \times \sum_{e \in \text{cited edges}} \text{amount}(e)$$
- Backend and UI: FastAPI with SSE streaming, and an HTML/JavaScript dashboard on vis-network where the camera follows the agent and the proven trail turns red. The case file opens as an animated folder with four tabs (Verdict, Accusations, Dropped leads, Evidence trail), next to an Examination panel where the auditor questions the agent.
- Tests: 223 with pytest, including tests against the real Snowflake warehouse.
Challenges we ran into
- A 7B model on a laptop isn't enough for a demo. We started with
qwen2.5:7bserved by Ollama over the campus network: 20–45 seconds per step, about 8 minutes per investigation. Moving the reasoning to Cortex cut that to about 70 seconds. Ollama stayed on as a fallback. - The model makes things up. It cited edges that didn't exist, accused a supplier of "payment with no invoice" while citing a payment that had one, and inflated amounts. Each of those became a guardrail rule.
- Filtering without losing the money. Our first Snowflake reduction (one-hop neighbors) kept 1 of 80 payments, and the agent could no longer prove anything. We had to anchor the audited company and the bank accounts.
- Our own data looked fraudulent. Cortex flagged 22 of 24 suppliers as vague because the legitimate synthetic invoices said "Miscellaneous supplies". It was right. We rewrote the legitimate descriptions so the signal would mean something.
- Gemini's free tier under hackathon load: 1 in 5 calls came back 503. Short retries and a chain of fallback models fixed it.
- An animation that froze. Two vis-network
focus()calls in the same frame left an orphaned animation that kept pulling the camera back in a loop. We replaced them with our own clock-based animations. - Silent failures. When Snowflake failed, the system fell back to local data without telling anyone. Now the sidebar and a banner show where the graph came from.
Accomplishments that we're proud of
- A system where the model doesn't get the last word.
- "No fraud proven" is an honest result, not a failure. And there are deliberate traps: odd-looking but clean suppliers, and companies SAT cleared ("desvirtuado") that are never accused.
- When a judge asks about something that wasn't proven, the answer is "that's not in the case file".
- Every model has a reason to be there: Cortex reasons fast and reads text right next to the data; Gemini writes and answers.
What we learned
- Trust doesn't come from the model: it comes from what the system won't let it do.
- The quality of synthetic data decides whether an AI signal means anything.
- Measure before you believe: almost every decision came from a number (seconds per step, payments kept, 503s per call).
- Graceful degradation is worth little if nobody can see it happened.
What's next for CORPIDE
- Ingest real CFDI 4.0 XML invoices instead of synthetic ones.
- Have every detector return its evidence edges, as the Cortex detector already does.
- Stream Cortex responses, so an investigation can be stopped mid-answer.
- Run a pilot with a real audit team.
Built With
- css3
- fastapi
- gemini-api
- google-genai
- html5
- javascript
- llama-3.1
- networkx
- ollama
- pydantic
- pytest
- python
- qwen2.5
- rest-api
- server-sent-events
- snowflake
- snowflake-cortex
- snowflake-sql-api
- uvicorn
- vis-network
- web-audio-api
Log in or sign up for Devpost to join the conversation.