diff --git a/AGENTS.md b/AGENTS.md index dc2ef09..5ac91bb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,8 @@ ## 代码入口 ```text -app/main.py # FastAPI:路由挂载 + trace/audit 中间件 + lifespan(风控 4 API + /api/simulate + /api/chat 已挂载) +app/main.py # FastAPI:路由挂载 + trace/audit 中间件 + lifespan(风控 4 API + /api/simulate + /api/chat 四端点已挂载) +app/api/chat.py # 对话:POST ""(同步)+ GET /sessions + GET /sessions/{id}/messages + POST /sessions/{id}/close(方案 B)+ POST /stream(SSE 流式,方案 C) app/api/deps.py # 鉴权工厂(T-01 JWT:Bearer 优先,dev debug 头兜底)+ 归属断言 app/service/auth_service.py # Auth SDK:JWT 验签/吊销(T-01);签发 CLI scripts/dev/issue_dev_token.py app/service/agent_service.py # LangGraph StateGraph 骨架 + DeepSeek(T-07) diff --git a/README.md b/README.md index 6e475e3..4dc9f89 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ | 了解需求 / 做方案评审 | [docs/PRD/PRD-风控监测Agent.md](docs/PRD/PRD-风控监测Agent.md)(v1.1)+ [附-风控规则表.md](docs/PRD/附-风控规则表.md) | | 起环境跑演示 | [docs/项目框架设计/演示SOP-风控模块.md](docs/项目框架设计/演示SOP-风控模块.md) + 本页「快速启动」 | -## 模块交付状态(截至 2026-09-07) +## 模块交付状态(截至 2026-09-08) -**后端功能全部完成,全量 pytest 482 passed 0 failed(sqlite + 真 MySQL 集成),核心接口实调验收通过。** +**后端功能全部完成,全量 pytest 503 passed 0 failed(sqlite + 真 MySQL 集成),核心接口实调验收通过。** | 里程碑 tag | 内容 | | --- | --- | @@ -26,6 +26,8 @@ | 阶段一 | 对齐 main 基准 AL-01~08(SUIT-001~008 退役 → core_ro.check_suitability 矩阵判定) | | `risk-m4` | 追加需求:FR-8 集中度预警 RISK-006 / FR-9 处置时效升级 RISK-007 / FR-10 代理人行为链 RISK-008 | +**前端接入面(2026-09-08 补齐,后端侧已齐)**:方案 B 会话管理三端点(`8328c24`)+ 方案 C SSE 流式对话(`01ec5fc`)。同步端点 `POST /api/chat` 契约不变,前端可任选同步/流式。详见下文「前端接入契约」。 + **架构定位**:风控 Agent 为**独立封装模块**——模块内自带鉴权(`app/api/deps.py` + `app/service/auth_service.py`)、防护、trace、引擎工厂;与宿主的耦合只走 4 个接缝(挂载点 / AuthContext / settings / 引擎工厂),由 `tests/test_module_boundary.py` 防呆锁定。详见《[风控Agent模块边界与合并接缝标注.md](docs/项目框架设计/风控Agent模块边界与合并接缝标注.md)》。 ## 铁律(改码前必记) @@ -70,7 +72,7 @@ cp .env.example .env # 填 MYSQL_PASSWORD / NEO4J_PASSWORD / DEEPSEEK_A 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 # 基线 482 用例(系统 Python) +python -m pytest # 基线 503 用例(系统 Python) ``` **演示库重灌(走查/集成测试前必做,两步缺一不可)**: @@ -101,6 +103,44 @@ dev 环境无 Bearer 时可用 `X-Debug-Role` / `X-Debug-Actor` 头兜底(仅 | `POST /api/risk/aml/scan` | 手动全量 AML 扫描(FR-5) | risk_officer | | `POST /api/simulate/trade` | 模拟交易(适当性阻断 + 规则引擎) | risk_demo 或客户本人 | | `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)** + +```bash +curl "http://127.0.0.1:8000/api/chat/sessions?limit=20&offset=0" \ + -H "Authorization: Bearer " -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//messages?limit=50&offset=0" ... +# → {"session_id":"...","items":[{seq_no,role,content,has_disclaimer,created_at}],"total":4,...} +``` + +**2)流式对话(方案 C,OpenAI 兼容 chunk)** + +``` +首帧 data: {"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` 兜(已知遗留) ## 文档地图 @@ -116,3 +156,5 @@ dev 环境无 Bearer 时可用 `X-Debug-Role` / `X-Debug-Actor` 头兜底(仅 ## 前端 React 19 + Vite 7 + TypeScript strict + Ant Design 5 + HashRouter——`web/` 目录尚未 init,为下一主线。 + +后端接入面已就绪,前端只需三件事:① `POST /api/chat/stream` 接 SSE(首帧拿 `session_id`/`disclaimer`,逐帧拼 `delta.content`,收到 `[DONE]` 收尾);② 进页面先 `GET /api/chat/sessions` 拉会话列表、选中后 `GET .../messages` 拉历史;③ 结束会话调 `POST .../close`。所有请求带 `Authorization: Bearer` + `X-Agent-Type`。 diff --git a/docs/memory/MEMORY.md b/docs/memory/MEMORY.md index 63c817a..5eefa6e 100644 --- a/docs/memory/MEMORY.md +++ b/docs/memory/MEMORY.md @@ -17,15 +17,15 @@ | --- | --- | --- | | `app/main.py` | **已集成** | 路由挂载 + trace 中间件(X-Trace-Id/X-Request-Id 贯通 + 500 兜底错误体)+ audit 中间件(T-02 http_access)+ lifespan(jwt_ready 校验 · Redis 网关注册 · 引擎 dispose) | | `app/api/risk.py` `simulate.py` `deps.py` | **已实现** | 风控 4 API + 模拟网关路由 + **JWT 鉴权工厂(T-01:Bearer 全环境优先;debug 头仅 dev+无 RS256 公钥时兜底;AGENT_ACCESS_MATRIX 准入)** | -| `app/api/chat.py` `audit_middleware.py` | **已实现(T-06/T-02)** | POST /api/chat(SessionGuard+归属+窗口+落盘)· http_access 访问审计 | +| `app/api/chat.py` `audit_middleware.py` | **已实现(T-06/T-02 + 前端接入 B/C)** | POST /api/chat(SessionGuard+归属+窗口+落盘)· **前端拉侧三端点**(`GET /sessions` / `GET /sessions/{id}/messages` / `POST /sessions/{id}/close`,方案 B `8328c24`)· **SSE 流式 `POST /api/chat/stream`**(OpenAI 兼容 chunk,方案 C `01ec5fc`);守卫抽 `_guard_request`/`_prepare_turn`/`_guard_session` 同步流式共用 · http_access 访问审计 | | `app/api/knowledge.py` `admin.py` | 空壳 | 待审计查询台与知识库 API(T-21 拍板一期只做脚本入库,上传/重建端点不做) | | `app/service/embedding.py` `milvus_service.py` `rag_service.py` | **已实现(T21-1/2/4)** | Ollama bge-m3 1024 维(失败不静默降级)/ kb_product_rules 建集合+upsert+合规过滤检索 / search_knowledge→chunks+source_refs 溯源 | | `app/tool/kb_tools.py` | **已实现(T21-5)** | search_knowledge 对话 Tool(skip_access_check 公开知识;仅 customer/advisor 意图开放,risk 不开放);kb_product_rules 首批 24 块已入库(data/kb 6 产品手册,scripts/kb/build_kb.py 可重灌) | | `app/service/auth_service.py` | **已实现(T-01)** | Auth SDK:JWT 验签(HS256 dev/RS256 生产)、必填 claims、jti 吊销(Redis fail-open);签发 CLI `scripts/dev/issue_dev_token.py` | | `app/service/risk/*` + `service/suitability.py` | **已实现** | RISK-001~005 规则 / 预警聚合 / L3 写入 / AML / 引擎编排 / suitability(**阶段一换核:内核改调 core_ro.check_suitability,SUIT-001~008 已退役**)/ locks+redis_gateway 公共原语(B7) | -| `app/service/agent_service.py` `memory_service.py` | **已实现(T-07/T-06/T-04/C2)** | LangGraph StateGraph(tool→llm→guard)+ DeepSeek(无 key 降级);T-04 tool 节点(关键词意图 customer/advisor/risk 分组 + tool_service.run_tool);C2 tool_node 守卫放宽(requires_customer=False 允许无绑定客户,支撑 A-6 全量待审);Redis 会话窗口 + MySQL 回源 | +| `app/service/agent_service.py` `memory_service.py` | **已实现(T-07/T-06/T-04/C2 + 方案 C)** | LangGraph StateGraph(tool→llm→guard)+ DeepSeek(无 key 降级);T-04 tool 节点(关键词意图 customer/advisor/risk 分组 + tool_service.run_tool);C2 tool_node 守卫放宽(requires_customer=False 允许无绑定客户,支撑 A-6 全量待审);Redis 会话窗口 + MySQL 回源。**方案 C:`stream_chat` 生成器(Tool 同步跑完→逐块推 LLM 文本)+ `needs_disclaimer`(首帧 meta 与落库尾部共用口径)** | | `app/service/tool_service.py` `app/tool/core_tools.py` `service/risk/chat_tools.py` | **已实现(T-04/C1)** | 对话 Tool 编排(统一注册表 get_registered_tool:core+risk / 归属校验 / agent_tool_call 落库)+ Core RO 三只读 Tool(L0/持仓/流水)+ C1 风控四只读 Tool(alert_query/customer_context/suitability_check/aml_lookup,RISK_TOOL_REGISTRY) | -| `app/repository/session_repository.py` | **已实现(T-06/T-04)** | agent_session / agent_message / agent_tool_call 读写 | +| `app/repository/session_repository.py` | **已实现(T-06/T-04 + 前端接入 B/C)** | agent_session / agent_message / agent_tool_call 读写;**方案 B 增 `list_sessions`(分页+total)/ `list_messages_page`(seq 升序分页,勿与 LLM 窗口的 `list_messages` 混用)/ `close_session`(条件更新防并发)**;**方案 C 增 `insert_turn`(user+assistant 同事务落库 + 事务内取 seq,修评审 P0/P1)** | | `app/gateway/` | **已实现** | 模拟交易网关(仅 gateway_repository 可 INSERT core_trade,B5) | | `app/repository/core_ro.py` | **已实现** | Core 只读 SELECT(含风控扩展 sum_trades_on_date / list_trades_range / list_active_customers);**阶段一吸收 main:check_suitability(C×R 矩阵判定,CURDATE() 改 Python 端 `_is_expired`)/ list_products_for_customer / list_holdings 合并(limit=500+新列)/ list_trades / get_customer_l0 扩列版** | | `app/repository/risk_repository.py` | **已实现** | risk_alert / risk_suitability_log / L3 / risk_aml_list / audit_log / input_guard_log 读写 | @@ -34,7 +34,7 @@ | `scripts/core/*.sql` + `reset.ps1` | **已实现** | Core 模拟库 DDL + 种子 | | `scripts/agent/` `scripts/demo/` `scripts/dev/` | **已实现** | AML 名单种子 + 风控演示数据 + subscribe_alerts/rebuild_alerts + issue_dev_token(JWT 签发) | | `scripts/sync/*.py` | **已实现** | 归属同步 + Neo4j 全图 | -| `tests/` | **已实现** | 30 个测试模块 406 用例(sqlite 隔离;DDL 单一事实源 `_ddl.py`;`test_integration_risk.py` 走真 MySQL + TRD-TEST- 前缀隔离;pytest 依赖装在系统 Python 3.13.14;阶段一 test_suitability 按 main 契约重写后基线 425→406) | +| `tests/` | **已实现** | 36 个测试模块 503 用例(sqlite 隔离;DDL 单一事实源 `_ddl.py`;`test_integration_risk.py` 走真 MySQL + TRD-TEST- 前缀隔离;pytest 依赖装在系统 Python 3.13.14;阶段一基线 425→406,前端接入 B/C 后 **482→503**)。**改路由必同步 `tests/test_main.py::test_all_routers_mounted` 的路径清单,否则必红** | | `docs/需求拆解/` | 已定 | 场景 P0、矩阵、合规原文 | | `docs/PRD/PRD-风控监测Agent.md` | **已冻结(v1.1)** | 风控 PRD v1.0 + v1.1 追加 FR-8/9/10(§4A)+ 规则表附录 | | `docs/项目框架设计/实现方案-风控追加需求v1.1-C4C6.md` | **已定稿** | C4~C6 编码依据(经独立 AI 评审修订闭环);分支/进度速览另见项目根 `交接文档.md` | @@ -60,6 +60,8 @@ > **本机 ①~⑤已执行、`.env` 已配置,勿重做**;本机状态与已知坑(mysql.exe 路径 / reset.ps1 交互式 -p / `redis`、`python-jose` 包漏装已补)见 `FLOW.md` §0 尾注。 > **演示库状态(2026-09-07)**:已按演示 SOP §2 重灌并清理冒烟数据(alerts 0 / AML 8 / 会话 0),pytest 全量可直接跑;**下次演示/走查后再按 SOP §2 重灌**(核查单⑥前置断言会对走查残留显式 fail,属防护行为)。 +**前端接入面已交付(2026-09-08,503 绿)**:**方案 B** 会话管理三端点(`GET /api/chat/sessions`、`GET /api/chat/sessions/{id}/messages`、`POST /api/chat/sessions/{id}/close`,commit `8328c24`)+ **方案 C** SSE 流式对话 `POST /api/chat/stream`(OpenAI 兼容 chunk:首帧 meta 带 disclaimer → delta → finish_reason=stop → `[DONE]`,commit `01ec5fc`)。三条硬约束:① 守卫全在返回 StreamingResponse 之前(SSE 一开改不了状态码,401/403/404/409/429/400 仍普通 JSON);② 整轮一次性落库(`insert_turn` 同事务),断连/异常不落消息;③ risk_manager 在对话线数据面一律 403(PRD 4A.1)。遗留 P2:无心跳帧、断连留空会话、流式 429 未单测。 + **下一步开发(见 TODO):** **模块侧交付完毕(2026-09-07:全量 pytest 482 绿 + 接口实调验收通过——suitability/check 阻断+放行、simulate/trade 阻断、三条鉴权边界 401/403/403 契约零偏差;`risk-m1~m4` tag 齐)。合并 main 已移交合并执行人,操作手册《docs/项目框架设计/合并注意事项-风控模块并入main.md》(含基底锁定/20 冲突裁决/14 静默文件/三硬伤/合并后必测,实测数据编制)。模块侧开放项:chat 链路 risk_suitability_log.actor_id 落 SYSTEM 待评估 / 前端 React 多 Agent 入口(`web/` 未 init,归属待拍板)。**演示走查按 `docs/项目框架设计/演示SOP-风控模块.md`(debug 头通道仍有效;JWT 通道签发用 `scripts/dev/issue_dev_token.py`;演示库已按 AL-08 expires_at 新口径重灌)。知识库入库:`python scripts/kb/build_kb.py`(先启 Ollama;**Milvus 数据路径必须纯英文**——faiss 不支持中文路径,本机 .env 已配 C:/Users/YUAN/.jinrong/milvus/)。 **禁止(改代码前必记):** Core 正式 C1~C5 不可被画像覆盖 · 审计表只 INSERT · 代理人草稿不外发 · 仅 R-02 可阻断交易 · 四 Agent 不互调 LLM。 @@ -79,8 +81,8 @@ ## 1. 项目简介 - **名称:** JinRong 金融四 Agent 智能管家 -- **当前阶段:** **模块交付完毕(2026-09-07)**——阶段一 AL-01~08 + 阶段二 C4~C6 全部完成,482 绿,接口实调验收通过(AI 代验),`risk-m1~m4` tag 齐;合并 main 已移交合并执行人(手册:《合并注意事项-风控模块并入main.md》) -- **当前优先级:** 模块侧开放项(actor_id 透传评估 / 前端 React 入口归属拍板);合并进度由合并执行人负责,模块侧不阻塞 +- **当前阶段:** **模块交付完毕(2026-09-07)+ 前端接入面交付(2026-09-08)**——阶段一 AL-01~08 + 阶段二 C4~C6 + 前端接入 B/C 全部完成,**503 绿**,接口实调验收通过(AI 代验),`risk-m1~m4` tag 齐;合并 main 已移交合并执行人(手册:《合并注意事项-风控模块并入main.md》) +- **当前优先级:** 前端 React 入口(`web/` init,后端接入面已就绪)/ 模块侧开放项(actor_id 透传评估);合并进度由合并执行人负责,模块侧不阻塞 ------ @@ -143,7 +145,7 @@ Core 模拟:scripts/core/reset.ps1 · 文档 docs/项目框架设计/Core模 种子:scripts/agent/seed-aml-list.sql(AML 名单)· scripts/demo/prepare_risk_demo.sql(reset 后重跑) 依赖:requirements.txt(LangGraph + langchain-core/openai + FastAPI + SQLAlchemy) 启动:uvicorn app.main:app --reload → GET /health -测试:python -m pytest(321 用例;集成测试需本机演示数据,未灌库时自动 skip) +测试:python -m pytest(503 用例;集成测试需本机演示数据,未灌库时自动 skip) 运维/演示脚本:scripts/demo/subscribe_alerts.py(订阅推送演示)· rebuild_alerts.py TRD-xxx(引擎异常补偿重放) JWT 联调:python scripts/dev/issue_dev_token.py --sub STAFF-30001 --roles risk_officer(+ Authorization: Bearer + X-Agent-Type) 配置:.env(见 .env.example) @@ -187,6 +189,6 @@ RBAC 联调账号:scripts/dev/rbac-seed-reference.md 2. 改动属于 api / service / tool / repository 哪一层? 3. 是否需 customer_id 归属与 JWT RBAC? 4. Core 是模拟库只读还是 agent 库读写? -5. 如何验证?(`python -m pytest` 全量(当前 321 绿)· uvicorn 启动 + /health · SQL / sync 脚本 · 对照 REQUIREMENTS 验收列) +5. 如何验证?(`python -m pytest` 全量(当前 **503 绿**)· uvicorn 启动 + /health · SQL / sync 脚本 · 对照 REQUIREMENTS 验收列) 大任务:FRAMEWORK/FLOW 与实现状态不符时先更新 memory 再编码(用户确认跳过除外)。