Commit Graph
68 Commits
Author SHA1 Message Date
GaoYiYuan_0626 22f2a41192 基金转换 T-12:补偿脚本(详情 + 预警)
阶段二失败(或阶段 1.5 引擎失败)后,仅凭 Core 侧数据把「详情 + 预警」两件事补回来
(PRD §7.1 / 架构 §5.4)。

落地(改 3 + 新增 2 脚本 + 测试 2 文件)
- convert_service:新增公开 compensate_convert(group_id, ...) —— 补偿的服务端单点入口
  · 锁键 convert:rerun:{gid},与 convert_fund 幂等重试路径同一个键
  · 详情侧:非 completed 才补写,复用 _finalize_from_core(不另写第二份阶段二)
  · 预警侧:幂等锚点 = 转出端 out_trade_id,复用 find_alerts_by_trade;
    命中即 skipped,否则跑 process_convert_event
- risk_repository:has_engine_error_audit 加 decision 参数(默认值不变)
  · convert 线阶段 1.5 失败审计用 engine_error,普通交易用 risk_engine_error,不是同一个码
- rebuild_alerts.py:新增 --convert-group(与 location 参数 trade_ids 互斥),薄封装
- 新增 scripts/agent/cleanup_pending_convert.py(架构 §2 与开发计划 §9 指定路径):
  超 convert_compensate_sla_hours 的 pending 占位 → status='expired'(标记不硬删,S2)
- 新增 scripts/dev/verify_convert_compensate.py:真库验证脚本(MySQL 8.0.46)
- 测试 +12:test_convert_service +4(补写 / 幂等 / missing / locked)、
  test_demo_scripts +8(--convert-group 分派与接线 + cleanup 脚本)

state 四态与 CLI 退出码
- rebuilt(0) / skipped(0 幂等) / missing(1 零写入) / locked(3)
- 退出码 2 保留给 argparse 用法错误,故 locked 取 3

真库专属证据(sqlite 单测给不了的,本任务核心增量)
- status='expired' 在 MySQL ENUM 上被接受(sqlite 该列是 VARCHAR,写什么都收)
- created_at < cutoff 在 DATETIME(3) 上的时间边界正确(超时进候选 / 未超时不进 / 复跑幂等)
- input_summary 是真 JSON 列,而 has_engine_error_audit 用 LIKE 判定:脚本先断言
  information_schema 的 DATA_TYPE='json' 再验命中,并反向断言决策码不匹配则不命中

顺带收口(用户指示)
- core_ro.concentration_profile 补 h.qty > 0,与 list_holdings 真正同口径
  · ratio 不变(归零行市值为 0),但 rows 不再多出已清仓产品、不虚占截断判定位
  · 新用例含跨出口一致性断言;突变验证:去掉 qty > 0 → 精准 1 条红

验证
- pytest -q → 731 passed / 3 skipped(基线 719 加 12,零回归)
- 突变验证 3 组精准命中:去掉幂等锚点(1 红)/ 去掉 status 过滤(2 红)/ 补偿无视锁(1 红)
- 真库 verify_convert_compensate.py 34/34,隔离数据零残留
- 全套 7 个真库脚本复跑零回归:seed 全 PASS / apply 24 / service 35 / engine 31 / lots 20 / tools 14 / compensate 34
2026-09-10 18:59:42 +08:00
GaoYiYuan_0626 a6ac39edd6 基金转换 T-11:工具与 SQL 汇总去重(FR-C15 / R-d)
convert 在 core_trade 落两条流水(转出 redeem + 转入 subscribe,共享 convert_group_id),
所有金额聚合读取方必须只计一次,否则同笔转换金额翻倍。

落地(改码 3 处 + 新增 1 脚本 + 测试 3 文件)
- rules.py:_amount_view → 公开 amount_view(提升而非复制别名,全仓唯一金额聚合口径);
  docstring 补「跨模块共用」说明
- core_tools.py:query_recent_trades 的 sum_amount 改走 amount_view(FR-C15);
  items / total_count 保持全量不变——两条流水是真实的两笔权益变动,读到两条是对的
- core_ro.py:list_holdings 加 h.qty > 0(convert 转出全部份额留下的 qty=0 归零行不是持仓);
  sum_trades_on_date 加 convert 去重条件(R-d),组内只计转出端
- 新增 tests/test_core_tools.py(3 条:汇总不翻倍 / 跨口径一致性 / 持仓不返回 qty=0 行)
- tests/test_core_ro_sum.py 追加 1 条 convert 去重用例(既有断言零改动)
- 新增 scripts/dev/verify_convert_tools.py:真库验证脚本(MySQL 8.0.46)

执行期裁定 2 条(已留痕)
- _amount_view 提升为公开而非复制:一个函数两个名字会漂移(自检第 13 问)
- SQL 条件从 IS NULL 扩为 IS NULL OR = '':Python 的 if not gid 把空串当无组,
  而 SQL 里 '' IS NULL 恒假。真库实证:只写 IS NULL → 合计数 400000(正确 450000,漏算 50000)。
  不加则 RISK-002 漏算,且 sqlite 单测若只造 NULL 数据永远发现不了
- 等价性边界:组内无 redeem 时 SQL 丢整组、amount_view 保留首条;由 R-b 保证不可达

验证
- pytest -q → 718 passed / 3 skipped(基线 714 加 4,零回归)
- 突变验证 3 组均精准命中:去掉去重条件(2 红)/ 去掉 qty>0(1 红)/ 汇总不走 amount_view(2 红)
- 真库 verify_convert_tools.py 14/14,隔离数据零残留
- 复跑受影响真库脚本零回归:T-8 31/31、T-7 35/35、T-10 20/20
2026-09-10 18:40:27 +08:00
GaoYiYuan_0626 5e6fa06d03 基金转换 T-10:普通申赎批次维护(FR-C16)
让 core_trade 的普通申赎同步维护 core_share_lot 批次,为 convert 提供权威份额口径。

落地(改码 3 处 + 新增 2 个脚本)
- gateway_repository:新增 insert_share_lots / deduct_share_lots,各自单事务、走 rw 账号;
  只落已分配结果、不含分配逻辑,避免出现第二份 FIFO 口径
- share_lot_repository:__init__ 增可选 core_ro 入参,让 trade_gateway 复用同一份 FIFO 选批口径(D18)
- trade_gateway:新增 _nav_as_of / _maintain_lots,在落流水后、规则引擎前调用
  · subscribe:取 T 日(含)前最新净值 → qty = amount ÷ 净值(2 位 HALF_UP)→ 建批次(confirmed_at = T)
  · redeem:FIFO 扣减;无批次但有持仓 → D8 兜底补建后再扣;两者皆无 → 降级跳过
  · 整段 try 包住,任何异常只 warning,绝不阻断交易(R-c(1))
- 新增 scripts/core/rebuild_lots.py:按 core_holding 快照重建批次(L-7),与网关 D8 同调 bootstrap_lots;
  默认只补建无批次的持仓行(幂等),--force 才先删后建,--dry-run 只报告
- 新增 scripts/dev/verify_convert_lots.py:真库验证脚本(MySQL 8.0.46)

