Files
group_xinghuo_jinrong/docs/PRD/PRD-架构改进与稳定性加固.md
T
GaoYiYuan_0626 497cee289f docs: 交接文档合并为单一入口 + 全仓指向统一到 交接文档.md §A/§B/§C
背景:交接文档的定位是「给 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。
2026-09-10 15:04:13 +08:00

10 KiB
Raw Blame History

PRD · 架构改进与稳定性加固

版本:v1.0(2026-09-09) 状态:待评审 → 通过后进入开发 代码基线:分支 risk-control-agent,HEAD 2d0e2fa,pytest 503 绿 关联文档:

  • 架构现状剖析:《docs/项目框架设计/架构设计说明书.md》
  • 问题清单与方案对比:《docs/项目框架设计/改进方案评审-问题清单与对比.md》(已过第一轮审核)
  • 开发计划(HOW):《docs/项目框架设计/开发计划-架构改进.md》(已过第二轮审核)
  • 任务清单:《docs/项目框架设计/TODO-架构改进.md》
  • 开工入口:项目根《交接文档.md》§C

本文定位:只定义做什么(WHAT)与为什么(WHY)及验收标准;怎么做(HOW)见开发计划。


1. 背景与目标

1.1 背景

2026-09-09 对 risk-control-agent 分支做了全量架构梳理,产出《架构设计说明书》,并在其中登记了 24 项待处理项(11 项改进空间 + 5 处文档口径不符 + 3 项非缺陷标注 + 5 项审核补充)。

经两轮独立 AI 审核(第一轮审问题清单、第二轮审开发计划),六项核心事实断言全部属实、无阻断级错误,设计方案核验成立。

用户随后拍板:补告警 + 换 Redis 分布式锁,理由是"马上要合并代码了"。

1.2 目标

目标 衡量方式
G1 消除多实例部署下的并发保护失效 Redis 锁生效;单进程行为不劣化
G2 消除"静默失败"——出了事没人知道 无 key、审计降级两处均有明确告警
G3 消除文档与代码事实不符 5 处口径修正完成,核对一致
G4 关键设计决策可追溯、不被后人误改 3 项"非缺陷"设计写入架构文档
G5 中间件顺序错误可被测试拦截 顺序被调换时测试变红

1.3 非目标(本期不做)

  • 不实现 R-05 动态评分(scoring.py 桩保留)
  • 不支持 convert 交易类型(接口协议变更,待产品确认)
  • 不做日志基建(格式化/落盘/全量接 trace_id)
  • 不收拢权限逻辑、不启用 model/ 层、不做 Tool 注册表合并
  • 不改变审计 fail-open 策略(保持现状 + 补告警)

2. 范围

2.1 本期范围(合并前实施,11 个原子项 → 6 个需求)

需求 ID 名称 原子项 批次
FR-01 文档口径修正 A1~A5 第 1 批
FR-02 非缺陷设计标注 G1~G3 第 1 批
FR-03 无 key 启动告警 C4 第 1 批
FR-04 审计降级告警强化 C3 相关 第 1 批
FR-05 Redis 分布式锁 B6 第 2 批
FR-06 中间件顺序测试守卫 B2 第 2 批

2.2 后续范围(合并后,本期不实施)

第 3 批(需拍板或 DDL):B1 唯一索引+重试、B5 限流 EXPIRE、C1 只读账号、F1 convert、F2 意图识别 第 4 批(重构类):D1 日志基建、E1~E4 可维护性、F3 动态评分、B3/B4 判据固化

详细设计见《开发计划-架构改进.md》§5、§6。


3. 需求清单

FR-01 文档口径修正

描述:修正 5 处"文档说法与代码实测不符",避免后续读者按错误数字核对。

动机:这些数字会被当作事实依据做决策(如"注入词表 42 条"影响安全评估、"Tool 4 个"影响能力盘点)。错了会引发误判。

明细与验收标准:

原子项 位置 现状 → 应为 验收
A1 docs/memory/MEMORY.md §0 42 条 → 45 条 与 input_guard.py:43-94 逐行计数一致
A2 docs/memory/MEMORY.md 仓库地图 4 个 → 5 个 与 chat_tools.py:328 注册表一致
A3 docs/项目框架设计/表设计/02-mysql-agent专用.sql 注释 5 张 → 6 张 与实建表一致(含 risk_aml_list)
A4 《架构设计说明书》§4.3.2 补明 scoring.py 为 NotImplementedError 桩 读者不会误以为评分已生效
A5 《架构设计说明书》§4.3.4 补明 locks.py 为进程内锁 读者知道多实例会失效

FR-02 非缺陷设计标注

描述:把 3 项"看起来像问题、实际设计正确"的决策写入架构文档。

动机:这三项每次评审都会被重新提出,消耗沟通成本。写明后可直接引用。

原子项 要写清什么
G1 X-Trace-Id 可伪造 有白名单防响应头注入;身份只认 JWT,trace_id 绝不用于权限判定
G2 trace_id 概率唯一 64 bit 随机,请求级;约 2^32 次请求才 50% 碰撞,无需数据库唯一校验
G3 admin.py / knowledge.py 空壳 T-21 拍板一期只做脚本入库;未挂载路由;空 ≠ RAG 缺失(能力在 service/rag_service.py)

验收:三项均在《架构设计说明书》中有独立条目,含"为什么不是问题"。

