一、客服 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 生成的本地产物
455 lines
29 KiB
Markdown
455 lines
29 KiB
Markdown
# 演示流程(照着走)
|
||
|
||
> **读者**:负责演示的人。本文按"操作 → 看到什么 → 这体现什么"三段式写,
|
||
> 可以直接照着念。
|
||
> **主线时长**:约 8 分钟;含讲解约 15 分钟。
|
||
> **全部内容均已实测**;标 ⚠️ 的地方是容易翻车的点。
|
||
|
||
> 🔴 **状态更正(2026-09-19)**:本文档写于 **2026-09-13/14**。此后客服 Agent 已**整体重构**
|
||
> (五出口 `E1`—`E5c`:澄清 / 计算 / 知识直返 / 证据约束生成 / 分级回退 + 转人工白名单),
|
||
> 且**投顾模块已整体清除**(`D4.4` / `D4.5`)。因此:
|
||
> - **怎么演**:以 `客服agent\D2.5-客服Agent演示脚本与账号速查-2026-09-19.md` 为**现行口径**(含实测台词表与账号表);
|
||
> - **已作废、不要念不要点**:§场景 7(投顾工作台)与文末账号表的 `advisor_t` 行 —— 该角色与账号**库里已不存在**;
|
||
> - **已更正**:场景 1 的答复口径、场景 4 的转人工口径、§答疑的三档阈值说明(见各行 🔁 标记)。
|
||
>
|
||
>
|
||
> **2026-09-14 复核**:本文档已按当天实测数据更新四处(「今日盈亏」已实现、投顾推荐方案
|
||
> 已开放自助审核发布、管理员工作台为八个标签页、角色权限数与已知空数据)。数字类结论都标了
|
||
> 实测日期——**换环境或换库后要重查**。
|
||
>
|
||
> **配套文档**(想看"为什么"而不是"怎么演"就看这几份):
|
||
> - `docs/演示用/全功能流程-大白话版.md` —— 从登入讲到六个门户的全部流程,不用术语;
|
||
> - `docs/演示用/记忆系统演示文档-2026-09-14.md` —— **记忆系统怎么演**(含三条可复现命令与实测输出);
|
||
> - `docs/演示用/记忆架构与Worker工作流程-大白话版.md` —— 记忆链路与后台 Worker 的详细工作流程;
|
||
> - `docs/演示用/今日盈亏实现说明-2026-09-14.md` —— 「今日盈亏」的口径、数据坑与复算命令。
|
||
|
||
---
|
||
|
||
## 0. 演示前准备
|
||
|
||
### 0.1 准备演示数据(首次、或换了机器才需要)
|
||
|
||
```powershell
|
||
python tools/seed_demo_data.py
|
||
```
|
||
|
||
10 步:账号权限 → 演示口令 → 客户账户 → **场内行情** → 风控预警样本 → 投顾数据 →
|
||
三类发布配置 → 知识库素材。脚本会逐步打印完成/失败。
|
||
|
||
> ⚠️ 第 2 步**不是幂等的**:重跑等于重设密码(bcrypt 每次加盐不同)。
|
||
|
||
### 0.2 一条必须知道的硬规则:**行情有效期只有 15 分钟**
|
||
|
||
下单要求行情快照落在 **15 分钟内**(`app/service/trade_service.py` 的 `MAX_QUOTE_AGE`),
|
||
**系统没有任何自动刷新机制** —— 超时后**所有委托直接返回 `503 行情已过期`**。
|
||
|
||
演示时踩过这个坑:明明上午刷过行情,下午演示第一单就 503。
|
||
|
||
**怎么办**:`start.ps1` **已经会替你刷新一次**(见 0.3),所以正常不用管。
|
||
万一演示中途下单报 503 —— 在新窗口跑一次就行,**不用重启任何服务**(已实测):
|
||
|
||
```powershell
|
||
python tools/sync_market_prices.py
|
||
```
|
||
|
||
它按产品 upsert(幂等),几秒内把**全部产品(当前库里 26 只)**行情刷新,15 分钟窗口重新计时。
|
||
|
||
> 不想让启动脚本联网刷行情:`start.ps1 -SkipPriceSync`
|
||
|
||
### 0.3 启动平台
|
||
|
||
**最省事的办法:双击桌面上的 `启动金融Agent平台.bat`。** 不用开 PowerShell、不用敲命令。
|
||
(仓库根目录也有一份 `启动平台.bat`,是同一个东西。)
|
||
|
||
> ⚠️ **`启动金融Agent平台.bat` 在桌面上,不在仓库里**(2026-09-14 补注):
|
||
> 仓库中只有 **`启动平台.bat`** 这一份(`Glob 启动*.bat` 只命中它)。
|
||
> 两份内容是同一个东西、由同一个生成器产出:
|
||
> ```
|
||
> python tools/make_launcher_bat.py # 改完 start.ps1 或想换路径就重跑它,桌面与仓库两份一起更新
|
||
> ```
|
||
> **不要手写那个 bat** —— 它必须同时满足 **GBK 编码 + CRLF 换行 + 无 BOM**,
|
||
> 缺任何一条 `cmd` 都会解析错乱(LF 换行会把 `echo` 的说明文字当命令执行,
|
||
> 实测报 `AT 命令已弃用`、`']' 不是内部或外部命令`)。
|
||
|
||
它按顺序做六件事:**找解释器 → 检查 MySQL/Redis/Milvus → 刷新行情 → 起两个窗口
|
||
→ 等 API 真正应答 → 自动开浏览器**,然后打印访问入口与账号。
|
||
|
||
> **重复双击是安全的**:API 按端口判断、Worker 按进程判断,已经在跑的不会起第二份。
|
||
> 演示前如果服务已经开着,直接双击它就等于"刷新行情 + 打开页面"。
|
||
|
||
想传参数就用命令行(bat 会把参数原样转给 `start.ps1`):
|
||
|
||
```powershell
|
||
powershell -ExecutionPolicy Bypass -File start.ps1 # 等价于双击
|
||
powershell -ExecutionPolicy Bypass -File start.ps1 -NoBrowser # 不自动开浏览器
|
||
powershell -ExecutionPolicy Bypass -File start.ps1 -Port 8100 # 换端口(8000 被别的程序占了)
|
||
```
|
||
|
||
它**开两个窗口**:
|
||
|
||
| 窗口 | 作用 | 少了它会怎样 |
|
||
|---|---|---|
|
||
| API | 所有接口与页面 | 什么都没有 |
|
||
| **Worker** | Agent 真正执行、领域事件投递、知识向量同步、记忆抽取与画像投影、会话片段聚合、**场外收件** | 客服对话一直"超时";新知识不进 Milvus(**无任何报错**);记忆不更新;场外收不到邮件 |
|
||
|
||
> ⚠️ 两个窗口都别关。启动脚本自己的窗口跑完就可以关(服务在独立窗口里)。
|
||
>
|
||
> 注:**风控定时扫描不在这个 Worker 里** —— 它是独立进程
|
||
> (`python -m app.worker.risk_scan_scheduler`)且默认关闭;演示时用的是风控工作台上的
|
||
> **「扫描」按钮**(同步执行,最长 60 秒)。
|
||
> 详细分工见 `docs/演示用/记忆架构与Worker工作流程-大白话版.md`。
|
||
|
||
### 0.4 开场前自检(务必做)
|
||
|
||
两条线互补,都跑一遍:
|
||
|
||
```powershell
|
||
python tools/e2e_smoke_test.py --read-only # 业务链路:登录→下单→成交、风控闭环、客服问答
|
||
python tools/portal_api_check.py # 接口契约:按前端的方式核对每个端点的状态码与字段
|
||
```
|
||
|
||
看最后一行:**44/44** 与 **41 项全通过**才开始演示(用例数会随开发增减,
|
||
判断健康的准则是 **0 FAIL**,不是绝对条数)。有 FAIL 就按 §4 排查。
|
||
|
||
> 两者的分工:`e2e_smoke_test.py` 回答"**这条业务走得通吗**",
|
||
> `portal_api_check.py` 回答"**接口返回的东西前端能不能正确显示**"。
|
||
> 后者是 2026-09-13 那轮"照着接口把前端测一遍"的固化 —— 当时靠它查出 6 个真缺陷
|
||
> (知识库页永远显示 0 块、详情页 5 行全是 `--`、配置项首次编辑必然 409、非法场景值冒成 500 等)。
|
||
> 加 `--write` 会再测一遍写操作(会产生数据);`--dangerous` 会动生效配置,**演示前不要加**。
|
||
|
||
### 0.5 打开第一个页面
|
||
|
||
<http://127.0.0.1:8000/portal/>
|
||
|
||
---
|
||
|
||
## 1. 主线场景
|
||
|
||
### 场景 1 · 访客咨询与知识边界(1.5 min)
|
||
|
||
| | |
|
||
|---|---|
|
||
| **打开** | <http://127.0.0.1:8000/portal/guest/products/> |
|
||
| **操作** | 点右下角客服浮窗 → 输入「场内基金的管理费率是多少」→ 发送 |
|
||
| **预期** | 🔁 **2026-09-19 更正**:知识库**没有**场内基金条目 ⇒ 现在走 **`E5b` 部分答 + 引导**(给出能查到的公开费率资料并邀请改写问法),**不会**再报出「0.15%~1.20%/年」那个区间(那是旧实现的答复)。<br>想演「直接答对」请改问 **「什么叫七日年化?」**(实测 `E3` 原文直返)。<br>另:客服浮窗已于 2026-09-19 **重建恢复**(`乙-15`)—— 此前一段时间它处于「已被整体清除」的状态 |
|
||
|
||
> ⚠️ 知识库文案是**人工审核入库时的快照**(当前 active 知识 24 份、元数据 199 行),
|
||
> 里面写的产品数量**可能与产品列表的当前数量不一致**。
|
||
> 被问到时如实说:"这是知识库里的内容,知识是人工入库的,产品列表取的是实时数据。"
|
||
> 不要现场改知识库文案来对齐数字。
|
||
|
||
**这体现什么**:客服不是"模型随便聊",而是**检索已发布知识后作答**,答案可追溯;
|
||
延迟稳定在 3–4 秒(一次意图分类 + 一次向量检索)。
|
||
|
||
**接着再问一句(体现安全边界)**:「我的账户里有多少钱?」
|
||
→ 预期:回复"该服务需要登录后才能查询您的个人信息",**不编造账户数字**。
|
||
|
||
> 产品页的数据来自**真实接口**(`GET /api/v1/products`):产品与净值取自 `fin_product`,
|
||
> 行情由 `tools/sync_market_prices.py` 从行情源同步,页面底部有来源声明。
|
||
>
|
||
> ⚠️ 两个**可能被问到、但其实不是故障**的地方:
|
||
> - 「最近涨跌」列取的是**行情源直接给出的当日涨跌幅**(腾讯返回,存在
|
||
> `fin_market_price.change_pct`),已接入。若某只显示"暂无",说明它走的是净值降级
|
||
> 路径(源没给涨跌幅),或行情没同步过 —— 跑一次 `sync_market_prices.py` 即可。
|
||
> 我们**不编**这个数:缺数据就显示"暂无",而不是 0(0 会被读成"今天平盘")。
|
||
> - 产品详情的**净值走势图现在是真实数据**:`fin_nav_history` 由
|
||
> `tools/sync_nav_history.py`(演示数据第 5 步)从东财净值接口同步,近 **120 个交易日**。
|
||
> 若图上显示"尚未接入",说明第 5 步没跑 —— 补跑即可,不用重启服务。
|
||
|
||
### 场景 2 · 客户资产(1 min)
|
||
|
||
| | |
|
||
|---|---|
|
||
| **打开** | <http://127.0.0.1:8000/portal/customer/login/> |
|
||
| **账号** | `cust_t` / `123456` |
|
||
| **预期** | 自动进资产总览:总资产约 10 万、可用资金、持仓市值(7 只持仓) |
|
||
|
||
**这体现什么**:持仓按**真实行情**计价(不是写死的假数);页面底部有数据来源说明。
|
||
|
||
点顶部导航「我的持仓」「资金流水」「成交明细」「盈亏分析」各看一眼即可。
|
||
|
||
> ✅ **「今日盈亏」现在可以直接讲**(2026-09-14 起是真实计算,不再是占位 0)。
|
||
> 实测口径与数字(当天数据):`cust_t` 看板 **今日盈亏 −132.70(−0.2528%)**;
|
||
> 逐只:`510300` −94.50、`510500` −27.30、`159948` −8.80、`511810` −1.80、
|
||
> `515450` +2.00、`588890` −2.30、`160128` 0.00。
|
||
>
|
||
> 被追问口径时一句话答:**"今天收盘值多少钱 − 昨天收盘值多少钱 − 今天买入花的钱 + 今天卖出收回的钱"
|
||
> (不含手续费);基准优先用行情(和同页的"最新价/市值"同源,客户能自己核对),
|
||
> 行情只有一天的产品回退用净值。** 语义上是"**最近一个交易日**"而不是盘中实时。
|
||
> 想现场证明它没算错:`python tools/check_today_profit_loss.py`(纯 SQL 独立复算并逐只比对)。
|
||
> 详见 `docs/演示用/今日盈亏实现说明-2026-09-14.md`。
|
||
|
||
### 场景 3 · 客户下单(1.5 min)⭐ 重点
|
||
|
||
| | |
|
||
|---|---|
|
||
| **打开** | 客户页 → 资产总览 → 「去下单」(**页面本身就有下单弹窗**),或直接用下边的接口 |
|
||
| **说明** | 弹窗里填基金代码 / 方向 / 数量即可;接口方式更直观,二者等价 |
|
||
|
||
```powershell
|
||
# 演示用:买入 100 份沪深300ETF
|
||
$body = '{"product_code":"510300","order_side":"buy","quantity":100}'
|
||
$r = Invoke-RestMethod -Uri http://127.0.0.1:8000/api/v1/auth/tokens -Method Post `
|
||
-Body '{"username":"cust_t","password":"123456"}' -ContentType application/json
|
||
Invoke-RestMethod -Uri http://127.0.0.1:8000/api/v1/users/me/orders -Method Post `
|
||
-Headers @{ Authorization = "Bearer $($r.data.access_token)"; "Idempotency-Key" = [guid]::NewGuid().ToString("N") } `
|
||
-Body $body -ContentType application/json | ConvertTo-Json -Depth 4
|
||
```
|
||
|
||
**预期**:`status=已成交`;`executed_price` = **当时的最新行情价**(演示前先刷一次行情,
|
||
按页面/返回里的真实数字念,**别背固定价**);成交金额 = 100 × 该价格;
|
||
再扣一笔手续费(按费率规则),`net_amount` 就是实际扣款。
|
||
|
||
**这体现什么**:
|
||
- **成交价来自外部行情**,不是模拟随机数;
|
||
- 下单前会做**适当性校验**(风险等级匹配),不匹配直接拒绝;
|
||
- 成交后委托、成交、资金流水、持仓**四处同时落账**(可当场刷新场景 2 的页面看变化)。
|
||
|
||
> ⚠️ 如果返回 `503 FUND_QUOTE_UNAVAILABLE` → 跑 §0.2 的行情同步。
|
||
>
|
||
> ⚠️ 幂等键只防"同一次请求被重发",**在页面上连点两下仍会下两笔**(前端每次提交生成新键,
|
||
> 属已知前端缺口)。演示时点一次就好。
|
||
|
||
### 场景 4 · 客服对话与转人工(1 min)
|
||
|
||
| | |
|
||
|---|---|
|
||
| **操作** | 客户页右下角浮窗 → 问「基金定投是什么」→ 点「转人工客服」 |
|
||
| **预期** | 先得到知识库答案;转人工返回 `202`,工单进入管理员队列(**新建的工单状态是 `pending`**) |
|
||
|
||
**这体现什么**:客户**主动**请求人工与"答不上来"是两条不同路径 —— 前者建工单(`P2` 写操作类)。
|
||
🔁 **2026-09-19 更正(`H-04` 转人工白名单)**:"答不上来"**不再**转人工。转人工只保留 **4 类**——客户显式要求人工 / `P0` 反诈 / `P1` 账户数据 / `P2` 写操作与争议。现在的作答顺序是「先澄清(`E1`)→ 再合并同章节证据作答(`E4`)→ 不成则给部分资料 + 引导(`E5b`)」;46 条金标实测转人工率由旧实现的 **47.8% 降到 10.9%**。
|
||
|
||
> ✅ **这张工单现在可以在管理员工作台里被真正处置掉**(2026-09-14 补齐):
|
||
> 分配 → 接单 → 解决 → 关闭(未解决可取消),每一步都写审计。
|
||
> 演完场景 8 时顺手走一遍,就能把"客户请求人工 → 坐席接管 → 结案归档"讲完整。
|
||
> 状态机与权限见 §场景 8 的表格。
|
||
|
||
### 场景 5 · 风控预警处置(2 min)⭐ 重点
|
||
|
||
| | |
|
||
|---|---|
|
||
| **打开** | <http://127.0.0.1:8000/portal/employee-console/login/> → `risk_t` / `666666` |
|
||
| **预期** | 概览:未闭环预警数、高风险数、待处理、已超时(**2026-09-14 实测:未闭环 7 条 / 预警总数 32 条**) |
|
||
|
||
> 队列空或想现场造一条:页面上的**「扫描」按钮**会同步跑一次风控扫描(最长 60 秒)。
|
||
> 也可以跑 `python tools/e2e_smoke_test.py` —— 它会自动造一条待处理预警并跑完整闭环。
|
||
|
||
**操作顺序(这是核心闭环)**:
|
||
|
||
1. 点预警队列里的某条 → 打开详情;
|
||
2. 详情里有**八类证据**入口:客户、产品、交易、资金流水、持仓、登录记录、预警、通知;
|
||
3. 点「确认接收」→ **处置状态不变**,只把"确认状态"置为已确认(记录谁、何时看到了这条预警);
|
||
4. 点「进入调查」→ 处置状态:待处理 → 调查中;
|
||
5. 点「完成结案」→ 状态变为**已结案**(只有"调查中"的才能结案),并且**按风险等级扣客户行为分**:
|
||
低 3 / 中 5 / 高 20(行为分 0–20 封顶)。
|
||
|
||
> 另有两个动作可以按需插入:**关闭误报**(→ 已排除,必须填理由)与**升级处理**
|
||
> (只打"已升级"标记,**不改处置状态**)。
|
||
|
||
**这体现什么**:预警处置是**有状态机、有留痕、有连带影响**的 ——
|
||
不是改个字段。每一步都记审计(谁、何时、做了什么),结案还会影响客户行为分。
|
||
|
||
> 若队列里没有「待处理」的预警(都已被处置过),跑
|
||
> `python tools/e2e_smoke_test.py` —— 它会自动造一条待处理预警并跑完整闭环。
|
||
|
||
**补一步「风控助手」(30 秒,很值得演)**:点上方「风控助手」标签 → 点预设的
|
||
「当前风险概览」按钮 → Agent 调 `get_risk_overview` **只读工具**返回未闭环预警概览,
|
||
结尾并声明"以上仅为查询与复核线索,不构成任何已确认、误报、关闭或升级的处置结论,
|
||
最终请由风控专员人工复核并留痕"。
|
||
|
||
**这体现什么**:Agent 只做**只读查询与研判草案**,处置动作一律留给人工。
|
||
实测这条问法命中的是 `risk_overview` 意图、置信度 **1.0000** ——
|
||
是配置好的意图分派,不是让模型自由发挥。
|
||
|
||
### 场景 6 · 风控日报(1 min)
|
||
|
||
| | |
|
||
|---|---|
|
||
| **操作** | 风控工作台 → 日报 → 生成 |
|
||
| **预期** | 文本**流式**逐段出现(SSE),一共 9 段;完成后可填多个邮箱发送 |
|
||
|
||
**这体现什么**:长任务走 SSE 流式,而不是让页面转圈等 30 秒。
|
||
|
||
> 结尾会标来源:模型可用时是"大模型生成",不可用时**自动降级为规则模板并如实标注**——
|
||
> 降级不是报错,被问到就这么讲。
|
||
> ⚠️ **发送邮件默认是"演练"**:本机 SMTP 开关默认关着、dry-run 默认开着,所以流程会走完、
|
||
> 状态会返回,但**不会真的投递**。要真发得先配 SMTP。
|
||
|
||
### 场景 7 · 投顾工作台(1 min)〔🔴 **2026-09-19 起整节作废 —— 投顾模块已整体清除,不要演**〕
|
||
|
||
| | |
|
||
|---|---|
|
||
| **打开** | <http://127.0.0.1:8000/portal/employee-advisor/dashboard/> |
|
||
| **账号** | ~~`advisor_t` / `abc12345`~~ 🔴 **该角色与账号库里已不存在(投顾模块已整体清除,`D4.4`/`D4.5`)—— 不要念、不要点** |
|
||
| **预期** | 概览四张卡 + 客户区(**页面内置 4 位演示客户**:张总/李阿姨/王工/陈医生,对应真实客户号 9101–9104)+ 7 个动作按钮 + 「已发布交付物」 |
|
||
|
||
**这体现什么**:投顾看到的是**自己服务的客户**的交付物。
|
||
数据范围按 `sys_customer_assignment` 归属关系判定(不是按 `data_scope` —— 那会把 all 级权限放大成"看全部客户"),
|
||
并且有**灰度开关**:没开放的账号直接 403。
|
||
|
||
选一个客户点「生成推荐方案」,结果区会给出方案与**六步流水线**进度。
|
||
若候选池为空,页面会**把原因写清楚**(证据缺失/流动性不够/适当性不匹配/没有客户目标)+ 计数 + 两条出路。
|
||
|
||
> **审核与发布的口径(2026-09-14 更新,别再照旧话术讲)**:
|
||
> - **产品推荐方案**:**投顾可以自助审核 / 驳回 / 发布给客户**(本次已开放),管理员复核队列同样能做;
|
||
> - **投资目标方案书**:投顾做到"录入目标 → 确认目标 → 查看方案书"为止,**审核与发布仍归管理员**。
|
||
>
|
||
> 没有后端也能演:网址加 `?demo=1` 进**离线演示模式**(本地引擎、无令牌、页面明确标注
|
||
> "演示模式 · 本地引擎"、方案标"规则模拟,不构成真实推荐")。**用它时务必主动说明这是演示模式。**
|
||
|
||
### 场景 8 · 管理员治理(1.5 min)
|
||
|
||
| | |
|
||
|---|---|
|
||
| **打开** | <http://127.0.0.1:8000/portal/employee-console/workspace/> |
|
||
| **账号** | `admin_t` / `88888888` |
|
||
| **预期** | 四张指标卡(有效角色 / 生效配置版本 / 可用模型端点 / 待人工复核)+ **八个标签页** |
|
||
|
||
**八个标签页,挨个点一遍,每处一句话**:
|
||
|
||
| 标签页 | 讲解要点 |
|
||
|---|---|
|
||
| 角色与权限 | 五个角色的权限数(**2026-09-14 实测**):admin **62** / advisor **31** / customer **26** / risk_operator **10** / operator **7**;还能按用户 ID 查"服务端实际解析出来的角色与数据范围" |
|
||
| 配置与模型 | 工具白名单、提示词**走发布状态机**(草稿 → 待审核 → 已审核 → 生效,可回滚;被取代的版本落 superseded),不是改配置文件;模型路由规则也在这里 |
|
||
| 审计记录 | 每一次权限判定、工具调用都有记录;没有"看敏感详情"的权限时,详情由服务端**脱敏** |
|
||
| 转人工工单 | 场景 4 建的工单在这里(**当前库里 40+ 张,多数是冒烟测试件**);只回**二次脱敏**摘要,不返回客户标识与原始对话。**可以处置**:分配 → 接单 → 解决 → 关闭(未解决可取消),按状态给按钮、可按状态筛 |
|
||
| 画像候选 | 客服从对话里提炼的画像候选,批准后才进正式记忆 |
|
||
| 投顾复核 | 待审的投顾交付物(推荐方案 + 投资方案书)→ 审核通过 / 驳回 / 发布给客户 |
|
||
| 知识库 | 上传文档(faq / product / policy)→ 自动切分入库并投向量同步;**客服就是靠它作答**(当前 active 知识 24 份、知识元数据 199 行) |
|
||
| 画像漂移复核 | 客户重新测评导致画像变化时人工确认是否采纳 |
|
||
|
||
**这体现什么**:**权限变更必须过审核流程并留痕** —— 这是金融场景的硬要求,
|
||
所以平台刻意**不提供**"直接改权限"的接口。
|
||
|
||
> ⚠️ 三个"前端未接、后端已有"的小缺口,被问到照实说:配置发布页没有"驳回/回滚"按钮、
|
||
> 模型端点页是只读的(当前 3 个端点)、审计页没有筛选框(只看最近 20 条)。
|
||
|
||
---
|
||
|
||
## 2. 备选场景(时间充足时)
|
||
|
||
| 场景 | 怎么演 | 体现什么 |
|
||
|---|---|---|
|
||
| 配置发布四态 | 管理员工作台建一个草稿版本 → 提交校验 → 审核 → 激活 | 改配置是走流程的,可回滚 |
|
||
| 五角色权限对比 | 用五个账号分别登录,看入口守卫各跳哪里 | 前端只是体验层,真正的拦截在服务端权限码与数据范围 |
|
||
| 知识入库 | `python tools/seed_knowledge_demo.py` 后复测场景 1 | 知识是**可运营**的,不是写死的 |
|
||
| 平台体检 | `python tools/e2e_smoke_test.py` | 40 项全绿,交付质量可自证 |
|
||
| 记忆链路 | 客户连问几轮 → 看 `memory_unit` 里新增长期记忆 → Worker 投影到 Neo4j/Milvus | "记住客户"是可查的,不是模型自己说记住了。**完整演法(含一键命令与实测输出)见 `docs/演示用/记忆系统演示文档-2026-09-14.md`**;快速跑:`python tools/memory_demo_chain.py` |
|
||
| 后台排障 | 停掉 Worker → 客服变"繁忙" → 查 `agent_run.status='queued'` → 起回 Worker 自动恢复 | 异步三段式的可观测性 |
|
||
|
||
---
|
||
|
||
## 3. 可能被问到的问题(建议话术)
|
||
|
||
**Q:这是真实行情吗?**
|
||
A:行情取自公开数据源(腾讯行情),成交价用的就是它。但**交易本身是模拟的** ——
|
||
平台是"场内基金模拟交易",不下真实单。
|
||
|
||
**Q:Agent 会不会乱答?**
|
||
A:不会。客服只回答**检索到的已发布知识**,且有置信度门槛:
|
||
命中分数 ≥0.75 直接答;0.55~0.75 之间还要求领先次优 ≥0.07。
|
||
但 🔁 **2026-09-19 更正**:门槛之下**不再等于"推给人工"**(`H-01`/`H-03`/`H-04`)——顺序是「同章节多块 → 合并作答(`E4`)→ 仍不确定 → 澄清一句(`E1`)→ 再不成 → 部分资料 + 引导(`E5b`)」,**只有 4 类白名单才转人工**。"宁可转人工也不答错"的取舍仍然成立,只是转人工不再是第一选择。
|
||
|
||
**Q:为什么有些问题答不上来?**
|
||
A:知识库是人工审核入库的,目前覆盖交易规则、20 只产品、费率与常见问答。
|
||
问不到的地方**先给出能查到的部分资料并邀请您换个问法**(`E5b`),确实需要人工时才转 —— **这是设计而不是故障**。
|
||
|
||
**Q:数据安全怎么保证?**
|
||
A:三层 —— 前端按权限隐藏入口;服务端每次请求**重新解析权限与数据范围**(令牌里不带权限);
|
||
工单等管理面接口**不返回客户标识与原始对话**。所有判定都有审计。
|
||
|
||
**Q:为什么不让管理员直接改权限?**
|
||
A:金融场景要求权限变更留痕可追溯,所以统一走配置发布流程(草稿→校验→审核→激活)。
|
||
|
||
**Q:客户看板上的"今日盈亏"是怎么算的?**
|
||
A:**2026-09-14 起是真实计算的**(此前是占位 0,已修)。一句话口径:
|
||
|
||
```
|
||
今日盈亏 = 今日市值 − 昨日持仓市值 − 今日买入金额 + 今日卖出金额 (不含交易费用)
|
||
昨日持仓数量 = 今日持仓数量 − 今日买入份额 + 今日卖出份额 (由当日成交反推)
|
||
```
|
||
|
||
基准**优先用行情**(最近两个交易日收盘价,与同页"最新价/市值"同源、客户能自己核对),
|
||
行情只有一天的产品(演示数据里 `15911` / `159991-159995`)**回退用净值**。
|
||
语义是「**最近一个交易日**相对前一交易日」的盈亏,**不是盘中实时** —— 这句一定要讲,
|
||
否则会被当成 bug。
|
||
|
||
**现场可自证**:`python tools/check_today_profit_loss.py`(纯 SQL 独立复算,与接口逐只比对,
|
||
不一致会以退出码 1 结束)。实测:`cust_t` = −132.70(−0.2528%)。
|
||
细节见 `docs/演示用/今日盈亏实现说明-2026-09-14.md`。
|
||
|
||
> 💡 当天买卖会让"今日盈亏"包含当日已实现部分(卖出份额仍算"昨收→卖出价",
|
||
> 当日买入的份额只算"买入价→今收")。这是刻意的,不是算错。
|
||
|
||
---
|
||
|
||
## 4. 出问题怎么办
|
||
|
||
| 现象 | 原因 | 怎么办 |
|
||
|---|---|---|
|
||
| 客户下单返回 `503 行情已过期` | 行情快照过期(**有效期仅 15 分钟**) | `python tools/sync_market_prices.py` —— **立即生效,无需重启服务** |
|
||
| 客服一直"繁忙/超时" | **Worker 没在跑** | 看 `start.ps1` 起的第二个窗口;或 `python -m app.worker` |
|
||
| **所有页面一起变慢/卡住**(不是某一个请求超时,而是客服、风控、看板**同时**迟钝) | 检索层用的是**同步** Milvus 客户端,单次 `search` 卡住会**阻塞整个 API 进程**(连 Worker 心跳一起停)—— 已知项,见 `代码库全面审查报告-2026-09-14.md` **P1-2** | 先在 Docker Desktop 确认 Milvus 在跑;**不用改代码也不用重启** —— Milvus 恢复后自动正常。这条是「演示版本不修、但要能一眼认出」的典型:平时完全无感,只有 Milvus 抖动时才现形 |
|
||
| 改了页面看不到效果 | 浏览器缓存 | `Ctrl+F5` 硬刷新 |
|
||
| 知识库问答答不上 | 向量没同步 | 确认 Worker 在跑,然后跑 `tools/seed_knowledge_demo.py` |
|
||
| 场外发送通知"成功"但对方没收到 | **SMTP 默认是演练模式**(开关关、dry-run 开) | 这是刻意配置:要在页面上演示"发送"就走完了;要真发得先配 SMTP(见 §5 已知空数据) |
|
||
| 风控扫描报"正在扫描中/忙" | 手工扫描与定时扫描**共用同一把数据库咨询锁**,互斥 | 等这一轮结束再点(定时扫描默认是关闭的,一般遇不到) |
|
||
| 平台起不来 | MySQL 没起 | 看 `start.ps1` 的依赖检查输出 |
|
||
| 风控队列没有"待处理"预警 | 演示样本都被处置过了 | `python tools/e2e_smoke_test.py`(自动造一条) |
|
||
|
||
**一条命令定位**:
|
||
|
||
```powershell
|
||
python tools/e2e_smoke_test.py
|
||
```
|
||
|
||
它逐条打印 6 条线 40 项的结果,FAIL 的那条就是问题所在。
|
||
|
||
---
|
||
|
||
## 5. 速查
|
||
|
||
**账号**
|
||
|
||
| 角色 | 用户名 | 密码 | 登录入口 |
|
||
|---|---|---|---|
|
||
| 客户 | `cust_t` | `123456` | `/portal/customer/login/` |
|
||
| 风控专员 | `risk_t` | `666666` | `/portal/employee-console/login/` |
|
||
| 管理员 | `admin_t` | `88888888` | 同上 |
|
||
| ~~投顾~~ 🔴 **已清除** | ~~`advisor_t`~~ | ~~`abc12345`~~ | **该角色与账号库里已不存在,不要念、不要点** |
|
||
| 运营 | `offsite_t` | `offsite123` | 同上(→ 运营工作台) |
|
||
|
||
> ⚠️ **`advisor_t` 已随投顾模块整体清除(上一行作废)**;`offsite_t` 仍有效但不在 RBAC 种子脚本里(2026-09-14 补注):
|
||
> `tools/seed_test_rbac.py` 的演示用户只有 **4 个**——`cust_t`(9001)、`risk_t`(9002)、
|
||
> `admin_t`(9003)、`review_t`(9004)。`offsite_t`(9006) 由
|
||
> **`tools/grant_*.py` 系列 / 投顾线自己的脚本**创建,**重跑种子不会重建它们**。
|
||
> 换机器时若这两个账号登录失败,先查 `sys_user` 里到底有没有这两行,而不是查密码。
|
||
> (此点 `docs/40-前端验收清单.md` 已有自述。)
|
||
>
|
||
> 另注:`sys_user` 已改为「存在则更新、不存在才插入」,故**重跑种子不会弄丢演示密码**。
|
||
|
||
**常用命令**
|
||
|
||
```powershell
|
||
python tools/seed_demo_data.py # 准备演示数据(10 步,首次/换机器)
|
||
python tools/sync_market_prices.py # 刷行情(15 分钟有效期,下单 503 时补跑)
|
||
powershell -ExecutionPolicy Bypass -File start.ps1 # 起 API + Worker(自动刷行情)
|
||
python tools/e2e_smoke_test.py # 全量体检(6 条线 44 项,含工单处置闭环)
|
||
python tools/e2e_smoke_test.py --read-only # 只读体检,不动数据
|
||
python tools/check_today_profit_loss.py # 「今日盈亏」纯 SQL 复算并与接口逐只比对
|
||
python tools/portal_api_check.py # 接口契约体检(按前端的方式调,41 项)
|
||
```
|
||
|
||
**已知的空数据 / 会"看着像坏了"的地方**(演示时别点进去尴尬;**2026-09-14 实测**)
|
||
|
||
| 位置 | 现状 | 原因 |
|
||
|---|---|---|
|
||
| 运营 · 场外邮件 | **当前 4 封**(不是 0 了),但**不会有新的** | 收件依赖真实可用的邮箱配置与常驻 Worker;发件还依赖 SMTP |
|
||
| 运营 · 通知发送 | 只"演练",不真发 | SMTP 开关默认关闭 + dry-run 默认开启 |
|
||
| 管理员 · 画像候选 | 0 条 | 需要客户对话触发候选抽取,且要管理员批准才进正式记忆 |
|
||
| 投顾 · 客户列表 | 页面内置 4 位演示客户 | 前端暂无"查我的客户"接口,真实范围在服务端按归属判定 |
|
||
| 风控 · 未闭环预警 | **7 条**(总数 32) | 想造新的就点页面上的「扫描」 |
|
||
|
||
**端口**:API `8000` / MySQL `3306` / Redis `6379` / Milvus `19530` / Neo4j `7687`
|