Files
group_xinghuo_jinrong/docs/项目框架设计/开发计划-风控模块.md
T

12 KiB
Raw Blame History

开发计划 · 风控监测 Agent 模块

版本:v1.1(AI 评审通过:修复 P1×2——假 AuthContext 规范落 deps.py、conftest 测试前置;P2×7 顺手收敛)· 2026-09-06 · 分支 feature/risk · 负责人 E
依据:PRD v1.0 + 架构设计 v1.1
铁律:每步完成即测,测试通过才进下一步;每个任务至少一个 commit,里程碑打 tag;涉及表结构/接口协议的步骤已获 PRD 批准
任务映射:T-31 = 阶段 A(交付=suitability 服务函数+单测;check API 随 B6)· T-30 = 阶段 B · T-32 = 阶段 C(交付=对话线;台账/处置 API 随 B6)——此边界说明用于对齐 docs/memory/TODO.md 文字,M4 时同步修订 TODO


阶段 A · 基建与公共校验(不依赖 T-01/T-07,可立即开工)

# 任务 产出 验证方式 依赖
A1 utils/trace.py + utils/desensitize.py + settings.py 扩展(risk_* 阈值)+ .env.example 同步 3 个工具模块 + 配置 单测:脱敏 DESENS-001~004 四规则(005 资产金额归前端组件,后端不做)、trace set/get 无
A2 core_ro.py 扩展 sum_trades_on_date(其余复用现有方法) Repository 方法 单测/手工 SQL 对照 前置:本机已执行 reset.ps1 灌库(FLOW §0 ③,一次性本机操作,非等待 T-05 任务)
A3 repository/risk_repository.py(risk_alert / risk_suitability_log / l3 / aml_list 读写) Repository 手工 SQL 对照 agent 库建表(含 risk_aml_list)
A4 service/suitability.py:SUIT-001~008 纯函数 + check 服务(落日志)+ AuthContext Pydantic 模型与 get_auth_context 工厂签名占位(实现归 B6) 公共校验服务 + AuthContext 接口冻结 单测:矩阵 25 组合 + 69/70 岁 + NULL 年龄 + 364/365 天(验收 A-8) A1、A2、A3

阶段 B · 事件驱动线(核心,不依赖 T-07)

