- 新增《风控Agent需求文档》:规则引擎、前置拦截、人工处置、工单闭环 - 新增 sql/risk_rule_seed.sql:20条风控规则种子数据(INSERT IGNORE)
16 KiB
风控 Agent 需求文档
版本:v1.0 | 日期:2026-09-12 | 状态:待评审 依据:《开发计划.md》§2.5 风控控制台、§5 风控Agent、Phase 4 关联:《风控Agent设计方案.md》
1. 背景与目标
为系统接入风控 Agent:每一笔申购/赎回在下单落账前,先经过风控规则引擎检测,命中规则的订单被「挂起」等待人工处置,未命中的正常成交。
核心目标:形成「检测 → 阻断 → 人工处置 → 回写」的合规闭环。
合规铁律:机器只负责告警 + 阻断;放行/拦截/冻结的处置权永远在人工。
2. 术语与核心概念
| 术语 | 表 | 定位 |
|---|---|---|
| 交易申请单 | trade_order |
风控拦截对象,状态机主表(事前) |
| 成交流水 | fin_transaction |
账务凭证,只含真正成交的单(事后) |
| 风控预警 | fin_risk_alert |
命中记录 + 人工处置留痕 |
| 风控规则 | risk_rule |
规则引擎的表驱动配置(参数) |
| 业务工单 | biz_work_order |
拦截/冻结后开启的独立审批链 |
两张链的关系:预警链(fin_risk_alert)在人工处置后结束;工单链(biz_work_order)只在「拦截/冻结」时额外开启,是处置的下游产物。
3. 现状与差距
- 当前
service/purchase.py/service/redeem.py为即时成交(直接扣款+加仓 / 减仓+入账),不产生任何交易留痕。 trade_order/fin_transaction/fin_risk_alert/risk_rule/biz_work_order五张表只有 DDL,无 model/repo/schema 实现。- 结论:风控 Agent 当前无数据可检测,需先补齐交易留痕。
4. 核心需求
FR1 · 前置拦截(交易留痕)
申购/赎回由「即时成交」改为「先建单 → 检测 → 通过才落账」。
请求(申购/赎回)
↓
FastAPI 鉴权(get_current_user)→ 获取当前用户
↓
适当性校验(硬拦截,申购专用):
客户风险等级 ≥ 产品风险等级,否则 1005 直接拒绝,不生成订单
↓
创建 trade_order,status = '待确认'
- 生成唯一 order_no(前缀+日期+序列,如 PO20260912xxxx)
- 填入 order_type / amount / shares / nav(申请口径)
- ★ 不碰账户余额、不碰持仓
↓
同步进入风控规则引擎检测(FR2)
↓
├─ 未命中 ──► status='已确认' + 落账(扣款+加仓 / 减仓+入账)
│ + 写 fin_transaction + confirm_time=now
│
└─ 命中 ────► 生成 fin_risk_alert(未处理)
+ trade_order.status='风控挂起' + risk_alert_id 关联
账户、持仓保持不动,等待人工处置(FR4)
关键约束:
- 建单 → 检测 → 落账/挂起 在单个事务内原子完成(无外部调用,全为 DB + 纯计算)。
- 落账逻辑复用现有
deduct_balance/holdings.upsert、holdings.redeem/credit_balance,不重写。
FR2 · 规则引擎检测(纯代码,不用大模型)
- 同步执行:在下单请求内当场完成,结果驱动状态流转。
- 纯规则引擎:阈值比较 + 历史流水统计,全程
if/else + SQL,不调用 LLM。 - 表驱动:
risk_rule表存参数(阈值/级别/权重/启停),代码存判断逻辑。
检测输入:当前 trade_order + 该客户历史流水 + 客户画像/账户/注册时间。
检测输出:命中规则列表(规则 ID + 规则名 + 命中详情)。
FR3 · 预警生成
- 聚合粒度:一笔订单命中多条规则,只生成一条预警;
alert_type取主规则,trigger_detail记录全部命中规则,alert_level取最高档。 - 分级:级别取命中规则中
risk_level最高档(低→蓝 / 中→黄 / 高→红)。 - 置信度:
confidence = 1 − Π(1 − weight_i)(命中规则越多、权重越高,置信度越高)。
FR4 · 人工处置(挂起自动,处置人工)
三个处置动作均为风控专员手动,系统绝不自动放行/自动作废:
| 动作 | 订单 | 预警 | 客户 | 工单 |
|---|---|---|---|---|
| 放行 | 已确认 + 落账 |
已排除 |
正常成交 | 无 |
| 拦截 | 失败(作废) |
已确认 |
站内信通知 | 生成「可疑交易上报」 |
| 冻结 | 失败(作废) |
已确认 |
sys_user.status=冻结 + 站内信通知 |
生成「冻结复核」 |
约束:
- 处置接口的
handler_id由后端从当前登录用户获取,不信任前端传入。 - 处置接口需校验角色:风控专员(
user_type=EMPLOYEE且employee_role=风控专员,或ADMIN)。 - 处置结果回写订单与预警表(
handler_id/handle_result/handle_time),全程留痕可追责。 - 放行时重新校验余额:挂起期间余额可能变化,申购放行若余额不足则放行失败、订单转「失败」。
FR5 · 工单链
- 生成时机:仅在「拦截」或「冻结」处置时自动创建,系统自动挂起阶段不产生工单。
- 创建方式:系统自动 INSERT,预填
order_type(可疑交易上报/冻结复核)、customer_id、submitter_id(当前专员)、priority(按预警级别)、biz_content(打包命中规则+订单号+预警 id);status默认「待处理」、current_node默认「初审」。 - 流转(后端接口驱动):
认领 提交审核 复核通过
待处理 ──────────► 处理中 ──────────► 待审核 ──────────► 已完成
(handler_id=自己) │
└──────► 已驳回(复核驳回,终态)
- 职责分离:复核人 ≠ 处理人,先做软校验(仅提示,不强制阻断)。
- 终点:工单「已完成」即项目内终点,不上报人行(监管报送为系统外动作,不在本项目范围)。
- 每次流转写
audit_log(谁、何时、从什么态改到什么态)。
5. 状态机
5.1 trade_order 订单状态机
待确认 ──(未命中)──────► 已确认 ──► 落账 + 写 fin_transaction
│
└──(命中)──────────► 风控挂起
│
├──(放行)──► 已确认 ──► 落账 + 写 fin_transaction
├──(拦截)──► 失败(作废)+ 通知客户 + 生成工单
└──(冻结)──► 失败(作废)+ 客户冻结 + 通知客户 + 生成工单
5.2 fin_risk_alert 预警状态机(无「处理中」中间态)
未处理 ──(放行)────► 已排除
│
├──(拦截)──────► 已确认
└──(冻结)──────► 已确认
防并发:处置接口用条件更新 WHERE id=? AND status='未处理',更新不到(rowcount=0)即视为已被他人处置,返回提示,不重复落账/工单/通知。
5.3 biz_work_order 工单状态机
待处理 ──认领──► 处理中 ──提交审核──► 待审核 ──复核通过──► 已完成
└──复核驳回──► 已驳回(终态)
6. 数据表设计
五张表 DDL 已在
sql/schema.sql,本次需补 ORM model + repository + schema。
6.1 trade_order 字段映射
| 字段 | 来源 |
|---|---|
order_no |
生成唯一单号 PO+日期+序列 |
customer_id |
当前用户 id |
product_id |
入参 product_id |
advisor_id |
客户自购为 null |
order_type |
申购 / 赎回 |
amount / shares |
申购金额+折算份额;赎回份额+折算金额 |
nav |
产品当前净值 |
fee |
0(暂无手续费) |
status |
待确认 / 风控挂起 / 已确认 / 失败 |
risk_alert_id |
挂起时关联预警 id |
confirm_time |
确认时间 |
6.2 fin_transaction 成交流水
仅在风控通过、落账成交时写一笔:transaction_no、order_id(关联申请单)、customer_id、product_id、transaction_type(申购/赎回)、amount、shares、nav、fee、status='已确认'。
6.3 fin_risk_alert 预警
| 字段 | 填什么 |
|---|---|
customer_id |
交易客户 |
order_id |
触发预警的申请单 |
alert_type |
主命中规则名 |
alert_level |
低(蓝)/中(黄)/高(红),取命中规则最高档 |
trigger_detail |
全部命中规则 + 触发详情 |
transaction_ids |
关联历史交易 ID 列表(聚合规则用) |
confidence |
预警置信度 |
status |
未处理 → 已确认 / 已排除 |
handler_id / handle_result / handle_time |
处置留痕 |
6.4 risk_rule 规则(表驱动)
20 条规则配置见 §7,字段:rule_id / rule_name / trigger_condition / threshold(JSON) / risk_level / weight / status。
6.5 biz_work_order 工单
见 §4 FR5,work_order_no 生成 WO+日期+序列。
7. 规则清单(20 条)
A 类 · 单笔规则(只看当前订单)
| 编号 | 规则名 | 触发条件 | 阈值 JSON | 级别 | 权重 |
|---|---|---|---|---|---|
| R001 | 大额申购-红 | 单笔申购金额 ≥ 100万 | {"amount":1000000} |
高 | 0.90 |
| R002 | 大额赎回-红 | 单笔赎回金额 ≥ 100万 | {"amount":1000000} |
高 | 0.90 |
| R003 | 大额申购-黄 | 单笔申购金额 ≥ 50万 | {"amount":500000} |
中 | 0.70 |
| R004 | 大额赎回-黄 | 单笔赎回金额 ≥ 50万 | {"amount":500000} |
中 | 0.70 |
| R014 | 适当性异常 | 客户风险等级 < 产品风险等级 | {} |
高 | 0.95 |
| R020 | 夜间交易 | 下单时间 00:00–06:00 | {"start":"00:00","end":"06:00"} |
低 | 0.35 |
B 类 · 聚合规则(查客户历史流水)
| 编号 | 规则名 | 触发条件 | 阈值 JSON | 级别 | 权重 |
|---|---|---|---|---|---|
| R005 | 高频交易 | 近 7 天申赎 ≥ 10 笔 | {"days":7,"count":10} |
中 | 0.65 |
| R006 | 当日密集交易 | 近 1 天申赎 ≥ 5 笔 | {"days":1,"count":5} |
中 | 0.60 |
| R007 | 密集小额申购 | 近 3 天 ≥ 5 笔、每笔 < 1万 | {"days":3,"count":5,"max_amount":10000} |
低 | 0.50 |
| R008 | 短炒赎回 | 同产品申购后 7 天内赎回 | {"days":7} |
中 | 0.65 |
| R009 | 快进快出 | 同产品申购后 3 天内全额赎回 | {"days":3} |
高 | 0.85 |
| R010 | 资金快进快出 | 入账后 24 小时内赎回 | {"hours":24} |
高 | 0.90 |
| R011 | 短期累计大额 | 近 7 天累计申赎 ≥ 200万 | {"days":7,"amount":2000000} |
高 | 0.85 |
| R012 | 中期累计大额 | 近 30 天累计申赎 ≥ 500万 | {"days":30,"amount":5000000} |
中 | 0.65 |
| R013 | 拆分规避大额 | 同 1 天多笔合计 ≥ 100万、单笔 < 50万 | {"days":1,"total":1000000,"each_lt":500000} |
中 | 0.70 |
| R018 | 全额清仓赎回 | 赎回后该产品持仓归 0 且金额 ≥ 10万 | {"amount":100000} |
低 | 0.45 |
| R019 | 对倒交易 | 近 7 天多产品互买互卖 | {"days":7} |
中 | 0.70 |
C 类 · 客户维度规则(看画像/账户/注册时间)
| 编号 | 规则名 | 触发条件 | 阈值 JSON | 级别 | 权重 |
|---|---|---|---|---|---|
| R015 | 新客户大额 | 注册 < 30 天且单笔 ≥ 50万 | {"days":30,"amount":500000} |
中 | 0.65 |
| R016 | 休眠账户激活 | 90 天无交易后首笔 ≥ 10万 | {"days":90,"amount":100000} |
低 | 0.55 |
| R017 | 整数金额 | 金额为整万且 ≥ 10万 | {"amount":100000} |
低 | 0.40 |
统计:高 6 条、中 8 条、低 6 条。
规则启用状态:R019 对倒交易初始 status=停用(实现最复杂、Mock 下难触发),其余 19 条默认启用。
8. 接口清单
8.1 交易接口(改造)
| 接口 | 说明 |
|---|---|
POST /api/purchase |
改造为前置拦截:适当性校验 → 建单 → 检测 → 落账/挂起 |
POST /api/redeem |
同上(赎回无适当性校验) |
8.2 处置接口(新增,风控专员)
| 接口 | 动作 | 副作用 |
|---|---|---|
POST /api/risk/alert/{id}/release |
放行 | 订单已确认 + 落账;预警已排除 |
POST /api/risk/alert/{id}/block |
拦截 | 订单失败 + 通知;预警已确认 + 生成工单 |
POST /api/risk/alert/{id}/freeze |
冻结 | 订单失败 + 客户冻结 + 通知;预警已确认 + 生成工单 |
8.3 工单接口(新增)
| 接口 | 动作 | 状态变化 |
|---|---|---|
GET /api/work-order/list |
列表 | — |
GET /api/work-order/{id} |
详情 | — |
POST /api/work-order/claim |
认领 | 待处理 → 处理中 |
POST /api/work-order/submit-review |
提交审核 | 处理中 → 待审核 |
POST /api/work-order/review |
复核 | 待审核 → 已完成 / 已驳回 |
8.4 规则引擎(内部服务,非 HTTP 接口)
detect(order, customer) -> 命中规则列表:加载启用规则 → 按rule_id路由到判断函数 → 传入交易数据 +threshold执行 → 汇总命中。
9. 关键设计约束(汇总)
- 单事务原子:建单 → 检测 → 落账/挂起在单个事务内完成。
- 落账复用:复用现有扣款/加仓、减仓/入账逻辑,不重写。
- 纯代码检测:规则匹配用
if/else + SQL,不用 LLM。 - 人工处置:挂起自动,放行/拦截/冻结人工,系统不自动处置。
- 防并发:处置接口条件更新
status='未处理',防重复处置。 - handler_id 后端取:不信任前端传入;处置/工单接口均校验角色权限。
- 放行重新校验余额:防挂起期间余额变化导致落账失败。
- 预警聚合:一笔订单一条预警;级别取最高;置信度按权重合成。
- 工单后置:仅拦截/冻结时生成,为处置的下游产物。
- 审计留痕:处置与工单流转均写
audit_log。
10. 实施分期
Step 1 建 5 张表的 model + repo + schema
Step 2 改 purchase/redeem:适当性校验 → 建单(待确认) → 调风控引擎
Step 3 写规则引擎(读 risk_rule + 查历史流水,输出命中列表)
Step 4 命中分支:挂起 + 生成预警;未命中:落账 + 写流水
Step 5 处置接口(放行/拦截/冻结)+ 权限 + 防并发 + 审计
Step 6 工单链(自动建单 + claim/review 接口) ← 可后置
11. 验收标准
- 正常申购/赎回:未命中规则 → 订单「已确认」、账户/持仓变动、
fin_transaction生成一条成交流水。 - 命中挂起:大额申购(如 ≥100万)→ 订单「风控挂起」、生成一条
fin_risk_alert(未处理)、账户与持仓不变。 - 放行:风控专员放行 → 订单「已确认」+ 落账、预警「已排除」。
- 拦截:风控专员拦截 → 订单「失败」、预警「已确认」、生成「可疑交易上报」工单、客户收到站内信。
- 冻结:风控专员冻结 → 订单「失败」、预警「已确认」、客户
sys_user.status=冻结、生成「冻结复核」工单。 - 防并发:同一预警被两次处置,第二次返回"已被处理",不重复落账/工单/通知。
- 权限:非风控专员调用处置接口被拒绝。
- 工单链:拦截/冻结生成的工单能走完「待处理 → 处理中 → 待审核 → 已完成/已驳回」。
- 留痕:处置与工单流转均有
audit_log记录(谁、何时、从什么态到什么态)。
12. 已确认决策清单
| # | 决策点 | 结论 |
|---|---|---|
| 1 | 风控时机 | 前置拦截(先建单 → 检测 → 通过才落账) |
| 2 | 检测方式 | 同步 + 纯规则引擎,不用 LLM |
| 3 | 落表 | trade_order 为风控主表,fin_transaction 为成交凭证 |
| 4 | 预警粒度 | 一笔订单聚合一条预警 |
| 5 | 分级置信度 | 级别取最高;confidence = 1 − Π(1 − weight_i) |
| 6 | 处置方式 | 挂起自动,放行/拦截/冻结人工 |
| 7 | 预警中间态 | 不加「处理中」,用条件更新防并发 |
| 8 | 工单时机 | 仅拦截/冻结时生成,人工处置之后 |
| 9 | 工单复核分离 | 软校验(仅提示),不强制 |
| 10 | 工单驳回 | 已驳回为终态,不回流 |
| 11 | 工单终点 | 「已完成」即终点,不上报人行 |
| 12 | R019 对倒交易 | 初始停用 |
| 13 | R014 适当性异常 | 保留作兜底 |
| 14 | 单号生成 | 前缀+日期+序列(PO... / WO...) |