Files
group_xinghuo_jinrong/docs/PRD/附-风控规则表.md
T

166 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 附表 · 风控规则表(冻结版)
> 版本:v1.1(**冻结部分 v1.0 原文不动** · 2026-09-07 追加 RISK-006/007/008 并入;变更已经独立 AI 评审→修订→复审通过,履行 PR 审批流程)
> 定位:团队共识文档。任何 Agent 涉及以下规则时,必须在此表查找,不得自行发明。
> 编号体系:`SUIT-*` 适当性 / `RISK-*` 事件规则引擎 / `AML-*` 名单 / `AI-*` AI 行为边界 / `TRADE-*` 交易时间 / `DESENS-*` 脱敏 / `MOCK-*` 模拟数据
> 与 PRD 的关系:本表是 PRD《风控监测Agent》FR-2/FR-3/FR-5/§7/§4A 引用的规则唯一权威来源。
> 变更:冻结后变更需 PR 审批并更新版本号。
---
## 1. 适当性管理规则(SUIT · 硬编码,不可绕过)
| 规则编号 | 规则内容 | 代码实现位置 | 违规后果 |
|---------|---------|-------------|---------|
| SUIT-001 | 客户风险等级 `C1` 只能购买 `R1` 产品 | `app/service/suitability.py` | 适当性违规 -10 |
| SUIT-002 | 客户风险等级 `C2` 只能购买 `R1/R2` 产品 | `app/service/suitability.py` | 适当性违规 -10 |
| SUIT-003 | 客户风险等级 `C3` 只能购买 `R1/R2/R3` 产品 | `app/service/suitability.py` | 适当性违规 -10 |
| SUIT-004 | 客户风险等级 `C4` 可购买 `R1-R4` 产品 | `app/service/suitability.py` | 适当性违规 -10 |
| SUIT-005 | 客户风险等级 `C5` 可购买 `R1-R5` 产品 | `app/service/suitability.py` | 适当性违规 -10 |
| SUIT-006 | 年龄 ≥ 70 岁的客户,适当性判定时**按最高 C3 封顶**(测评得 C4/C5 仍按 C3 校验);`age IS NULL` 跳过本条并在 reasons 标注"年龄缺失,建议人工复核" | `app/service/suitability.py` | 适当性违规 -10 |
| SUIT-007 | Agent 生成**产品推荐**前必须调用 `suitability_check()`;不匹配时**禁止生成推荐语**,必须输出拒绝话术(含原因 + 引导联系持证投顾)。推荐输出须附免责声明并标注"需经持证投顾审核",禁止具体操作指令(见 AI-006) | 客户财富 / 代理人助手 Agent | 适当性违规 -10 |
| SUIT-008 | 客户风险等级有效期为 **365 天**(`.env: RISK_ASSESSMENT_VALID_DAYS` 可配),超期后等级矩阵照常计算,但**阻断新产品购买**并提示重新测评 | `app/service/suitability.py` | 功能缺失 -10 |
### 适当性匹配矩阵(代码直接用)
```
R1 R2 R3 R4 R5
C1 ✓ ✗ ✗ ✗ ✗
C2 ✓ ✓ ✗ ✗ ✗
C3 ✓ ✓ ✓ ✗ ✗
C4 ✓ ✓ ✓ ✓ ✗
C5 ✓ ✓ ✓ ✓ ✓
```
> 判定语义(对齐 PRD FR-2):`is_matched` = 纯矩阵结果(含 SUIT-006 封顶后判定);`blocked` = 最终是否阻断(= NOT is_matched 或 SUIT-008 过期)。
---
## 2. 事件规则引擎(RISK · 风控监测 Agent)
| 规则编号 | 规则内容 | 触发阈值 | 系统动作 | 代码实现位置 |
|---------|---------|---------|---------|-------------|
| RISK-001 | 单笔大额预警 | 单笔 ≥ 500,000 元 | 预警单(pending_review)+ Redis 推送,**不自动拦截** | `app/service/risk/` |
| RISK-002 | 单日累计大额预警 | 同一客户当日申赎合计 ≥ 500,000 元(口径:`core_trade`) | 同上 | 同上 |
| RISK-003 | 频繁交易预警 | 同一客户同一产品当日申赎合计 ≥ 3 笔 | 同上(当日同客户同类型聚合出单) | 同上 |
| RISK-004 | 接近阈值试探预警 | 5 分钟内 ≥ 3 笔且每笔 ≥ 400,000 元 | 预警单(pattern) | 同上 |
| RISK-005 | 先小后大模式预警 | 当日首次大额前已存在 ≥ 3 笔 ≤ 10,000 元(不要求连续) | 预警单(pattern),文案标注模式 | 同上 |
| RISK-006 | 高风险资产配置集中度预警(v1.1) | 客户持仓中 R4+R5 市值占比 ≥ `RISK_CONCENTRATION_THRESHOLD`(默认 80%;市值口径 = `core_holding.market_value` 直接汇总) | 预警单(落库 `alert_type='pattern'` + `payload.alert_subtype='concentration'`,并入事件类聚合)+ L3 watch + Redis 推送 | `app/service/risk/rules.py` |
| RISK-007 | 预警单处置时效监控与自动升级(v1.1) | pending_review 超 `RISK_ESCALATION_L1_HOURS`/`L2_HOURS`(默认 4h/24h,AML 缩短为 1h/4h,settings 注入) | 升级推送(notify_role 追加 risk_manager/compliance)+ `payload` 写升级标记 + audit 留痕;**不改 `status`** | `app/service/risk/escalation_service.py` + `scripts/cron/escalation_scan.py` |
| RISK-008 | 代理人异常行为链识别(v1.1) | 24h 内同一代理人诱导调仓 ≥3 次;或 72h 内越权试探(`AUTH_403_SCOPE`)≥5 次;或 24h 内非名下客户查询被拒 ≥10 次(阈值/窗口 settings 注入) | 代理人维度独立预警单(落库 `alert_type='pattern'` + `payload.alert_subtype='agent_behavior'`)+ Redis 推送(risk_officer/risk_manager) | `app/service/risk/agent_behavior_service.py` + `scripts/cron/agent_behavior_scan.py` |
### ⚠️ RISK-007 状态机与升级补充约束(v1.1 追加,对照真实 DDL)
- **`risk_alert.status` ENUM 冻结五值不含 `escalated`(DDL 已核实)→ 本期不使用任何新状态值**
- 升级信息全部由 `payload` 承载:`escalation_level`(1/2)+ `escalated_at`;**LEVEL_1 与 LEVEL_2 同机制,均先持久化 payload 标记再推送**(LEVEL_1 持久化是幂等依据);该标记**仅定时任务可写**,人工处置 API 不感知不覆盖
- 升级幂等:仅当"本次计算级别 > payload 已记录级别"才执行动作,同级别不重复推送
- 每张单独立计时独立升级;同客户多张单同周期同级升级合并推送降噪
- 人工处置照常走 `pending_review → 三态`(FR-4 冻结不变);`handler_result` 枚举外值 → 422
- 定时任务审计留痕显式 `new_trace()` 生成 trace_id 并贯通 payload 与推送消息
- `escalated` 状态列入二期改表挂账
### ⚠️ RISK-008 出单维度与数据源补充约束(v1.1 追加)
- 不按客户维度聚合,按 `payload.actor_id`(代理人)+ 自然日去重(`risk_alert` 无 `actor_id` 列,代理人标识由 payload 承载;`customer_id` 冻结 NOT NULL → 填行为链主要涉及客户,payload 声明其语义为"涉及客户")
- 同代理人同日命中多个子条件(A/B/C)→ 只一张 pending 单,`triggered_rules` 追加子条件标识
- `risk_score = 75`(静态映射,R-05 评分模型接入后替换)
- **数据源统一为 `audit_log`**:条件 B = `event_type='authz' AND code='AUTH_403_SCOPE'`;条件 C = `event_type='authz' AND code IN ('AUTH_403_NOT_OWNER','AUTH_403_NOT_ASSIGNED')` ∪ 对话线 blocked 归属类拒绝(`record_authz_denial` 双写);条件 A = `event_type='trade_request'` 按发起人 actor 归属且**排除本人交易**(演示期 risk_demo 扮演代理人,见演示 SOP);`input_guard_log` 不作为数据源(网关路径不写且无 code 字段)
- **行为链明细展示范围**:仅 `risk_officer` / `risk_manager`(上级角色,staff_type='risk_officer' + roles=["risk_manager"],不改表);其他角色 fail-closed;对话线入口维持"仅 risk_officer",经理统一走 HTTP 台账只读
### ⚠️ 风控 Agent 行为边界(硬约束)
- ❌ **禁止自动冻结账户**
- ❌ **禁止自动拦截已发生交易**(唯一例外:SUIT 不匹配时阻断**交易请求**进入流程)
- ❌ **禁止自动调整客户正式风险等级(C1~C5)**
- ❌ **禁止自动上报监管**
- ✅ **只能:监测 → 预警 → 通知 → 记录**(处置由风控专员人工完成)
---
## 3. 反洗钱名单规则(AML)
| 规则编号 | 规则内容 | 说明 |
|---------|---------|------|
| AML-001 | 名单命中 → 生成 `alert_type='aml'` 最高级预警(risk_score=95)+ 紧急通知(`notify_role=["risk_officer","compliance"]`)+ 标记 L3 `monitor_tier='high'` | **不自动冻结账户**;处置由风控专员/合规官人工完成 |
| AML-002 | 匹配算法一期仅 `display_name` 归一化 + 相似度 ≥ `match_threshold`(默认 0.85);证件/银行卡匹配待 Core 提供证件数据后启用 | 名单镜像表 `risk_aml_list` |
---
## 4. 数据脱敏规则(DESENS)
| 规则编号 | 字段类型 | 脱敏规则 | 示例 | 代码实现位置 |
|---------|---------|---------|------|-------------|
| DESENS-001 | 身份证号 | 保留前 3 位 + 后 4 位,中间 `****` | `110****1234` | `app/utils/desensitize.py` |
| DESENS-002 | 手机号 | 保留前 3 位 + 后 4 位,中间 `****` | `138****1234` | 同上 |
| DESENS-003 | 姓名 | 保留姓氏,其余 `**` | `张**` | 同上 |
| DESENS-004 | 银行卡号 | 保留后 4 位,其余 `****` | `****1234` | 同上 |
| DESENS-005 | 资产金额 | 前端展示保留整数位,隐藏小数 | `¥1,234,***.**` | 前端组件 |
### 脱敏原则
- **数据库层**:存储原始数据(`customer_id` 为系统内部代理键,**不脱敏**,保证审计索引有效)
- **API 层**:返回脱敏后数据(默认)
- **前端层**:二次确认脱敏(防后端遗漏)
- **LLM 上下文**:传入 LLM 的客户数据必须已脱敏(预警 payload、通知、对话输出)
- **审计 `input_summary`**:脱敏后落库(按上表规则)
---
## 5. AI 行为边界规则(AI)
| 规则编号 | 规则内容 | 违规后果 |
|---------|---------|---------|
| AI-001 | **AI 不得代替客户执行申购、赎回、转账等交易操作** | 安全事故 -10 |
| AI-002 | 所有投资分析类输出必须附带标准免责声明 | 功能缺失 -5 |
| AI-003 | 产品推荐、投资方案必须标注「需经持证投顾审核」 | 功能缺失 -5 |
| AI-004 | AI 不得生成具体收益承诺(如「保证年化 8%」) | 安全事故 -10 |
| AI-005 | AI 不得询问客户密码、验证码、CVV 等敏感信息 | 安全事故 -10 |
| AI-006 | AI 不得输出具体操作指令("现在买入 X 万元""我帮您下单""帮你调仓"类表述一律拦截);推荐仅限产品与理由 | 安全事故 -10 |
### 标准免责声明(强制追加)
```
本内容仅为投资分析参考,不构成任何直接投资建议,不构成对任何产品的收益承诺,
据此操作风险自负,请谨慎对待。最终投资方案需经持证投资顾问审核确认。
```
### 预警系统级声明(区别于客户免责声明)
```
本预警由系统自动生成,最终判定需经风控专员人工审核。
```
---
## 6. 交易时间规则(TRADE · 本期风控不涉及,供后续模块沿用)
| 规则编号 | 规则内容 | 代码实现位置 |
|---------|---------|-------------|
| TRADE-001 | 交易日为周一至周五(不含法定节假日) | `app/utils/trade_calendar.py`(后续模块) |
| TRADE-002 | 交易日 15:00 前提交的申购/赎回,按 T 日净值处理 | 交易模块 |
| TRADE-003 | 交易日 15:00 后提交的申购/赎回,按 T+1 日净值处理 | 交易模块 |
| TRADE-004 | 基金申购确认份额一般为 T+1,查询持仓为 T+2 | 交易模块 |
| TRADE-005 | 非交易日提交的申请,顺延至下一个交易日处理 | 交易模块 |
> 模拟交易网关一期不校验交易时间窗口(演示可随时发起),真实交易系统接入后由其自行遵守。
---
## 7. Mock 数据一致性规则(MOCK)
| 规则编号 | 规则内容 | 校验脚本 |
|---------|---------|---------|
| MOCK-001 | 客户年龄范围:18-80 岁 | `validate_customer_age()` |
| MOCK-002 | 身份证号格式:18 位,校验位正确 | `validate_id_card()` |
| MOCK-003 | 手机号:11 位,以 1 开头 | `validate_phone()` |
| MOCK-004 | 开户时间 < 首次交易时间 | `validate_timeline()` |
| MOCK-005 | 风险测评时间 < 首次购买时间 | `validate_timeline()` |
| MOCK-006 | 单笔交易金额 ≤ 账户总资产 × 120% | `validate_trade_amount()` |
| MOCK-007 | 客户风险等级与年龄匹配(≥70岁最高C3) | `validate_risk_age_match()` |
| MOCK-008 | 交易记录的产品风险等级 ≤ 客户风险等级 | `validate_trade_suitability()` |
| MOCK-009 | 产品收益率范围:货币 1.5%-3%,债券 3%-6%,混合 5%-15%,股票 -20%~30% | `validate_product_return()` |
---
*本表 v1.0 冻结(2026-09-06),v1.1 追加 RISK-006/007/008(2026-09-07 经独立评审→修订→复审通过后并入);任何规则变更需经团队评审并更新版本号。原本地《02-业务规则表》停止维护,以本表为准。*