# 任务 产出 验证方式 依赖
B1 service/risk/rules.py:RISK-001~005 纯函数 规则函数 单测:各规则命中/不命中 + RISK-004 窗口 + RISK-005 非连续 A1
B2 alert_service.py:聚合去重(进程内锁 + 锁内 check-insert)+ 审计落库 + risk:pub:alert PUBLISH(同步 Redis 单例)。备注:merge 原语(append_alert_event)已下沉 repo(A3),本任务只做聚合决策与编排(评审 P2-5 口径) 预警服务 单测:聚合合并、risk_score 取 max、去重追加;并发冒烟:两线程同客户同日首单 → 预警单数=1 且 events[] 含两笔 A3
B3 profile_l3.py:get_l3 → 最高档合并 → insert_l3/update_l3。备注:非原子,并发首单需 catch IntegrityError 转更新(或复用 B2 锁)(评审 P2-7③);B3 评审后口径(P3-4 · 用户拍板 2026-09-06):L3 risk_score 一期不写(保持 NULL,归 R-05 评分模型首写),tier/tags/last_alert_id/computed_at 照 FR-7 L3 写入 单测:normal→high 不降级、AML 后大额不回落 A3
B4 aml_service.py(归一化+相似度匹配、scan_all)+ engine.py(process_trade_event 组装 + 预留客户事件钩子)+ scoring.py 占位签名(FR-7 预留) 引擎完整 单测:AML 阈值边界;引擎集成冒烟 B1、B2、B3。B4 评审修复(2026-09-06):FR-4 payload 客户上下文(L0+近 30 天统计)补组装;core_ro 增 list_trades_range(升序)/list_active_customers;种子名单 3 条改名维持唯一演示命中;core_cash_flow 上下文一期未接(PRD「如有」),B8 前评估;真实名单数据接入时 payload/审计出口接 utils/desensitize(B9b 前核查项);复审观察项:30 天窗口口径(现 31 自然日)/申赎混合求和方向,R-05 接入前统一
B5 app/gateway/(trade_gateway + gateway_repository 仅 INSERT core_trade)+ api/simulate.py 薄路由 网关 集成:convert 400、阻断不落 trade A4、B4
B6 app/api/deps.py:AuthContext(actor_id/roles/customer_id,字段按 JWT 手册冻结)+ get_auth_context() 工厂——dev 模式从 X-Debug-Role/X-Debug-Actor 请求头构造、app_env != development 启动时检测 debug 头直接拒绝;T-01 就绪后仅替换工厂内部为 JWT 解析,签名不变。另:api/risk.py 4 个 API(GET alerts / POST handle / POST suitability/check / POST aml/scan)+ 归属校验(含 compliance 强制 aml 过滤)。备注:依赖层须校验 handler_result 枚举(repo 不校验);本阶段顺手统一 NotFoundError 异常(utils/exceptions.py 现为占位)(评审 P2-7①②) 鉴权依赖 + 4 个 API Swagger 手测 + 权限矩阵(按 debug 头切换角色/身份执行 A-7/A-9 用例) A4、B2、B4(aml/scan 依赖 scan_all)
B7 main.py 集成:路由挂载 + lifespan(双 Engine 单例注入 + Redis 单例 + trace 中间件)。备注:顺手提取 utils/db.py 引擎工厂收敛 core_ro/risk_repository 双份 _default_engine(评审 P2-6);B3 挂账(B3 评审 P2-5):① _run_locked 锁原语公共化(alert_service/profile_l3 现复用私有实现)② L3 写侧 Redis 缓存 DEL 钩子(PRD §5.1 profile:l3:{customer_id} 更新时 DEL,profile_l3.upsert_profile_l3 已留痕)③ 删除 core_ro.list_trades 死代码(B4 改用 list_trades_range 后无调用方,复审 N3)④ 统一响应外壳落地(utils/response.py 现占位,simulate/risk 路由届时一并包裹,B5 评审 P2-2;错误体对齐手册 §10 error_code/message/trace_id,B6 评审 P3-2);B6 挂账(B6 复审):⑤ lifespan 启动期检测 app_env != development + debug 头依赖直接拒绝启动(现为请求时 RuntimeError,B6 评审遗漏①)⑥ 引擎工厂须覆盖 deps/simulate/risk 三处每请求 RiskRepository()/CoreReadOnlyRepository() 实例化点并 dispose(现复用 _default_engine 不释放,B6 复审 P3)⑦ handle_alert 的 update_alert_status 与 insert_audit_log 两事务非原子——统一事务或补偿记录(B6 复审 P3)⑧ input_guard_log 双写缺口(手册 P-05 要求 audit_log+input_guard_log,表归 T-03 底座,B7 接 T-02 审计中间件时统一补) 可运行应用 uvicorn 启动 + /health + 全路由可达 B5、B6

B7 复审(独立 AI 评审 · 2026-09-06):有条件通过 → 已闭环。 挂账①②③④⑤⑦证实落地、⑧确认随 T-02 无烂尾、红线全守住。P1-1 dispose_engines 仅 cache_clear 未真 dispose——已修(utils/db.py 手写单例字典 + 显式 Engine.dispose(),新增 tests/test_db.py 3 例,183 绿)。挂账:P2-1 L3 DEL 钩子无行为断言随 B8;P2-2 未捕获异常 500 无 trace 头回写 + 422/404/405 错误码补齐归 T-02;P3-1 update_alert_status 建议标注 deprecated(防绕过同事务审计)、P3-2 locks 降级文案中性化(可随 B8 顺手);P3-3 生产误配 app_env=development 检查归 B9b SOP;P3-4 独立 request_id(现沿用 trace_id)归 T-02。偏差留痕:成功响应不包裹外壳 = B5 P2-2「返回体不变」演进口径,M4 复核时对齐开发计划文本。 | B8 | tests/conftest.py:a) session fixture 启动校验演示数据就位(CUST-4001 测评 <365 天、risk_aml_list ≥8),缺失则中止并提示先跑 FLOW §0 ③④;b) fixture 幂等代跑 prepare_risk_demo.sql;c) teardown 按 TRD-TEST- 清 core_trade + 关联 risk_alert/risk_suitability_log/audit_log + 还原 L3 行。顺手集中 sqlite 测试 DDL 为单一事实源(B4 评审 P3-12,各测试文件手写 DDL 收敛;CURRENT_TIMESTAMP 改 localtime 或 fixture 固定时间,防 UTC/本地日界错位——B5 评审 P3-4);集成测试交易统一走 trade_id_factory 注入 TRD-TEST- 前缀(trade_gateway 已留参数,B5 评审 P3-2)。集成测试:A-1A-5、A-7(状态机/compliance 403/GET 强制 aml)、A-9 越权、trace 一致性断言;补 platform 审计 input_summary 的 JSON 解析断言(含引擎输出/阻断 reasons,B5 复审 L1);B7 复审 P2-1:补 L3 DEL 钩子行为断言(fake 记录 deletes,断言 key=profile:l3:{customer_id} 与降级路径);P3-2 locks 降级文案中性化顺手改 | 测试套件 + fixture | pytest 全绿 | B7 | | B9a | 演示/运维脚本开发:scripts/demo/subscribe_alerts.py(订阅演示)+ scripts/demo/rebuild_alerts.py(按 trade_id 幂等重放补偿) | 2 个脚本 | 手工执行验证 | B2、B4(可与 B5B8 并行) | | B9b | 演示链路走查:reset.ps1 → prepare_risk_demo.sql → agent 库建表 → seed-aml-list.sql → Swagger 逐条过 A-1A-5、A-7A-9(A-6 归 M3)。B6 挂账核查单:① 预警类 API 响应体含固定 disclaimer「本预警由系统自动生成,最终判定需经风控专员人工审核」(PRD §6/规则表 §5,B6 复审 P3-7)② aml/scan 幂等防护(重复扫描同命中客户重复出单,演示点击即复现,B6 评审 P3-6)③ B6 时代码以 TestClient 独立挂 router 等价验证,本次补一次真 Swagger 手测(B6 复审遗漏⑤)④ analyst 台账只读权限扩展待 Wave 3 分析 Agent 接入时定权限矩阵(手册 §5.3 有 risk:alert:read,现 fail-closed 拒绝,B6 复审观察③)⑤ 生产/演示机 app_env=development 误配检查(B7 复审 P3-3:误配时 debug 头可达且启动校验放行,列入演示 SOP) | 演示 SOP | 按 PRD §8 验收表逐条打勾 | B8、B9a |

