背景:交接文档的定位是「给 AI 接手用的入口」,此前三份分散(项目根 交接文档.md
+ docs/交接文档-基金转换.md + docs/交接文档-架构改进.md),且 docs/ 两份停留在旧版
(v1.0 / v1.1,不含 T-0~T-2b、609 passed、D20 实施等进度),新会话极易被误导。
改动:
- 三份合并为项目根 交接文档.md v3.0(545 行 = §0 公共层 + §A 风控主线 + §B 基金转换线
+ §C 架构改进线);该文件在 .gitignore:47 内,按用户要求不入库(交接文档只留本地)
- 全仓指向统一到 交接文档.md §A/§B/§C:AGENTS.md(含顶部新增「接手先读交接文档.md」)、
docs/memory/{MEMORY,TODO,FRAMEWORK,ITERATION}、PRD-架构改进与稳定性加固、
开发计划-架构改进、TODO-架构改进、开发计划-基金转换交易
- 两份 docs/交接文档-*.md 加「已废弃(2026-09-10)· 勿读」横幅并指向新入口,
保留作历史留档(不删除)
- 记录事故:对 docs/ 下中文名文件使用 git rm 会静默抹除整个 docs/ 目录(复现 2 次、
退出码 0),已零损失恢复;纪律写入 交接文档.md §0.4 与工作区记忆
无代码改动;pytest 609 passed / 3 skipped。
14 KiB
交接文档 · 架构改进与稳定性加固(给执行 AI)
⛔ 本文件已废弃(2026-09-10)—— 本线已结项,请勿据此开工
内容已全部并入项目根
交接文档.md§C(三线合并版唯一交接入口),且本文件停留在 v1.1(执行后回写), 不含 §7.2 七项手工冒烟 7/7 PASS、push 状态纠正(037ce7e早已推送)、残留重灌复绿等结项记录。 ⚠️ 下方「commit 但不 push」「待收尾」等表述已过期。 正确入口:交接文档.md§C —— 本文件仅作历史留档,内容已过期,勿读。
版本:2026-09-09 v1.1(执行后回写) 用途:给执行代码改动的 AI(hy3)。读完本文即可开工,不需要重读全部代码。 代码基线:分支
risk-control-agent,HEAD2d0e2fa,pytest 503 绿 本轮已完成的规划:PRD + 架构设计说明书 + 开发计划 + TODO + 两轮独立审核,全部已通过审核。
⚡ 当前状态(v1.1,2026-09-09 21:10 更新)
第 1、2 批代码已全部落地并提交 037ce7e,pytest 实测 510 绿。未 push。
| 项 | 状态 |
|---|---|
| T-101~T-109(文档勘误 + 标注 + 告警) | ✅ 已完成 |
| T-201.1 / .2 / .3(Redis 双层锁 + 测试) | ✅ 已完成 |
| T-202(中间件顺序守卫) | ✅ 已完成 |
| 全量 pytest | ✅ 510 passed(503 基线 + 6 锁测试 + 1 守卫) |
| commit | ✅ 037ce7e(未 push) |
| 手工冒烟(§7.2 七项) | ❌ 未执行 ← 下一个会话优先做 |
| 第 3、4 批(合并后) | ⏸ 按原计划不动 |
下一个会话开工三件事:① 跑 python -m pytest -q 复核 510;② 做 §7.2 七项手工冒烟(尤其"停 Redis"和"调换装饰器验证守卫变红");③ 补齐后报告用户,由用户浏览器目视确认再 push。
已改动文件(本次提交):
app/main.py # DEEPSEEK_API_KEY 缺失告警(函数内延迟 import,无循环导入)
app/utils/authz.py # 越权审计失败日志补 trace_id
app/api/audit_middleware.py # 审计失败日志补 status/path/request_id
app/service/risk/redis_gateway.py # +acquire_lock(SET NX EX) / +release_lock(Lua)
app/service/risk/locks.py # run_locked 改双层(Redis 为主、进程内为备)
tests/test_locks_redis.py # 新增 6 条(monkeypatch 假网关,不依赖本机 Redis)
tests/test_audit_middleware.py # +1 条顺序守卫;_http_access 查询加 trace_id 列
docs/memory/MEMORY.md # A1 42→45 / A2 4→5
docs/项目框架设计/表设计/02-mysql-agent专用.sql # A3 5→6
docs/项目框架设计/架构设计-风控模块.md # A4/A5 标注 + §5.8 G1/G2/G3
⚠️ 注意文件名:《架构设计说明书》实际落盘为
docs/项目框架设计/架构设计-风控模块.md(本次 A4/A5/G1~G3 标注写在这里);另有全量版docs/项目框架设计/架构设计说明书.md(新增,9 章,40KB)。两者并存,改标注时别改错文件。
你的任务:按 TODO 执行代码改动。只做清单内的事,不做范围外改动。
0. 一句话任务
改 11 个原子项:9 项是文档修正和加告警(低风险),2 项是把进程内锁换成 Redis 分布式锁 + 加一个测试守卫。改完 pytest 必须仍全绿。
1. 五分钟背景
1.1 项目是什么
XingHuo 智能财富管家:金融四 Agent(客户财富 / 代理人 / 数据分析 / 风控)共用数据层与合规底座,四 Agent 不互调 LLM。
技术栈:Python 3.13 + FastAPI + LangGraph + MySQL 双库(jinrong_core 只读模拟 / jinrong_agent 业务)+ Redis + Milvus + Neo4j。
风控模块已完整交付(503 测试绿),马上要合并进 main 分支——这是本次改进的时间背景。
1.2 本次改进的来源
2026-09-09 做了全量架构梳理,发现 24 项待处理项。经两轮独立 AI 审核(第一轮审问题清单、第二轮审开发计划),无阻断级错误,方案核验成立。
用户拍板:补告警 + 换 Redis 分布式锁。
1.3 为什么要分批
因为"马上合并代码":合并前只做低风险的 9+2 项,DDL 类(唯一索引)和重构类(日志基建、权限收拢)一律放合并后。
2. 你要做的事(范围,严格按此执行)
| 批次 | 原子项 | 内容 | 风险 | 状态 |
|---|---|---|---|---|
| 第 1 批 | A1~A5 | 修 5 处文档口径错误 | 极低(纯文档) | ✅ |
| G1~G3 | 补 3 项"非缺陷"设计标注 | 极低(纯文档) | ✅ | |
| C4 | 无 key 启动告警(加一行日志) | 低 | ✅ | |
| C3 相关 | 审计降级日志补 trace_id(2 处) | 低 | ✅ | |
| 第 2 批 | B6 | Redis 分布式锁(核心改动) | 中 | ✅ |
| B2 | 中间件顺序测试守卫(新增测试) | 低 | ✅ |
详细验收标准:《docs/项目框架设计/TODO-架构改进.md》(逐项可勾选,照着做即可)
不要做的(第 3、4 批,合并后再说):
- ❌ 加
agent_message唯一索引(DDL,需先清洗历史数据) - ❌ 限流
EXPIRE改造 - ❌
core_roDB 只读账号(需运维) - ❌ 支持
convert交易(接口协议变更,待产品确认) - ❌ 日志基建、权限收拢、model 层启用、注册表合并
- ❌ 意图识别升级、R-05 动态评分
3. 必读文档(按优先级)
| 顺序 | 文档 | 看什么 |
|---|---|---|
| 1 | 本文(交接文档) | 全局、坑、禁止事项 |
| 2 | docs/项目框架设计/TODO-架构改进.md |
执行清单,照着勾选 |
| 3 | docs/PRD/PRD-架构改进与稳定性加固.md |
做什么、为什么、验收标准 |
| 4 | docs/项目框架设计/开发计划-架构改进.md |
怎么做(代码设计、降级策略、风险) |
| 5 | docs/项目框架设计/架构设计说明书.md |
架构现状(必要时查) |
| 6 | docs/项目框架设计/改进方案评审-问题清单与对比.md |
24 项问题全景(必要时查) |
4. 已冻结的设计决策(不能改)
这些经过两轮审核,改了会破坏已验证的方案:
4.1 Redis 锁:双层降级,三条规则不可改
run_locked(key, fn)
├─ Redis 抢到锁 → fn(locked=True),finally 释放
├─ Redis 等待超时 → fn(locked=False) ← 与原语义一致,不可改成抛异常
└─ Redis 不可用(任何异常)→ 退回进程内锁 ← 不可改成直接降级
"任何异常"包括:
ConnectionError(Redis 没起)AttributeError(测试 FakeGateway 没有锁方法) ← 关键,见 §5.2
4.2 参数(已定,勿随意改)
| 参数 | 值 | 说明 |
|---|---|---|
LOCK_TIMEOUT_SECONDS |
2.0 | 保持原值,不改 |
LOCK_TTL_SECONDS |
30 | 二审从 10 上调(10 秒无实测支撑) |
_LOCK_KEY_PREFIX |
lock: |
与 sess:/ratelimit:/auth: 隔离 |
_RETRY_INTERVAL |
0.05 | 抢锁重试间隔 |
4.3 契约不变
run_locked(key, fn) 签名与 fn(locked) 回调语义完全不变,三处调用点一行都不用改。
4.4 不放 ensure_trace() 兜底
B2 测试守卫要能"真的报警"。若加 ensure_trace() 兜底,顺序错了会被掩盖,trace_id 与响应头不一致,更难查。
5. 关键代码位置与坑
5.1 run_locked 调用点是三处(极易漏)
| # | 位置 | key | 保护对象 |
|---|---|---|---|
| 1 | alert_service.py:180 |
agg:event:{customer_id}:{date} |
交易事件预警聚合 |
| 2 | alert_service.py:239 |
agg:suitability:{customer_id}:{product_id}:{date} |
R-02 适当性阻断预警 |
| 3 | profile_l3.py:155 |
l3:{customer_id} |
L3 监测层 |
⚠️ 第 2 处最易漏(初版规划就漏过,被二审抓出)。它是
record_suitability_alert的聚合锁,而 R-02 是全系统唯一能阻断交易的规则。漏改会让最关键路径失去并发保护。
5.2 测试不依赖本机 Redis
tests/test_integration_risk.py:6 明确写"redis_gateway fake(不依赖本机 Redis)"。多个测试用 monkeypatch.setattr(redis_gateway, "_gateway", FakeGateway()) 注入假网关。
已核查:所有 FakeGateway(在 test_main.py / test_audit_middleware.py / test_auth_jwt.py / test_chat.py / test_profile_l3.py / test_risk_api.py 等)均无 acquire_lock / release_lock,也无 __getattr__。
→ 调用会抛 AttributeError → 你的代码必须归为"Redis 不可用"→ 退回进程内锁。这是设计,不是 bug。
5.3 Redis 网关现状
app/service/risk/redis_gateway.py:单例 _gateway,set_gateway 注入点,get_gateway 惰性创建。已有 publish/delete/exists/set_ex/incr/rpush/lrange/ltrim/expire。你要新增 acquire_lock / release_lock,纯新增不动现有方法。
5.4 中间件顺序(B2 相关)
main.py audit 先注册(81 行)、trace 后注册(87 行)。Starlette add_middleware 用 insert(0)、build_middleware_stack 用 reversed → 数组越靠前越外层 → trace 在外层先执行。
调换两个装饰器 → audit 先跑 → current_trace() 空 → 全站审计静默丢 trace_id,不报错。这就是要加测试守卫的原因。
5.5 TTL 引入的新失败模式(已知,接受)
进程内锁拿到后永不过期;Redis 锁有 TTL,若临界区耗时超 30 秒会自动解锁,另一实例可抢到。
这是 TTL 的固有代价,兜底是:L3 有数据库乐观锁、预警聚合有"锁内重查锚点",最坏是重复 pending 单,非数据损坏。已写入风险表,不要试图消除它(消除就会变成"进程崩溃后死锁")。
6. 实施顺序
1. 跑基线 python -m pytest -q 记录数字(应 503)
2. 第 1 批 T-101 → T-109(文档 + 2 处告警)
3. 跑测试 确认仍 503 绿
4. 第 2 批 T-201(Redis 锁)→ T-202(测试守卫)
5. 跑测试 确认仍 503 绿,新增测试已计入
6. 手工冒烟 见 §7
7. commit **不 push**
建议:T-201 单独一个 commit,T-202 单独一个 commit,便于回滚。
7. 验收方式
7.1 自动化
python -m pytest -q # 必须全绿,用例数 ≥ 503
7.2 手工冒烟
| # | 操作 | 期望 |
|---|---|---|
| 1 | 清空 .env 的 DEEPSEEK_API_KEY → 启动 |
日志出现降级告警,不阻塞启动 |
| 2 | 恢复 key → 启动 | 无该告警 |
| 3 | POST /api/simulate/trade 阻断 + 放行各一次 |
行为与改动前一致 |
| 4 | 检查预警聚合 | 仍"同客户同日仅一张 pending 单" |
| 5 | 停 Redis → 跑一次交易 | 业务仍可完成(退回进程内锁) |
| 6 | 恢复 Redis → 再跑一次 | 行为与停服前一致 |
| 7 | 调换 main.py 两个装饰器 → 跑 T-202 测试 |
测试变红(证明守卫有效)→ 恢复 |
7.3 完成判定
- FR-01~FR-06 验收标准逐条通过(见 PRD §3)
- pytest 全绿且 ≥ 503(实测 510)
- 未新增配置项、未新增依赖、未改路由
- commit 但不 push(
037ce7e),等用户在浏览器目视确认 - §7.2 七项手工冒烟(未执行) —— 这是目前唯一缺口
8. 禁止事项
8.1 项目五条红线(碰了就是事故)
- Core 表只读
- 审计表只 INSERT
- 不自动冻结
- 不自动改风险等级
- 仅 R-02 可阻断交易
8.2 本次改动纪律
- ❌ 不做清单外的改动(看到别的问题记下来,不要顺手改)
- ❌ 不改
run_locked对外签名 - ❌ 不改三处调用点的调用方式
- ❌ 不新增第三方依赖(
redis库已在requirements.txt) - ❌ 不新增配置项(若决定配置化
LOCK_TTL_SECONDS,须同步.env.example) - ❌ 不改路由(若改,必须同步
tests/test_main.py::test_all_routers_mounted路径清单) - ❌ 不 push(commit 即可,等用户目视确认)
9. 项目特有坑(踩过的)
- 系统 Python 3.13.14(
C:/Users/YUAN/AppData/Local/Programs/Python/Python313/python.exe)装了依赖和 pytest;managed 3.13.12 没装。用系统 Python 跑测试和 uvicorn。 - 本机
.env已配置、bootstrap ①~⑤ 已执行,勿重做。 - 演示库重灌两步缺一不可:
scripts/core/reset.ps1+scripts/demo/prepare_risk_demo.sql。只跑第一步 → 33 客户中 27 人风评过期,会误判为 bug。 - sqlite 不支持绑定 Decimal:测试里插
core_trade.amount须转 float。 - Milvus 数据路径必须纯英文(faiss 不支持中文路径),已配
C:/Users/YUAN/.jinrong/milvus/。 - 文档里的旧数字不可信:注入词表 42(应为 45)、Tool 4 个(应为 5)、专用表 5 张(应为 6)——这些正是本次 T-101~T-103 要修的,改代码时别拿旧数字当依据。
10. 完成后
已完成的回写(v1.1):
- 更新
docs/项目框架设计/TODO-架构改进.md:T-101~T-202 全部勾选,实测用例数改为 510(原记录写 511 有误,实际新增 6 锁 + 1 守卫 = 7 条) - 更新本交接文档:新增"当前状态"块、§2 状态列、§7.3 勾选
- 在
docs/memory/2026-09-09.md追加实施记录 —— 待补 - 未 push(
037ce7e),等用户目视确认
下一个会话的收尾动作:
- 跑
python -m pytest -q复核 510 绿 - 做 §7.2 七项手工冒烟,逐条在 TODO 里勾选
- 把冒烟结果写进
docs/memory/2026-09-09.md - 报告用户 → 用户在浏览器目视确认 UI 后才允许 push;push 后打里程碑 tag
11. 有问题怎么办
- 发现文档与代码不符 → 以代码为准,并在 TODO 备注里记下差异
- 发现清单外的严重问题 → 不要顺手改,记下来报告用户
- 对设计有疑问 → 查《开发计划-架构改进.md》对应章节;仍不明确则停下问用户,不要猜