2026-09-07 01:36:44 +08:00
|
|
|
|
# 演示 SOP · 风控监测 Agent(B9b)
|
|
|
|
|
|
|
2026-09-07 01:59:15 +08:00
|
|
|
|
> 版本:v1.1 · 2026-09-07 · 配套 PRD v1.0 §8 验收表 + 开发计划 B9b 核查单
|
2026-09-07 01:36:44 +08:00
|
|
|
|
> 适用:演示机/本机(`app_env=development`,debug 鉴权头可用;生产部署禁用,见核查单⑤)
|
2026-09-07 01:59:15 +08:00
|
|
|
|
> 首次走查记录:2026-09-07,A-1~A-5/A-7/A-9(A-6 归 M3 对话线、A-8 由单测矩阵 + P2-8 手工 SQL 对照覆盖)+ 核查单①~⑥ 全部通过(11/11 PASS)
|
2026-09-07 01:36:44 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 前置条件
|
|
|
|
|
|
|
|
|
|
|
|
- 本机 bootstrap ①~⑤ 已执行(`.env`、依赖、双库、种子)——见 `docs/memory/FLOW.md` §0;
|
|
|
|
|
|
已就位机器勿重跑,只按本 SOP §2 重灌即可。
|
|
|
|
|
|
- `redis` 包已安装(requirements 内;B9a 曾发现环境漏装,重装环境时留意)。
|
|
|
|
|
|
- 本机 MySQL 可达、Redis 服务在跑(订阅演示需要;不可达时推送降级仅日志,走查其余项不受影响)。
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 演示链路重灌(每次演示/走查前执行)
|
|
|
|
|
|
|
|
|
|
|
|
**目的**:清空上一轮演示/走查残留(trade/预警/审计/L3),使验收断言从干净状态出发。
|
|
|
|
|
|
|
|
|
|
|
|
**脚本化方式(推荐,自动化执行不卡交互)**——Git Bash:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
export MYSQL_PWD=<密码>
|
|
|
|
|
|
MYSQL="C:/Program Files/MySQL/MySQL Server 8.0/bin/mysql.exe" # mysql.exe 不在 PATH(FLOW §0 坑)
|
|
|
|
|
|
"$MYSQL" -h 127.0.0.1 -u root -e "DROP DATABASE IF EXISTS jinrong_core; DROP DATABASE IF EXISTS jinrong_agent;"
|
|
|
|
|
|
for f in scripts/core/00-create-database.sql scripts/core/01-ddl.sql scripts/core/02-seed-base.sql \
|
|
|
|
|
|
scripts/core/03-seed-customers.sql scripts/core/04-seed-holdings.sql scripts/core/05-seed-trades.sql \
|
|
|
|
|
|
scripts/core/06-seed-nav.sql "docs/项目框架设计/表设计/01-mysql-共用底座.sql" \
|
|
|
|
|
|
"docs/项目框架设计/表设计/02-mysql-agent专用.sql" scripts/agent/seed-aml-list.sql \
|
|
|
|
|
|
scripts/demo/prepare_risk_demo.sql; do
|
|
|
|
|
|
"$MYSQL" -h 127.0.0.1 -u root --default-character-set=utf8mb4 < "$f"
|
|
|
|
|
|
done
|
|
|
|
|
|
python scripts/sync/sync_advisor_rel.py # 归属同步(28 行)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-07 01:59:15 +08:00
|
|
|
|
**交互方式**:`.\scripts\core\reset.ps1`(PowerShell,交互输密码;`-SkipNeo4j` 可跳过 Neo4j 同步)。
|
|
|
|
|
|
注意:reset.ps1 **只重建 jinrong_core**,且 agent 建表 SQL 是裸 `CREATE TABLE`——
|
|
|
|
|
|
交互方式需先手动 `DROP DATABASE IF EXISTS jinrong_agent;` 再跑后 4 个 SQL,否则
|
|
|
|
|
|
ERROR 1050(表已存在)中止且残留未清。脚本化方式已含双库 DROP,可照抄。
|
|
|
|
|
|
(两种方式均不含 Neo4j 同步;风控演示不依赖 Neo4j,需要时手动跑 `scripts/sync/sync_neo4j.py`。)
|
2026-09-07 01:36:44 +08:00
|
|
|
|
|
|
|
|
|
|
**重灌完成标志**:`seed-aml-list.sql` 输出 8 条名单;`prepare_risk_demo.sql` 输出 7 行测评
|
|
|
|
|
|
(6 行 90 天、CUST-1004 保持过期);`sync_advisor_rel` 输出 upserted 28 rows。
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 启动与入口
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
uvicorn app.main:app --reload # Swagger: http://127.0.0.1:8000/docs
|
|
|
|
|
|
python scripts/demo/subscribe_alerts.py # 另开终端:订阅预警推送(Ctrl+C 退出)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-07 03:08:27 +08:00
|
|
|
|
debug 鉴权头(dev 兜底通道,T-01 已接 JWT):`X-Debug-Role` + `X-Debug-Actor`。
|
2026-09-07 01:36:44 +08:00
|
|
|
|
走查角色:`risk_officer`(STAFF-90001) / `compliance`(STAFF-40001) / `customer`(CUST-1001/CUST-1002)
|
|
|
|
|
|
/ `advisor`(STAFF-10087,名下无 CUST-3001) / `risk_demo`(STAFF-DEMO,simulate 白名单)。
|
|
|
|
|
|
|
2026-09-07 03:08:27 +08:00
|
|
|
|
JWT 通道(T-01,可选演示):`python scripts/dev/issue_dev_token.py --sub STAFF-30001 --roles risk_officer`
|
|
|
|
|
|
签发后请求带 `Authorization: Bearer <token>` + `X-Agent-Type: risk`(JWT 通道强制 X-Agent-Type
|
|
|
|
|
|
交叉校验;debug 头与 JWT 不可同时生效时 JWT 优先)。
|
|
|
|
|
|
|
2026-09-07 01:36:44 +08:00
|
|
|
|
## 4. 验收走查(PRD §8 · A-6 归 M3 对话线)
|
|
|
|
|
|
|
|
|
|
|
|
Swagger 逐条执行;预期响应与库内核验点如下(`trade_id` 以实际响应为准)。
|
|
|
|
|
|
|
|
|
|
|
|
| # | 操作(Swagger) | 预期 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| A-1 | `POST /api/simulate/trade`,customer 头 CUST-1001,body:CUST-1001 / PROD-161725 / subscribe / 10000 | `blocked=true`,`block_reason` 含 SUIT-001;库核验:`core_trade` 无该 trade_id;`risk_suitability_log.is_blocked=1`;`risk_alert`(trade_id 定位)suitability/pending;audit `suitability_blocked`(agent_type=platform);订阅端收到推送且 trace 与响应头一致 |
|
|
|
|
|
|
| A-2 | 同上,risk_demo 头,CUST-4001 / PROD-161725 / subscribe / 20000 | `blocked=true`,reasons 含 SUIT-006(≥70 按 C3)与 SUIT-003,**无 SUIT-008** |
|
|
|
|
|
|
| A-3 | 同上,CUST-3001 / PROD-510300 / subscribe / 500000 | `blocked=false`,`triggered_rules=["RISK-001","RISK-002"]`,预警单 score=70/pending;`core_trade` 落库;audit `trade_accepted`;**订阅端实时打印推送** |
|
|
|
|
|
|
| A-4 | 同上,CUST-9527 / PROD-510300 / subscribe / 1000,连发 4 笔 | 第 3 笔触发 RISK-003;第 4 笔 `alert_ids` 与第 3 笔相同(并入同单),该客户 freq 类单数=1,payload.events 长度=2 |
|
|
|
|
|
|
| A-5 | 同上,CUST-1002 / PROD-005828 / subscribe / 100 | `aml_hit=true`;aml 独立单 score=95(按本笔 trade_id 定位);L3 `monitor_tier=high`;compliance 头 `GET /api/risk/alerts` 仅见 aml 单且含该单;`core_customer.is_active` 仍=1(不冻户) |
|
|
|
|
|
|
| A-7 | `POST /api/risk/alerts/{A-3的alert_id}/handle`,officer 头,`confirmed_suspicious` → 再处置一次 → 换 compliance 头再试 | 200(状态 confirmed_suspicious,handler_id=STAFF-90001)→ 409 STATE_CONFLICT → 403;audit 留痕(alert_handle/alert_handled) |
|
|
|
|
|
|
| A-9 | `POST /api/risk/suitability/check`:customer CUST-1002 查 CUST-3001;advisor STAFF-10087 查 CUST-3001 | 403 `AUTH_403_NOT_OWNER` / `AUTH_403_NOT_ASSIGNED`;audit `event_type=authz, decision=forbidden` 各 1 条 |
|
|
|
|
|
|
|
|
|
|
|
|
**B9a 脚本演示(运维位)**:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python scripts/demo/rebuild_alerts.py <trade_id> # 补偿重放:rebuilt → 再跑 skipped → 不存在 exit 1
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-07 01:59:15 +08:00
|
|
|
|
**A-8 映射交代**:`suitability_check` 单元级验收不在 Swagger 走查——由单测矩阵(25 组合 + 边界,
|
|
|
|
|
|
`tests/test_suitability.py`)与真库手工 SQL 对照(P2-8 收口:4 组演示组合 SQL 推导与函数判定一致)覆盖。
|
|
|
|
|
|
|
2026-09-07 01:36:44 +08:00
|
|
|
|
## 5. 核查单(B6/B7/B8 挂账收口)
|
|
|
|
|
|
|
|
|
|
|
|
| # | 挂账来源 | 检查项 | 结论(2026-09-07 走查) |
|
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
|
| ① | B6 复审 P3-7 | 预警类 API(GET alerts / POST handle / POST aml/scan)响应体含固定 `disclaimer`「本预警由系统自动生成,最终判定需经风控专员人工审核。」 | ✅ 已实现并真服务验证 |
|
|
|
|
|
|
| ② | B6 评审 P3-6 | aml/scan 幂等:同客户当日已有 aml 单(含交易触发/已处置)不重复出单,响应 `skipped_existing` 指向既有单 | ✅ 已实现并真服务验证 |
|
2026-09-07 01:59:15 +08:00
|
|
|
|
| ③ | B6 复审遗漏⑤ | 真服务手测(区别于 TestClient 等价验证):uvicorn 真库走查 A-1~A-5/A-7/A-9(A-6 归 M3、A-8 见 §4 映射交代) | ✅ 本 SOP §4 即该手测 |
|
2026-09-07 01:36:44 +08:00
|
|
|
|
| ④ | B6 复审观察③ | analyst 角色台账 fail-closed:`GET /api/risk/alerts` → 403(risk:alert:read 权限扩展待 Wave 3 分析 Agent 时定矩阵) | ✅ 403 保持 |
|
|
|
|
|
|
| ⑤ | B7 复审 P3-3 | 生产/演示机误配检查:本机 `.env` `APP_ENV=development`(演示机允许 debug 头);`app_env != development` 时启动期拒绝 debug 依赖(单测覆盖),部署非 dev 机时必须核对 | ✅ 本机=development 已确认 |
|
2026-09-07 01:59:15 +08:00
|
|
|
|
| ⑥ | B8 复审 P2-1 | 集成测试对演示库当日状态的隐式耦合 → 已加前置断言(A-1/A-3/A-4 清洁检查 fail+提示、A-5 按 trade_id 定位、A-4/A-9 COUNT 断言加当日窗),代码随 B9b 落地 | ✅ 已落地;交叉数据在场时 pytest 显式 fail 提示重灌 |
|
|
|
|
|
|
| 附 | 阶段 A 评审 P2-8(T-31 名下) | `suitability_check` 真库手工 SQL 对照:4 组演示组合(SUIT-001 首因 / 70 岁封顶 / 等级匹配 / SUIT-008 过期)SQL 推导与函数判定逐项一致 | ✅ 2026-09-07 收口 |
|
2026-09-07 01:36:44 +08:00
|
|
|
|
|
|
|
|
|
|
## 6. 走查后状态与注意事项
|
|
|
|
|
|
|
|
|
|
|
|
- 走查产生的交易/预警/审计/L3 **保留在演示库**(演示痕迹即演示资产);再次演示前回到 §2 重灌。
|
|
|
|
|
|
- **重灌后才能跑 pytest 集成测试**:核查单⑥前置断言会对当日演示数据显式 fail(防交叉失真)。
|
|
|
|
|
|
- B9a subscribe 窗口未开时推送不丢失告警——Redis Pub/Sub 无持久化,演示时先开订阅再发交易。
|
|
|
|
|
|
- A-6(对话线验收)归 M3(T-01/T-03/T-07 就绪后补)。
|