返回 AIPA 笔记
AIPA Day 6

W1 原型实现复盘 — 契约先行与诚实标注

W1 原型实现复盘 — 契约先行与诚实标注

2026-06-20
contract-firstsynthetic-dataevalsintegrity

日期: 2026-06-20 阶段: Phase 1 - 产品定义×评测×可观测底座 标签: #contract-first #synthetic-data #evals #integrity

核心问题

Day 4/5 写的是设计,今天复盘实际写出来的代码src/aml/ 五个模块 + 14 个测试)教会了什么。四个工程问题在实现中被迫给出明确答案:(1) 为什么类型契约要在任何实现之前定死;(2) 为什么确定性合成数据是 eval 体系的前提而非锦上添花;(3) 货币用整数分 number 而非 bigint——与本仓库 dsdb-lab ledger 的选择相反,理由是什么;(4) 规则引擎作为「LLM 必须打败的基线」,得分与窗口语义要怎么设计才经得起对照。这些答案构成 W2 错误分析与 P3 LLM 接入的工程地基。

关键内容

A. 类型契约先行:types.ts 是规格书,不是事后注释

src/aml/types.ts(119 行)先于 generator/typology/sarDraft/evalBaseline 提交并冻结,四个实现模块只依赖它、互相零依赖,可并行开发。实现期验证了三个具体收益:

  1. 契约即不变量AmlCase.label 字段把「金标 ground truth」写进类型——任何案件天生带标签,eval 不需要单独的标注文件;RuleHit.evidenceTxIds 在 Day 5 设计时就为 SAR 引用与 UI 高亮预留,实现 sarDraft.tscitedTxIds 直接从 hits flatMap 去重,零返工。
  2. 字面量类型把诚实做成编译期约束SarDraft.generatedBy: 'rule-template' 是字面量单值类型,不是 string。P3 接入 LLM 时必须改类型(如 'rule-template' | { model: string }),这次变更会出现在 code review 的 diff 里——「悄悄把规则模板说成 LLM」在类型系统层面不可能发生。
  3. topTypology: TypologyId | null:契约强制「证据不足就说不知道」,下游 UI/SAR 都必须处理 null 分支(sarDraft 的「未达判定阈值」段落即此分支的产物),拒绝「永远给个答案」。

B. 确定性合成数据:eval 的前提,不是优化

生成器复用 dsdb-lab 的 createPrng@/src/dsdb-lab/shared/prng),并由三条测试钉死确定性:同 seed 两次 generateDataset deep-equalgetGoldenDataset() 模块级 memoize 返回同一引用toBe);memoize 结果与同 seed 重算一致。为什么这是前提:

  • 回归可比性:CI 门槛(structuring recall ≥0.85 等)只有在「每次跑的是同一批 66 案」时才有意义。数据集若有任何非确定性,recall 波动就无法归因到代码变更——eval 退化为噪声。
  • 失败可回放:案件 id(C001…C066)跨运行稳定,W2 错误分析可以永久引用「C019 为什么漏判」;这正是 Hamel/Shreya 错误分析→开放编码流程(Lenny's, 2025-09)对数据的隐含要求。
  • 金标可版本化:seed 'aipa-golden-v1' 就是版本号。P3 扩到 ≥100 案时换 v2 seed,v1 的全部历史数字仍可逐字节重现。

产业侧同构:AMLNet(arXiv 2509.11595, 2025-09)以 regulation-aware 生成器产出 100 万+合成交易(覆盖 placement/layering/integration + structuring),检测集成 F1 0.90(precision 0.84 / recall 0.97),并发布数据集供可复现实验;IBM AMLworld(arXiv 2306.16424, 2023-06)的核心论证至今成立——合成数据的金标是完备的,而真实数据里大量洗钱从未被发现,标签天然残缺。

C. 整数分货币纪律:这里选 number,ledger 选 bigint,都对

本仓库 2026-05-30 的提交 9700ca7(fix(dsdb/ledger): integer (bigint) accounting instead of JS float)是教训来源:float 货币在 dsdb-lab ledger 里曾是真实的诚信债。但 AML 模块没有照搬 bigint,而是用整数分 number,理由是边界条件不同:

  • 量级有界:单案最大金额 $40,000 = 4,000,000 分;2^53 分 ≈ 9 万亿美元,安全余量 9 个数量级。ledger 的余额是无界累加值,才需要 bigint。
  • 序列化成本JSON.stringify 遇 bigint 直接抛 TypeError,React props、图表库、未来的 OTel span 属性全要手工转换;number 整数零摩擦。
  • 生成纪律消灭舍入:所有金额按「整美元 ×100」生成(usd() 辅助函数),测试断言全量 Number.isInteger(amountCents)。一个诚实的实现偏差值得记录:Day 4 设计稿写 layering 每跳「乘 0.95–0.99 后取整」,实现改成了整千美元递减amountK -= 1..2)——既彻底消灭舍入争议,又让「金额整数化」本身成为可检测特征(LAYER-02 规则的依据)。设计被实现修正,这正是 prototype-first 的预期收益(Builder.io 五步法, 2026-01)。

