feat: add Nailong Fund advisor capabilities

This commit is contained in:
Windows
2026-09-11 09:47:40 +08:00
parent 5907fcd6d2
commit a4499defee
143 changed files with 12255 additions and 89 deletions
+2
View File
@@ -673,7 +673,9 @@ Agent 边界: Agent 只读查询委托与成交,不得代客下单
| `GET /api/v1/sim-orders`、`/api/v1/holdings` | B | 交易域文档 | 只读,按客户归属过滤 |
| `POST /api/v1/risk-alerts/{alert_id}/actions` | B | 风控域文档 | Agent **不能**确认、关闭或升级预警(03 §10.3) |
| `POST /api/v1/risk-scan-runs` | B | 风控域文档 | 规则引擎产生预警,模型只做辅助研判(03 §10.1) |
| `GET/POST /api/v1/onboarding/risk-questionnaire/**` | B | 17-开户风险测评设计 | 客户填写问卷;评分和画像仅后台可见 |
| `POST /api/v1/advisory-plans/{plan_id}/reviews` | B | 投顾域文档 | Agent 只生成草案,审核由持证投顾执行(03 §8) |
| `GET/POST /api/v1/advisor/investment-goals/**` | B | 16-投资目标业务设计 | 采集和确认目标;目标书仅生成待审核草稿,不发布建议或交易 |
| `GET/POST /api/v1/client-facing-contents` | B | 投顾域文档 | 草稿状态内容不得作为正式建议返回(03 §8) |
| `GET/POST /api/v1/handover-tickets` | B | 客服域文档 | 状态流转由人工执行(02 §7.2、03 §6.4) |
| `GET/POST /api/v1/faq-synonyms` | B | 知识运营文档 | 只有 `approved` 参与检索(02 §7.3) |
+3 -1
View File
@@ -785,7 +785,9 @@ Outbox 消费者按 `event_id` 幂等。失败事件保留并重试,超过阈
| 业务域 | 接口入口 | 归属文档 | Agent 边界 |
|---|---|---|---|
| 客服工单 | `/api/v1/customer-service/handover-tickets/**` | 客服业务文档 | 可生成摘要和转人工请求,不分配、接单、解决或关闭工单 |
| 开户测评 | `/api/v1/onboarding/risk-questionnaire/**` | 17-开户风险测评设计 | 客户填写问卷;评分和画像仅后台可见 |
| 投顾方案 | `/api/v1/advisory-plans/**` | 投顾业务文档 | 只生成分析草案,不代替投顾审核发布 |
| 投资目标 | `/api/v1/advisor/investment-goals/**` | 16-投资目标业务设计 | 采集和确认目标;目标书仅生成待审核草稿,不发布建议或交易 |
| 场内模拟交易 | `/api/v1/sim-orders/**` | 交易业务文档 | 只读查询,不创建、确认或撤销委托 |
| 风控扫描 | `/api/v1/risk-scans/**` | 风控业务文档 | 可解释规则结果,不启动人工处置 |
| 风险预警 | `/api/v1/risk-alerts/**` | 风控业务文档 | 只读分析,不确认、升级或关闭预警 |
@@ -956,7 +958,7 @@ GET /internal/metrics
| O002 | `GET /internal/health/ready` | 内网 | 否 | `200/503` | 否 |
| O003 | `GET /internal/metrics` | 监控系统 | 否 | `200` | 否 |
业务域接口 `/customer-service/handover-tickets/**`、`/advisory-plans/**`、`/sim-orders/**`、`/risk-scans/**` 和 `/risk-alerts/**` 的具体方法、请求体、领域状态机和错误码分别由对应业务文档登记;它们仍必须遵守本文第 3-5、11 和 12 节。
业务域接口 `/customer-service/handover-tickets/**`、`/onboarding/risk-questionnaire/**`、`/advisory-plans/**`、`/advisor/investment-goals/**`、`/sim-orders/**`、`/risk-scans/**` 和 `/risk-alerts/**` 的具体方法、请求体、领域状态机和错误码分别由对应业务文档登记;它们仍必须遵守本文第 3-5、11 和 12 节。
## 20. 变更流程
+1 -1
View File
@@ -2,7 +2,7 @@
> 版本:v1.0
> 测试日期:2026-09-09
> 测试对象:`jr-agent-platform` 底座代码(86 个 Python 文件)
> 测试对象:奶龙基金底座代码(86 个 Python 文件)
> 测试依据:`AGENTS.md`、`docs/00-新数据库基线设计.md`、`docs/01-通用Agent平台开发设计.md`、`docs/02-数据库建表设计.md`、`docs/03-平台端到端流程文档.md`、`docs/05-接口文档.md`、`TODO.md`
> 测试方式:自动化测试执行 + 静态与类型检查 + 真实 HTTP 接口冒烟 + 契约逐项核对
> **测试结论:综合 55/100(D+)。工程基建扎实,但鉴权为空、端到端不通,不可用于任何真实流量。**
+2 -1
View File
@@ -1,4 +1,4 @@
# Agent 底座使用文档
# 奶龙基金 Agent 底座使用文档
> 版本:v1.0
> 适用范围:底座负责人、业务 Agent 开发人员、接口联调人员
@@ -33,6 +33,7 @@ Copy-Item .env.example .env
```dotenv
JWT_ISSUER=jr-local
JWT_AUDIENCE=jr-agent-platform
APP_NAME=奶龙基金
JWT_PUBLIC_KEY_PATH=config/jwt/jwt-public.pem
MYSQL_DSN=mysql+asyncmy://用户名:密码@127.0.0.1:3306/jr
REDIS_URL=redis://127.0.0.1:6379/0
+1 -1
View File
@@ -3,7 +3,7 @@
> 版本:v1.0
> 评估日期:2026-09-09
> 评估对象:客服、风控、投顾、运营四个业务域接入公共 Agent 底座的可行性
> 评估依据:四份业务域工作流程文档 + `jr-agent-platform` 底座代码现状核对
> 评估依据:四份业务域工作流程文档 + 奶龙基金底座代码现状核对
> **评估结论:四个域 100% 阻塞在同样两个公共出口上(模型调用、工具调用)。补完这两个出口前,交付给组员做业务实现会大面积返工。**
---
+52
View File
@@ -0,0 +1,52 @@
# 投资目标采集与目标书
> 版本:v1.0
> 状态:实施中
> 修订日期:2026-09-10
## 1. 范围
本能力先完成投顾流程的输入闭环:采集并确认收益目标、最大可承受回撤、流动性需求和投资期限,生成待审核的投资目标书草稿。仅支持场内基金模拟交易的后续分析输入,不创建委托、不执行交易,也不构成收益承诺。
策略组合草案、适当性复核、持证投顾审核和内容发布属于后续流程;本期不会将草稿作为正式投资建议返回或发布。
## 2. 数据与兼容性
新增表 `advisor_investment_goal` 保存每次采集的结构化目标版本,并关联已有的 `client_facing_content` 记录。目标书使用 `content_type=investment_goal_book`、`review_status=pending_review` 存储为对客内容草稿。
兼容性核对:本期迁移只执行 `CREATE TABLE advisor_investment_goal`,不修改、重命名、删除或复用《00-新数据库基线设计.md》中的任何表或字段。`fin_customer_profile`、`fin_risk_assessment` 和 `client_facing_content` 的结构均保持不变。
## 3. 采集字段
| 字段 | 规则 |
|---|---|
| 预期年化收益区间 | 0 至 100,区间下限不得超过上限;必须附带业绩比较基准名称 |
| 最大回撤 | 0 至 100 的百分比 |
| 流动性需求 | 可随时使用、7 日内、30 日内或 30 日后可使用 |
| 投资期限 | 1 至 600 个月 |
| 备注 | 不得包含保本、保证收益、稳赚、无风险或收益承诺表述 |
目标书必须展示“期望/业绩比较基准口径,不构成收益承诺”的风险提示。
## 4. 状态与权限
目标状态:`pending_confirmation -> confirmed -> superseded`。确认新的目标时,同一客户此前已确认的目标会变为 `superseded`;目标书草稿仍保持 `pending_review`,确认目标不等同于审核或发布。
权限按照资源及数据范围独立配置:
- `investment-goal:write:self`、`investment-goal:read:self`、`investment-goal:confirm:self`:客户只能处理本人目标。
- `investment-goal:write:customer`、`investment-goal:read:customer`、`investment-goal:confirm:customer`:员工仅可在 `own_customers` 或 `all` 范围内处理客户目标。
所有采集和确认动作都写入 `interaction_audit`。投顾 Agent 只可通过 `query_investment_goal` 读取当前调用者的目标,不具备写入权限。
部署时由超级管理员受控新增上述权限码并分配到相应角色,不在业务迁移中自动授予任何角色。启用 Agent 查询前,还需在已发布配置中登记 `namespace=agent_tools`、`config_key=advisor:investment_goal`、`value_json={"allowed_tools":["query_investment_goal"]}`;否则公共 ToolExecutor 会拒绝该工具调用。
## 5. HTTP 入口
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/api/v1/advisor/investment-goals` | 采集目标并生成待审核目标书草稿,要求 `Idempotency-Key` |
| GET | `/api/v1/advisor/investment-goals/current` | 查询本人当前目标 |
| GET | `/api/v1/advisor/customers/{customer_id}/investment-goals/current` | 员工按归属查询客户当前目标 |
| POST | `/api/v1/advisor/investment-goals/{goal_no}/confirmations` | 确认目标,要求 `Idempotency-Key` |
| GET | `/api/v1/advisor/investment-goals/{goal_no}/goal-book` | 查看关联目标书草稿及审核状态 |
+45
View File
@@ -0,0 +1,45 @@
# 开户风险测评与后台画像
> 版本:v1.0
> 状态:实施中
> 修订日期:2026-09-10
## 1. 范围
客户第一次登录时必须完成开户风险测评。系统以是否存在有效的正式风险测评判断是否完成;没有有效测评的客户只能访问 `/api/v1/onboarding/**`,其他已鉴权业务入口返回 `403 ONBOARDING_REQUIRED`。
问卷来自《风险测评问卷》,共 13 道单选题。提交前必须确认真实、准确、完整声明。每天最多提交两次,滚动 365 天最多提交八次;正式测评有效期为提交时起一年。
## 2. 数据边界与兼容性
不新增、不删除或修改任何数据库表和字段:
- `fin_risk_assessment`:保存提交答案、后台总分、C1-C5 投资者类型和有效期,是正式风险测评权威记录。
- `profile_snapshots`:生成版本化后台客户画像;切换当前版本时仅变更既有 `is_current` 状态。
- `memory_sync_outbox`:同一事务创建 Milvus、Neo4j 两条画像同步事件。
- `interaction_audit`:记录完成事件,不记录原始答案和分数。
本期不修改 `fin_customer_profile`、`fin_risk_assessment`、`profile_snapshots` 或任何其他基线表的结构。客户接口禁止返回答案、总分、C1-C5、风险标签、画像或评分规则;这些内容仅可由已授权后台流程读取。
## 3. 后台评分规则
评分规则版本为 `opening-risk-v1`,在服务端以固定映射执行。问题按财务承受能力、投资经验、投资期限、产品偏好、投资态度和损失承受能力评分,总分范围 13 至 57:
| 总分 | 内部风险等级 | 后台画像 |
|---:|---|---|
| 13-22 | C1 | 谨慎型 |
| 23-31 | C2 | 稳健型 |
| 32-40 | C3 | 平衡型 |
| 41-49 | C4 | 成长型 |
| 50-57 | C5 | 进取型 |
评分映射不出现在客户 HTTP 响应、Agent 输出、日志或审计详情中。后续变更评分规则必须新建问卷版本,不得覆盖历史测评结果。
## 4. HTTP 入口
| 方法 | 路径 | 客户可见内容 |
|---|---|---|
| GET | `/api/v1/onboarding/risk-questionnaire` | 是否需要填写、13 道题和确认声明;已完成时不返回问卷和任何测评结果 |
| POST | `/api/v1/onboarding/risk-questionnaire/submissions` | 仅返回完成状态、问卷版本和有效期;要求 `Idempotency-Key` |
提交请求包含 `answers`(`q1` 至 `q13` 的选项编号)与 `declaration_accepted=true`。接口不接受客户端传入分数、风险等级、画像或有效期。
@@ -0,0 +1,124 @@
# 投顾 Agent 意图与资产配置设计
> 版本:v1.0
> 状态:实施中
> 修订日期:2026-09-10
## 1. 意图分类
`advisor` Agent 在公共 `IntentClassifier` 中声明并描述以下投顾意图。描述与意图白名单一同进入受路由模型的分类提示,模型输出仍必须是白名单内的 JSON Intent。
| Intent Key | 典型触发 | 当前路由 |
|---|---|---|
| `product_recommend` | 推荐、有什么适合、给客户推荐 | 已上线约束满足、图谱增强与多因子排序的只读分析,不创建交易 |
| `portfolio_analysis` | 持仓分析、集中、分布在哪些行业、风险预警 | 已上线 MySQL 权威持仓集中度、行业穿透和规则化预警;Neo4j 仅提供异步关系投影后的定性解释,不参与数值计算 |
| `asset_allocation` | 配置比例、怎么分配、资产建议 | 已上线只读配置比例分析 |
| `comparison` | 共同持仓、有什么不同、对比 | 已受控识别;多实体图谱查询和跨客户授权服务未上线,不生成对比报告 |
当模型分类服务不可用或尚未返回分类结果时,`AdvisorAgent` 仍按上述典型触发词做保守回退路由。交易、申购、赎回、下单和成交确认请求始终优先拒绝,不进入任何投顾分析路径。
## 2. 资产配置
资产配置是分析功能,不创建委托、不写入交易表、不执行场内基金模拟交易。工具链如下:
1. `generate_asset_allocation` 读取当前有效的正式风险测评对应画像。
2. 读取当前已确认的投资目标;未采集或未确认时只提示补齐流程。
3. 以内部风险等级、投资期限、流动性要求和最大回撤为输入,输出现金管理类、债券类、权益类场内基金的目标比例。
4. 短期限或日流动性需求提高现金管理比例;较低回撤上限限制权益比例。比例合计固定为 100%。
客户回复只展示资产类别和比例,不展示问卷答案、总分、C1-C5、风险画像、画像标签或评分规则。内部画像读取仅供受控 Agent 工具使用。
## 3. 产品推荐
`recommend_products` 按以下顺序执行,任何一步失败都不会进入下一步或生成交易指令:
1. 从 `fin_product` 读取状态为“上市”、且位于系统开放交易时间窗内的场内产品。
2. 使用当前有效正式测评的内部风险等级,对每个候选执行适当性硬过滤;未通过的产品不会参与排序。
3. 通过固定的 `Product -> Industry/Category` 查询读取至多一跳 Neo4j 关系。Neo4j 不是产品、风险或交易状态的权威来源,连接失败时图谱分数为零,并在对客结果中明确降级。
4. 按收益代理 30%、风险匹配 25%、期限匹配 15%、分散信号 15%、图谱信号 15% 排序,最多返回三个候选。当前基线没有权威的预期收益率字段,因此收益项暂保持中性,不会编造收益预测。
5. 以受控模板生成理由,只说明产品已通过适当性过滤、与已确认期限相符及图谱可用性;不向客户或外部模型暴露问卷答案、C1-C5、资产规模或画像原文。
用户提供的理由模板中包含内部风险等级和资产规模,这两项不能直接用于对客生成文本。若后续要引入模型生成,必须先通过模型路由、脱敏输入契约和合规模板审核,且保留确定性模板作为降级路径。
## 4. 权限与发布配置
本期未修改数据库基线中的既有表或字段,也没有新增迁移。画像读取复用 `fin_risk_assessment` 与 `profile_snapshots`,投资目标复用既有的 `advisor_investment_goal` 兼容扩展。
启用资产配置前,超级管理员需要在权限中心受控配置并授予客户:
- `customer-profile:read:self`
- `investment-goal:read:self`
- `asset-allocation:generate:self`
- `product-recommendation:read:self`
- `suitability:read`
- `portfolio-analysis:read:self`
还需要在同一个已发布版本的 `agent_tools` 配置中登记:
```json
{
"namespace": "agent_tools",
"config_key": "advisor:asset_allocation",
"value_json": {"allowed_tools": ["generate_asset_allocation"]}
}
```
产品推荐还需要登记独立的工具白名单:
```json
{
"namespace": "agent_tools",
"config_key": "advisor:product_recommend",
"value_json": {"allowed_tools": ["recommend_products"]}
}
```
`query_customer_profile` 是内部只读工具,不作为客户可见接口,也不得单独回显其结果。
## 5. 持仓分析
`analyze_portfolio` 只读取权威的 `fin_holding` 和 `fin_product`,不创建、修改或取消委托。它使用新建的 `advisor_product_industry_exposure` 作为版本化参考数据,按以下规则输出:
1. 按已估值持仓市值计算产品占比与产品 HHI。
2. 将每个产品的市值按行业暴露权重穿透,输出主要行业及行业 HHI。
3. 行业参考数据覆盖低于阈值时,不输出行业集中度结论;市值缺失时不将其按零处理,而是给出数据质量提示。
4. 规则化预警包括单产品集中、单行业集中、当前持仓适当性待复核、产品状态关注、行业覆盖不足和数据异常。
`advisor_product_industry_exposure` 为唯一新增表,保留每个 `product_id` 在 `as_of_date` 的行业权重快照,引用未变更的 `fin_product.id`。迁移不修改、重命名、删除或复用基线中的任何表或字段。行业暴露的维护方必须提供可审计的 `source`,每一产品快照的行业权重总和不得超过 100%;异常快照不会参与行业穿透计算。
启用前需要在已发布配置中增加工具白名单:
```json
{
"namespace": "agent_tools",
"config_key": "advisor:portfolio_analysis",
"value_json": {"allowed_tools": ["analyze_portfolio"]}
}
```
预警阈值通过同一发布批次中的配置受控调整,所有值均为 1 至 100 的整数百分比:
```json
{
"namespace": "portfolio_analysis",
"config_key": "thresholds",
"value_json": {
"single_product_concentration_pct": 30,
"single_industry_concentration_pct": 40,
"min_industry_coverage_pct": 80
}
}
```
后续 Neo4j 投影只能异步同步经审核的 `Product -> EXPOSED_TO_INDUSTRY -> Industry` 参考关系及来源版本。`fin_holding` 的持仓市值、`fin_product` 的状态和风险等级始终以 MySQL 为准;图谱不可用时,持仓分析仍可返回产品层结论和明确的数据覆盖提示。
## 6. 图谱增强投影
持仓分析的数值结论始终由 MySQL 中的 `fin_holding`、`fin_product` 和
`advisor_product_industry_exposure` 计算。Neo4j 仅保存异步投影的最小关系快照:
`Customer -> HOLDS -> Product -> EXPOSED_TO_INDUSTRY -> Industry`。
`PortfolioGraphProjectionWorker` 只能在持仓或行业暴露参考数据变更后异步触发;它不投影市值、成本、盈亏、产品名称或客户姓名、联系方式等身份资料。图谱查询只能由 `RelationshipService` 中的固定 Cypher 发起,不接收 Agent 或调用方传入的任意 Cypher。
图谱查询成功时,`analyze_portfolio` 仅补充“多只持仓产品关联同一行业”的定性关系证据,不参与 HHI、行业占比或预警阈值的计算。Neo4j 不可用、返回异常或投影尚未完成时,工具返回显式降级标记,MySQL 的持仓报告、数值和风险提示仍然可用。
当前 worker 已具备可被任务调度器或事件 outbox 调用的投影入口;接入运行环境时必须使用持久化事件、幂等键和失败重试,不能在用户同步请求中直接执行投影。
@@ -0,0 +1,67 @@
# 权威产品适当性与合同字段接入
## 适用范围
本功能只接入南方基金的场内 ETF/LOF 模拟交易产品。`fin_product.risk_level` 中的
历史测试映射不是权威适当性依据,推荐服务不会使用它作为生产准入条件。
产品适当性等级必须来自**实际销售机构**的产品风险等级披露。基金管理人、第三方行情
网站和历史波动率都不能替代该披露。基金合同字段必须来自南方基金官网、交易所公告或
基金合同原文,并保留原文 HTTPS 链接及 SHA-256 文件哈希。
## 数据表
- `advisor_product_suitability_reference`:按销售机构、生效期保存 R1-R5 适当性等级。
- `advisor_product_contract_snapshot`:按生效期保存基金类型、投资范围、业绩比较基准、
风险收益特征、托管人、费率、成立日和合同来源。
两张表均为新增表,不修改基线表或字段。相同产品和生效期的来源哈希不可覆盖;原文变化
时必须创建新的生效版本。
## 导入
先执行迁移,再设置实际销售机构名称:
```powershell
python -m alembic upgrade head
$env:PRODUCT_SUITABILITY_SALES_INSTITUTION = "实际销售机构全称"
```
适当性 CSV 必须包含以下表头:
```text
product_code,sales_institution,risk_level,effective_from,effective_until,source_url,document_title,document_published_at,document_sha256,source,review_status,verified_by,verified_at
```
合同 CSV 必须包含以下表头:
```text
product_code,effective_from,effective_until,fund_type,investment_scope,performance_benchmark,risk_return_characteristics,custodian_name,management_fee_rate_pct,custodian_fee_rate_pct,inception_date,source_url,document_title,document_published_at,document_sha256,source,review_status,verified_by,verified_at
```
先校验再写入:
```powershell
python tools/import_product_governance_reference.py --suitability data/suitability.csv --contracts data/contracts.csv --dry-run
python tools/import_product_governance_reference.py --suitability data/suitability.csv --contracts data/contracts.csv
```
只有 `review_status=verified` 且包含 `verified_by`、带时区的 `verified_at`、HTTPS 原文链接
和 64 位 SHA-256 的记录,才会参与推荐。未配置销售机构或没有同时具备有效适当性和合同
证据的产品,推荐接口会安全返回相应的 `*_reference_required` 状态。
## 南方基金官网同步
仓内的南方场内 ETF/LOF 可使用官网直销产品详情页同步。同步器读取官网明确标注“评级
来自南方基金”的 R1-R5,读取官网法律文件目录中的基金合同原件并计算文件 SHA-256。
它不使用第三方平台、产品类型或历史波动率推导风险等级。
```powershell
$env:PRODUCT_SUITABILITY_SALES_INSTITUTION = "南方基金管理股份有限公司直销"
python tools/sync_nanfang_official_product_governance.py --dry-run
python tools/sync_nanfang_official_product_governance.py
```
同步记录的核验人固定标注为 `system:official-source-sync`,表示已完成官网来源、风险等级、
合同文件及哈希的一致性校验;不等同于人工合规复核。生产环境应按销售机构的变更周期复跑
同步,并由合规人员复核版本差异。