Files
group_fqcd_jr/app/static/portal/README.md
T
张胜宇 5d0becb67d 客服 Agent 重构收口:五出口决策链 + 知识库档位隔离 + 前端入参边界(答辩演示版本)
一、客服 Agent 智能增强(正面回应"不智能、动不动就转人工")
- 决策链由 2 个出口扩到 5 个:E1 澄清 / E2 计算型 / E3 知识直返 / E4 证据约束生成 / E5 分级回退
- 转人工从"默认动作"降为最后一档 E5c,只保留 4 类白名单:
  P0 反诈 / P1 账户与个人数据 / P2 写操作与争议 / 用户明确要求人工
- 46 条金标实测(修复前 → 修复后):
  转人工率 43.5% → 10.9%;出口准确率 45.7% → 100%;事实正确率 69.6% → 100%
  禁忌违反 1 → 0;档位越权 / 无出处数字 / 误拒 四项零容忍全 0
- 安全不变量 INV-1~INV-5;零容忍规则未删,改的是挂载点
  (输出侧字面黑名单 → 检索层档位隔离 + 判定层合规词表 + 输出守护)

二、知识库:档位单点化与物理隔离
- 新增 app/core/knowledge_tier.py 作为档位规则唯一落点(G-03),
  knowledge_contracts.py 原定义块改为显式再导出(X as X,非副本)
- 档位过滤由 bool 默认值(fail-open)改为 tiers 必填集合(缺参即 TypeError)
- Milvus 侧四集合按 visibility 分区键物理隔离;双 schema 收敛为一套
- 新增 app/core/actor.py:访客三元组与匿名判定的唯一构造/判定点(G-01/G-01b)
- 新增 app/core/fund_fee_rules.py:费率计算纯函数

三、前端入参边界对齐(本轮 W11 新修,4 处"校验宽于存储")
- message 加 max_length=8000(与浮窗 widget.js 的 maxlength 一致)
- session_id 加 1—64;idempotency_key 上限 128 → 64(对齐列宽 String(64))
- feedback_type 加 max_length=32(对齐列宽 String(32))
- 8 条路径参数补 min_length=1 + max_length=64 + 字符集正则
  ({session_id} / {run_id} / {handover_id})
- 改前超限值会落到 MySQL 才失败(500);改后一律 422 AGENT_INPUT_INVALID + 字段级定位
- 新增 tests/unit/api/test_frontend_boundaries.py(33 例),含"端点表 ↔ OpenAPI 全量对照"

四、投顾模块整体清除(D4.4 / D4.5)
- 删除投顾相关 controller / schema / model / repository / service 及门户页面
- tools/portal_api_check.py 同步作废 AD003/AD005/AD011/A047 四条用例与 advisor_t 登录
  (端点与账号均已不存在,此前稳定报 3 条假红)

五、验证(提交前实测)
- pytest -q:1856 passed / 2 skipped / 0 failed
- ruff check app tools tests:19(= 基线);mypy app:2(= 基线)
- 前端接口契约体检 portal_api_check.py:38 项,通过 34,失败 0,跳过 4
- 全链路冒烟 e2e_smoke_test.py --read-only:31/31
- HTTP 全链路探针 http_probe.py:11/11 succeeded
- 跨文档一致性 _consistency.py:GATE PASS
- 真机边界复验 12 条:12/12 符合预期