口径订正(联网查证 4 家管理人业务规则后)
- redeem 的 amount ÷ 净值 折算属本项目简化建模,不是行业标准
- 行业铁律是「金额申购、份额赎回」:投资者以份额申报,登记机构按 T 日净值算金额
  (睿远业务规则 §65 / 华泰保兴 §69 / 东方基金 §57 / 国投瑞银 §33;无一家公募支持按金额赎回)
- 派生风险:未知价法下 T 日净值当日不可得(T+1 公告),_nav_as_of 实取 T−1 净值,
  故此处算出的份额只是估算值
- 已记入 PRD §10.1 已知差异清单;trade_gateway 注释同步订正

验证
- pytest -q → 714 passed / 3 skipped(基线 697 加 17,零回归)
- R16 test_redeem_accepted_without_alert 零改动通过(由 R-c(1) 降级保住)
- 真库 20/20;T-6 24/24、T-7 35/35 复跑零回归;真库隔离数据零残留
- 突变验证 2 组:切断接线 → 精准 2 条红;关掉 D8 兜底 → 精准 2 条红(含 D18 同源断言)
2026-09-10 18:28:48 +08:00
GaoYiYuan_0626 d0097d6004 基金转换 T-9:API 模型 + 网关分派(HTTP 层 convert 走通)+ 展示位数口径修复
一、T-9 本体:HTTP 层 convert 端到端走通

- api/simulate.py:TradeRequest 三型字段分池(subscribe/redeem → product_id+amount;
  convert → from/to_product_id + qty + 可选 client_request_id)+ model_validator 分支校验;
  未知类型放行给网关抛 400(保住既有 purchase → 400 断言);model_dump(exclude_none=True);
  PROCESSING → 202;异常捕获由 except LookupError 收窄为 except NotFoundError
  (原写法把 KeyError 这类编程错误静默转成 404,实测掩盖 convert 分支真实诊断)。
- gateway/trade_gateway.py:移除 convert 显式拒绝,新增 _submit_convert 分派
  (只做参数映射 + 仓储装配);convert 不写 trade_request 审计(审计归 convert_service)。
- utils/response.py:错误体合入 exc.extra(TOO_MANY_LOTS 的 batch_count/max_lots);
  既有 ApiError 无 extra 属性 → 老错误体逐字节不变。
- utils/trace.py + main.py:正则收敛单点定义。执行期发现 trace.py 与 main.py 各有一份
  内容完全相同的白名单副本 —— S4 要防的「漂移」其实已经发生,现将常量上移 trace.py
  成公开 HEADER_ID_PATTERN(同时解决 main→simulate 反向导入成环)。

二、展示位数口径修复(执行期发现 → 联网核验 → 修复 → 文档订正)

发现:同一逻辑响应两种写法 —— 首次 "53456.95" vs 幂等重放 "53456.9500",数值相等、字符串不等。
根因不是 T-7 写错,是契约缺位:§2.5 只规定「金额/份额 2 位」,净值、费率、申请份额的
回显位数根本没定义 → 实现只能 str(Decimal) 原样出网 → 位数随数据来源漂移。

修复:convert_service 新增 _q(value, unit) + _D2/_D4 规格常量作对外唯一出口 ——
金额/份额 2 位、净值/费率/份额尾差 4 位;响应 + 审计 summary + 异常日志共用该出口;
原 _s() 全部替换。首次路径幂等(除 requested_qty/actual_qty/lot[].qty 由 4 位补齐 2 位外不变)。

依据(2026-09-10 联网核验 7 家管理人公告):金额/份额「四舍五入保留至小数点后两位」;
「申请转换份额精确到小数点后两位」;净值保留 4 位第 5 位四舍五入(中欧/国泰公告由 3 位提高至 4 位);
费率以百分比 2 位表示。已知不统一:易方达 ETF 场外份额取整数位、南方基金取截断 → 取主流口径
并记入 PRD 已知差异(未来接真实 TA 需按基金合同配置化)。

三、文档订正

- PRD → v0.9.2:§2.5 拆 2.5.1 计算精度 / 2.5.2 展示位数(新增按字段分类的规格表 + 外部依据);
  §5.3 示例 requested_qty/actual_qty/lot_breakdown[].qty 4 位 → 2 位(原示例与 §2.5
  「计算与对外展示按 2 位」自相矛盾,属漏改);字段类型约定补「位数不自由 + 两条路径须逐字节一致」。
- 架构 → v1.0.1:§1 原则 11 补「str() 前必须按 §2.5.2 量化」,无结构变更。

四、验证

- 新增 tests/test_convert_integration.py(8 条真 MySQL 端到端,CNV-TEST-/TRD-TEST- 前缀隔离):
  折算与 PRD §5.3 逐项吻合、两条流水同组、持仓与批次如实变动、明细 completed + 审计、
  幂等重试不产生第二组、跨主体 400、未知类型 400,以及
  「首次与重放逐字段逐字节相等」+「展示位数规格」两条新闸门。
- test_trade_gateway.py +17(11 条错误码映射全表参数化 · 202 · 200 透传 · 不写 trade_request 审计)。
- test_integration_risk.py:R15 处置 —— 端到端已迁入新文件,原槽位改造为
  test_invalid_type_400_and_no_new_trade_audit(改用 purchase 触发),保住「校验失败不落审计」不变量。
- pytest -q → 697 passed / 3 skipped(基线 672 +25,零回归)。
- 真库复跑:T-6 24/24 · T-7 35/35 · T-8 31/31;calc_convert_demo.py 15/15。
- 突变验证 4 组:关掉 convert 分派 → 21 条红;关掉错误体 extra 展开 → 精准 1 条;
  关掉 client_request_id 正则 → 精准 1 条;关掉 _q() 展示量化 → 2 条红
  (assert '50000.0000' == '50000' 直接复现原缺陷)。均已恢复。
2026-09-10 18:00:20 +08:00
GaoYiYuan_0626 7ad1c8204d 基金转换 T-8:规则引擎改造(_amount_view 去重视图 + process_convert_event)
一次转换落两条流水(转出 redeem + 转入 subscribe,同 convert_group_id),
金额聚合类规则若两条都算会翻倍报假预警,故按「逐笔 / 聚合」拆成两个视图。

- rules.py:新增 _amount_view —— 同组内只保留 redeem 那条(无 convert_group_id
  的交易恒等通过、组内无 redeem 保首条、绝不删行);run_rules 双视图分流:
  RISK-001/003/004 用全量 eligible(逐笔判定),RISK-002/005 走金额视图去重。
- alert_service.py:抽出公开 build_trade_event(事件体结构唯一定义),
  record_trade_alerts 新增可选 events 参数;缺省 None 退化为单条,
  既有调用零改动。新建单落 payload.events 全部,聚合追加只追首条(只认转出端)。
- engine.py:抽 _run 共用实现;process_trade_event 变薄封装(签名与行为不变);
  新增 process_convert_event(out_trade, in_trade, ...) 与 _notify_error_hook
  (hook 自身异常吞掉,原始异常照常上抛)。

实质影响:阶段 1.5 从「ImportError 静默跳过」变为「真跑」——T-7 部署时
process_convert_event 不存在,T-8 落地后同一笔转换会真实出单 + 写 L3 + 落审计;
T-7 真库脚本复跑仍 35/35,无连带破坏。