FR-03 无 DeepSeek key 启动告警

描述:启动时若 DEEPSEEK_API_KEY 未配置,打 WARNING 日志。

动机:当前无 key 时静默走 _degraded_reply,生产漏配会"看起来正常",用户和运维都察觉不到。

验收标准:

  1. 清空 .env 的 DEEPSEEK_API_KEY → 启动日志出现降级告警,且不阻塞启动
  2. 恢复 key → 无该告警
  3. 告警位置与 main.py:61-65 现有 dev secret 告警一致(沿用同模式)
  4. 不产生循环导入(若引入,改用字面量常量)

FR-04 审计降级告警强化

描述:两处审计失败日志补充 trace_id 与关键上下文字段。

动机:当前 utils/authz.py:70-76 与 audit_middleware.py:78 已打日志,但不含 trace_id——审计失败时连是哪次请求都不知道,无法追溯。

验收标准:

  1. utils/authz.py 审计失败日志含 code / agent / actor / trace_id
  2. audit_middleware.py 审计失败日志含 status / path / request_id
  3. 日志级别与降级语义不变(仍不阻塞业务)
  4. 失败路径仍返回原响应(行为零变化)

FR-05 Redis 分布式锁

描述:把 locks.py 的进程内锁改造为「Redis 为主、进程内为备」的双层结构。

动机:threading.Lock 跨进程不可见,多实例部署时同一客户并发请求会同时进入临界区。用户明确要求,因"马上合并代码"(合并后可能多实例部署)。

验收标准:

# 标准
1 run_locked(key, fn) 签名与 fn(locked) 回调语义完全不变,三处调用点无需修改
2 Redis 可用时跨进程互斥生效(Redis 侧 key 带 TTL)
3 Redis 不可用(含 ConnectionError、Fake 缺方法的 AttributeError)→ 退回进程内锁,不抛异常、业务照常完成
4 Redis 可用但等待超时 → fn(locked=False) 降级执行,与原语义一致
5 释放只删自己持有的锁(错误 token 释放返回 False,锁仍在)
6 三处调用点全覆盖:agg:event: / agg:suitability: / l3:(不得只改两处)
7 全量 pytest 503 仍绿(新增锁测试计入总数)
8 三处调用点的 key 均带 lock: 前缀,与既有 sess:/ratelimit:/auth: 命名空间隔离

关键约束:测试环境不依赖本机 Redis(redis_gateway 一律 fake 注入),新代码不得假设 Redis 可用。

FR-06 中间件顺序测试守卫

描述:新增测试,断言 audit_log 中 trace_id 非空。

动机:main.py 中 audit 先注册(81 行)、trace 后注册(87 行),依赖 Starlette 的 insert(0) + reversed 语义保证 trace 在外层先执行。调换两个装饰器会使 audit 先执行、current_trace() 返回空串,全站审计静默丢失 trace_id,且不报错。

验收标准:

  1. 新增测试在正常顺序下通过
  2. 临时调换装饰器顺序 → 测试变红(确认测试确实有效)
  3. 恢复顺序 → 测试转绿
  4. 不放 ensure_trace() 兜底(会掩盖顺序错误,导致 trace_id 与响应头不一致)

4. 非功能需求

类别 要求
兼容性 不破坏任何现有 API 契约;不改路由;不新增配置项(若配置化 LOCK_TTL_SECONDS 需同步 .env.example)
依赖 不新增第三方依赖(redis 库已在 requirements.txt)
测试 全量 pytest 保持 503 绿,新增测试随改动交付
可回滚 每个需求独立可回滚;FR-05 退回进程内锁即等价原行为

5. 约束与红线

项目五条红线(任何改动不得触碰):

  1. Core 表只读
  2. 审计表只 INSERT
  3. 不自动冻结
  4. 不自动改风险等级
  5. 仅 R-02 可阻断交易

技术约束:

  • Windows 原生部署,系统 Python 3.13.14
  • 测试不依赖本机 Redis
  • 改路由须同步 tests/test_main.py::test_all_routers_mounted 路径清单
  • 新增依赖须用户确认

6. 风险

风险 影响 应对
FR-05 引入 TTL 新失败模式:临界区超 TTL 导致并发进入 中(最坏重复 pending 单,非数据损坏) TTL 取 30 秒;L3 乐观锁 + 锁内重查兜底;取得 p99 后按 max(30, p99×5) 调整
测试期 Redis 不可用产生大量日志噪声 低 被捕获不导致失败;对比基线确认不漂移
合并冲突(locks.py/redis_gateway.py 有在途改动) 中 合并前确认目标分支无在途改动
FR-03 引入循环导入 低 若出现,改用字面量常量

7. 验收总纲

本次改进全部完成的判定:

  • FR-01~FR-06 六项验收标准逐条通过
  • python -m pytest -q 全绿且用例数 ≥ 503(新增测试计入)
  • uvicorn 启动冒烟:/health 正常,无 key 告警按预期出现
  • POST /api/simulate/trade 阻断 + 放行路径各一次,预警聚合仍为"同客户同日仅一张 pending 单"
  • 停 Redis → 业务仍可完成;恢复 Redis → 行为一致
  • 未新增配置项、未新增依赖、未改路由

交付纪律:commit 但不 push,等用户在浏览器目视确认后再推送。