JinRong · 金融四 Agent 智能管家

四个 Agent(客户财富 / 代理人助手 / 数据分析 / 风控监测)共用统一数据层与合规底座;Agent 之间不互调 LLM,跨 Agent 协作走 L1/L2/L3 画像与预警表。

本分支(risk-control-agent)= 风控 Agent 模块 + 基金转换(convert)交易的交付分支。风控模块全量交付(2026-09-07);基金转换 T+1 受理/确认分离模型已于 2026-09-12 全流程收官(AIcoding 六步:PRD → 架构 → 开发计划 → T-1~T-17 开发 → 集成测试全绿)。合并 main 的事宜见《合并注意事项》,后续人员请按下表对号入座。

你是哪种后续人员?

角色 先读
接手模块开发 / 维护 交接文档.md(本地)→ docs/memory/MEMORY.md §0 → docs/memory/TODO.md
接手 / 评审基金转换功能 docs/PRD/PRD-基金转换交易.md(v1.1)→ 架构设计-基金转换交易.md(v2.1)→ 开发计划-基金转换交易.md(v2.0,含集成测试记录 §10)
负责把模块并入 main docs/项目框架设计/合并注意事项-风控模块并入main.md(自包含操作手册)
了解需求 / 做方案评审 docs/PRD/PRD-风控监测Agent.md(v1.1)+ 附-风控规则表.md
起环境跑演示 docs/项目框架设计/演示SOP-风控模块.md + 本页「快速启动」
前端开发(接入已留接口) 本页「核心接口一览」+「前端接入契约」(chat 四端点 + 基金转换四端点,契约均有 curl/字段级样例)

最新更新(2026-09-12 · 基金转换线全流程收官)

基金转换(转出 A 基金 → 转入 B 基金)已从「一期显式拒收」放开为完整可交易,建模对齐真实业务 T+1 受理/确认分离:T 日受理(未知价、不扣份额、15:00 前可撤销)→ T+1 登记机构批量串行确认(权益扣除 + 登记)→ T+2 可查可用。

  • AIcoding 六步全流程完成:需求讨论(10 项真实业务拍板)→ PRD v1.1 → 架构 v2.1 → 开发计划 v2.0(三轮独立审核)→ T-1~T-17 逐任务开发 → 第 6 步集成测试通过。
  • 集成测试结果:全量 pytest 849 passed / 8 skipped 零失败;CONVERT_STRESS=1 真库并发 7/7;9 个真库验证脚本 9/9 退出码 0;紧池场景受理 + 串行确认 = 100% 成功率(旧实时扣减模型同场景 80%,结构性消除争抢);端到端性能 P95 94.8ms(硬门禁 2s,余量约 20 倍)。
  • 种子数据补拉:净值种子重抓至 2026-09-11(东方财富真实净值 × 14 产品,历史值零修订);交易日历经联网核实与交叉校验(169 净值日 0 偏差)无需变更。
  • 关键能力:受理幂等(client_request_id)/ 在途占用校验(可用 = 批次余量 − 未确认受理单)/ 部分成交 / 强制全转 / T 日撤单两道闸门 / 缺净值 nav_pending 挂起不降级 / SLA 超时 expired 释放 / 补差费(口径 B)/ 逐批次 FIFO 计费(<7 日 1.5% …)/ 份额位数按产品配置(half_up/truncate)/ A/C 份额类别互转开关 / 资金流中转(core_cash_flow 同事务两条)/ 规则引擎确认后恰好一次(RISK-002 计一次)。
  • 旧模型退役:v0.x 两阶段实时编排 convert_fund 及其依赖链已删除(466 行),risk_convert_detail 镜像写收敛为 sync_mirror 单点。

架构定位不变:风控 Agent 为独立封装模块——模块内自带鉴权(app/api/deps.py + app/service/auth_service.py)、防护、trace、引擎工厂;与宿主的耦合只走 4 个接缝(挂载点 / AuthContext / settings / 引擎工厂),由 tests/test_module_boundary.py 防呆锁定。详见《风控Agent模块边界与合并接缝标注.md》。

模块交付状态(截至 2026-09-12)

风控模块 + 基金转换全部完成,全量 pytest 849 passed / 8 skipped(sqlite + 真 MySQL 集成),核心接口实调验收通过。