验证:新增 tests/test_convert_engine.py(15 用例)+ test_convert_service.py
接线回归 1 条;pytest 672 passed / 3 skipped(基线 656 +16,零回归);
新增 scripts/dev/verify_convert_engine.py 真 MySQL 验证 31/31;
突变验证(关掉去重视图 → 4 条变红)确认用例非假绿。
2026-09-10 17:34:35 +08:00
GaoYiYuan_0626 318cb39a1f 基金转换 T-7:convert_service 八步编排(关键路径 · 幂等前置 + 三阶段)
- 新增 app/service/convert/convert_service.py:八步编排(①-④ 校验不落库 / ⑤ 占位 /
  ⑥ apply_convert / ⑦ 阶段 1.5 引擎不阻断 / ⑧ 回写 + 审计),幂等判定前置到校验之前
  以修复同键重试误报 InsufficientShares
- 新增 tests/test_convert_service.py(17 用例)
- 新增 scripts/dev/verify_convert_service.py(真 MySQL 验证 35/35)
- 改 app/repository/convert_repository.py:complete_convert 由纯 UPDATE 改三步法
  upsert(无占位直跑也能落完成行,R-a 口径)
- 改 app/repository/core_ro.py:新增 has_convert_trades / list_convert_trades /
  list_convert_lot_details 三个只读方法
- 改 app/config/settings.py:新增 6 个 convert 配置项
- 修 tests/test_convert_calc.py:TestPurity 排除编排层 convert_service.py

基线 639 → 656 passed / 3 skipped,零回归
2026-09-10 17:12:01 +08:00
GaoYiYuan_0626 82c9467fb7 基金转换 T-6:apply_convert 阶段一单事务(core 库唯一写入口)
新增 app/gateway/convert_core_repository.py:两条流水(redeem/subscribe,
R-b 不使用 ENUM 的 convert)+ N×条件 UPDATE 批次扣减 + 转入新批次
+ 两端 core_holding + N×计费明细,全部收口在 with engine.begin() 单事务。

实现级要点(与开发计划 §6.1 的三处差异,已回写该节执行记录):
- core_holding 改用「基于列当前值的增量 UPDATE」,入参不带持仓快照:
  MySQL 的 UPDATE 是当前读,并发两笔自然累加;用快照算绝对值会互相覆盖。
- 数值参数参与算术时写成 (:x + 0.0):实测 sqlite 在 UPDATE 算术表达式中
  不把 TEXT 绑定参数转数值(传 '120' 时 c 不变),加 +0.0 后两库一致。
- pnl_pct 拆成独立 UPDATE 重算,避开 MySQL「SET 从左到右」的顺序坑。

验证:pytest 639 passed / 3 skipped(基线 634 +5,零回归);
新增 scripts/dev/verify_convert_apply.py 真 MySQL 验证 24/24 一致,
含真并发两笔首次转入同一产品的终态断言(MySQL RR 的 gap lock 实测留痕)。
2026-09-10 16:26:42 +08:00
GaoYiYuan_0626andWorkBuddy 4fd2ec05c8 基金转换 T-4+T-5(并行组 A):convert_repository 代理侧 + locks.try_lock 非阻塞抢锁
T-4(convert_repository · agent 库 risk_convert_detail):
- 新增 7 方法:insert_placeholder(占位 pending) / complete_convert(回写 completed+折算详情)
  / mark_failed / get_by_group_id / get_by_client_request_id(None 直接返回 None)
  / list_expired_candidates(hours) / mark_expired(S2 标记不硬删)
- 引擎走 agent 库 rw(D20);status 5 值枚举;不与 risk_repository 混职责
- Decimal 折算字段经 _to_bind 转 float 绑定(sqlite 不支持直接绑 Decimal,同 T-3)

T-5(locks.try_lock):
- 单次非阻塞抢锁:抢到返回 _Token(with 进入 True),抢不到立即 _NoLock(进入 False,不等不降级)
- Redis 不可用退回进程内 Lock.acquire(blocking=False);_NoLock.__enter__ 返回 False 避免 with None 报错
- 不改 run_locked(D4:其等 2s 后降级 fn(False),语义相反;3 处调用点零改动)

测试:test_convert_repository.py 6 用例 + test_locks_redis.py +4 用例(既有 6 例零改动)
pytest 全量 634 passed / 3 skipped(基线 624 + 10,零回归)

Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
2026-09-10 15:53:31 +08:00
GaoYiYuan_0626andWorkBuddy 3244922a55 基金转换 T-3:core_ro 新增 5 个只读方法 + share_lot_repository 读侧
- core_ro 新增 get_nav_as_of(D10) / get_redeem_fee_rules / list_share_lots(S1 确定性排序)
  / sum_remain_qty(仅 >0) / get_holding;不动 get_latest_nav 与 list_trades_range 白名单
- 新增 share_lot_repository:core_share_lot 读侧 FIFO 贪心选批 + 汇总,排序复用 core_ro 的 ORDER BY(D18 单一副本)
- 新增 tests/test_share_lot.py 15 用例;pytest 全量 624 passed / 3 skipped(基线 609,零回归)
- 同步开发计划 §5.1 执行记录与交接文档 §B 状态(v1.5 / T-3 ✅ / 基线 624)

Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
2026-09-10 15:42:55 +08:00
GaoYiYuan_0626 c5182f1910 feat(convert): 基金转换 T-0/T-0b 门禁 + T-1 数据层 + T-2/T-2b 纯函数与实算回填
T-0 / T-0b(门禁 · 2026-09-10)
- T-0:sqlite 与 MySQL 结构对齐 —— core_holding 统一为 qty/cost_amount/as_of/pnl_pct
  + PK + UNIQUE(customer_id, product_id);补 core_product_nav;新增建库自校验
  _assert_ddl_aligned()(R-g);test_db.py 增 3 条门禁用例(含反向验证门禁失效)
- T-0b:DB 账号分离(D20)—— 新增 scripts/core/00-grant.sql(三账号逐表授权);
  settings.py 增 3 组账号;db.py 改 get_engine(db, role),缓存键改为 (库名, 角色),
  账号未配置回退单账号;core_ro→ro / gateway_repository→rw / risk·session_repository→rw;
  tests/conftest.py 四处显式 role="admin"(R-e)

T-1(数据层)
- scripts/core/01-ddl.sql:新建 core_fee_rule / core_share_lot / core_convert_lot_detail;
  core_trade 加 convert_group_id + idx_convert_group;core_product 加 8 列 + fee_rate 补 COMMENT
- 新增 07-seed-fee-rule.sql(赎回费 5 档 × 14 产品,按 22 号文 §10)/ 08-seed-share-lot.sql
  (58 行持仓 → 61 行批次,Σ remain_qty 恒等于 qty)/ 09-seed-org.sql(管理人 + TA +
  申购费率 + 最低持有余额,v1.1 按「管理人全产品线」重排)
- reset.ps1 追加 07/08/09;02-mysql-agent专用.sql 追加 risk_convert_detail
- tests/_ddl.py 同步 4 表 + 新增 REQUIRED_CONVERT_TABLES 建库门禁
- 新增 scripts/dev/verify_convert_seed.py(pymysql 等价 reset 流程 + 8 条 DoD 断言,
  含断言 ⑧「费率档 ↔ product_type 匹配」,越档即 FAIL)

