<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Following the Money]]></title><description><![CDATA[Following the Money]]></description><link>https://team-musketeer.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 29 Oct 2026 07:52:25 GMT</lastBuildDate><atom:link href="https://team-musketeer.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[I work in AML. So I built an agent that follows the money through a graph]]></title><description><![CDATA[How Team Musketeer built Hound: AI agents on a FalkorDB graph that clear false positives, catch mule rings and write SARs you can trace.]]></description><link>https://team-musketeer.hashnode.dev/i-work-in-aml-so-i-built-an-agent-that-follows-the-money-through-a-graph</link><guid isPermaLink="true">https://team-musketeer.hashnode.dev/i-work-in-aml-so-i-built-an-agent-that-follows-the-money-through-a-graph</guid><category><![CDATA[falkordb]]></category><category><![CDATA[graph database]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[fintech]]></category><category><![CDATA[hackathon]]></category><dc:creator><![CDATA[Manav Sinh]]></dc:creator><pubDate>Sun, 18 Oct 2026 13:28:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ad4c64588e83054b4ee5d66/292ae0ad-a878-4382-b613-225415c467a9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most of my working day in anti-money-laundering is spent proving alerts wrong. Not catching criminals. Proving that the payroll company paying 160 people is, in fact, a payroll company.</p>
<p>So when Graph Hacks 2026 came around, I didn't want to build a chatbot. I wanted to build the thing I actually wish I had at work. My team (Team Musketeer) and I ended up with Hound.</p>
<p><a class="embed-card" href="https://youtu.be/vA2FIvR-nkA">https://youtu.be/vA2FIvR-nkA</a></p>

<p><strong>Live demo:</strong> <a href="https://hound-99l2.onrender.com">hound-99l2.onrender.com</a> · <strong>Code:</strong> <a href="https://github.com/manav-Mnv/hound-aml">github.com/manav-Mnv/hound-aml</a></p>
<h2>Why alerts are such a pain</h2>
<p>Banks run simple rules over every transaction. Too much cash, too many transfers out, money to a risky country. The rules fire all day. A NICE Actimize industry survey put it bluntly: almost 95% of alerts turn out to be false positives.</p>
<p>Each one still gets a human. That person opens five systems, copies account numbers into a spreadsheet, checks phone numbers by hand, and sometimes finds out a colleague is working the same ring from a different alert. And the ones that aren't false positives really matter. In 2024 TD Bank paid about $3 billion after laundering networks moved hundreds of millions through it.</p>
<p>Here's the thing I keep coming back to. Whether an alert is real is almost never in the alert. It's in the connections. Who else logs in from this phone? Where did the money go next? Who actually owns this supplier? Did someone already clear this customer last month? Those are graph questions. So we built Hound on a graph.</p>
<h2>One graph for everything</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6ad4c64588e83054b4ee5d66/b382ac4d-1c97-4984-b717-9ed7f61f4705.png" alt="Hound's alert landscape: every alerted account and what ties them together" style="display:block;margin:0 auto" />

<p>Hound loads two made-up banks into FalkorDB as two separate graphs, <code>hound:alpha</code> and <code>hound:beta</code>, on one instance. A third graph, <code>hound:consortium</code>, holds nothing except hashed identifiers that the banks have flagged. Customers, accounts, phones, devices, IPs, addresses, companies and ownership stakes are all nodes. Transfers and cash deposits are edges with amounts and timestamps.</p>
<p>The decision I'm happiest with: the agents' memory lives in the same graph as the money. A case is a node connected to the people it's about. Every query an agent runs is saved as an Evidence node with the exact Cypher and parameters. Findings point to evidence. SARs point to evidence. When an analyst writes a lesson ("this company pre-funds payroll, it's fine"), that lesson hangs off the company itself. So "have we seen this before?" isn't a search any more. It's two hops.</p>
<h2>How the agents work</h2>
<p>Six ordinary rules, written in Cypher, produce 71 alerts. A triage agent scores all 71 in about 200 ms using graph features: identity clusters from WCC, PageRank, account age, past false positives, consortium hits. It also groups alerts that belong to the same ring, so 71 alerts become 43 candidate cases before anyone opens one.</p>
<p>The investigator has 17 tools. Some are multi-hop Cypher: follow money forward in time, find loops that come back to where they started, find cash deposited in one place and sent on within 48 hours. Some are FalkorDB's built-in algorithms (WCC, PageRank, betweenness, label propagation). One lets it write its own read-only Cypher, which runs through <code>GRAPH.RO_QUERY</code>, so the database itself won't let it change anything. Before it does any of that, it reads its memory: the playbook for that kind of case and whatever lessons analysts left behind.</p>
<p>Three cases from the demo show why the graph matters.</p>
<p><strong>The mule ring.</strong> A student's account forwards 95% of what comes in within hours. WCC over devices and IPs finds eight new customers sharing two phones. The money ends up at a crypto exchange. And the recruiter's phone number, hashed, matches something Beta Credit Union reported in September. Alpha never sees Beta's customers. It only learns that there's a match. That's roughly how 314(b) information sharing is meant to work.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ad4c64588e83054b4ee5d66/c6e8564e-67cd-4095-93cd-6283e98aae67.png" alt="The mule ring: eight new customers sharing two phones, plus the consortium hit" style="display:block;margin:0 auto" />

