类型学比对引擎升级 I — structuring/layering 的阈值、可解释与证据对齐
类型学比对引擎升级 I — structuring/layering 的阈值、可解释与证据对齐
日期: 2026-08-20 阶段: Phase 3 - AML 调查 Copilot 标签: #structuring #layering #explainable-rules
核心问题
Day 64-66 把证据汇集这条「进水口」和它的成本闸都修好了。今天进 pipeline 的②段——类型学比对:拿到一份证据 dossier,怎么判它是 structuring、layering,还是 normal?为什么这一段在 2026 的合规落地里坚持用确定性规则引擎而非 LLM?
P1 已经造了 typology.ts(assessCase + V2 的 assessCaseV2),跑出 recall 1.0×3 类型学、FPR 5.6%。今天不是从零造轮子,而是回答三个「为什么这么造」的深问题,并把 structuring/layering 两类的阈值与可解释性讲透:
- 阈值怎么定才不被洗钱者反向利用? 结构化的本质是「贴着 $10,000 CTR 门槛下方走」——若检测阈值也写死在 $10,000,洗钱者只要再降一点就躲过。今天用「Searching for Smurfs」(2025) 的实证回答:洗钱者确实知道并贴线,所以规则必须用「贴线带」而非「单点阈值」。
- 规则输出怎么和证据 schema 对齐、并带可解释理由? SAR 起草段(③段)要引用每条命中的「触发了哪条规则、具体数值、阈值」。P1 的
RuleExplanation(含triggers: {label,value,threshold,op,unit})就是为此设计。 - 它和 P1 规则基线是什么关系? 不是取代,是「同一引擎、V2 加可解释层」——
assessCaseV2在assessCase之上叠加 explanations / muleGraph / arbitration,不改 base 判定。
关键内容
A. structuring 阈值:为什么是「贴线带 $8,000-$10,000」而非「单点 $10,000」
美国 BSA 要求现金交易超 $10,000 报 CTR(31 CFR 1010.311,监管)。结构化(smurfing)就是把大额现金拆成多笔单笔 <$10,000 来躲 CTR。若检测规则写「单笔 ≥$10,000 才可疑」,等于只抓不躲申报的笨贼——真正的结构化全在 $10,000 下方。
「Searching for Smurfs: Testing if Money Launderers Know Alert Thresholds」(Journal of Quantitative Criminology, 2025) 用 bunching estimator 实证检验了一个关键假设:洗钱者是否知道并贴着阈值走。核心逻辑——「Knowing a threshold value would be valuable for the money launderer; it would allow them to make transactions just below the threshold, lowering their probability of detection while minimizing their number of transactions and smurfing costs」。结论是阈值附近存在 bunching(金额分布在阈值下方异常堆积),且机密的银行内部告警阈值一旦泄露,洗钱者会贴新阈值走。
这条实证直接决定了 typology.ts 的阈值设计——用「带」不用「点」:
// src/aml/typology.ts(现有常量)
export const CTR_THRESHOLD_CENTS = 1_000_000 // $10,000 CTR 申报门槛(监管)
const STRUCT_BAND_MIN_CENTS = 800_000 // 贴线带下限 $8,000
// STRUCT-01:≤10 天窗口内 ≥3 笔落在 [$8,000, $10,000) 的现金存款
checkStruct01 的判定不是「金额 ≥ X」,而是「金额 ∈ [$8,000, $10,000) 且 ≥3 笔且 ≤10 天」——这正是 AMLNet (arXiv:2509.11595, 2025-09) 建模结构化时用的「random splits with some transactions just below threshold ($8,500-$9,900)」配方。判定伪代码:
checkStruct01(case):
band = cashDeposits(case).filter(t => $8,000 ≤ t.amount < $10,000) // 贴线带
best = argmax over windows of (txs within [d, d+9 days]) // 滑动 10 天窗
if len(best) < 3: return null // 笔数门槛
return RuleHit{ STRUCT-01, score=0.6, evidenceTxIds=best, desc=含具体数值 }
为什么带下限设 $8,000 而非 $0?——精确率。下限以下是日常小额现金(买菜、找零),全算进来会把 normal 阴性案件误报。P1 的 normal 生成器刻意放了一个「现金密集型商户」案件(3 笔 $8,200-$9,800 营业款),它会触发 STRUCT-01——这是保留的真实感 FP,正是 5.6% FPR 的来源。带下限是 precision/recall 的调节旋钮。
反直觉洞察①(检测阈值必须和申报阈值脱钩):把检测门槛等同于监管申报门槛($10,000)是新手最常见的错。Smurfs 论文证明洗钱者知道阈值并贴线,所以「单点 $10,000」规则的召回率对结构化趋近 0——真正的结构化交易全部在阈值下方。正确做法是检测阈值(贴线带 $8,000-$10,000 + 笔数 + 窗口)严于且低于申报阈值。更狠的现实:连内部机密告警阈值都可能泄露给洗钱者,所以单一固定阈值天然脆弱,必须叠加「多笔 + 短窗 + 渠道」的组合条件,让单维贴线躲不过。
B. layering 阈值:从「金额」转向「资金流拓扑」
structuring 看的是金额分布(贴线、多笔),layering 看的是资金流动结构——外部流入后经多个账户链式快进快出。这是一个图上的路径搜索问题,不是金额阈值问题。checkLayer01 的核心是沿内部转账边做 DFS,找最长合规链:
follow(account, arrivedDay, arrivedCents, visited): // 链式过账 DFS
candidates = internalDebits(account).filter(t =>
t.day ∈ [arrivedDay, arrivedDay+2] AND // 停留 ≤2 天(DWELL)
t.amount ≥ arrivedCents * 0.8 AND // 转出 ≥80%(FORWARD_RATIO)
t.counterparty ∉ visited) // 防环
return 最长的 [hop, ...follow(hop.next, ...)]
// 命中条件:链上账户数 ≥3(即 ≥2 跳)+ 链头外部流入 ≥$5,000
三个阈值各自的依据:
| 阈值 | 值 | 依据 | 调松/调紧的后果 |
|---|---|---|---|
| 每跳停留天数 | ≤2 天 | 「快进快出」是 layering 定义特征;停留久=正常资金沉淀 | 调松→把正常转账误判为过账(FP↑) |
| 每跳转出比例 | ≥80% | 过账要保留绝大部分本金,留一点当幌子 | 调紧到 100%→漏「留小尾巴」的真案(FN↑) |
| 最小链长 | ≥3 账户(≥2 跳) | 单跳是普通转账;多跳才是「分层」 | 调到 ≥2 账户→普通 A→B 转账全中(FP↑) |
| 链头流入下限 | ≥$5,000 | 过滤工资等小额合法入账 | 调到 $0→工资入账后正常消费链误判 |
AMLNet (2025-09) 对 layering 的分层建模口径印证了这套阈值:「low-sophistication: 2-3 layers splitting funds among 2-3 accounts with fixed delays; medium-sophistication: 3-5 layers with randomized delays and unique destinations per layer」。P1 的 genLayeringCase 生成 3-5 个内部账户、每跳停留 0-2 天、整千金额($25k→$24k→$22k),正落在 AMLNet 的「medium」区间。
为什么 layering 用图 DFS 而非 SQL 聚合?——因为「链」是路径属性,不是账户属性。一个账户单看「收一笔、转一笔」毫无可疑;只有把它放进「外部流入→A→B→C→外部流出」这条路径里,每跳都满足「快进快出 ≥80%」,才构成 layering。这是图算法不可被聚合查询替代的本质原因。
反直觉洞察②(layering 的可疑度在边上,不在点上):审查单个账户、单笔交易,layering 看起来完全正常——每笔都是合法的内部转账。可疑信号只存在于交易之间的时序+比例关系(这笔进来 2 天内 80% 又出去了)。所以 layering 检测不能靠「给每笔交易打风险分再求和」——那样每笔都低分、加起来还是低分。必须在资金流图的边上做模式匹配。P1 用
internalDebitsByAccount建邻接表 +followDFS,正是把「点的检测」升级成「路径的检测」。这也是为什么纯 LLM 读交易列表容易漏 layering——它擅长读文本,不擅长在隐式图上做路径搜索。
C. 可解释命中:规则输出与证据 schema 的对齐
②段的输出要喂给③段 SAR 起草,所以命中不能只给一句中文描述——必须给结构化、可机读的触发事实,让 SAR 能逐条引用「触发了 STRUCT-01,因为贴线存款 4 笔 ≥ 阈值 3 笔」。这就是 P1 RuleExplanation 的设计:
// src/aml/typology.ts(现有 RuleTrigger / RuleExplanation)
interface RuleTrigger {
label: string // 「贴线现金存款笔数」
value: number; threshold?: number // 实测 4 / 阈值 3
op?: '>='|'>'|'<'|'<='|'==' // 比较算子
unit: 'count'|'cents'|'days'|'ratio'|'accounts'
}
explainHit(case, hit) 把每条 RuleHit 升级为带 triggers 列表的 RuleExplanation。对 STRUCT-01 它产出四条触发事实:贴线存款笔数(4 ≥ 3)、窗口天跨度(8 ≤ 10)、合计金额、单笔上限(< CTR)。每条都是「value op threshold」三元组——可被 evalChecks 的代码型检查逐条核对(Day 18 的代码型 eval 层),也可被 UI 高亮成证据锚点。
三层对齐保证证据可溯源(这是合规审计的硬要求):
RuleHit.evidenceTxIds ⊆ case.transactions(必须存在 → cited_tx_exist 检查)
RuleExplanation.triggers ← 从 evidenceTxIds 对应的交易重新算(不复述 desc,重新核对)
SarDraft.citedTxIds = dedupe(hits.flatMap(evidenceTxIds))(SAR 只能引用命中证据)
explainHit 的关键纪律——triggers 的 value 是从 evidenceTxIds 对应交易重新计算的,不是复制 RuleHit.description 里的数字。这样若 description 和实际证据脱节(幻觉风险),代码型检查能抓到。这是「确定性引擎」相对 LLM 的核心优势:每个数字都有可重算的来源,审计追问「这个 4 是怎么来的」时,能指到具体 4 笔交易 id。
与 P1 规则基线的关系(不是取代,是分层):
| 层 | 函数 | 产出 | P3 角色 |
|---|---|---|---|
| base 判定 | assessCase | hits + scores + topTypology | 不变,recall 1.0 的基线 |
| V2 可解释 | assessCaseV2 | + explanations + muleGraph + arbitration | ②段主输出,喂③段 SAR |
| 代码型核对 | runCodeChecks | cited_tx_exist 等 pass/fail | eval gate,核对②段自洽 |
assessCaseV2 在 assessCase 之上只加不改——base 的 scores/topTypology 语义完全保留(唯一例外:多类型学并列时用 arbitration.primary 覆盖 topTypology 打破平手,Day 68 详谈)。这保证 P1 的 recall 1.0 不被 V2 破坏。
设计要点/决策表
| 要点 | 决策 | 理由 |
|---|---|---|
| structuring 阈值 | 贴线带 [$8,000, $10,000) + ≥3 笔 + ≤10 天,不用单点 $10,000 | 洗钱者知道并贴线(Smurfs 2025),检测须严于申报阈值 |
| 带下限 $8,000 | 过滤日常小额现金 | precision 旋钮;下限太低则日常现金误报 |
| layering 检测 | 资金流图 DFS(停留≤2天/转出≥80%/链长≥3),非金额阈值 | 可疑度在边的时序+比例关系上,不在单点 |
| 链头流入下限 | ≥$5,000 | 过滤工资等小额合法入账 |
| 命中输出 | 结构化 RuleTrigger{value,threshold,op,unit},非纯文本 | ③段 SAR 要可机读引用;代码型检查要可核对 |
| triggers 来源 | 从 evidenceTxIds 重算,不复制 description | 防 description 与证据脱节;每个数字可溯源 |
| 与 base 关系 | assessCaseV2 只加可解释层,不改 base 判定 | 保 P1 recall 1.0 不被破坏 |
对本项目的落地
src/aml/typology.ts的 structuring/layering 阈值已落地且不改:CTR_THRESHOLD_CENTS=$10,000、STRUCT_BAND_MIN_CENTS=$8,000、LAYER_DWELL_DAYS=2、LAYER_FORWARD_RATIO=0.8、LAYER_MIN_CHAIN_ACCOUNTS=3、LAYER_MIN_INFLOW_CENTS=$5,000——本日把它们的实证依据(Smurfs 贴线/AMLNet 分层口径)补全为设计文档,供面试与审计追问时引用,而非改代码。explainHit/RuleExplanation是②→③段的接口:Day 64 pipeline ②段输出assessCaseV2(case),其explanations字段(每条命中的 triggers)是③段 SAR 起草引用证据的来源。P3 的 LLM 起草 prompt 应只喂 explanations 的结构化 triggers,不喂原始交易全表——让 LLM 在「已核对的事实」上叙述,降低幻觉。- 代码型检查兜底:
src/aml/evalChecks.ts的cited_tx_exist/top_typology_consistent在 CI 核对②段输出自洽——SAR 引用的 txId 必须存在、topTypology 必须与最高分一致。这是确定性引擎相对 LLM 的可审计优势。 - 降级路径复用(呼应 Day 66):成本极限降级(90%+)时整条 LLM pipeline 退回
assessCaseV2(零成本)+draftSar(模板)——②段规则引擎本就是降级安全网,recall 1.0 保证降级后仍不漏报。 - 诚实标注:
typology.ts头注已写明「确定性规则引擎(非 LLM)」;本日不引入任何 LLM 进②段。mule_network 的图度量与多类型学叠加仲裁留 Day 68(升级 II)详谈。
参考资料
- Sumarliah / van Koningsveld et al. — Searching for Smurfs: Testing if Money Launderers Know Alert Thresholds, Journal of Quantitative Criminology(Springer,arXiv:2309.12704):bunching estimator 检验阈值附近金额堆积;洗钱者知阈值则贴线以降被测概率、减交易数与 smurfing 成本;机密告警阈值泄露后洗钱者贴新阈值——故单点阈值脆弱 (2025)
- arXiv:2509.11595 — AMLNet: A Knowledge-Based Multi-Agent Framework to Generate and Detect Realistic Money Laundering Transactions:structuring 配方含 random splits just below threshold ($8,500-$9,900);layering 低复杂度 2-3 层/2-3 账户,中复杂度 3-5 层/随机延迟/每层唯一目的地;检测 F1 0.90(precision 0.84, recall 0.97)(2025-09)
- FinCEN — 31 CFR 1010.311 / Form 112:现金交易超 $10,000 须报 CTR(BSA,监管)(持续)
- amlwatcher / aiprise — Key AML Typologies 2026 / 10 AML Typologies Every Bank Needs to Know:结构化+smurfing 跨渠道(银行/移动钱包/预付卡/跨境 FinTech)持续高风险,须 holistic cross-channel 分析而非孤立规则;现代监控对每笔 ACH/wire/card/internal 评分 structuring/layering/rapid movement (2026)
- 本仓库
src/aml/typology.ts(STRUCT-01/02 + LAYER-01/02 规则、贴线带/DFS 阈值常量、RuleTrigger/RuleExplanation/explainHit/assessCaseV2)、src/aml/generator.ts(genStructuringCase/genLayeringCase 配方、现金密集型商户 FP)、src/aml/evalChecks.ts(cited_tx_exist/top_typology_consistent 核对)(2026-06)
SOTA 检查 (2026-06-11)
- 「确定性规则引擎做类型学判定 + LLM 只做叙述」是 2026-06 合规落地的主流分工:FIS-Anthropic (2026-05) 与 Co-Investigator (2025-09) 都把「比对已知类型学」放在确定性/可审计的一侧,LLM 负责证据汇集与叙述生成——因为类型学判定要回答审计追问「为何判 structuring」,规则的「value op threshold」可解释性是 LLM 黑箱给不了的。
- 「检测阈值须严于申报阈值」在 2026 仍是 live 的设计纪律:Smurfs 论文 (2025) 的「launderers know and bunch」结论未被推翻;2026 typology 指南一致强调 cross-channel holistic 分析(单维贴线易躲),印证「贴线带 + 多条件组合」优于「单点阈值」。
- 图方法在 layering 检测上升温:DELATOR (multi-task GNN on transaction graphs) 等图神经网络方案在大规模交易图上抓 layering 优于规则,但牺牲可解释性——合规场景下 GNN 适合做「召回候选」,最终定性仍回退确定性规则给可审计理由。本项目原型用规则 DFS(可解释)是对的,规模化后可考虑「GNN 召回 + 规则定性」两阶段。
- 待跟踪:贴线带下限 $8,000 与 layering 的 80%/2天 阈值是 v1 经验值;W2-W3 用更大合成集(或公开 SynthAML/AMLworld)做阈值灵敏度分析,按 precision/recall 曲线回填最优值;mule_network 图度量与叠加仲裁见 Day 68。
- 过时警示:把检测阈值写死成监管申报阈值($10,000 单点)是 2024 前规则系统的经典缺陷,对结构化召回趋近 0——反直觉洞察①是 live 踩坑点,至今仍有初级 TM 系统犯此错。