六、纪律与文档
- 可改文件白名单 A-09(docs/46)与底座会签申请单 A-10(docs/47,组 1—组 4 全部受理)
- 零 DDL:未新增/修改任何表结构,89 张业务表与基线一致
- 证据留痕:docs/evidence/**(含 46 条金标 score、快照、清除与重建记录)
- 未提交(刻意排除,见提交说明):仓库内 客服agent/ 与 开发文档/ 是 2026-09-16 前的
  过期副本(Todolist 440 行 vs 权威 D2.1 1167 行),权威正本在仓库外;
  _chunks_report.txt 是 tools/build_knowledge_chunks.py 生成的本地产物
2026-09-20 14:33:30 +08:00

62 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Portal 路由与角色映射
正式门户使用原生 JavaScript、原生 CSS 与同源 `fetch`,静态资源统一从 `/static/portal/` 加载。
| 路由 | 角色 | 数据源 |
|---|---|---|
| `/portal/guest/home/` | 访客 / 全部 | P001(推荐位取前 3 只) |
| `/portal/guest/products/` | 访客 / 全部 | P001 |
| `/portal/guest/product-detail/?code=` | 访客 / 全部 | P001(**不含历史净值曲线**) |
| `/portal/customer/login/` | 未登录客户 | A034 |
| `/portal/customer/dashboard/` | customer / admin | T001、T002 |
| `/portal/customer/holdings/` | customer / admin | T006 |
| `/portal/customer/profit-loss/` | customer / admin | T001 |
| `/portal/customer/orders/` | customer / admin | T003 |
| `/portal/customer/transactions/` | customer / admin | T007 |
| `/portal/customer/cash-ledger/` | customer / admin | T009 |
| `/portal/customer/risk-questionnaire/` | customer | ONB001、ONB002 |
| `/portal/employee-console/login/` | 未登录员工 / 管理员 | A034 |
| `/portal/employee-console/workspace/` | admin / super_admin | A002-A006、A012、A033、A035-A040、客服转人工管理接口 |
| `/portal/employee-risk/dashboard/` | risk_operator / admin / super_admin | `/api/v1/risk/**`、R001-R003 |
| `/portal/employee-advisor/dashboard/` | advisor / admin / super_admin | `/api/v1/advisor/recommendations/published`(本人 + 名下归属客户的**已发布**交付物) |
| 运营账号默认进入 `/portal/employee-operations/offsite/` | operator / admin / super_admin | `/api/v1/offsite-fund/mails`、`/api/v1/offsite-fund/mailbox-status` |
> 投顾页的数据口径:接口按「本人 + `sys_customer_assignment` 里名下归属客户」过滤,且同时覆盖
> `investment_goal_book`(方案书,发布后 `review_status='published'`)与
> `advisor_recommendation_plan`(推荐方案,审核后 `review_status='approved'`)两类内容。
> 方案书的**审核与发布都要求管理员**(`investment-goal:review` / `publish` 都带 `admin=True`),
> 投顾自己发不出来 —— 这是有意设计的复核环节,不是缺陷。
客服浮窗(`common/customer-service-widget/`)由公开首页、基金产品页和客户工作台统一挂载,
挂载点是 `common/layout/app-shell.js` 的 `mountShell()` —— **按页面形态白名单**
(`public` / `customer`)挂,员工四类工作台刻意不挂;样式也是同一个地方注入,
避免九个页面各写一份 `?v=` 版本号。
访客使用 `/api/v1/visitor-tokens`
获取短期令牌(角色 `visitor`,权限只有 `agent:run` + `knowledge:query`),因此**必须**走
`query_knowledge` 这个工具名;登录客户走 `search_knowledge`。两者都要出现在发布配置的
`agent_tools/customer_service:<intent>` 白名单里,缺哪一条,对应人群就一问即失败。
公开产品数据来自 **`GET /api/v1/products`(编号 P001,见 `docs/05` §19)**,
产品与净值取自 `fin_product`(`status='上市'`)、行情取自 `fin_market_price` 的最新一行。
访客页面通过 `common/visitor-token.js` 取短期访客令牌后调用;该文件是访客令牌的**唯一实现**
(客服浮窗也用它),不要在页面里另写一份,否则存储 key 与过期判断迟早不一致。
**公开页一律用访客令牌**,即使浏览器里还留着登录令牌:`api-client.js` 对调用方
显式传入的 `Authorization` **不再覆盖**(此前会覆盖,于是同一个公开页对访客和已登录
用户显示不同内容)。客户工作台则相反,走登录令牌并建会话(`C001`),
这样多轮澄清与「转人工」才能挂到同一个 `session_id` 上。
**公开产品数据不得与登录后的真实账户数据混用。**
三点口径(改前端前先读):
1. **`change_pct` 可能是 `null`**(行情只同步过一个交易日时算不出涨跌)。
调用方必须显示"暂无",**不得当成 `0`** —— `formatPercent` 收到 `null` 会渲染成 `+0.00%`,
那等于告诉客户"今天平盘"。
2. **历史净值曲线没有数据源**:`fin_nav_history` 目前 0 行,详情页因此**不画走势图**,
并显式说明"尚未接入"。此前那条曲线是 mock 里 12 个编造点位 —— 走势图最容易被当成真数据。
3. **产品级披露在 `common/product-notes.js`**(如 510300 的"同指数参考产品,非本公司发行")。
`fin_product` 没有这个字段,所以它留在前端;新增需要披露的产品时改那一份。
凡渲染公开产品的页面都要挂 `data-source-notice` 说明数据来源。
客户页面均由 `common/auth.js` 执行入口守卫,接口路径只在 `common/api-client.js` 的端点表登记。