T-2 / T-2b(纯函数包 + 示例实算回填)
- 新增 app/service/convert/ 7 文件:__init__ / types / calc / fee / nav / lot_bootstrap / errors
  (纯函数,不查库、不碰 SQL;所有量化显式 ROUND_HALF_UP;lot_bootstrap 用 zlib.crc32
   保证 D18 跨进程同源)
- 新增 tests/test_convert_calc.py 93 用例(12 类:HALF_UP 反向自证 / 分档边界 /
  FIFO 含同 confirmed_at 兜底 / 双口径 / 强制全转与强制赎回 / PRD §5.3 全链自证 /
  纯函数零 IO 依赖断言)
- 重写 scripts/dev/calc_convert_demo.py:去掉脚本内公式副本,改为调用生产 calc.py,
  末尾与 PRD §5.3 逐项比对(不一致即退出码 1),兼作一致性门禁

验证
- pytest 609 passed / 3 skipped(516 → +93,零回归)
- verify_convert_seed.py 8/8 PASS;calc_convert_demo.py 15/15 与 PRD §5.3 一致

文档:PRD v0.9.1(费率分类修正)· 架构 §7 签名回填 / §8.3 错误码注 / §15 T-2 完成 ·
开发计划 §1.5 新增 R-h + §4.2·§4.3 执行记录 · AGENTS.md · docs/memory
2026-09-10 14:45:55 +08:00
GaoYiYuan_0626 037ce7edca docs(架构改进): 补齐 PRD/开发计划/TODO/交接文档,落地无密钥告警与 Redis 分布式锁
一、流程文档(按 AIcoding 六步落地,供新会话从交接文档开工)
- 新增 docs/PRD/PRD-架构改进与稳定性加固.md:6 条 FR(文档勘误、非缺陷说明、
  无密钥启动告警、审计失败告警、Redis 分布式锁、中间件顺序测试)
- 新增 docs/项目框架设计/改进方案评审-问题清单与对比.md:24 项问题分档 A~G,
  经两轮独立 AI 评审,无阻断级错误
- 新增 docs/项目框架设计/开发计划-架构改进.md:HOW 层设计,含合并前只做低风险
  11 项的批次策略
- 新增 docs/项目框架设计/TODO-架构改进.md:T-101~T-109、T-201~T-202 可勾选项
- 新增 docs/交接文档-架构改进.md:自包含交接入口,hy3 新会话可直接开工
- 新增 docs/项目框架设计/架构设计说明书.md:按模块/分层逐一讲解的全量架构说明

二、代码改动(T-107/108/109、T-201.1、T-201.2)
- app/main.py:启动时 DEEPSEEK_API_KEY 缺失告警,明确告知将走降级回复
- 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 为主、进程内锁为备;
  Redis 超时沿用 fn(locked=False) 降级语义,Redis 不可用(含测试 Fake 缺方法的
  AttributeError)安全退回进程内锁,绝不抛异常

三、文档勘误(A1/A2/A3)
- MEMORY.md:文件数 42→45、Tools 4→5
- 02-mysql-agent专用.sql:会话表 5→6
- 架构设计-风控模块.md:同步更正

四、测试
- 新增 tests/test_locks_redis.py:覆盖抢锁成功、占用超时、Redis 故障降级、
  Fake 缺方法降级、只删自己锁、三处调用点 key 前缀
- tests/test_audit_middleware.py:补充告警字段断言
- 全量 pytest 510 passed(原基线 503)
2026-09-09 18:10:03 +08:00
GaoYiYuan_0626 01ec5fce32 feat(chat): 新增 SSE 流式对话端点 POST /api/chat/stream(方案 C)
契约(OpenAI 兼容 chunk,AI SDK / fetch-event-source 可直接接):
首帧 meta(session_id/trace_id/disclaimer)→ delta → finish_reason=stop → [DONE]。
前端侧:免责声明由首帧下发、前端常驻渲染;落库文本仍按原口径拼尾部。

设计拍板:
1. 新增独立端点,原 POST /api/chat 契约与既有测试零影响;
2. 鉴权/限流/输入防护全部在返回 StreamingResponse 之前完成(SSE 一开就改不了
   状态码),401/403/404/409/429/400 仍是普通 JSON;
3. 整轮一次性落库:中途异常/断连不落消息(Tool 留痕已落可审计),不产生
   半截内容污染历史窗口。

实现:
- agent_service:抽 needs_disclaimer/_degraded_reply/_base_state;新增 stream_chat
  生成器(Tool 节点同步跑完再推 LLM 文本,无 key 走降级整块);
- api/chat:抽 _guard_request(准入→空白→限流→注入拦截)与 _prepare_turn
  (归属+会话解析/创建),同步与流式共用,守卫零偏差;
- session_repository:新增 insert_turn——user+assistant 同事务落库 + 事务内
  取 seq,修掉评审 P0(落库失败会半截落且前端收不到 [DONE] 挂起)与 P1
  (两次写非原子);同步端点一并改用。

测试:新增 9 例(契约/免责/降级/异常不落库/落库失败/鉴权边界/超长/closed/Tool
留痕),全量 pytest 494→503 绿。遗留:无心跳帧(长生成空隙靠反代 timeout 配置),
断连留空会话待清理策略。
2026-09-08 14:23:33 +08:00
GaoYiYuan_0626 8328c2403e feat(chat): 新增前端拉侧三端点(会话列表/历史消息/关闭会话)
方案 B:补前端对话页「拉」侧能力(chat 此前只有 POST 发消息):
- GET /api/chat/sessions:本人 + 本 Agent 线会话分页列表(created_at 倒序,total 供分页器)
- GET /api/chat/sessions/{id}/messages:历史消息 seq_no 升序分页(closed 会话仍可读)
- POST /api/chat/sessions/{id}/close:active→closed + closed_at;重复/非 active 409

守卫复用:抽取 _resolve_agent_type / _assert_chat_entry / _guard_session,
POST "" 改为复用同套守卫(行为零回归);risk_manager 在对话线数据面保持
同口径 403(PRD 4A.1 冻结);会话仓储新增 list_sessions / list_messages_page /
close_session(条件更新防并发静默写)。