<p><strong>The false positive that stays closed.</strong> A payroll processor pays 160 people every two weeks, and the fan-out rule fires every single time. Hound pulls up the note an analyst attached to that company back in August, checks that each batch was funded before it went out, and closes the alert with the reason written down.</p>
<p><strong>The sanctions problem nobody's rule caught.</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6ad4c64588e83054b4ee5d66/40450334-d5b9-4c43-8096-d08828bd9a72.png" alt="" style="display:block;margin:0 auto" />

<p>A wire to a high-risk country turns out to pay a supplier that's 30% owned by a sanctioned person, and 25% owned by a holding company that another sanctioned party owns outright. Add it up and that's 55%. Under OFAC's 50% rule the supplier is blocked, even though its name is on no list. In a graph that's a four-hop ownership walk. In a spreadsheet it's an afternoon.</p>
<h2>Every sentence has a receipt</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6ad4c64588e83054b4ee5d66/57599447-6bff-4d5c-8f4c-f611ec7e8cb0.png" alt="The Evidence tab: every SAR citation opens the exact Cypher that ran on FalkorDB" style="display:block;margin:0 auto" />

<p>A SAR is only useful if an examiner believes it. So Hound's SAR writer has to attach evidence IDs to every sentence it writes. Then a reviewer agent goes through the draft line by line. Do the cited evidence nodes exist in this case? Does every dollar amount in the sentence actually appear in them?</p>
<p>We tested this by slipping a fake $999,999 transfer into a draft. The reviewer flagged it, the writer fixed it, and the saved SAR only contained numbers that came from the graph. In the app, every citation is clickable and shows you the exact Cypher behind it.</p>
<h2>Did it work?</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6ad4c64588e83054b4ee5d66/fe9d8dce-b0a5-4e1a-8a02-9f36e478ec38.png" alt="Benchmark: 41/41 false positives closed, 5/5 schemes caught, 0 false SARs, 5 accounts no rule flagged" style="display:block;margin:0 auto" />

<p>The data is synthetic, and the answers are kept outside the graph so the agents can't cheat. Out of 71 alerts (41 false positives and five planted schemes), Hound closed all 41 false positives with written reasons, escalated all five schemes, filed zero false SARs, merged duplicate alerts on the same rings, and found five smurf accounts that no rule had flagged at all. The whole queue takes about 30 seconds.</p>
<h2>What we learned</h2>
<ul>
<li><p><strong>Put the memory next to the data.</strong> Once cases, lessons and evidence sat in the same graph as the transactions, recall and audit trails came almost for free.</p>
</li>
<li><p><strong>FalkorDB is quick enough to sit inside every agent step.</strong> Most of our tools come back in 2 to 25 ms on about 11k nodes and 58k relationships, and the graph algorithms are one CALL away.</p>
</li>
<li><p><strong>Separate graphs make the two-bank story honest.</strong> One instance, one graph per bank, plus a hashed consortium graph is close to how this would really have to work.</p>
</li>
<li><p><strong>Explainability has to be enforced.</strong> Making the writer cite evidence, and having something check the numbers, is what makes the SAR worth trusting.</p>
</li>
<li><p><strong>Pin your versions.</strong> We built on FalkorDB 6.0, the new Rust engine, hit two edge cases (we wrote them up with repros), and learned to pin the Docker tag the hard way.</p>
</li>
</ul>
<p>Hound doesn't make the call. A person does. But that person now starts from a case that already shows its work, instead of five browser tabs and a spreadsheet.</p>
<p><strong>Try it:</strong> <a href="https://hound-99l2.onrender.com">hound-99l2.onrender.com</a> · <strong>Code:</strong> <a href="https://github.com/manav-Mnv/hound-aml">github.com/manav-Mnv/hound-aml</a></p>
<p><em>Built by Team Musketeer for Graph Hacks 2026, hosted by WeMakeDevs and FalkorDB.</em></p>
]]></content:encoded></item></channel></rss>