里程碑 tag 内容
Wave 0 / T-04 / T-03 / T-21 JWT 鉴权 · 审计中间件 · chat 闭环 · LangGraph+DeepSeek · Core 只读 Tool · 输入防护 · Milvus 知识库
risk-m1 适当性校验(已换核为 C×R 矩阵契约:match_result 五值 + JR-AST/FM 规则编号)
risk-m2 事件线全链路(RISK-001~005 规则引擎 / 预警聚合 / L3 / AML / 交易网关 / 4 个 API)
risk-m3 对话线闭环(风控四只读 Tool + StateGraph risk 分支;现为五只读 Tool,C5 追加 query_overdue_alerts)
阶段一 对齐 main 基准 AL-0108(SUIT-001008 退役 → core_ro.check_suitability 矩阵判定)
risk-m4 追加需求:FR-8 集中度预警 RISK-006 / FR-9 处置时效升级 RISK-007 / FR-10 代理人行为链 RISK-008
基金转换 T+1(2026-09-12 收官) 受理/确认分离全链路:受理幂等 · 在途占用 · T+1 批量串行确认 · 部分成交 · 撤单 · 补偿与 SLA 清理 · 引擎时机收口(详见上节与本页「前端接入契约」3)

前端接入面(后端侧已齐):方案 B 会话管理三端点(8328c24)+ 方案 C SSE 流式对话(01ec5fc)+ 基金转换受理/撤单/查询/确认四端点(2026-09-12)。同步端点 POST /api/chat 契约不变,前端可任选同步/流式。详见下文「前端接入契约」。

更新说明(2026-09-09,已推送 origin/risk-control-agent,待合并 main)

给接手合并的人:本分支在 2026-09-07 模块交付后,又做了一轮「架构改进与稳定性加固」(T-101~T-202),已全部推送到远程(HEAD = fadc5e1),pytest 实测 510 passed 0 failed(较 503 基线 +7)。所有改动均为文档 / 告警 / 锁原语修正,接口契约与表结构零变更,合并 main 无新增冲突面。重点如下。

最关键的稳定性改动:锁原语升级为 Redis 双层分布式锁(T-201)

  • 改前:app/service/risk/locks.py 用进程内 threading.Lock,多实例部署会失效(各实例各锁各的,无跨进程互斥)。
  • 改后:redis_gateway.acquire_lock / release_lock(Lua 脚本,仅删自己的锁,防误删)+ run_locked 三档降级:
    1. 获取锁 → 持锁执行;
    2. 获取超时(2s)→ 无锁执行并记日志;
    3. Redis 不可用 / 任何异常 → 回退进程内锁兜底,永不抛异常。
  • 调用点三处(key 统一前缀 lock:):alert_service 交易事件预警聚合 / 适当性阻断预警(R-02)聚合 / profile_l3 写入。
  • 配套测试 tests/test_locks_redis.py(6 例)覆盖获取 / 超时 / 三档降级 / 仅删己锁。

其余改动一览

任务 内容 落点
T-101 输入防护注入词表补齐至 45 条 app/service/input_guard.py
T-102 风控只读 Tool 补 query_overdue_alerts,共 5 个只读 Tool(RISK_TOOL_REGISTRY) app/service/risk/chat_tools.py
T-103 agent 专用 SQL 注释表数 5→6 张 docs/项目框架设计/表设计/02-mysql-agent专用.sql
T-104 架构设计补 scoring.py 桩说明(一期 NotImplementedError、L3 risk_score 恒 NULL) docs/项目框架设计/架构设计-风控模块.md §2
T-105 架构设计补 locks.py 进程内锁说明(多实例失效,T-201 已升级双层) docs/项目框架设计/架构设计-风控模块.md §5.2
T-106 架构设计新增 §5.8 三项设计遗留(G1 X-Trace-Id 白名单 / G2 uuid4 概率唯一 / G3 admin·knowledge 空壳非 RAG 缺失) docs/项目框架设计/架构设计-风控模块.md §5.8
T-107 启动期缺 DEEPSEEK_API_KEY 显式告警(对话走降级前缀) app/main.py lifespan
T-108 鉴权拒绝留痕补 4 字段(code/agent/actor/trace_id) app/utils/authz.py
T-109 审计中间件降级日志补 status/path/request_id app/api/audit_middleware.py
T-202 审计中间件运行于 trace 中间件内层的顺序守卫测试(交换顺序即红) tests/test_audit_middleware.py