测试:新增 12 例(分页、越权 403+留痕、404、409、manager 拒绝、limit 钳制、
JWT 通道),路由挂载清单同步;全量 pytest 482→494 绿。独立 AI 评审 P0=0,
P1(close 并发 rowcount 静默 200)已修复。
2026-09-08 14:07:38 +08:00
GaoYiYuan_0626 66ec7eba08 feat: C6 FR-10 代理人行为链 RISK-008
代理人异常行为链识别(RISK-008)落地:
- 新增 agent_behavior_service:三条件证据聚合(A 诱导调仓/B AUTH_403_SCOPE 越权试探/C AUTH_403_NOT_OWNER|NOT_ASSIGNED 越权查询),按代理人维度独立出 pattern 单,payload.actor_id 指向代理人,审计仅 INSERT event_type=agent_behavior_detected。
- risk_repository 新增 list_audit_events / find_agent_behavior_alert / merge_agent_behavior_payload(同日同代理人一张单,证据并集)。
- trade_gateway.submit_trade 补 actor_id 透传(代理人发起交易归属发起人,缺省 SYSTEM);simulate 路由传入 auth.actor_id。
- chat_tools 新增 query_agent_behavior 只读 Tool(agent_id 过滤 + 客户脱敏),tool_service 补意图词与摘要。
- scripts/cron/agent_behavior_scan.py 定时扫描脚本。
- 修复 append_alert_event 序列化缺 default=str(C6 evidence 含 datetime 字段)。
- 单测 12 例(_count_induce 边界 / 三条件 / 出单去重 / payload 归属 / Tool 过滤脱敏)。
全量 pytest 482 passed 0 failed(470 基线 + 12 C6)。
2026-09-07 19:53:39 +08:00
GaoYiYuan_0626 90d0660b44 feat: C5 FR-9 预警处置时效升级(RISK-007,P0)
- 仓储:新增 list_pending_alerts_all / update_alert_escalation(统一读改写 payload,
  升级标记独占写入,status/handler_* 列不碰;_parse_alert 补 created_at/handled_at
  字符串→datetime 解析,兼容 sqlite 原生 DDL)
- escalation_service.scan_and_escalate:扫描判级(普通 4h/24h、AML 1h/4h 短通道)、
  幂等闸门(仅升不降)、先持久化再推送、按 (customer_id,level) 降噪合并一次推送、
  通知链累积(L1 含 risk_manager / L2 含 compliance)、逐单+任务级审计
- scripts/cron/escalation_scan.py:15min 定时扫描壳(sys.path 引导 + new_trace + JSON 摘要)
- 对话线 query_overdue_alerts Tool + 注册表 + 意图词(置于 alert_query 之前)+ summarize 分支
- C5 前置:seed STAFF-31001/31002(risk_manager) + deps 矩阵放行 + chat.py 显式 deny +
  risk.py 台账全量只读分支;JWT 手册 §5.3/§5.4/§6.1 增补 risk_manager
- 测试:conftest 回拨 fixture + test_escalation_service(9) + risk_api/manager(5) +
  chat deny(1) + chat_tools overdue(2);全量 470 绿(453+17)
2026-09-07 19:28:04 +08:00
GaoYiYuan_0626 fe4801bc0a feat: C4 FR-8 持仓集中度预警(RISK-006)
依据《实现方案-风控追加需求v1.1-C4C6.md》§2;不改表结构(alert_type/status
复用 payload 承载,audit_log.event_type 为 VARCHAR 可直接扩)。

1. settings.py + .env.example:一次性加齐风控追加 v1.1 共 11 项配置(C4~C6 共用)。
2. core_ro.concentration_profile(customer_id, limit=500):一次 SQL 取明细
   (LIMIT limit+1 探测截断)+ Python 端按 min_risk_code in (R4,R5) 聚合;
   收口挂账 #1(PRD 字面为 list_holdings,改聚合封装,docstring 注明偏离)。
3. rules.py:RULE_SCORES/RULE_ALERT_TYPES 加 RISK-006=60/pattern;RuleHit 加
   alert_subtype;RiskThresholds 加 concentration_threshold 且 from_settings
   必须补读(评审 P1-2:漏读会让 conftest monkeypatch 失效打穿现有断言);
   新增纯函数 rule_concentration——空仓不触发、截断视同达标(保守告警)、
   阈值边界 79.9% 不触发 / 80% 触发、R4+R5 为 0 不触发。
4. engine.process_trade_event:run_rules 之后、record_trade_alerts 之前并入
   集中度命中(不动 run_rules 签名);命中后 L3 打 high_risk_concentration
   标签 + 写 risk_concentration 审计(金额只落合计与前 5 条摘要)。
5. risk_repository:find_pending_event_alert 改候选 LIMIT 50 + Python 过滤掉
   payload.alert_subtype 含 agent_behavior 的单(评审 P0-1:代理人维度行为链单
   不得充当客户维度事件单的聚合锚点);append_alert_event 加 extra_subtypes
   合并进 payload.alert_subtype(不传时行为与原先一致,向后兼容)。
6. alert_service:subtypes 集合维护(空集不注入 payload,评审 P2-3);
   追加时 alert_type 按「老单规则 ∪ 本批规则」重算(评审 P1-3,修掉既有
   large_amount 单被本批仅 RISK-006(60) 翻转为 pattern 的缺陷);
   _publish_alert 加 notify_role/extra 可选参数(C5/C6 复用)。
7. 对话线:chat_tools.customer_context 加 profile(concentration_ratio/
   r45_value/total_value/holdings_truncated),tool_service.summarize 加
   「高风险持仓占比 X%(仅供参考)」;不新增意图词。
8. 02-redis-keys.md 增补 alert_subtype / escalation_level 附加推送字段。

测试:conftest 加 autouse _disable_concentration_rule(阈值推 1.01 做回归隔离,
现有用例断言零改动);test_risk_rules 加 RISK-006 纯函数 6 例;新建
tests/test_concentration_c4.py 11 例(与 RISK-001 同单聚合、score max=70、
L3 tag、risk_concentration 审计、仅集中度也出单、subtype 合并、P0-1 回归、
alert_type 不翻转、对话线 ratio)。全量 453 绿(436 + 17)。
2026-09-07 19:05:02 +08:00
GaoYiYuan_0626 8c226f0c6d chore: 风控Agent模块自治边界标注 + AL-09 合并预处置
背景:远端 main 新提交 3995cb4(09-07 17:20,交接文档漏记)为 Wave 0 鉴权/
chat/防护平行实现,与已完工 T-01/T-02/T-03/T-06 同名不同路径;试合并实测
20 文件冲突(原记 9 个),另有 13 个 main 新增文件不报冲突会静默并入。
拍板:风控 Agent 按独立封装模块自治,与宿主耦合收敛到 4 个接缝。

1. 《风控Agent模块边界与合并接缝标注》入库存档:A~D 四类文件归属表;
   4 接缝(S1 挂载点 main.py / S2 AuthContext / S3 settings / S4 引擎工厂);
   20 冲突文件逐个裁决(core_ro、model/suitability、conftest、02-seed-base
   以模块版为准;chat/main/settings/agent_service 等公共层以 main 为主);
   三处硬伤处置:issuer 不一致改为适配器映射不统一、STAFF-90001 必保、
   main infer_roles 未知 actor 默认 analyst(fail-open)记宿主侧 P1。
2. app/api/auth_adapter.py:S2 接缝适配器预制件(当前未接线,AL-09 接入)。
   鸭子类型读宿主 ctx 故不依赖宿主文件;sub→actor_id、trace_id→contextvar、
   perm_matches 兼容宿主 `前缀:*` 通配;缺主体即 HostAuthAdapterError,
   fail-closed 不静默降级。
3. tests/test_module_boundary.py:边界防呆 4 类断言——模块私有文件存在、
   禁止跨层 import 宿主私有实现(gateway.*/config.database/middleware.*/
   utils.input_guard/model.schemas)、AuthContext 契约完整(actor_id 与
   has_role 多参)、settings 私有字段与 AGENT_TYPES 四值不漂移。