阶段 C · 对话线(依赖 Wave 0 的 T-01 JWT / T-03 输入防护 / T-07 LangGraph)

# 任务 产出 验证方式 依赖
C0 阶段 C 开工前置:requirements 增补 pytest-asyncio(异步 Tool 单测需要) 依赖落地 pytest --version 正常 T-07 临近
C1 service/risk/chat_tools.py:alert_query / customer_context / suitability_check / aml_lookup 四个只读 Tool Tool 集 单测:各 Tool 输出结构 + alert_id 溯源 B6、T-07
C2 agent_service.py 注册 risk 分支 StateGraph(intent → tool → respond)+ DeepSeek + 输出规范(alert_id、仅供参考标注) 对话闭环 A-6:"今天有多少待审预警" C1、T-01、T-03
C3 A-6 验收 + 边界测试(诱导处置 → 拒绝并引导 API) 验收记录 手测 C2

里程碑与提交策略

里程碑 内容 提交
M1 阶段 A 完成(suitability 可用) 逐任务 commit + 打 tag risk-m1
M2 阶段 B 完成(事件线全链路,验收 A-1A-5、A-7A-9) 逐任务 commit + 打 tag risk-m2
M3 阶段 C 完成(对话线验收 A-6) 逐任务 commit + 打 tag risk-m3
M4 收尾:MEMORY/TODO 状态复核与边界文字对齐 docs: 风控模块验收记录

Commit 规范(自《03-团队分工》内联,该文档当前在仓库外):

格式: <type>: <描述>
type: feat 新功能 / fix 修复 / docs 文档 / refactor 重构 / test 测试 / chore 辅助
粒度: 每个任务至少一个 commit(A1、A2…各自独立可回溯);里程碑完成时打 tag
示例: feat: suitability 公共校验服务(SUIT-001~008)
  • 每任务完成 = 代码 + 测试通过 + 验证方式打勾 + 即时更新 docs/memory/TODO.md 勾选,方可进下一任务;M4 仅做最终复核
  • 阶段 B 期间若 T-01/T-02 就绪,B7 的 lifespan 立即接审计中间件(T-02)

已知依赖风险

  1. T-05 未执行:A2/B8 前需本机灌库(reset.ps1 + 共用底座 SQL,FLOW §0 ③④,一次性本机操作)
  2. T-01 未做 → 假 AuthContext 过渡规范(B6 落地,零返工):
    • AuthContext Pydantic 模型(actor_id / roles / customer_id)与 get_auth_context() 工厂签名在 A4 冻结
    • dev 模式(app_env=development)从 X-Debug-Role / X-Debug-Actor 请求头构造 AuthContext;生产环境(app_env != development)启动时检测到 debug 依赖注册直接拒绝启动
    • T-01 就绪后仅替换工厂内部为 JWT 解析,接口签名与调用方零改动,无双份维护
    • B8 权限类用例(A-7/A-9)通过切换 debug 头执行,可完整验证权限矩阵
  3. Ollama 未启动:不影响风控模块(无 Embedding 依赖)