接手合并须知:分支 risk-control-agent 已推送远程(HEAD fadc5e1),合并 main 时本批改动无需处理新冲突——仅文档与告警/锁原语,接口契约与表结构零变更。操作手册见《docs/项目框架设计/合并注意事项-风控模块并入main.md》与本页「模块交付状态」里程碑表。

铁律(改码前必记)

  • Core 正式 C1~C5 不可被画像覆盖;Core 库只读(仅模拟交易网关可 INSERT core_trade)
  • 审计表(audit_log 等)只 INSERT
  • 代理人草稿不自动外发客户;风控不自动冻户
  • 仅适当性 R-02 可阻断交易
  • 四 Agent 不互调 LLM
  • 不改表结构(改表需用户确认);新增依赖须用户确认

后端结构

app/
├── api/          # 路由:chat、risk(4 API)、simulate、deps(JWT 鉴权工厂)、audit_middleware
├── service/      # agent_service(LangGraph StateGraph)、risk/*(rules/engine/alert/aml/L3/C4~C6)、
│                 # suitability、tool_service、auth_service、input_guard、RAG 三层
├── tool/         # core_tools(Core 只读 Tool)、kb_tools(知识库检索 Tool)
├── gateway/      # trade_gateway 模拟交易网关(唯一可写 core_trade)
├── model/        # schemas、suitability(21 列契约)、entities(29 表 ORM 参考)
├── repository/   # core_ro(Core 只读,含 check_suitability 矩阵)、risk_repository、session_repository
├── config/       # settings(双库 + risk_* 阈值 + JWT)
└── utils/        # trace、authz、response(统一错误体)、db、exceptions

scripts/          # core 灌库、demo 演示数据、kb 知识库入库、cron 定时扫描、dev 签发 token
tests/            # 30+ 模块;_ddl.py 为 sqlite DDL 单一事实源;test_module_boundary.py 锁模块边界
data/kb/          # 6 只种子产品手册(知识库语料)

环境要求

  • Python 3.13(项目依赖与 pytest 装在系统 Python 3.13.14)
  • MySQL 8.0(双库:jinrong_core 模拟 Core 只读 / jinrong_agent 业务库)、Redis 8
  • Neo4j Desktop(全图同步)、Ollama(bge-m3,1024 维,仅做 embedding)
  • Windows 原生部署,不使用本地 Docker(见技术选型文档)

快速启动

cp .env.example .env          # 填 MYSQL_PASSWORD / NEO4J_PASSWORD / DEEPSEEK_API_KEY
pip install -r requirements.txt
# 首次灌库与同步:见 docs/memory/FLOW.md §0(权威步骤)
uvicorn app.main:app --reload # → GET http://127.0.0.1:8000/health
python -m pytest              # 基线 849 用例(系统 Python 3.13.14)

演示库重灌(走查/集成测试前必做,两步缺一不可): scripts/core/reset.ps1(交互式输密码)→ scripts/demo/prepare_risk_demo.sql。 ⚠️ 只跑第一步不跑第二步 → 33 客户中 27 人风评过期、约 82% 适当性检查返回 risk_expired,这是种子设计不是 bug。 💡 非交互环境用 python scripts/dev/verify_convert_seed.py 等价替代(pymysql 重放同一组 00~11 SQL + 自动补跑演示数据 + DoD 断言);净值种子过期(真实净值只到抓取当日)时重跑 python scripts/core/fetch_nav.py --days 60 补拉。

调接口(JWT 通道):

# 签发 token(dev)
python scripts/dev/issue_dev_token.py --sub STAFF-90001 --roles risk_officer
# 调用须同时带两个头
curl -X POST http://127.0.0.1:8000/api/risk/suitability/check \
  -H "Authorization: Bearer <token>" -H "X-Agent-Type: risk" \
  -H "Content-Type: application/json" \
  -d '{"customer_id":"CUST-1001","product_id":"PROD-005827"}'

dev 环境无 Bearer 时可用 X-Debug-Role / X-Debug-Actor 头兜底(仅 development + 未配 RS256 公钥时生效)。

核心接口一览