4. tests/test_auth_adapter.py:适配器 11 例(映射/回退/fail-closed/trace 绑定/通配)。

基线:406 → 436 全绿(演示库已按演练 SOP §2 重灌:AML 8 条 / 演示 7 行 / sync 33 rows)。
2026-09-07 18:28:18 +08:00
GaoYiYuan_0626 b5fd52ef02 AL-08: 演示库重灌与种子核对对齐 main 基准——prepare_risk_demo.sql 改写 expires_at 口径(演示客户刷新为测评 90 天前/剩余 275 天,CUST-1004 保持过期演示 FM-03,替代 SUIT-008 evaluated_at+365 旧写法);演示 SOP 同步(§2 完成标志 33 客户口径+expires_at 剩余天数,A-1 预期改 SUIT_RISK_MISMATCH,A-2 改 FM-01 SUIT_AGE_CONFIRM 网点确认);网关阻断响应补全 main 契约字段(match_result/mismatch_type/requires_disclosure/needs_branch_confirm/rule_refs)+audit detail 加 block_response_code;集成测试 4 例旧口径断言更新(A-1 trace 一致性断机器码/A-1 audit JSON 断机器码/A-2 重写为 FM-01 语义/settings 退役断言)。SOP §2 脚本化全量重灌双库验证通过(AML 8 条/演示 7 行/sync 33 rows);全量 406 passed 0 failed 0 skipped(集成测试真库走 21 列新表+expires_at 数据恢复执行) 2026-09-07 17:49:39 +08:00
GaoYiYuan_0626 ec11ec42c0 AL-07: test_suitability 按 main 契约重写——七判定路径正反例(not_found 客户/产品两向/FM-03 过期/JR-AST-PRO 专业豁免优先于矩阵 forbidden/JR-AST-012 矩阵 forbidden/矩阵缺行 fail-closed/allowed/矩阵披露与产品 requires_disclosure 两源/FM-01 age70+R5 阻断+69岁与R2 两边界);compute_rule_refs 四类组合(含报告样例 过期+不匹配→['JR-AST-012','FM-03']);build_suitability_log_row 逐字段 21 列映射断言+bool→int 转换;服务集成端到端(21 列落库/check_source r02_trade 与 r02_chat/actor_id/request_ref 透传/兼容字段 rule_id=机器码 映射/NotFound 结构化不抛);MySQL 方言冒烟(真库连通时验 SQL 可执行性,未灌库自动 skip)。可控日期用远未来/远过去 expires_at 种子。全量 395 passed 0 failed;1 skip=集成模块待 AL-08 重灌演示 SQL expires_at 2026-09-07 17:42:55 +08:00
GaoYiYuan_0626 38da477be4 AL-06: 对话 Tool 与 API 响应对齐 main 基准——chat_tools.suitability_check 返回体扩展 match_result/mismatch_type/requires_disclosure/needs_branch_confirm/block_response_code/rule_refs(替代 rule_id/reasons/effective_level 展示),check_source='r02_chat',not_found 转结构化 found=False(NotFoundError except 死代码清除);tool_service.summarize suitability 分支按新枚举重写文案(网点当面确认 FM-01/签署风险揭示书/测评过期 FM-03/专业豁免,附 rule_refs 依据);risk_api 断言改 block_response_code。全量 373 passed 0 failed(1 skip=test_suitability 占位待 AL-07)。遗留:chat 链路 risk_suitability_log.actor_id 暂落 SYSTEM(run_tool 统一透传需动 core_tools/kb_tools 冻结 func 契约,发起者留痕已由 agent_tool_call 覆盖) 2026-09-07 17:39:04 +08:00
GaoYiYuan_0626 adf5e8013a AL-05: suitability 服务重构换核对齐 main 基准——判定内核由 SUIT-001~008 纯函数矩阵改为 core_ro.check_suitability(C×R 数据驱动);SuitabilityResult 扩 main 新字段(match_result 五值/mismatch_type 七值/requires_disclosure/needs_branch_confirm/block_response_code/rule_refs)并保留旧字段兼容层(rule_id=机器码别名/effective_level=customer_level/reasons=[reason]);check_core/cap_by_age/match_by_matrix/is_assessment_valid 删除(SUIT-006 被 FM-01 替换,SUIT-008 被 FM-03 expires_at 替换);settings.risk_assessment_valid_days 退役(.env/.env.example 注释);NotFoundError 不再抛(not_found 结构化,main 契约);写库走 build_suitability_log_row + insert_suitability_log 升级 21 列(rule_refs JSON 序列化);check_source 参数化(网关 r02_trade/risk.py manual)+actor_id 透传;网关阻断响应补 block_response_code;model 抽 compute_rule_refs 公共函数防两处漂移;测试配套:_ddl 加矩阵 25 行种子 helper、conftest fixture 灌种子+集成就位校验改 expires_at 口径、trade_gateway/risk_api fixture 补种子与 expires_at、404 两例改 200+blocked 新契约、repository 21 列断言、settings 退役断言、test_suitability 占位 skip 待 AL-07 重写。sqlite 端到端七语义+落库验证通过;剩 4 failed 均为 chat_tools/API 旧展示断言(AL-06 处理) 2026-09-07 17:34:32 +08:00
GaoYiYuan_0626 e99ec6775a AL-02: risk_suitability_log 重建 21 列对齐 main 基准——底座 SQL 采纳 main 版(product_name/investor_category/match_result 五值/mismatch_type 七值/requires_disclosure/needs_branch_confirm/risk_was_expired/block_response_code/check_source 四值/actor_id/rule_refs JSON + 6 索引);07-说明.md 直接采用 main 版 P0 契约;tests/_ddl.py sqlite 同步(21 列 + core_suitability_rule 新表 + core_customer/core_customer_risk/core_product 扩列,VARCHAR 近似 ENUM 与现有风格一致);修 test_risk_api 位置式 INSERT 补列名。全列 INSERT 验收通过;剩余 15 failed 均为旧契约断言(报告预告作废集,AL-05/06/07 清零) 2026-09-07 17:05:15 +08:00
GaoYiYuan_0626 1c51149912 feat: T21-5 知识库 Tool 接入对话——kb_tools(search_knowledge)挂统一注册表第三层; ToolSpec 新增 skip_access_check(公开知识无归属语义, advisor 无绑定客户可查, 意图层限定 customer/advisor); 意图关键词 customer/advisor 组尾挂 kb 词(Core 词优先, risk 不开放); tool_node 按 spec 白名单注入 query; summarize 溯源口径; 单测 13 例, 425 绿 2026-09-07 12:05:36 +08:00
GaoYiYuan_0626 e2a444453e feat: T21-4 RAG 检索编排——rag_service.search_knowledge(query→bge-m3 向量→Milvus 检索→chunks+source_refs 溯源去重), 异常原样上抛不吞(防 LLM 编造回答), 空白 query 短路, ensure_collection 幂等防御; 单测 8 例注入 mock, 412 绿 2026-09-07 12:01:45 +08:00
GaoYiYuan_0626 7edf522aff feat: T21-3 知识库首批数据+入库脚本——data/kb 6 只种子产品手册(每只 prospectus/fee/rule/risk 四节 24 块, 费率与 core_product seed 对齐)+build_kb.py(front-matter+节标题切块/doc_type 白名单/Core 权威元数据缺省即中止/一次批量 embedding/upsert 幂等/--dry-run); 脚本单测 10 例, 404 绿 2026-09-07 11:55:24 +08:00
GaoYiYuan_0626 1056a95343 feat: T21-2 Milvus 接入层——kb_product_rules 幂等建集合(冻结 11 字段+AUTOINDEX COSINE)+upsert 入库+search_kb(溯源字段必带+effective_date 合规过滤+标量过滤); 规避 pymilvus 3.x import 阶段读 .env 文件 URI 炸 import 的坑(setdefault 预置合法 MILVUS_URI); 单测 8 例真 Lite 临时库, 394 绿 2026-09-07 11:49:43 +08:00
GaoYiYuan_0626 e34973f018 feat: T21-1 embedding 服务——Ollama bge-m3 批量 /api/embed(1024 维), 连接/模型/结构/维度四类失败一律 EmbeddingError 不静默降级(拍板口径), 空输入短路; settings 增 embed_dim/embed_timeout; 单测 10 例 mock HTTP 不依赖 Ollama, 386 绿 2026-09-07 11:46:24 +08:00
GaoYiYuan_0626 207fc79f61 test: T-03 评审闭环(PASS, P0=0)——P2-1 词表规范化断言修正/P2-2 补 7 条同构变体(你现在是管理员/ignore the above instructions 等)+变体单测/P2-3 窗口 TTL 断言+EXPIRE 失败取舍锁定; P3 session_id 落库截断+trace 兜底注释, 378 绿 2026-09-07 11:16:15 +08:00
GaoYiYuan_0626 cc6f34fbbd feat: T-03 输入防护限流(T3-3)——actor 级 Redis 固定窗口 30 次/分(settings 可调), 超限 429 GUARD_RATE_LIMITED+留痕, Redis 故障 fail-open, 测试 4 例(429/窗口滚动/fail-open/注入计数), 376 绿 2026-09-07 11:10:26 +08:00
GaoYiYuan_0626 65b45aa917 feat: T-03 输入防护接入 chat(T3-2)——注入/超长命中即拒 400 + input_guard_log 留痕(fail-fast 不建会话, 留痕降级不改拒绝语义), Pydantic 硬顶 20000, 集成测试 7 例 2026-09-07 11:02:36 +08:00
GaoYiYuan_0626 497554da43 feat: T-03 输入防护服务(T3-1)——input_guard 纯函数检测(注入短语词表/oversize 4000) + 单测 20 例(不误杀用例/判定顺序锁定), 365 绿 2026-09-07 10:59:25 +08:00
GaoYiYuan_0626 c92d8c7783 test: 阶段C 评审 P2 补 advisor 归属覆盖与空 product_id 单测(345 绿) 2026-09-07 10:31:58 +08:00
GaoYiYuan_0626 930ea9db56 feat: 阶段C 风控对话线(C1 四只读 Tool + C2 risk 分支意图 + C3 A-6 验收与边界)
- C1: app/service/risk/chat_tools.py 新增 alert_query/customer_context/suitability_check/aml_lookup 四只读 Tool,挂 RISK_TOOL_REGISTRY(与 core_tools.ToolSpec 同构)
- C1: tool_service.run_tool 改查 core+risk 统一注册表并恒传 risk_repo;_normalize_params 读 spec 白名单;core_tools 三函数加 risk_repo=None;summarize 补四工具分支
- C2: match_intent 按 agent 分组扩 risk 关键词;agent_service.tool_node 守卫放宽(requires_customer=False 的 Tool 允许无绑定客户运行,支撑 A-6 全局待审)
- C3: A-6 验收(risk_officer 问待审预警→alert_query 全量→回复含待审数)+ 诱导处置边界(无处置 Tool、系统提示禁自动处置、诱导只触发只读且预警状态不变)
- 测试 tests/test_risk_chat_tools.py(21 例);全量 pytest 342 绿(原 321 + 21)
2026-09-07 10:26:54 +08:00
GaoYiYuan_0626 1216d26083 fix: T-04 复审闭环——P2-1 test_chat 夹具补 _risk_repo 打桩(防越权双写写脏演示 MySQL)/P3 归属码改为显式集合+前缀兜底/truncated 判定多取 1 条防恰好 500 误报, 321 绿 2026-09-07 09:51:38 +08:00
GaoYiYuan_0626 40a9ee21a6 fix: T-04 评审闭环——P1-1 留痕降级提 error 带定位字段/P1-2 对话越权接鉴权双写(utils/authz.py 下沉公共出口,deps 复用,service 不反向依赖 api)/P2 入参白名单+days 钳制[1,365]/list_holdings SQL LIMIT+truncated/时间窗集中 _now_naive; 补测 24 例(days 边界/非法入参/inactive 归属/双写断言/降级日志/上下文截断), 321 绿 2026-09-07 09:42:37 +08:00
GaoYiYuan_0626 301c78edc3 feat: T-04 Core RO Tool 节点——chat Tool 接入+归属校验+agent_tool_call 落库
- app/tool/core_tools.py: 三只读 Tool(query_customer_profile/holdings/recent_trades, Core RO 仅 SELECT) + TOOL_REGISTRY 白名单(requires_customer); JSON 安全化(Decimal 两位/时间 isoformat)
- app/service/tool_service.py: match_intent 关键词意图(仅 customer/advisor; risk/analyst 空转归 C1/C2) + assert_tool_access 归属断言(customer 本人/advisor assigned/risk_officer 全量/其余拒, 口径对齐 deps.assert_customer_access) + run_tool 编排(白名单→校验→执行→agent_tool_call 落库, 落库失败降级 warning)
- agent_service: 图 START→tool→llm→guard; Tool 结果注入 LLM 上下文(SystemMessage); 降级回复带 Tool 摘要; chat() 可选 session 上下文(缺省空转, T-07 兼容)
- session_repository: insert_tool_call(message_id 一期 NULL, session_id+trace_id 可还原)
- 归属拒绝口径: Tool 层不抛 403 改 blocked 留痕(AUTH_403_* 同码), 对话内呈现——API 层 deny 铁律不变
- tests: test_chat_tools 20 例(三态+落库字段+降级+图注入) + test_chat 4 例(全链路/JWT 外 debug 通道), _ddl 补 agent_tool_call/core_holding, 297 绿
- 真库冒烟: uvicorn+真 MySQL/Redis chat 触发持仓查询 success 落痕 47ms, 现场已清理
2026-09-07 08:43:36 +08:00
GaoYiYuan_0626 61711cb70a fix: Wave 0 独立 AI 评审闭环(有条件通过→闭环, 273 绿)——P2-1 python-jose>=3.5.0(防 CVE-2024-33663/33664) P2-2 audit 降级+OPTIONS 测试 P2-3 JWT 通道 chat 3 例 P2-4 debug 兜底双闸门(dev+无 RS256 公钥) P2-5 留痕失败降级显式化(401/403 拒绝语义不漂移, _safe_unauth_audit 保护 repo 构造); P3 顺手: 401 归 platform/值域外 400 补留痕/Bearer 大小写不敏感/jti 加 uuid/CLI permissions+exp 上限/dev 默认密钥 warning/LLM 单例加锁/chat 主角色优先序+message strip+guard 日志脱敏/必填 claims 参数化+none 算法固化; P3 登记项(会话枚举/seq_no 锁/trace_id 口径/文档同步)归 M4 文档 2026-09-07 03:03:12 +08:00
GaoYiYuan_0626 ee5c78af66 feat: T-06 chat 最小闭环——api/chat(X-Agent-Type 分流+准入矩阵+customer 归属固定本人/advisor G-01 归属校验+SessionGuard actor/agent 一致+closed 409) + session_repository(agent_session/agent_message, 派生表补 alias 修 MySQL 1248) + memory_service(Redis 窗口 sess:{agent}:{id}:msgs TTL2h N20, miss/异常回源 MySQL) + main 挂载 + tests/test_chat 12 例, 260 绿 2026-09-07 02:41:42 +08:00
GaoYiYuan_0626 fb18c04a8a feat: T-07 LangGraph StateGraph 骨架——compose(system 四 Agent 边界 prompt)→llm(DeepSeek/langchain-openai, 无 key 降级)→guard(customer/risk 免责声明+has_disclaimer, 未知类型 fail-safe 最严口径) + chat() 入口 + 图/LLM 单例缓存与测试隔离出口 + tests/test_agent_service 8 例(mock LLM 不依赖外网) 2026-09-07 02:34:57 +08:00
GaoYiYuan_0626 3cec034026 feat: T-02 审计中间件——http_access 全量访问审计(auth 注入 request.state, 排障路径跳过, 写库失败降级) + 独立 request_id(X-Request-Id 白名单透传贯通, B7 复审 P3-4) + 422/404/405/500 统一错误体(500 由 trace 中间件兜底生成并回写 trace 头, B7 复审 P2-2) + input_guard_log 双写(deps 401/403 留痕, platform 网关跳过 ENUM 口径, 挂账⑧收口) + repo.insert_input_guard_log + tests/test_audit_middleware 12 例, 240 绿 2026-09-07 02:32:03 +08:00
GaoYiYuan_0626 36f476f450 feat: T-01 JWT 鉴权——auth_service(Auth SDK: HS256/RS256 验签+必填claims显式校验+jti吊销 fail-open) + deps 工厂替换(Bearer 全环境, debug 头降级 dev 兜底) + X-Agent-Type 交叉校验(手册§4.6/§5.4 准入矩阵) + issue_dev_token CLI + redis_gateway exists/set_ex + lifespan 改验 jwt_ready + tests/test_auth_jwt 18 例, 228 绿 2026-09-07 02:23:33 +08:00
GaoYiYuan_0626 539d4915d7 fix: B9b 复审闭环——P2-1 SOP 交互重灌补 DROP jinrong_agent(reset.ps1 只重建 core+裸 CREATE TABLE 会 1050 中止); P2-2 补做 T-31 名下 P2-8 手工 SQL 对照收口(4 组合真库 SQL 推导与函数判定一致, 登记 SOP 核查单附行); P2-3 SOP 走查范围与 11/11 口径统一(A-6 归 M3/A-8 映射交代); P3: A-4/A-9 COUNT 当日窗+已处置单幂等用例+suitability/check 阻断响应补 G-08 两要素(advice/notice)+记忆文档数字同步; 留痕 scan_all 幂等读后写无锁取舍; 209 绿 2026-09-07 01:59:15 +08:00
GaoYiYuan_0626 03d77b9451 feat: B9b 演示走查——核查单收口: ①预警类 API 固定 disclaimer(alerts/handle/aml-scan) ②aml/scan 幂等防护(find_recent_aml_alert 同客户当日已有 aml 单不出单+skipped_existing) ⑥集成测试前置断言(A-1/A-3/A-4 清洁检查 fail+重灌提示, A-5 按 trade_id 定位); 演示 SOP 落地(脚本化重灌 MYSQL_PWD 方式); 真服务走查 A-1~A-5/A-7/A-9+核查单①②④⑤ 11/11 PASS(核查单⑥防护现场复现), 208 绿; M2 达成 2026-09-07 01:36:44 +08:00
GaoYiYuan_0626 e62fb1d291 fix: B9a 复审闭环——P2-1 skipped 分支 has_engine_error_audit 中间态警示(input_summary LIKE)+docstring 局限声明; P2-2 非首笔 trade 幂等用例; P3 顺手: 双脚本连接自检/ImportError 分支/非 dict payload raw 兜底/get_trade_by_id confirmed 过滤/勿并行声明, 8 例 204 绿, warning 路径真库复验后清理; docs 同步复审结论 2026-09-07 01:02:12 +08:00
GaoYiYuan_0626 fdf487dc32 feat: B9a 演示/运维脚本——subscribe_alerts(risk:pub:alert 订阅演示/连接自检/--duration) + rebuild_alerts(按 trade_id 幂等重放: find_alerts_by_trade payload LIKE 查已入单防重复出单与重复 append, 含 aml/已处置单) + core_ro.get_trade_by_id 只读扩展 + test_demo_scripts 5 例, 201 绿; 真库手工验证(出单→幂等 skip→missing exit1; 订阅端到端 trace 贯通)后现场清理; docs 同步(TODO/MEMORY/FLOW/开发计划) 2026-09-07 00:43:25 +08:00
GaoYiYuan_0626 516b1132a3 fix: B8 复审 P1-1——conftest except pytest.SkipRequested 引用不存在属性致 skip 路径炸收集(删, Skipped 继承 BaseException 直传, 错密码故障注入验证 skip 不 error); P3 顺手: teardown l3_snapshot 哨兵 + A-1 补 trace 响应头/suitability_log 两环; P2-1 演示库耦合前置断言挂 B9b 核查单⑥ 2026-09-06 23:52:59 +08:00
GaoYiYuan_0626 c6ad0885d3 test: B8 收敛——sqlite 测试 DDL 单一事实源 _ddl.py(10 文件 45 处手写 DDL 清零)+localtime 时区收敛(B5 评审 P3-4, utcnow 边界修复)+测试内遗留建表清理, 196 绿; docs 同步 B8 完成状态 2026-09-06 23:37:03 +08:00
GaoYiYuan_0626 e7bd8880e2 test: B8 主体——conftest(演示数据校验/幂等代跑/TRD-TEST- teardown)+集成测试 A-1~A-5/A-7/A-9+trace一致性+审计JSON(11例)+_ddl.py 单一事实源+L3 DEL 行为断言(B7复审P2-1)+locks 文案中性化(P3-2), 196 绿 2026-09-06 23:27:37 +08:00
GaoYiYuan_0626 fe29b736ea fix: B7 复审 P1-1——dispose_engines 显式 Engine.dispose()(手写单例字典替代 lru_cache, test_db 3 例, 独立评审有条件通过→闭环) 2026-09-06 23:06:32 +08:00
GaoYiYuan_0626 6195349dd6 feat: main 集成(路由挂载/trace 中间件/lifespan) + B7 挂账收口(引擎工厂/统一错误体/锁公共化/L3 缓存 DEL/死代码清理/处置原子事务/启动期 debug 校验, B7) 2026-09-06 20:48:50 +08:00