D. 规则基线的 eval 设计哲学:让 LLM 有一个必须打败的对手

  • 得分两级制:主规则 0.6 + 辅规则 0.4,求和 cap 1,阈值 0.5——主规则单独命中即过阈,辅规则单独命中不过阈。语义:单一弱信号(如「新户高频」MULE-02)不足以定性,必须有结构性证据(如 fan-in/fan-out 模式 MULE-01)打底。
  • 窗口语义统一:「10 天窗」在生成器与规则引擎里是同一个定义——含首尾日历日,即 dayOffset 跨度 ≤9(14 天窗=跨度 ≤13)。这消灭了一类沉默 bug:若生成器按跨度 10 生成而规则按跨度 9 检,recall 会无声流失且无人知晓原因。
  • 防串扰分区:STRUCT 只看 cash 渠道、MULE 只看带外部对手名的 ach/wire、LAYER 只看 internal;layering 中间账户 openedDaysAgo ≥90 避免误触 MULE-02 的新户判定。这保证金标「label↔规则」映射干净,但也是已知局限——真实类型学会叠加出现,W2 错误分析要主动攻击这个假设。
  • 决策余量:MULE-01 的转出比阈值 60%,而生成器 fan-out 在 85–95%——25 个百分点的余量让规则不靠贴线侥幸通过;LAYER-01 用 DFS 追最长链(visited 集合防环),链长定义「账户数 ≥3 即 ≥2 跳」覆盖最短合法链。
  • 刻意保留的误报:normal 第 0 件是现金密集型商户(3 笔 sub-$10k 营业款现金存款),必然触发 STRUCT-01 → FPR = 1/18 ≈ 5.6%。这是 AML 史上最经典的误报原型,留着它让 precision 指标非平凡,也给 P3 的 LLM 一个明确的「打败基线」靶子:规则分不清商户营业款与拆分,LLM 能吗?

E. 诚实标注与本仓库 2026-05 诚信止血的一致性

SAR 草稿引言段固定包含「基于合成数据」「非 LLM 输出」声明,且由测试钉死expect(intro).toContain('合成数据') / toContain('非 LLM'))。这与 2026-05-30 阶1 诚信止血三连(c0609d1 假鲸鱼地址→真实地址+「示意」标注、64d8d77 CostMeter「估算」标签、cce3459 硬编码价格→实时价)是同一条价值观:演示不许假装是真。差别在于这次把诚实从「上线前补丁」前移成「编译期类型 + 测试不变量」——EU AI Act Article 50(2026-08-02 生效)要求 AI 生成内容标注,本项目在 LLM 还不存在时就把标注管道跑通了。

设计要点/决策表

要点说明与已有方案差异
类型契约先行冻结types.ts 先提交,四模块只依赖契约、互不依赖多数 demo 边写边改类型;这里契约变更=显式 diff,P3 换 LLM 引擎接口不变
确定性三连测试同 seed deep-equal + memoize 同引用 + 重算一致AMLNet(2025-09)发布静态数据集求可复现;本项目用 seed 让数据集成为代码,可版本化可扩展
整数分 number 而非 bigint量级有界(«2^53)+ 序列化零摩擦 + 整美元×100 生成与 dsdb-lab ledger 的 bigint(2026-05-30)互为对照:无界累加用 bigint,有界+UI 密集用整数分 number
主 0.6/辅 0.4/阈 0.5主规则单独过阈、辅规则单独不过阈比单分数阈值多一层「证据强度」语义,给 SAR 的 Why 段落天然素材
窗口=含首尾日历日10 天窗≡跨度≤9,生成器与规则共用定义消灭 off-by-one 这类沉默 recall 杀手;窗口语义写进常量注释
刻意 FP(现金密集商户)index 0 必然误报,FPR 钉在 5.6%无噪声金标上规则轻松满分、eval 失去判别力;这是给 LLM 留的明确超越点
诚实标注=测试不变量generatedBy 字面量类型 + intro 文案断言与阶1 诚信止血(2026-05-30)同价值观,但前移到编译期/测试期

