依据仓库既有设计与已落地代码反推整理,内容与当前实现一致(非前瞻设计)。 - docs/46-投顾Agent需求文档.md · 背景与三个真实断点(交付无落点 / 文案不可读 / 客户无入口) · 目标 G1-G5 与 4 条非目标(不真实下单、不预测收益、不自动投资、不做场外) · 角色场景 SC-01~SC-05、两条主链路流程图 · 功能需求 FR-01~FR-21(含口径、优先级、验收标准) · 业务规则:候选池硬约束 fail-closed、5 条合规红线、状态机、FM-03 熔断 · 非功能需求、异常处理表、权限矩阵、数据需求、验收清单、11 项待确认问题 - docs/47-投顾Agent功能架构文档.md · 五层架构总览、后端模块划分与 3 条设计约束、数据模型与关系 · 接口清单(投顾 8 / 客户 3 / 依赖能力 3 组) · 三张时序图(生成推荐、审核发布、申报受理) · 外部依赖降级矩阵、前端模块结构与 2 条硬约定、权限灰度、配置清单、已知约束
15 KiB
15 KiB
投顾 Agent 功能架构文档
文档版本:v1.0(2026-09-16) 适用范围:投顾 Agent 的后端服务、前端模块、数据模型、接口与降级策略 配套文档:
docs/46-投顾Agent需求文档.md(需求与验收) 说明:本文描述的是仓库当前已落地的实现,不是前瞻设计
1. 架构总览
┌─────────────────────────────────────────────────────────────────┐
│ 客户端门户 /portal/customer/ │
│ advisor-plans(我的投顾方案 / 主动申报 / 申报进度) │
│ dashboard · holdings · orders · cash-ledger · risk-questionnaire│
└─────────────────────────────────────────────────────────────────┘
│ HTTPS + Bearer JWT
┌─────────────────────────────────────────────────────────────────┐
│ 投顾工作台 /portal/employee-advisor/dashboard/ │
│ dashboard.js(壳/引导/指标) │
│ ├ actions-module 七项操作 + 熔断闸门 + 结果渲染 │
│ ├ assistant-module 自然语言意图路由 │
│ ├ history-module 历史方案记录(只读 + 详情弹窗) │
│ ├ service-request-module 客户申报队列(受理/驳回) │
│ ├ published-module 已发布交付物 │
│ ├ customer-module 客户选择 │
│ └ advisor-engine / advisor-config 本地演示引擎与常量 │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ FastAPI 入口层 app/api/controllers/ │
│ recommendations.py 推荐(生成/历史/已发布/审核/发布/删除)│
│ advisor_service_requests.py 客户申报 + 投顾受理 │
│ public_platform.py 公开产品与净值(P001/P002) │
│ onboarding.py / trading.py 风险测评 / 场内模拟交易 │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ 服务层 app/service/ │
│ ProductRecommendationService 选品 + 排序 + 审核发布(核心) │
│ AdvisorServiceRequestService 客户申报与受理 │
│ AdvisorReasonService 推荐依据的大模型增强(可选) │
│ SuitabilityService 服务端权威风险画像 │
│ InvestmentGoalService 投资目标 │
│ MarketPriceSyncService 行情同步 │
│ AuthorizationService / IdentityService 权限与身份 │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ 存储与依赖 │
│ MySQL(业务主库) · Redis(限流/缓存) · Milvus(知识向量) │
│ Neo4j(图谱,可降级) · 外部行情/净值源 · DeepSeek(可选) │
└─────────────────────────────────────────────────────────────────┘
2. 后端模块划分
| 层 | 文件 | 职责 |
|---|---|---|
| 控制器 | app/api/controllers/recommendations.py |
三个 router:advisor(投顾)、admin(平台管理员)、client(客户自助) |
| 控制器 | app/api/controllers/advisor_service_requests.py |
客户申报(/users/me)+ 投顾受理(/advisor) |
| 服务 | product_recommendation_service.py |
硬约束过滤、排序、审核、发布、历史;唯一选品入口 |
| 服务 | advisor_service_request_service.py |
申报创建/查询、队列、受理(联动生成草稿)、驳回 |
| 服务 | advisor_reason_service.py |
推荐依据的大模型增强(合规守卫 + 失败回退) |
| 仓储 | advisor_product_repository.py |
权威候选产品(场内可交易 + 适当性证据 + 合同快照) |
| 模型 | advisor_service_request.py |
申报单 ORM 模型与状态常量 |
| 模型 | investment_goal.py |
ClientFacingContent(方案/方案书) |
设计约束(踩过坑换来的)
- 客户自助路由不挂投顾灰度闸门(
enforce_advisor_rollout)—— 那是投顾业务灰度,客户查看自己的方案不该被它拦。 - 归属范围只有一处实现:
ProductRecommendationService._visible_customer_ids(本人 +sys_customer_assignment),历史、已发布、申报队列全部复用它。 - 写操作统一走
ApiTransactionService.execute(16-128 位 ASCII 幂等键)。
3. 数据模型
| 表 | 说明 | 关键字段 |
|---|---|---|
client_facing_content |
方案与方案书 | content_type(advisor_recommendation_plan / investment_goal_book)、customer_id、review_status、published_at、draft_content |
advisor_service_request |
客户申报单 | request_no(唯一)、customer_id、amount_wan、horizon、risk_preference、note、status、handled_by/at、advisor_note、result_content_id |
sys_customer_assignment |
投顾-客户归属 | 决定可见范围 |
fin_product / fin_market_price / fin_nav_history |
产品 / 行情 / 历史净值 | 图表与指标数据源 |
api_request_receipt |
幂等 | user_id + scope_hash + idempotency_key + request_hash |
interaction_audit |
审计留痕 | 操作人、动作、明细、时间 |
关系要点
advisor_service_request.result_content_id ──▶ client_facing_content.id
client_facing_content.customer_id ──▶ sys_user.id(客户)
sys_customer_assignment ──▶ 投顾可见客户集合
「已发布 / 已发送」不落库:由
published_at是否为空推导,避免两处状态不一致。
4. 接口清单
4.1 投顾侧(/api/v1/advisor,挂限流 + 灰度)
| 方法 | 路径 | 权限 | 端点 ID | 幂等 |
|---|---|---|---|---|
| POST | /advisor/recommendations |
product-recommendation:generate:customer |
ADVISOR_RECOMMEND |
✅ |
| GET | /advisor/recommendations/history |
product-recommendation:history |
ADVISOR_HISTORY |
— |
| GET | /advisor/recommendations/published |
product-recommendation:read:self |
ADVISOR_PUBLISHED |
— |
| DELETE | /advisor/recommendations/{contentId} |
product-recommendation:delete |
ADVISOR_DELETE_RECOMMENDATION |
✅ |
| POST | /advisor/recommendations/{contentId}/reviews |
product-recommendation:review |
— | ✅ |
| POST | /advisor/recommendations/{contentId}/publications |
product-recommendation:publish |
— | ✅ |
| GET | /advisor/service-requests |
advisor-request:read |
ADVISOR_SERVICE_REQUESTS |
— |
| POST | /advisor/service-requests/{requestNo}/reviews |
advisor-request:review |
ADVISOR_SERVICE_REQUEST_REVIEW |
✅ |
4.2 客户侧(/api/v1/users/me,只挂限流)
| 方法 | 路径 | 权限 | 端点 ID |
|---|---|---|---|
| GET | /users/me/advisor-contents |
product-recommendation:read:self |
MY_ADVISOR_CONTENTS |
| GET | /users/me/advisor-requests |
advisor-request:read:self |
MY_ADVISOR_REQUESTS |
| POST | /users/me/advisor-requests |
advisor-request:write:self |
ADVISOR_REQUEST_CREATE |
4.3 依赖的既有能力
| 端点 | 用途 |
|---|---|
ONB001 / ONB002 |
风险测评取题与提交 |
T001–T009 |
账户看板、委托、持仓、成交、资金明细 |
P001 / P002 |
产品列表(含最新行情)与历史净值序列(图表数据源) |
5. 关键时序
5.1 生成推荐方案
sequenceDiagram
participant A as 投顾前端
participant C as recommendations.py
participant S as ProductRecommendationService
participant R as AdvisorProductRepository
participant G as 图谱 Neo4j
participant L as DeepSeek 可选
participant DB as MySQL
A->>C: POST /advisor/recommendations (Idempotency-Key)
C->>S: generate(payload, context, key)
S->>S: 权限 + 熔断 + 目标校验
S->>R: 权威候选产品
R-->>S: 候选(含硬约束证据)
S->>G: 组合行业上下文
G-->>S: 正常 / 降级 degraded
S->>S: 过滤 + 排序取 TopN
S->>L: 真实参数 → 推荐依据
L-->>S: 文案 / 失败(回退规则文案)
S->>DB: 落库 pending_review
S-->>A: 返回结果(信封,含 content_id)
5.2 审核与发布
sequenceDiagram
participant A as 投顾/管理员
participant C as recommendations.py
participant S as ProductRecommendationService
participant DB as MySQL
A->>C: POST .../reviews {decision: approved}
C->>S: review(content_id, decision, context, key)
S->>DB: review_status = approved(幂等)
A->>C: POST .../publications
C->>S: publish(content_id, context, key)
S->>DB: review_status + published_at
S-->>A: 发布成功
Note over DB: 此后客户接口才能读到该方案
5.3 客户申报受理
sequenceDiagram
participant U as 客户
participant C1 as advisor_service_requests.py
participant S as AdvisorServiceRequestService
participant P as ProductRecommendationService
participant A as 投顾
participant DB as MySQL
U->>C1: POST /users/me/advisor-requests
S->>S: 校验风评有效性(FM-03)
S->>DB: 落库 pending
A->>C1: GET /advisor/service-requests
A->>C1: POST .../reviews {decision: accepted}
S->>P: generate(customer_id) 自动生成草稿
P-->>S: content_id
S->>DB: status=accepted + result_content_id
S-->>A: 返回草稿编号(待审核)
6. 外部依赖与降级矩阵
| 依赖 | 用途 | 不可用时 | 表现 |
|---|---|---|---|
| MySQL | 主存储 | 不可用即故障 | 无降级 |
| Redis | 限流 | 自动降级为放行 | 不限流 |
| Neo4j | 组合行业图谱 | 降级 | 方案标记 graph_status=degraded,业务继续 |
| 行情源(腾讯) | 最新价 / 涨跌幅 | 无行情 | 相关指标显示「暂无行情」 |
| 净值源(东财) | 历史净值 | 无数据 | 折线图显示「暂无净值数据」 |
| DeepSeek | 推荐依据改写 | 回退 | 使用规则文案,reason_source=rule |
行情与净值没有周期性刷新:演示前需跑
tools/sync_market_prices.py与tools/sync_nav_history.py(后者全量一次会被超时终止,建议按--codes分块)。
7. 前端模块结构
投顾工作台(employee-advisor/dashboard/)
| 模块 | 职责 |
|---|---|
dashboard.js |
壳、引导、指标卡、在线探测与占位 |
actions-module.js |
七项操作的表单/请求/熔断/结果渲染 |
assistant-module.js |
自然语言意图路由(复用 actions 的 resolve) |
history-module.js |
历史方案记录(只读 + 详情弹窗) |
service-request-module.js |
客户申报队列(受理/驳回) |
published-module.js |
已发布交付物 |
customer-module.js |
客户选择 |
advisor-engine.js / advisor-config.js |
本地演示引擎与常量 |
客户端(customer/advisor-plans/ + common/)
| 文件 | 职责 |
|---|---|
advisor-plans.js |
已收到方案列表、申报表单、我的申报 |
common/advisor-plan-view.js |
投顾与客户共用的方案可视化渲染(折线图/环形图/指标) |
common/advisor-plan.css |
上述组件的样式(含涨跌色变量) |
两条硬约定
- 同一页面内,同一模块的
?v=版本号必须一致 —— 不同版本会被浏览器当成两个模块,导致双实例。 - 共享模块不 import
api-client.js,apiClient 由调用方传入(依赖注入),否则两端版本不同会多出实例。
8. 权限与灰度
| 项 | 说明 |
|---|---|
| 权限码定义源 | tools/seed_test_rbac.py(投顾 9071-9074 为客户申报相关) |
| 投顾授权工具 | tools/grant_advisor_role.py(权限码必须与种子一致,由 check_rbac_seed_consistency.py 校验) |
| 客户授权 | 由种子 CUSTOMER_PERMISSIONS 决定;既有环境需按同名补建并授权 |
| 灰度 | ADVISOR_ROLLOUT_ENABLED + ADVISOR_ROLLOUT_CUSTOMER_IDS;管理员不受限 |
| 幂等 | 所有写接口要求 16-128 位 ASCII Idempotency-Key |
9. 配置清单
| 配置 | 说明 |
|---|---|
ADVISOR_ROLLOUT_ENABLED / ADVISOR_ROLLOUT_CUSTOMER_IDS |
投顾灰度 |
ADVISOR_REASON_LLM_ENABLED |
推荐依据是否启用大模型 |
ADVISOR_REASON_LLM_BASE_URL / _MODEL / _TIMEOUT_SECONDS |
模型地址、模型名、超时 |
DEEPSEEK_API_KEY |
模型密钥(.env 已被 .gitignore 忽略,不得入库) |
NEO4J_URI / NEO4J_PASSWORD |
图谱,留空即降级 |
10. 已知约束与演进方向
| 约束 | 说明 | 演进建议 |
|---|---|---|
| 候选池可能为空 | 本环境适当性证据与合同快照暂无数据,fail-closed 导致候选为 0 | 接入治理侧证据导入(tools/import_product_governance_reference.py) |
| 本地演示引擎 | 画像评分/调仓建议为前端规则模拟 | 后端补接口后切换数据源 |
| 行情/净值无自动刷新 | 依赖手工同步脚本 | 增加定时同步或接入实时源 |
| 涨跌色口径 | 新图表为涨红跌绿;产品列表/收益明细为绿涨红跌 | 统一口径后改 advisor-plan.css 两个变量即可 |
| alembic 版本指针 | 本地库指针与实际结构对不上,新表依赖手工应用 DDL | 修复版本指针后回归 alembic upgrade head |