接口 说明 权限
GET /api/risk/alerts 预警台账分页(FR-4) risk_officer / risk_manager(只读)/ compliance(仅 aml)
POST /api/risk/alerts/{id}/handle 人工处置(状态机 + 审计) risk_officer
POST /api/risk/suitability/check 适当性校验(C×R 矩阵,G-01 归属) customer 本人 / advisor 名下 / risk_officer
POST /api/risk/aml/scan 手动全量 AML 扫描(FR-5) risk_officer
POST /api/simulate/trade 模拟交易(适当性阻断 + 规则引擎;trade_type=convert 时为基金转换受理 → 202 + 受理回执) risk_demo 或客户本人
POST /api/simulate/trade/convert/{gid}/cancel 基金转换撤单(T 日 15:00 前;超窗/非 accepted → 409) 交易 owner(本人)
GET /api/simulate/trade/convert/{gid} 基金转换进度查询(受理/确认/驳回终态回执) 交易 owner / 代理人(查询 scope)
POST /api/admin/convert/confirm?accept_date= T+1 确认批处理触发(捞当日 accepted 串行确认,运维接口) risk_officer
POST /api/chat 对话(X-Agent-Type 分流四 Agent;风控线五只读 Tool) 按准入矩阵
GET /api/chat/sessions 会话列表(本人 + 本 Agent 线,分页) 按准入矩阵
GET /api/chat/sessions/{id}/messages 会话历史消息(seq_no 升序分页) 仅本人会话
POST /api/chat/sessions/{id}/close 关闭会话(active→closed) 仅本人会话
POST /api/chat/stream SSE 流式对话(OpenAI 兼容 chunk) 按准入矩阵

对话线(含上述全部 /api/chat*)仅 risk_officer:risk_manager 放行 HTTP 台账只读(GET /api/risk/alerts),进对话线一律 403(PRD 4A.1 冻结口径)。

前端接入契约

1)会话管理(方案 B)

curl "http://127.0.0.1:8000/api/chat/sessions?limit=20&offset=0" \
  -H "Authorization: Bearer <token>" -H "X-Agent-Type: risk"
# → {"items":[{session_id,agent_type,customer_id,title,status,created_at,...}],"total":2,"limit":20,"offset":0}

curl "http://127.0.0.1:8000/api/chat/sessions/<sid>/messages?limit=50&offset=0" ...
# → {"session_id":"...","items":[{seq_no,role,content,has_disclaimer,created_at}],"total":4,...}

2)流式对话(方案 C,OpenAI 兼容 chunk)

首帧  data: {"id":"<trace_id>","object":"chat.completion.chunk",
             "choices":[{"delta":{"role":"assistant"},"finish_reason":null}],
             "meta":{"session_id":"sess-…","trace_id":"…","has_disclaimer":true,
                     "disclaimer":"以上内容由 AI 生成,仅供业务参考,不构成投资建议。"}}
中间  data: {…,"choices":[{"delta":{"content":"文本块"}}]}
结束  data: {…,"choices":[{"delta":{},"finish_reason":"stop"}],"meta":{"has_disclaimer":true}}
      data: [DONE]
异常  data: {"error":{"code":"STREAM_FAILED|PERSIST_FAILED","message":"…"}} → data: [DONE]

前端须知:

  • 免责声明由首帧 meta.disclaimer 下发,前端需常驻渲染(customer/risk 线合规要求);落库文本仍按原口径拼在尾部
  • 401/403/404/409/429/400 在流开始前返回普通 JSON,只有正常流才是 text/event-stream
  • 断连/生成异常 = 整轮消息不落库(Tool 留痕仍可审计),前端需提示重试
  • 心跳帧未做:长生成空隙靠反代 proxy_read_timeout 兜(已知遗留)

3)基金转换(T+1 受理/确认分离,2026-09-12)

# ① 受理:POST /api/simulate/trade —— trade_type=convert 时走受理分支(份额申报,不是金额!)
curl -X POST http://127.0.0.1:8000/api/simulate/trade \
  -H "Authorization: Bearer <token>" -H "X-Agent-Type: customer" -H "Content-Type: application/json" \
  -d '{"trade_type":"convert","customer_id":"CUST-1001","from_product_id":"PROD-110022",
       "to_product_id":"PROD-003095","qty":"50000.00","client_request_id":"<uuid>"}'