对本项目的落地

  • 已入仓:src/aml/types.ts(契约)、src/aml/generator.ts(66 案金标,seed aipa-golden-v1)、src/aml/typology.ts(6 条规则)、src/aml/sarDraft.ts(5W1H 模板)、src/aml/evalBaseline.ts(recall/FPR/混淆矩阵)、src/aml/__tests__/aml.test.ts(14 测试,含 CI 门槛断言)。
  • 实测:npx vitest run src/aml 14/14 通过,npm run typecheck 干净。
  • W2 衔接:错误分析(开放编码→轴向编码)将以本文 D 节的「防串扰假设」「刻意 FP」为第一批攻击面;P3 W10 金标扩 ≥100 时按 D 节余量原则补「贴线案件」(如转出比恰 60–65% 的 mule)。
  • 待办(W1 收口风险):三屏 UI(src/components/aml/)尚未入仓,数据层契约已 100% 就绪。

参考资料

  • AMLNet: A Knowledge-Based Multi-Agent Framework to Generate and Detect Realistic Money Laundering Transactions, arXiv 2509.11595(2025-09)— regulation-aware 生成 + F1 0.90 检测集成 + 可复现数据集发布
  • Exploring the In-Context Learning Capabilities of LLMs for Money Laundering Detection in Financial Graphs, arXiv 2507.14785(2025-07,v2 2025-10)— LLM 在合成 AML 图场景上做 few-shot 推理、可输出分析师式解释——P3「LLM 打基线」的学术参照
  • AMLworld: Realistic Synthetic Financial Transactions for Anti-Money Laundering Models, arXiv 2306.16424(2023-06)— 合成金标完备 vs 真实标签残缺的原始论证
  • Hamel Husain & Shreya Shankar, evals 方法论(错误分析→开放编码→轴向编码→LLM-as-judge→代码型检查),Lenny's Newsletter(2025-09)
  • OpenAI CPO「PM 最重要的技能是写 evals」(2025-08)
  • Builder.io, prototype-first 五步法(2026-01)— 「设计被实现修正」即其第 4-5 步的预期回路
  • EU AI Act Article 50 透明义务,2026-08-02 生效;Digital Omnibus 临时协议(2026-05-07)确认其不在推迟之列
  • 本仓库提交 9700ca7 / c0609d1 / 64d8d77(2026-05-30)— dsdb-lab bigint 记账与阶1 诚信止血的一手出处

SOTA 检查 (2026-06-11)

  • 「确定性合成金标 + 可复现 eval」是当前 AML×AI 研究的现行标准做法:AMLNet(2025-09)与 Tide(2026-03,见 Day 4)都把数据集可复现性当作贡献的一部分发布;本项目用 seed-as-version 的轻量实现与之同向,无过时风险。
  • LLM 直读交易图做类型学判定已有学术验证(arXiv 2507.14785, 2025-07):LLM 能输出分析师式红旗解释——这支持 P3「LLM 版对照同一金标打基线」的可行性,但该文也未声称超越专用模型,规则基线在 P3 之前仍是本项目的 SOTA 内部解
  • 过时认知警示 1:「JS 处理货币必须上 bigint/decimal 库」是一刀切教条——本仓库同时存在 bigint(ledger,无界累加)与整数分 number(AML,有界+序列化密集)两个正确答案,选型依据是量级边界与序列化路径,不是流行度。
  • 过时认知警示 2:「合成数据上 recall 高=产品能用」——AMLNet 自家 F1 0.90 也只在自家分布内成立;本项目所有数字必须带「66 案合成金标」限定语,外推声明等 P3 扩集 + 红队后再做。