# 投顾 Agent 迁移 TODO > 目标:以 `qyqy_develop` 为新底座,在不直接合并 `lzl` 的前提下,重新接入现有投顾 Agent 能力。 > > 使用规则:每完成一项,将 `[ ]` 改为 `[x]`,并在任务后补充提交号、测试结果或问题记录。 ## 迁移进度记录 ### 最新 qyqy_develop 基座合并(2026-09-11) 已基于最新 `origin/qyqy_develop@76e87a33a7` 创建集成分支 `lzl_qyqy_integration`,并在不修改 `lzl_develop` 的前提下合并投顾成果。 平台、鉴权、风控、场外、推广、知识库、CORS、请求校验及既有 Agent 注册均以 `qyqy_develop` 为准;开户问卷、投资目标、持仓分析、动态资产配置、产品推荐、 会话闭环、画像治理、场内行情双源和投顾工具以 `lzl_develop` 为准,互不冲突的 能力均已保留。 数据库基线 `docs/00-新数据库基线设计.md` 未修改。新增 `20260911_merge_advisor_risk_heads` 仅收敛投顾链与基座风险索引链的 Alembic 图, `upgrade`/`downgrade` 均无 DDL;当前唯一 head 为 `20260911_merge_adv_risk_heads`。本机开发 JWT 密钥已按基座工具生成至被忽略的 `config/jwt/dev/`,不进入版本控制。 合并验证结果:单元测试 `1186 passed, 2 skipped`,契约测试 `21 passed`,Ruff 通过,MyPy 对 `223` 个 app 源文件通过。测试仅出现既有第三方弃用警告和 Alembic 配置弃用警告,无失败项。 ### 阶段一:迁移准备 已完成本地冻结和质量基线:提交 `5dcaf7c`,标签 `advisor-before-base-migration`。数据库备份保存于 `.migration-backups/jr_agent-before-base-migration.sql`,接口快照保存于 `.migration-backups/current-openapi.json`。 阶段一测试结果:单元/契约测试 `259 passed`,集成测试 `21 passed`,Ruff 通过,MyPy 通过,数据库结构审计通过,Alembic 当前版本为 `20260911_adv_profile_tags`。 当前阻塞:远程仓库 `47.106.207.27:3000` 暂时无法连接;`lzl_develop` 尚未确认存在。当前新底座已包含 `tools/audit_constraints.py`,旧库版本不兼容问题待切换独立测试库处理。 已有测试库只读复核(2026-09-11):`migration_state_check.py` 和 `alembic current` 均显示 `20260911_adv_profile_tags (head)`,但结构审计发现 8 张历史场外表,约束审计发现 `fin_holding`、`fin_market_price`、`fin_nav_history`、`sys_customer_assignment` 共 12 项唯一键 漂移。因此拒绝在原 `jr_agent` 库执行 `upgrade` 或任何修复性 DDL;该库不能计入“已有测试库 迁移成功”,仍以独立迁移库 `jr_agent_qyqy_migration` 作为新底座验收库。 ### 阶段二:建立新开发分支 已基于本地缓存的 `origin/qyqy_develop@6516ccb` 创建 `lzl_develop`。新底座单元/契约测试 `447 passed`,Ruff 通过,MyPy(103 个源文件)通过。 注意:当前 MySQL 测试库仍记录旧投顾迁移版本 `20260911_adv_profile_tags`,新底座无法解析该版本;后续必须使用新底座基线重新初始化或准备独立迁移数据库,不能直接在该旧库上继续升级。 ### 阶段三:公共底座适配 已新增最小投顾 Agent `advisor`,通过新底座的 `BaseAgent`、`AgentFactory`、`ToolExecutor`、意图声明和场内行情只读工具运行。当前只接入行情查询,产品、问卷、投资目标、持仓和推荐能力留待后续阶段逐项接入。 阶段三测试结果:专项测试 `9 passed`,全量单元/契约测试 `448 passed`,Ruff 通过,MyPy(104 个源文件)通过。适配提交:`5e8eecc`。 ### 阶段四:数据库和迁移 已将旧投顾迁移按 `qyqy_develop` 的 `20260910_drop_review_separation` head 重新接入为一条连续链,新增产品治理、合同证据、行业/资产分类、历史行情、行情源健康、 数据质量、动态配置回测、投资目标、会话目标抽取和画像标签治理表。未修改基线迁移, 未删除、重命名或改变基线表和字段;迁移契约测试对此有静态校验。 阶段四测试结果:迁移契约 `2 passed`,全量单元测试 `442 passed`,Ruff 通过,MyPy (104 个源文件)通过;独立 MySQL 库 `jr_agent_qyqy_migration` 在线执行 `alembic upgrade head` 成功,结构审计显示 `72` 张业务表无缺失或多余,约束/ORM 审计通过。 原 `jr_agent` 库仍记录旧投顾迁移版本 `20260911_adv_profile_tags`,不能直接用新底座升级, 已保留不动,待数据库负责人按迁移方案另行切换。 ### 阶段五:产品数据和适当性(进行中) 已恢复 `hq.py` 公开数据适配器、场内产品导入工具、南方官网适当性/合同同步工具, 并新增投顾证据只读模型与 Repository。独立测试库已导入 19 个南方场内 ETF/LOF, 同步 19 条官网适当性披露和 19 条合同证据,实际权威风险分布为 R1=1、R2=4、R3=6、 R4=7、R5=1;资产分类成功 18 个,1 个因合同证据不足跳过。每个等级至少四个产品 不能由系统伪造,保留为未完成验收项。 阶段五已完成部分测试:产品证据 Repository `2 passed`,官网治理解析 `3 passed`, 治理导入/分类导入 `5 passed`,合计专项 `10 passed`;Ruff、MyPy、数据库结构审计和 约束审计通过。阶段五后续补齐了历史平均成交额流动性证据、流动性门槛失败关闭、 R1-R5 适当性硬过滤和排除原因。权威数据验收仍保留两个限制:R1/R5 尚不足四个 测试产品,1 个产品因合同证据不足未完成资产分类;完整推荐侧联动留待阶段十。 阶段五测试结果:专项 `7 passed`,全量单元测试 `473 passed, 3 warnings`,Ruff 和 MyPy 通过。阶段五核心提交:`291cb7b`、`47a5ca4`。 ### 阶段六:开户风险问卷 已完成开户问卷查询和提交接口、服务端 C1-C5 评分与风险等级生成、开户画像快照、 问卷有效期和每日/年度提交次数限制。首次登录访问非 onboarding 接口时会被拦截, 问卷结果通过 `MemorySyncOutbox` 同步画像投影;客户响应不包含答案、总分、风险等级、 画像或来源置信度。提交使用 `Idempotency-Key`,重复请求按请求摘要幂等处理。 阶段六测试结果:专项测试 `7 passed`,全量单元测试 `464 passed, 3 warnings`,Ruff 通过,MyPy(116 个源文件)通过。数据库审计工具已执行,但默认 `.env` 连接的是旧 `jr_agent` 库,结构审计发现旧库的场外表和基线约束差异;阶段四已验证独立库 `jr_agent_qyqy_migration` 的迁移结构,不能将旧库结果作为新底座验收结果。 阶段六提交:`acb9175`。 ### 阶段七:投资目标和目标书 已完成投资目标结构化采集、目标书草稿生成、客户确认、投顾审核和发布闭环。目标 创建后状态为 `pending_confirmation`,目标书使用基线 `client_facing_content` 的 `pending -> approved -> published` 审核状态;未经审核不能发布。目标查询工具只向 Agent 暴露客户最新的 `confirmed` 目标,未确认目标不会进入后续推荐和配置输入。 目标收益范围、最大回撤、流动性要求、期限、业绩比较基准和备注均有服务端校验, 目标书含收益非承诺、非交易指令披露;写操作统一使用 `Idempotency-Key` 并写入审计。 阶段七测试结果:专项测试 `5 passed`,全量单元测试 `468 passed, 3 warnings`,Ruff 通过,MyPy(122 个源文件)通过。阶段七提交:`60c4a49`。 ### 阶段八:持仓分析 已基于现行 `fin_holding`、`fin_product` 和投顾行业/历史指标表完成只读持仓分析: 支持持仓查询、市值汇总、单产品集中度、行业穿透、产品/行业 HHI、行业与历史指标 覆盖率,并在市值或行业数据不足时返回明确的降级状态和警告。MySQL 是数值分析的 唯一事实来源,输出只生成分析结论,不生成交易指令;Neo4j 仅通过 `RelationshipService` 的固定关系模板提供行业重叠辅助上下文,连接失败、超时或结果 异常时明确降级,不影响 MySQL 数值分析。 阶段八测试结果:图谱与持仓专项测试 `8 passed`,全量单元测试 `475 passed, 3 warnings`,Ruff 通过,MyPy(127 个源文件)通过。阶段八核心提交:`281e76e`、`8ffd08b`。 阶段九已完成动态资产配置与真实历史回测:接入 C1-C5 基础权重、收益目标、最大回撤、 流动性和投资期限约束,读取经适当性、合同证据、资产分类和数据质量门槛过滤的场内基金 历史指标,输出动态权重、静态基线、数据覆盖率和因子证据;覆盖不足时静态降级。已接入 `advisor` Agent 的 `asset_allocation` 意图、`generate_asset_allocation` 只读工具和 `POST /api/v1/advisor/asset-allocation` 接口;管理员可通过回测接口生成静态/动态收益、 最大回撤、再平衡次数和限制条件,并写入新增回测证据表。 阶段九测试结果:专项测试 `9 passed`,全量单元/契约测试 `491 passed, 3 warnings`,Ruff 通过,MyPy(135 个源文件)通过。阶段九提交:`5ea36e4`(动态配置核心提交:`ff71a1a`)。 阶段十已完成产品推荐与审核发布核心:推荐前读取有效风险测评和已确认投资目标,使用权威 场内产品、销售机构适当性、合同证据和流动性数据做硬过滤,再结合风险匹配、流动性和期限 进行排序;每个入选产品返回证据卡片,每个排除产品返回原因。推荐方案写入基线 `client_facing_content` 并默认进入 `pending_review`,管理员审核通过后发布,客户只能读取已 发布方案;Agent 和接口均明确不生成交易委托。 阶段十测试结果:推荐服务专项测试 `3 passed`,全量单元/契约测试 `493 passed, 3 warnings`, Ruff 通过,MyPy(138 个源文件)通过。阶段十提交:`0d42779`;生产数据库和端到端联调待完成。 阶段十一已完成会话闭环核心:投顾 Agent 支持投资目标、产品推荐、持仓分析、资产配置、 行情和对比意图路由;投资目标会话抽取只接受用户明确陈述,按会话合并多轮字段,写入内部 `advisor_goal_conversation_extraction` 快照并根据缺口追问。缺口状态使用既有 `svc_conversation_session.clarification_round` 持久化,达到 10 轮后转人工;Agent Run 完成 时同步保存 `last_intent`。目标抽取记录、会话审计、Agent Run、Outbox、记忆召回和 Episode 聚合均沿用公共底座链路,客户响应不返回抽取字段、置信度、内部表名或治理信息。对比意图当前 完成安全路由和参数提示,对比计算工具仍是后续待办。 阶段十一测试结果:专项 `4 passed`;单元/契约 `497 passed, 3 warnings`;Ruff 通过;MyPy (140 个源文件)通过。集成测试 `25 passed, 3 failed, 1 skipped`:失败均为既有测试库状态 问题(`chk_config_release_separation` 未按新底座迁移撤下、测试账号外键缺失,以及 UTC 测试依赖的数据库状态),不是本阶段代码回归;独立迁移库和端到端会话验收仍待执行。 阶段十二已完成画像标签治理闭环:开户问卷继续生成带置信度、来源类型、来源引用和画像版本 的标签;标签值、来源变化和置信度明显下降会生成待复核候选画像。新增画像治理 Service 和 管理员接口,可查看标签证据、查看待复核队列并审核通过/拒绝。审核通过在同一事务中切换当前 画像、激活候选标签并写入 Milvus/Neo4j 两条 `memory_sync_outbox` 事件;审核拒绝保留旧画像。 推荐、资产配置和持仓分析的 API 与 Agent 工具均接入漂移复核暂停闸门,客户接口不暴露内部 标签、置信度、来源和审核信息。当前项目没有独立的调仓模拟入口,该项未虚报完成。 阶段十二测试结果:专项 `12 passed`;单元/契约 `499 passed, 3 warnings`;Ruff 通过;MyPy (142 个源文件)通过;OpenAPI 已确认新增 3 个管理员画像治理路由。数据库结构未新增迁移, 复用阶段四已建立的画像标签、漂移复核和 Outbox 表;独立迁移库真实端到端审核已通过: 第二次问卷提交生成 1 条待复核记录,管理员审核通过后可查询 8 条内部标签。 阶段十三已完成全端验收(代码提交前):单元测试 `491 passed, 3 warnings`,契约测试 `8 passed`, Ruff 和 MyPy 通过;独立迁移库 `jr_agent_qyqy_migration` 使用 `root` 账号执行 `alembic upgrade head`、结构审计和约束审计均通过,集成测试 `28 passed, 1 skipped`。 真实 HTTP + JWT + Worker 验收脚本已补齐首次登录问卷前置,HTTP 替身治理链路 `9/9` 通过, 覆盖问卷拦截/提交、Agent 授权、越权、未注册 Agent、SSE 内容事件、Worker 完成态和结果查询。 Redis 不可用时实测按设计降级放行;生产装配模式因本机 Milvus/模型等依赖连接阻塞,已终止并保留为部署环境复验项。 原 `jr_agent` 库的集成测试仍为 `25 passed, 3 failed, 1 skipped`,失败是旧约束、测试账号外键和 UTC 状态差异;其结构审计多出 8 张历史场外表,约束审计有 12 条历史唯一键差异,均未修改原库。 阶段十四完成行情增量管道修复:`hq.py` 新增场内日线 K 线成交额解析(Eastmoney f56),净值历史同步 按交易日合并真实成交额;新增 `tools/sync_advisor_market_data.py`,可一次完成历史增量、指标重算和 数据质量快照 upsert。历史接口失败时保留净值、成交额为空,质量状态继续 `rejected`,不绕过推荐和动态 配置的失败关闭门槛。专项测试 `7 passed`,全量单元测试 `494 passed, 3 warnings`,Ruff 和 MyPy 通过。独立迁移库真实刷新结果:历史 `5240` 条、`19` 个产品,成交额非空 `0` 条;东方财富历史 K 线 端点批量请求出现 `RemoteProtocolError`,因此质量 `rejected=19`,产品推荐和动态配置真实验收仍待 行情源恢复后复验。实现提交:`930dd59`。 阶段十五完成对比分析工具:新增 `ProductComparisonQuery` 和只读 `ProductComparisonService`,通过 公共 `ToolExecutor` 注册 `compare_products`,读取已审核的场内产品证据、合同字段、流动性和行业暴露, 输出共同行业、差异字段和证据来源;缺少权威证据时返回 `evidence_required`,不生成交易指令。客户侧 对比意图已可从消息提取 2 至 4 个基金代码并返回摘要。专项测试 `4 passed`,全量单元测试 `497 passed, 3 warnings`,契约测试 `8 passed`,独立迁移库集成测试 `28 passed,1 skipped`,Ruff 和 MyPy 通过。 真实验收:`159511` 与 `510500` 对比返回 `ready`、2 个产品和差异字段。实现提交:`b6429e0`。 阶段十六完成双源行情编排:新增 `MarketQuoteSyncService` 和 `tools/sync_advisor_market_quotes.py` 入口及三个行情健康/来源运行/告警表的 ORM 映射,东方财富主源失败或部分返回时按优先级切换腾讯备用源;记录来源状态、连续失败次数、开放告警 及恢复关闭,成功行情按字段精度量化后写入场内行情快照。真实独立库同步验证两源均失败时返回 `degraded=true`、不写入伪行情并进入失败告警链路;本次无可用行情,推荐和动态配置继续失败关闭。 专项测试 `6 passed`,全量单元测试 `499 passed,3 warnings`,契约测试 `8 passed`,Ruff 和 MyPy 通过。实现提交:`2376585`;命令入口提交:`fbb1171`。 本次继续完成灰度闸门和回滚手册:新增 `AdvisorRolloutService`,由环境变量控制投顾灰度, 开启后管理员放行、客户按白名单放行,未命中返回 `403 AGENT_PERMISSION_DENIED` 并写入 `advisor.rollout_denied` 审计;已接入投顾业务路由和 `advisor` Agent 运行入口。新增操作手册 `docs/31-投顾Agent灰度与回滚操作手册.md`。专项测试 `6 passed`,全量单元测试 `505 passed, 3 warnings`,Ruff 和 MyPy(146 个源文件)通过。实现提交:`f5dd5b8`。生产/联调环境的 实际灰度与回滚演练仍待执行。 ### 阶段十七行情源稳定性与同步可观测性(2026-09-12,代码完成,真实源待恢复) 针对历史 K 线批量请求被公开供应商提前断连的问题,历史行情请求增加了串行节流、明确的 重试次数、指数退避、完整请求头和 JSON 响应格式校验;同步结果增加成交额观测数和无数据产品数, 命令行可直接识别“净值有数据但成交额缺失”的状态。仍坚持成交额失败关闭,不以成交量推算成交额, 也不把不提供成交额的接口当作备用源。专项测试 `10 passed`,Ruff 和 MyPy 通过。 真实复验结果:东方财富历史 K 线接口当前返回 `RemoteProtocolError`(连接建立后未返回响应), 因此独立库的 19 个产品成交额覆盖率仍为 `0`,数据质量仍为 `rejected`;产品推荐和动态配置 不能标记为真实动态验收通过。待供应商恢复或配置受信任的成交额数据源后,重新执行 `python tools/sync_advisor_market_data.py --days 400 --limit 100` 并复核质量快照。 本阶段追加验证:在隔离新库 `jr_agent_qyqy_empty_20260912` 中执行当前唯一 head,结构审计为 `89` 张业务表,约束/ORM 审计通过;导入合规基线后集成测试为 `79 passed, 1 skipped`。 该库仅用于验收,不替代仍存在版本漂移和结构缺失的历史测试库。 继续验收时发现并修复标准脚本 `tools/acceptance_check.py` 的治理替身签名未跟随 `BaseAgent` 新增的 `agent_type` 关键字参数,避免将脚本自身的 `TypeError` 误判为业务失败。 ### 阶段十八灰度准入验收(2026-09-12,准入通过,回滚演练待执行) 在隔离库开启 `ADVISOR_ROLLOUT_ENABLED=true` 并配置客户 `9001` 白名单后, 投资目标接口实测:白名单客户进入业务层返回 `404`、非白名单客户返回 `403`、管理员返回 业务层 `404`,证明客户白名单和管理员旁路均生效。Redis 不可用时限流按既定策略降级放行, 拒绝请求写入 `advisor.rollout_denied` 审计。该阶段未执行进程重启后的开关关闭回滚演练。 ### 阶段十九灰度回滚演练(2026-09-12,完成) 使用同一隔离库和客户 `9001` 完成进程级前后对照:灰度开启且客户不在白名单时返回 `403`; 以新进程加载 `ADVISOR_ROLLOUT_ENABLED=false` 后返回业务层 `404`,不再被灰度闸门拦截; 管理员访问仍进入业务层。演练未执行数据库 downgrade,业务数据和拒绝审计记录保留。 ### 阶段二十行情增量运维能力(2026-09-12,代码完成,真实源待恢复) 历史同步工具新增 `--product-code` 可重复参数,支持单只或小批量基金的增量重试,并让指标与 质量快照只重算指定产品,降低公开行情源限流或断连后的恢复压力。未改变真实成交额失败关闭 规则;行情源不可用时仍如实保留成交额为空并标记质量拒绝。工具参数检查通过,Ruff 和 MyPy 通过。 ### 阶段二十一腾讯历史行情备用源(2026-09-12,完成) 东方财富历史净值或历史 K 线不可用时,行情适配器现在切换到腾讯财经历史日线接口 `https://proxy.finance.qq.com/ifzqgtimg/appstock/app/newfqkline/get`。腾讯响应是 JSONP, 适配器解析交易日、收盘价和供应商明确提供的成交额字段(原单位为万元,落库前换算为元), 不使用成交量乘价格推算成交额;每条记录保留 `tencent_hq_history` 或 `eastmoney_hq_nav+tencent_hq_history` 来源标记。东方财富可用的日期仍优先使用东方财富, 腾讯只补缺或在东方财富净值接口无数据时提供完整的场内历史记录。 专项测试 `10 passed`,Ruff 通过。真实接口验证 `159511` 返回 `267` 条交易日记录; 隔离迁移库 `jr_agent_qyqy_migration` 单品同步为 `267` 条、成交额 `267` 条,质量状态 `accepted`,来源为 `eastmoney_hq_nav+tencent_hq_history`。随后批量同步 19 只南方场内基金, 18 只质量状态为 `accepted`,成交额观测合计 `4793` 条;`160129` 的腾讯接口返回空日线, 因此保持 `rejected`,待权威源提供该产品历史数据后再恢复,未写入伪造数据。 - [x] 接入腾讯历史行情备用源。 - [x] 实现东方财富净值失败时的完整历史回退。 - [x] 验证来源标记、成交额单位转换和质量快照。 - [x] 在隔离迁移库完成单品和 19 只产品批量真实同步。 - [x] 回归专项测试、Ruff 检查。 ### 阶段二十二灰度核心链路验收(2026-09-12,完成) 在隔离迁移库开启 `ADVISOR_ROLLOUT_ENABLED=true`,配置客户 `9001` 白名单,使用真实 JWT 通过 FastAPI HTTP 层验证核心投顾入口:开户问卷查询 `200`,持仓分析 `200/ready`,资产配置 `200/ready` 且 `dynamic=true`、指标覆盖率 `100.00%`,产品推荐 `200/ready` 且选出 3 个产品、 返回 10 条排除原因。请求均未生成交易指令;数据库核对客户 `9001` 的模拟订单和成交记录均为 `0` 条。Redis 未启动时限流按既定降级策略放行,记录到本次接口延迟约 `168.88ms`、 `4134.46ms`、`2070.41ms`、`4046.30ms`,作为当前单次灰度验收基线。 - [x] 灰度持仓分析。 - [x] 灰度资产配置。 - [x] 灰度产品推荐。 - [x] 灰度画像漂移复核。(客户 9927 第二次问卷生成 1 条待复核;复核期间资产配置 `409`;管理员审核后 `200/approved` 并恢复业务层) - [x] 灰度风险问卷。(专用客户 9927 首次查询 `required=true`、提交 `201`、提交后 `required=false`;后台评分 C1/13,客户响应不含评分) - [x] 记录灰度期间错误、延迟和降级次数。(本次 Redis 降级 4 次请求,延迟基线已记录) - [x] 确认没有产生真实交易委托。(模拟订单、成交记录均为 0) ### 阶段二十三演示环境迁移准备(2026-09-12,工具完成,目标机待执行) 已新增受控初始化编排脚本 `tools/bootstrap_advisor_demo.py`:默认只预览,只有同时传入 `--apply --confirm-demo-database` 才会对配置的目标库写入。脚本只允许使用全新、可销毁的 演示库,明确禁止复制或直接升级存在结构漂移的旧 `jr_agent`。它串联当前迁移、RBAC、 投顾角色、演示账号、合规基线、演示测评/归属、权威产品证据、行情同步和结构审计。 已新增 `tools/publish_advisor_demo_config.py`,在目标环境通过配置发布状态机继承既有配置项, 并发布 advisor 六个意图的工具白名单,避免代码已部署但工具因缺少 `config_release` 而失败关闭。 产品适当性与合同 CSV 仍必须来自真实披露文件,脚本不伪造来源 URL 或文件摘要。 - [x] 准备演示库初始化编排脚本和最小化写入保护。 - [x] 准备投顾 Agent 工具白名单发布脚本。 - [x] 准备演示环境迁移说明和专项单元测试。(`3 passed`、Ruff 通过) - [ ] 在另一台演示电脑的新建演示库实际执行初始化、启动服务并完成 HTTP 验收。 ## 一、迁移准备 - [ ] 确认远程仓库可访问。(当前失败:连接 `47.106.207.27:3000` 被拒绝) - [x] 确认 `origin/qyqy_develop` 的最新提交。(已确认本地缓存引用为 `6516ccb`,待远程恢复后重新 fetch) - [ ] 确认是否存在远程 `lzl_develop`。(远程暂不可访问) - [x] 确认当前 `lzl` 工作区没有未解释的代码改动。(已提交 `5dcaf7c`;`.idea` 和 `.migration-backups` 已加入忽略) - [x] 提交 `lzl` 当前所有投顾开发成果。(`5dcaf7c`) - [x] 创建迁移前标签 `advisor-before-base-migration`。 - [x] 备份当前测试库。(`.migration-backups/jr_agent-before-base-migration.sql`) - [x] 导出当前数据库结构和迁移版本。(数据库迁移 head:`20260911_adv_profile_tags`) - [x] 导出当前产品、适当性、合同和行情数据。(`.migration-backups/advisor-evidence-20260912.sql`,10 张产品/证据/行情表,约 1.05MB) - [x] 保存当前接口清单和 Swagger 截图。(OpenAPI JSON:`.migration-backups/current-openapi.json`) - [x] 保存当前单元、契约和集成测试结果。(259 个单元/契约测试,21 个集成测试) - [x] 保存当前投顾 Agent 端到端验收记录。(`docs/验收与审计/advisor-e2e-acceptance-20260912.md`) ## 二、建立新开发分支 - [x] 基于 `origin/qyqy_develop` 创建 `lzl_develop`。(基于本地缓存 `6516ccb`,远程恢复后需重新 fetch) - [x] 确认新分支工作区干净。(`619a5e6` 后无业务代码改动,迁移备份已忽略) - [x] 建立迁移模块分支或提交规范。(按本 TODO 的模块提交名执行) - [x] 确认 `qyqy_develop` 的 Python、数据库和中间件版本要求。(已核对 `pyproject.toml`、`.env.example` 和底座文档) - [x] 确认新底座的启动命令和环境变量。(已核对 `uvicorn app.main:app`、Worker 入口和环境配置) - [x] 确认新底座的数据库迁移 head。(代码 head:`20260910_drop_review_separation`;旧测试库版本不兼容) - [x] 确认新底座的公共测试可以通过。(单元/契约 `447 passed`) 建议命令: ```powershell git fetch origin qyqy_develop git switch -c lzl_develop origin/qyqy_develop python -m pytest tests/unit tests/contract -q python -m ruff check app tests tools alembic python -m mypy app ``` ## 三、公共底座适配 - [x] 阅读并记录 `app/core/contracts.py` 的请求和响应契约。 - [x] 阅读并记录 `app/core/errors.py` 的错误码和错误信封。 - [x] 确认 JWT 鉴权和 RBAC 解析流程。 - [x] 确认 `BaseAgent` 的标准执行流程。 - [x] 确认 `AgentFactory` 的 Agent 注册方式。 - [x] 确认 `ToolExecutor` 的工具鉴权和角色限制。 - [x] 确认模型路由、模型降级和超时处理方式。 - [x] 确认 Redis、Milvus、Neo4j 的接入方式。 - [x] 确认记忆抽取、召回、生命周期和 Episode 聚合流程。 - [x] 确认 Worker 注册和事件消费方式。 - [x] 适配投顾 Agent 的公共请求和响应结构。(复用 `BaseAgent` 和 `AgentRequest/CoreResult`) - [x] 适配投顾工具注册。(复用 `query_fund_quote` 只读工具) - [x] 适配投顾 Agent 的 `AgentDefinition`。(新增 `advisor`,版本 `0.1.0`) - [x] 适配 `app/main.py` 的路由注册。(复用已注册 Agent Run 入口,无新增旁路) - [x] 确认所有写接口使用 `Idempotency-Key`。(底座契约测试通过) - [x] 确认所有管理操作写入审计。(底座契约测试通过) - [x] 确认所有跨存储同步使用 Outbox。(底座 Worker/事件测试通过) - [x] 完成底座适配提交 `advisor/base-adaptation`。(`5e8eecc`) 验收: - [x] 公共底座单元测试通过。(已包含于 `447 passed`) - [x] 公共底座契约测试通过。(已包含于 `447 passed`) - [x] AgentFactory 测试通过。(已包含于 `447 passed`) - [x] ToolExecutor 测试通过。(已包含于 `447 passed`) - [x] 鉴权和权限边界测试通过。(已包含于 `447 passed`) - [x] Ruff 检查通过。 - [x] MyPy 检查通过。(103 个源文件) ## 四、数据库和迁移 - [x] 对比 `qyqy_develop` 数据库基线和 `docs/00-新数据库基线设计.md`。(迁移契约测试 + 在线结构审计) - [x] 确认没有删除或重命名基线表。(14 个投顾迁移仅 `CREATE TABLE`) - [x] 确认没有删除、重命名或复用基线字段。(迁移契约测试通过) - [x] 确认没有改变基线字段类型和可空性。(未修改基线迁移) - [x] 确认迁移链只有一个 head。(`20260911_adv_profile_tags`) - [x] 迁移产品参考数据表。 - [x] 迁移产品治理和适当性表。 - [x] 迁移产品合同证据表。 - [x] 迁移产品行业和资产类别表。 - [x] 迁移历史行情表。 - [x] 迁移产品指标快照表。 - [x] 迁移行情源监控和失败记录表。 - [x] 迁移行情质量表。 - [x] 迁移资产配置回测表。 - [x] 迁移投资组合图谱投影检查点表。 - [x] 迁移投资目标表。 - [x] 迁移会话目标抽取表。 - [x] 迁移画像标签表 `advisor_profile_tag`。 - [x] 迁移画像漂移复核表 `advisor_profile_drift_review`。 - [x] 为历史当前画像回填标签证据。(迁移包含回填逻辑;独立空库无历史画像) - [x] 在空库执行迁移。(`jr_agent_qyqy_migration`) - [ ] 在已有测试库执行迁移。(2026-09-11 只读审计拒绝:80 张表、8 张历史场外表、12 项唯一键漂移;不得直接升级) - [x] 执行数据库结构审计。(72 张业务表,无缺失或多余) - [x] 执行数据库约束审计。(唯一键与 ORM 映射通过) - [x] 完成数据库迁移提交 `advisor/database-migrations`。(`9ed536e`) 验收命令: ```powershell python -m alembic upgrade head python tools/audit_schema.py python tools/audit_constraints.py ``` ## 五、产品数据和适当性 - [x] 迁移场内基金产品模型。(复用 `FundProduct`,新增证据表只读映射) - [x] 迁移产品查询 Repository。(`AdvisorProductRepository`) - [x] 导入南方场内基金产品。(独立库 19 个 ETF/LOF) - [x] 导入 R1-R5 产品适当性等级。(官网披露;五级均有,实际分布见阶段记录) - [x] 导入基金合同字段和权威来源。(19 条官网合同证据) - [x] 导入销售机构披露信息。(南方基金直销披露) - [x] 导入产品状态和交易状态。(仅导入 `SSE/SZSE` 且 `上市` 产品) - [x] 接入产品治理变更监控。(`ProductGovernanceMonitorService` + 官网同步工具) - [x] 接入产品资产规模指标。(`advisor_product_reference_snapshot`) - [x] 接入产品历史行情指标。(增量 NAV 同步 + `ProductMetricService`) - [x] 接入产品流动性指标。(平均日成交额、最新行情时间、流动性状态) - [x] 实现场内基金过滤。(Repository 强制 `SSE/SZSE`) - [x] 实现适当性硬过滤。(R1-R5 不匹配失败关闭并返回排除原因) - [x] 实现合同证据过滤。(verified + 有来源 URL/文档摘要) - [x] 实现来源缺失时的失败关闭。(缺失权威证据不进入候选) - [x] 完成产品数据提交 `advisor/product-data`。(专项 `7 passed`;完整推荐侧联动留待阶段十) 验收: - [x] R1、R2、R3、R4、R5 均有可查询产品。(19 个南方场内产品,五级均有) - [ ] 每个风险等级至少有四个测试产品。 - [ ] 各基金类型分类正确。(1 个产品因合同证据不足跳过分类) - [x] 场外基金不会进入场内交易表。 - [x] 不适配产品会被排除并说明原因。(硬过滤返回 `reason_code`) - [x] 产品来源和合同证据可追溯。 ## 六、开户风险问卷 - [x] 迁移问卷查询接口。 - [x] 迁移问卷提交接口。 - [x] 迁移服务端评分规则。 - [x] 迁移 C1-C5 风险等级生成。 - [x] 迁移开户画像快照生成。 - [x] 迁移问卷有效期控制。 - [x] 迁移每日和年度提交次数限制。 - [x] 迁移首次登录拦截。 - [x] 迁移画像同步 Outbox 事件。 - [x] 确认客户响应不返回答案、总分、风险等级和画像。 - [x] 确认问卷重复提交具有幂等性。 - [x] 完成风险问卷提交 `advisor/risk-questionnaire`。(专项 `7 passed`;全量单元 `464 passed`) 验收: - [x] 新客户访问投顾业务会被拦截。 - [x] 新客户可以查询问卷。 - [x] 新客户可以成功提交问卷。 - [x] 问卷结果正确写入后台表。 - [x] 客户响应不包含内部评分信息。 - [x] 投顾工具可以读取必要的内部画像投影。 ## 七、投资目标和目标书 - [x] 迁移投资目标创建接口。 - [x] 迁移收益目标下限和上限。 - [x] 迁移最大回撤字段。 - [x] 迁移流动性要求字段。 - [x] 迁移投资期限字段。 - [x] 迁移业绩比较基准字段。 - [x] 迁移目标备注字段。 - [x] 迁移目标书生成。 - [x] 迁移客户确认流程。 - [x] 迁移投顾审核流程。 - [x] 迁移当前目标查询工具。 - [x] 迁移目标缺口状态。 - [x] 确认目标创建后为 `pending_confirmation`。 - [x] 确认未确认目标不能用于推荐和配置。 - [x] 确认目标书未审核不能对客发布。 - [x] 确认收益目标不被表述为收益承诺。 - [x] 完成投资目标提交 `advisor/investment-goal`。(专项 `5 passed`;全量单元 `468 passed`) ## 八、持仓分析 - [x] 迁移持仓查询。 - [x] 迁移持仓市值计算。 - [x] 迁移单产品集中度计算。 - [x] 迁移行业集中度计算。 - [x] 迁移 HHI 指标计算。 - [x] 迁移行业穿透计算。 - [x] 迁移行情和行业数据覆盖率计算。 - [x] 迁移 Neo4j 图谱增强。(固定关系模板、参数化查询、2 秒超时) - [x] 确认 MySQL 是数值分析权威来源。 - [x] 确认 Neo4j 只做关系增强。 - [x] 实现图谱不可用时的降级。(连接失败、超时或结果异常均标记 degraded) - [x] 实现关键数据不足时的降级。 - [x] 确认分析结果不生成交易指令。 - [x] 完成持仓分析提交 `advisor/portfolio-analysis`。(专项 `8 passed`;全量单元 `475 passed`) 验收: - [x] 无持仓时返回明确状态。 - [x] 缺少市值时不计算集中度。 - [x] 行业覆盖不足时不输出确定性行业结论。 - [x] 图谱不可用时仍可返回可靠的数值分析。(当前图谱未配置,数值分析仍可返回) - [x] 客户看不到内部数据库查询细节。 ## 九、动态资产配置 - [x] 迁移 C1-C5 基础配置。 - [x] 迁移投资期限约束。 - [x] 迁移流动性约束。 - [x] 迁移最大回撤约束。 - [x] 迁移收益目标约束。 - [x] 迁移历史行情指标读取。 - [x] 迁移动态权重优化器。 - [x] 迁移流动性覆盖率。(以历史指标覆盖率和平均日成交额作为证据) - [x] 迁移动态配置回测。(滚动 120 个交易日、每 20 个交易日动态再平衡) - [x] 保存配置回测证据。(`advisor_allocation_backtest_run`) - [x] 实现数据覆盖不足时的降级状态。(覆盖少于两个资产类别时返回静态配置) - [x] 确认输出配置比例而不是买卖指令。 - [x] 完成资产配置提交 `advisor/asset-allocation`。(专项 `9 passed`;全量 `491 passed`) 验收: - [x] 收益目标参与优化。 - [x] 最大回撤参与优化。 - [x] 流动性要求参与优化。 - [x] 投资期限参与优化。 - [x] 动态配置与静态配置可以对比。(输出 `dynamic` 和 `strategic_allocation`) - [x] 回测结果包含限制条件和数据覆盖率。 ## 十、产品推荐和审核发布 - [x] 迁移产品推荐 Agent。 - [x] 迁移客户画像读取。(使用服务端权威风险测评投影) - [x] 迁移投资目标读取。(只读取已确认目标) - [x] 迁移适当性硬过滤。 - [x] 迁移合同证据过滤。 - [x] 迁移行情和流动性校验。 - [x] 迁移图谱增强。(组合行业关系作为辅助上下文,故障时降级) - [x] 迁移多因子排序。(风险匹配、流动性和期限) - [x] 迁移个性化推荐理由。 - [x] 迁移推荐证据卡片。 - [x] 迁移排除原因。 - [x] 迁移推荐方案生成。 - [x] 迁移 `pending_review` 状态。 - [x] 迁移管理员审核接口。 - [x] 迁移审核拒绝原因。 - [x] 迁移审核通过后发布。 - [x] 确认审核前不可对客发布。 - [x] 确认推荐不生成交易委托。 - [x] 完成产品推荐提交 `advisor/recommendation`。(专项 `3 passed`;全量 `493 passed`) 验收: - [x] 画像未完成时不能推荐。 - [x] 投资目标未确认时不能推荐。 - [x] 不适配产品不会进入候选列表。 - [x] 每个推荐产品都有证据。 - [x] 每个排除产品都有原因。 - [x] 推荐方案默认进入待审核。 - [x] 审核通过后客户才能查看发布内容。 ## 十一、会话闭环 - [x] 迁移意图分类。(沿用公共 `IntentClassifier`;新增对比意图声明) - [x] 迁移产品推荐意图。 - [x] 迁移持仓分析意图。 - [x] 迁移资产配置意图。 - [x] 迁移对比分析意图。(`compare_products` 只读工具已接入,支持 2 至 4 个场内基金代码) - [x] 迁移投资目标意图。 - [x] 迁移会话实体抽取。(`GoalConversationService`,只抽取明确目标字段) - [x] 迁移投资目标缺口识别。(按当前会话所有用户轮次合并) - [x] 迁移缺口追问状态。(使用 `clarification_round`,上限 10 轮) - [x] 迁移会话状态持久化。(保存 `last_intent` 和缺口轮次) - [x] 迁移记忆召回。(复用 `PlatformGovernance` 组合召回) - [x] 迁移 Episode 聚合。(复用 `EpisodeWorker`) - [x] 迁移 Agent Run 持久化。(复用 `AgentPersistenceService`) - [x] 迁移 Outbox 事件。(复用 Agent Run 和记忆事件链路) - [x] 迁移会话审计。(目标缺口评估和运行/工具均写统一审计) - [x] 确认用户消息可以形成完整闭环。(专项 + 单元/契约测试通过) - [x] 确认缺少字段时只追问必要信息。(专项测试覆盖) - [x] 确认工具失败时返回可理解的降级结果。(既有各业务工具降级契约通过) - [x] 完成会话闭环提交 `advisor/conversation`。(真实库端到端验收待完成) ## 十二、画像标签和漂移复核 - [x] 迁移画像标签模型。(复用新增 `advisor_profile_tag`) - [x] 迁移标签值保存。 - [x] 迁移标签置信度保存。 - [x] 迁移来源类型保存。 - [x] 迁移来源引用保存。 - [x] 迁移来源置信度保存。 - [x] 迁移画像版本保存。 - [x] 迁移标签生效状态。 - [x] 迁移标签值变化检测。 - [x] 迁移来源变化检测。 - [x] 迁移置信度下降检测。 - [x] 迁移候选画像保存。(复核记录保存候选快照) - [x] 迁移漂移复核队列。 - [x] 迁移管理员查看标签证据接口。 - [x] 迁移管理员审核通过接口。 - [x] 迁移管理员审核拒绝接口。 - [x] 审核通过后切换当前画像。 - [x] 审核通过后创建 Milvus 同步事件。 - [x] 审核通过后创建 Neo4j 同步事件。 - [x] 审核期间暂停产品推荐。 - [x] 审核期间暂停资产配置。 - [x] 审核期间暂停持仓分析。 - [ ] 审核期间暂停调仓模拟。 - [x] 确认客户不能读取内部标签、置信度和审核信息。(无客户侧标签路由) - [x] 完成画像治理提交 `advisor/profile-governance`。(真实库端到端审核验收待完成) ## 十三、全端测试 - [x] 执行全部单元测试。(`505 passed, 3 warnings`,Python 3.13) - [x] 执行全部契约测试。(`8 passed`,Python 3.13) - [x] 执行全部集成测试。(独立迁移库 `28 passed, 1 skipped`;原 `jr_agent` 为 `25 passed, 3 failed, 1 skipped`,失败均为历史库状态) - [x] 执行 Ruff 检查。(通过) - [x] 执行 MyPy 检查。(通过,`142` 个 app 源文件;验收工具单独检查也通过) - [x] 执行数据库结构审计。(独立迁移库 `72` 张业务表通过) - [x] 执行数据库约束审计。(独立迁移库通过) - [x] 验证空库迁移。(独立迁移库按基线迁移链执行 `alembic upgrade head` 成功) - [ ] 验证已有测试库迁移。(2026-09-11 只读审计拒绝,原因见阶段一记录) - [x] 验证首次登录问卷流程。(真实 HTTP 验收:查询 `200`、首次提交 `201`、业务接口拦截解除) - [x] 验证投资目标创建和确认流程。(真实 HTTP:创建 `201`、确认 `200`;目标书审核/发布均 `200`) - [x] 验证产品推荐流程。(隔离迁移库真实 HTTP 返回 `ready`,选出 3 个产品、返回 10 条排除原因;推荐只读调用未写入方案) - [x] 验证持仓分析流程。(真实 HTTP 返回 `ready`,2 条场内模拟持仓、集中度预警;行业数据按实际缺失降级) - [x] 验证动态资产配置流程。(隔离迁移库真实 HTTP 返回 `ready`,`dynamic=true`,指标覆盖率 `100.00%`,输出配置比例而非交易指令) - [x] 验证推荐审核发布流程。(真实 HTTP 审核和发布均 `200`;当前方案无产品,原因见推荐数据门槛) - [x] 验证画像漂移复核流程。(真实 HTTP:问卷 `201`、发现 1 条待复核、审核 `200`、管理员标签查询 8 条) - [x] 验证行情主源失败切换备用源。(东方财富历史接口真实失败,腾讯源真实返回并落库;19 只中 18 只 accepted,1 只无数据保持 rejected) - [x] 验证 Redis 不可用时的降级。(真实 HTTP 限流链路实测放行;专项测试通过) - [x] 验证 Neo4j 不可用时的降级。(专项测试通过) - [x] 验证 Milvus 不可用时的降级。(专项测试通过) - [x] 验证模型端点失败时的降级。(专项测试通过) - [x] 验证幂等键重复请求。(单元/集成测试通过) - [x] 验证权限越权请求。(真实 HTTP 验收通过) - [x] 验证客户数据隔离。(单元/集成测试通过) - [x] 验证审计记录和 Outbox 事件。(独立迁移库集成测试通过) 统一命令: ```powershell python -m pytest tests/unit -q python -m pytest tests/contract -q python -m pytest tests/integration -q python -m ruff check app tests tools alembic python -m mypy app python -m alembic upgrade head python tools/audit_schema.py python tools/audit_constraints.py ``` ## 十四、灰度发布 - [x] 在本地测试库完成迁移。(独立库 `jr_agent_qyqy_migration` 已验证) - [ ] 在联调库完成迁移。 - [x] 仅开放测试客户和管理员账号。(代码闸门已完成,环境实测待执行) - [ ] 灰度产品查询和适当性过滤。 - [x] 灰度风险问卷。(专用客户 9927 首次提交闭环通过) - [x] 灰度投资目标。(隔离库实测:白名单客户放行、非白名单客户 403、管理员放行) - [x] 灰度持仓分析。(隔离迁移库真实 HTTP `200/ready`) - [x] 灰度资产配置。(真实 HTTP `200/ready`,`dynamic=true`) - [x] 灰度产品推荐。(真实 HTTP `200/ready`,3 个候选、10 条排除原因) - [x] 灰度推荐审核发布。(客户创建 `pending_review`,管理员审核 `approved`、发布 `published`,客户可读取) - [x] 灰度画像漂移复核。(真实 HTTP 生成、暂停、审核、恢复闭环通过) - [x] 记录灰度期间错误、延迟和降级次数。(Redis 降级和 4 次接口延迟已记录) - [x] 确认客户数据未越权暴露。(白名单客户 9001 访问客户 9002 目标返回 `403`;管理员画像治理查询 `200`) - [x] 确认没有产生真实交易委托。(客户 9001 模拟订单、成交记录均为 0) ## 十五、回滚准备 - [x] 保存迁移前数据库备份。(`.migration-backups/jr_agent-before-base-migration.sql`) - [x] 保存迁移前 Git 标签。(`advisor-before-base-migration`) - [x] 保存每个模块的提交号。(已回填阶段记录) - [x] 保存每个模块的测试结果。(已回填阶段记录) - [x] 保存迁移后的结构审计结果。(独立迁移库 72 张业务表) - [x] 准备关闭新投顾入口的配置开关。(`ADVISOR_ROLLOUT_ENABLED=false`) - [x] 准备应用代码按提交回滚方案。(见 `docs/31-投顾Agent灰度与回滚操作手册.md`) - [x] 确认数据库不执行破坏性 downgrade。(见回滚手册) - [x] 确认新增表保留,不自动删除。(见回滚手册) - [x] 确认失败 Outbox 可以重试或人工处理。(沿用公共 Outbox 重试/死信机制) - [x] 确认原画像和原推荐结果可以保留。(回滚只关闭入口,不删除业务数据) - [x] 完成灰度回滚演练。(隔离库完成开关关闭后的新进程验证,客户入口恢复,未执行破坏性 downgrade) ## 十六、最终完成标准 - [x] `qyqy_develop` 底座测试全部通过。(当前集成分支单元 `1187 passed, 2 skipped`;契约 `21 passed`) - [x] 投顾业务测试全部通过。(已包含于最新集成分支全量单元与契约测试) - [x] 数据库基线审计通过。(隔离空库 `jr_agent_qyqy_empty_20260912`:结构审计 89 张业务表、约束/ORM 审计通过) - [x] 空库迁移成功。(当前 head `20260911_merge_adv_risk_heads`,`alembic upgrade head` 成功) - [ ] 已有测试库迁移成功。 - [x] 鉴权和首次登录拦截有效。(HTTP + JWT 验收通过) - [x] 风险问卷后台评分有效且客户不可见。(专项与 HTTP 验收通过) - [x] 投资目标采集、确认和审核有效。(真实 HTTP 创建、确认、审核、发布通过) - [x] 持仓分析数值来源可靠。(真实 HTTP 返回 ready,MySQL 数值分析为事实来源) - [x] 收益、回撤、流动性参与动态配置。(真实 HTTP 返回 `dynamic=true`,指标覆盖率 `100.00%`) - [x] 产品推荐具备适当性过滤、证据卡片和排除原因。(真实 HTTP 已验证) - [x] 推荐方案审核发布闭环有效。(灰度真实 HTTP 创建、审核、发布和客户读取均通过) - [ ] 会话实体抽取和目标缺口追问有效。 - [ ] 画像标签具备置信度和来源。 - [x] 画像漂移复核闭环有效。(真实 HTTP 已验证待复核、暂停和审核通过) - [x] 行情双源和失败告警有效。(行情主源失败后腾讯历史源成功补齐;产品级失败保持拒绝) - [ ] Redis、Neo4j、Milvus 和模型故障具备降级行为。 - [ ] 所有关键写操作具备幂等和审计记录。 - [ ] 每个迁移模块都有独立提交、测试结果和回滚点。 - [ ] 完成迁移验收记录和交接文档。