# → 202(受理回执){"blocked":false,"accepted":true,"idempotent":false,"status":"accepted",
#     "convert_group_id":"CNV-…","client_request_id":"<uuid>",
#     "requested_qty":"48000.00","qty":"50000.00",   ← qty=实际受理份额(强制全转时与申请不同)
#     "forced_full_transfer":false,"min_hold_action":"force_transfer",
#     "accept_date":"2026-09-11","confirm_date":"2026-09-14","available_date":"2026-09-15",
#     "cancel_deadline":"2026-09-11 15:00","estimated":true}

# ② 撤单:T 日 cancel_deadline 前可撤
curl -X POST http://127.0.0.1:8000/api/simulate/trade/convert/CNV-xxx/cancel -H ...
# → 200;超窗 / 非 accepted → 409 CANCEL_NOT_ALLOWED

# ③ 进度查询(未确认时返回 processing 语义,前端可轮询)
curl http://127.0.0.1:8000/api/simulate/trade/convert/CNV-xxx -H ...
# → confirmed 后返回确认回执(两端净值/金额/费率/逐批明细/confirm_date)

# ④ T+1 确认批处理(运维/演示触发,risk_officer)
curl -X POST "http://127.0.0.1:8000/api/admin/convert/confirm?accept_date=2026-09-11" -H "X-Agent-Type: risk" ...

前端须知:

  • 入参是份额(qty),不是金额——真实业务「金额申购、份额赎回/转换」;15:00 截点后受理自动顺延下一交易日(confirm_date/available_date 回执已算好)
  • 两类 202 分辨:status=accepted = 新受理;幂等重试带同一 client_request_id 会命中在飞单(processing 语义)并回显预计确认日——网络超时后原样重发即可,不会重复受理
  • 错误码:TOO_MANY_LOTS(400,批次 >200 需拆分申请)/ INSUFFICIENT_SHARES(可用份额不足)/ CROSS_ENTITY_NOT_SUPPORTED(400,A/C 份额类别互转未开通)/ CANCEL_NOT_ALLOWED、CONCURRENT_CONFLICT(409)
  • 未知价法:受理时不折算金额,确认回执才含按 T 日净值折算的两端金额与费率;T+1 缺净值会挂 nav_pending(确认延迟,非失败)
  • requested_qty(客户申请)与 qty(实际受理)可能不同:触发最低持有余额强制全转时受理段会把指令收敛为实际全转量,前端展示以 qty 为准

文档地图

目录 / 文件 内容
docs/memory/ 项目记忆六件套(MEMORY/TODO/REQUIREMENTS/FRAMEWORK/FLOW/ENVIRONMENT),入口为 MEMORY.md §0
docs/PRD/ 风控 PRD v1.1(§4A = 追加需求)+ 风控规则表 + PRD-基金转换交易 v1.1(T+1 受理/确认分离)
docs/项目框架设计/ 架构设计 · 开发计划 · 架构设计-基金转换交易 v2.1 / 开发计划-基金转换交易 v2.0(含集成测试记录 §10) · 实现方案 C4~C6 · 表设计(SQL 单一事实源)· 技术选型与 JWT 手册 · Core 模拟底座 · 演示 SOP · 边界标注 · 合并注意事项
docs/需求拆解/ 业务场景优先级、数据交互矩阵、合规约束、四角色用户故事(docx)
docs/业务记忆管理/ 业务记忆分层手册
交接文档.md 模块开发交接(本地保留,不入库)

前端

React 19 + Vite 7 + TypeScript strict + Ant Design 5 + HashRouter——web/ 目录尚未 init,为下一主线。

后端接入面已就绪(chat + 基金转换),前端只需: ① POST /api/chat/stream 接 SSE(首帧拿 session_id/disclaimer,逐帧拼 delta.content,收到 [DONE] 收尾); ② 进页面先 GET /api/chat/sessions 拉会话列表、选中后 GET .../messages 拉历史;③ 结束会话调 POST .../close; ④ 基金转换:POST /api/simulate/trade(convert 分支)受理 → GET /api/simulate/trade/convert/{gid} 轮询进度 → T 日内可 POST .../cancel 撤单(完整契约见上节「前端接入契约」3)。 所有请求带 Authorization: Bearer + X-Agent-Type。

S
Description
金金融融的
Readme
24 MiB
Languages
Python 84.5%
TypeScript 9%
JavaScript 4.3%
CSS 1.1%
PowerShell 1%