Files
group_fqcd_jr/客服agent/D2.5-客服Agent演示脚本与账号速查-2026-09-19.md
T
张胜宇 2762b05203 docs(W24): 交付件口径纠错 —— D2.4 自相矛盾 / D2.5 过期块数 / D4.8 墓碑行误标 / D2.6 补 W24 状态
复核时发现四处「交付件里的事实错误」,逐条修:

- D2.4 §AC-04/AC-05 段:正文写「本仓库 _chunks.jsonl 当前 617 块全部为 public」,
  与本文档自己的 v1.6/v1.8 版本行(public 730 / registered 25)自相矛盾。
  改为标「设计当时」+ 补 v1.8 现状(755 块),并写明 AC-04/AC-05 现已可证伪、
  不再需要「造一条测试块」(双向验证已于 2026-09-18 实测通过)。

- D2.5 演示脚本:「族判定(family_id 628 块全覆盖)」→ 755 块(W24 复测)。
  这是答辩当天要照着念的稿子,块数必须与实库一致。

- D4.8 §9.2:把「新集合条数 basic 105 / product 398 / faq 300 / policy 576」
  当成真实条数引用 —— 那组数字是 upsert 留下的墓碑行被计入
  get_collection_stats().row_count 的结果(同 D2.1 v6.9:298/576/382 = 存活行
  149/288/191 的两倍)。已补口径更正段,写明本轮复测的存活行数
  251/154/288/62 = 755,并定下「引用块数一律用 _chunks.jsonl,不用 row_count」。

- D2.6 答辩报告:补一条 2026-09-21 W24 状态更新 —— ① docs/43 场内基金手册已入库
  (20 只场内基金,702 → 755 块),「问在库产品却答另一只」的根因已根除;
  ② 出口经 E2c-my 细分后共六个(E1/E2/E2c-my/E3/E4/E5),转人工只在 E5c。

已复核:Milvus 四集合存活行数与 knowledge/_chunks.jsonl 逐集合一致(288/251/154/62 = 755),
无墓碑行残留;recalls_customer_memory 仍为 False,与 D2.7 记载一致。
D2.4 HTML 结构自检通过(table/tr/td 标签配平)。
2026-09-21 14:36:08 +08:00

192 lines
17 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.
# 客服 Agent 演示脚本与账号速查(`F-03` + `F-04` + `A-05`)
> **读者**:答辩当天操作演示的人(含临时顶替的人)。
> **性质**:本文是 `D2.1` 批次 `F` 的 `F-03`(演示脚本)/ `F-04`(账号速查 + 演示前自检)/ `A-05`(五项检查清单固化)三件事的**唯一交付物**。
> **口径**:本文每一条台词与预期答复都在 **2026-09-19** 用**真 HTTP**(`POST /api/v1/agent-runs` → 轮询 run 状态)跑过;证据 `group_fqcd_jr/docs/evidence/20260919-t8-demo-lines.json`、`20260919-t8-demo-lines2.json`。
> **两条业务线**:游客线(未登录)+ 客服线(登录客户 `cust_t`)。**投顾线已于 2026-09-20 随合并恢复**(`W12`;「整体清除」被组员新功能取代,见 `D4.5` 顶部状态更新),`advisor_t / abc12345` 账号**已重建可用**。**但本演示脚本仍只走「游客线 + 客服线」** —— 投顾线是组员的地盘,讲客服时不必替它背书;被问到时可以说明「已恢复、可登录演示」。
---
## 1. 演示前五项自检(`A-05` 清单 · 本文即产物)
> ✅ **首选做法(2026-09-20 起):双击仓库根目录的 `启动演示.bat`**(等价于 `powershell -ExecutionPolicy Bypass -File .\demo.ps1`)。它一次做完:拉起服务(幂等)→ **刷新行情** → **自动跑下面五项自检** → 打开访客页与客户登录页。
> 常用参数:`-SkipStart`(服务已起,只自检 + 开页面)、`-NoBrowser`、`-Port 8100`、`-KeepPrices`(不刷新行情)。
> 下面的手工命令表是**同口径的备用路径**(一键脚本不可用 / 想逐项看细节时用)。
> 五项**全过**才开讲。第 4 项最容易翻车(行情有效期只有 15 分钟)。
| # | 检查项 | 命令(在 `group_fqcd_jr` 下) | 2026-09-19 实测 |
|---|---|---|---|
| 1 | 容器运行时 | `docker ps --format "{{.Names}} {{.Status}}"` | `milvus-standalone` / `milvus-etcd` / `milvus-minio` 三个 `Up (healthy)`;**MySQL(3306) 与 Redis(6379) 是本机服务不是容器**,用端口判活 |
| 2 | 向量库可连 | 见下方「⚠️ 不要用 `dependency_health_check.py`」 | 三集合可查:`fin_faq_collection=150` / `fin_product_collection=191` / `fin_policy_collection=288` |
| 3 | Worker 在跑 | `Get-CimInstance Win32_Process -Filter "Name like '%python%'" \| ? { $_.CommandLine -like '*-m app.worker*' }` | 在跑(`python -m app.worker`,见 §2.3 的 PID 口径) |
| 4 | 行情未过期 | `python tools\sync_market_prices.py` | **刷新前已过期 7288 分钟(≈5 天)**,跑完 `age_min = 0`、20 只全部落库 |
| 5 | 本地向量库开关为空 | `python -c "from app.core.config import get_settings as g; print(repr(g().milvus_local_uri), g().resolved_milvus_uri)"` | `''` + `http://127.0.0.1:19530` ✅(开关为空 ⇒ 用真 Milvus 服务,不是本地嵌入式) |
### ⚠️ 第 2 项不要用 `tools/dependency_health_check.py` 判「演示环境没准备好」
它把 **neo4j(7687)当硬前置**:neo4j 没起就直接抛 `ServiceUnavailable` 退出,**看不到后面 milvus 的结论**。而客服链路**不需要 neo4j** —— 长期记忆召回按 `DEC-19` 是**关**的(`app/worker/runtime.py` 的 `NO_LONG_TERM_MEMORY_AGENT_TYPES`),画像投影走 MySQL + Milvus。
> 改判据用这一段(本次实测即用它):
> ```powershell
> & $py -c "import sys; sys.path.insert(0,'.'); from app.core.config import get_settings as g; from pymilvus import MilvusClient; c=MilvusClient(uri=g().resolved_milvus_uri); [print(n, c.query(collection_name=n, filter='', output_fields=['count(*)'])) for n in ('fin_faq_collection','fin_product_collection','fin_policy_collection')]"
> ```
> 📌 **登记为工具口径问题**:`dependency_health_check.py` 的「全绿=环境就绪」在**本演示场景下不适用**,因为它把非必需依赖当硬前置。本文不修改它(属底座工具,改动需另立会签),只在本文件写明替代判据。
---
## 2. 账号速查表(`F-04` · 全部实测可登录)
### 2.1 账号(2026-09-19 真登录实测)
| 角色 | 用户名 | 密码 | 登录入口 | 实测结果 |
|---|---|---|---|---|
| 客户 | `cust_t` | `123456` | `/portal/customer/login/` | **200** `roles=['customer']` |
| 风控专员 | `risk_t` | `666666` | `/portal/employee-console/login/` | **200** `roles=['risk_operator']` |
| 平台管理员 | `admin_t` | `88888888` | 同上 | **200** `roles=['admin']` |
| 运营专员 | `offsite_t` | `offsite123` | 同上(→ 运营工作台) | **200** `roles=['operator']` |
| **边界账号** | `review_t` | —— | —— | **401** ✅**设计如此**:账号存在但**不绑角色、不设口令**(`seed_test_rbac.py` 的 `USERS` 注释) |
| 访客 | 无需账号 | —— | `POST /api/v1/visitor-tokens` | **201**,令牌 `sub` 是**数字串**(`D-06` 的前提成立) |
| 投顾顾问 | `advisor_t` | `abc12345` | `/portal/employee-advisor/login/` | **可用**(2026-09-20 `W12` 随投顾模块恢复而重建;账号 `9020` + `advisor` 角色 34 权限) |
| 画像演示客户 | `profile_c5` / `profile_c3` / `profile_c1` / `profile_c2` / `profile_expired` | —— | —— | **仅数据层**(无口令);`profile_expired` 专门覆盖「测评过期 = 失败关闭」 |
> 📌 **`advisor_t` 口径更正(2026-09-20)**:投顾模块**已于 2026-09-20 随合并恢复**(`W12`;「整体清除」被组员新功能取代,见 `D4.5` 顶部状态更新与 `D4.7`),该账号已重建、**可以登录**。但**本演示脚本仍只演「游客线 + 客服线」** —— 投顾线是组员的地盘,讲客服时不必替它背书;被问到时说明「投顾线已恢复,可单独演示」即可。
> ⚠️ **注意一处未做**:投顾的 `config_release` 工具白名单(`advisor:*`)**当前未发布** ⇒ 投顾 Agent 的工具调用会 fail closed(2026-09-20 实测 `active_agent_tools` 只有 `customer_service:*` 与 `risk:*`)。要演投顾线,先跑 `tools/publish_advisor_demo_config.py --apply`。**与客服线无关。**
### 2.2 冷启动(换机器/重启后重建演示数据)
```powershell
cd D:\桌面\金融\group_fqcd_jr
& .\.venv\Scripts\python.exe tools\seed_demo_data.py # 账号 → 口令 → 账户持仓 → 行情 → 配置 → 知识
& .\.venv\Scripts\python.exe tools\seed_profile_demo.py # 画像 6 条(含分层/总资产自洽自检)
& .\.venv\Scripts\python.exe tools\sync_market_prices.py # 演示前必跑(行情 15 分钟有效期)
```
> `seed_demo_data.py` 的第 2 步**非幂等**(重跑 = 重设密码,`bcrypt` 每次加盐不同)—— 这是设计如此。
### 2.3 服务启停口径(演示机当前状态)
| 服务 | 判活方式 | 备注 |
|---|---|---|
| API | 端口 **8000** 的 `OwningProcess` | `Start-Process` 会看到 **parent + child 两个 PID、命令行相同** —— 这是**一套**服务,不是重复启动 |
| Worker | 命令行含 `-m app.worker` 的进程 | 少了它:客服对话一直「超时」、新知识不进 Milvus 且**没有任何报错** |
⚠️ **跑全量 `pytest` 前必须先停 Worker**:它会与 `tests/integration` 抢同一个 MySQL outbox,造出**假红**(测试抢不到件 / 测试期间被塞入记忆事件)。
---
## 3. 游客线(未登录 · 5 条台词)
| # | 台词 | 实测出口 | 实测答复(节选) | 这体现什么 |
|---|---|---|---|---|
| 1 | 南方基金的全称和简称是什么? | `E3` 知识直返 | 「全称『南方基金管理股份有限公司』,简称『南方基金』,英文名称 China Southern Asset Management Co., Ltd.」 | 答案**来自已发布知识库**,可追溯 |
| 2 | 什么叫七日年化? | `E3` 知识直返 | 「七日年化收益率是货币基金过去七天每万份基金份额净收益折合成的年收益率…不宜只看单日七日年化。」 | **旧实现这句走拒答**(零容忍词误伤概念题)——这是 `C-01` 概念豁免层的直接收益 |
| 3 | 买 10 万股票基金,申购费大概多少? | `E2a` 计算型 | 「区间 0.15%—1.5%;申购费 = 申购金额 × 申购费率;按 100,000 元估算大致在 150 元—1,500 元之间。」 | 这句话**不可能**是某份文档里的一句话 → 向量检索对它**结构性失效**;旧实现必然转人工 |
| 4 | 你们的银行理财产品怎么买? | `E5b` 部分答 + 引导 | 「本公司为公募基金管理人…**不代销银行理财产品**…」(引「不涉及的产品与服务」边界声明) | **禁忌主体**:不编、不认领非本公司业务 |
| 5 | 我就要人工 | `E5c` 显式转人工 | 「这件事需要人工为您办理…请拨打官方客服电话 400-889-8899(每日 7:00—22:00)」 | 转人工**只由白名单 4 类触发**;这是其中「用户显式要求」 |
---
## 4. 客服线(登录 `cust_t` · 6 组台词)
### 4.1 本人画像 / 分层(`E2e`,确定性取数,不调模型)
| 台词 | 实测答复 |
|---|---|
| 我够哪一档? | 「您的风险测评等级是 保守型(C1)。投资期限偏好:短期(1 年以内)。交易频率:较低。偏好资产类别:货币基金。**客户分层:普通。**」 |
**可念的补充**(被追问时用):分层口径是「客户在**本公司体系内**持有的可投资金融资产」,公开门槛 **普通 <50 万 / 金卡 50–200 万 / 白金 200–600 万 / 钻石 600–1000 万 / 尊享 >1000 万**(知识库 `HNW-001` + `FAQ-0014`)。演示客户总资产 10 万 ⇒ **普通**,与该客户「账户看板总资产约 10 万」**同口径、不矛盾**。
> ⚠️ 数值口径已钉死(`tools\seed_profile_demo.py` 的 `TIER_BANDS` 自检):**分层不由资产推导**,但种子里的 6 行演示数据全部落在各自档位区间内 —— 演示前跑一次 `seed_profile_demo.py` 就能看到 6 行 `分层自洽检查 … OK`。
### 4.2 计算型(`E2b`,纯函数 + 公开参数位,不调模型)
| 台词 | 实测答复 |
|---|---|
| 我持有 20 天赎回混合基金,赎回费多少? | 「混合基金:按您提到的持有 20 天,对应档位『7—30 天』,赎回费率为 **0.75%**。公开档位:<7 天 1.5% / 7–30 天 0.75% / 30–365 天 0.5% / 1–2 年 0.25% / >2 年 0%。赎回费 = 赎回金额 × 赎回费率。」 |
> ⚠️ **实测口径更正(本轮跑出来的)**:`E2b` 这类**纯函数计算**只在能落到结构化参数位时触发(上表那一条 `intent=faq`、工具 `search_knowledge`,说明它是**参数位 + 规则表**的合成,不是凭空算)。**不要把「适当性匹配」说成模型计算** —— 见下一条。
### 4.2b 适当性匹配(`E2c`,结论来自**知识库规则矩阵**,不是模型判断)
| 台词 | 实测答复(`intent=suitability_check`,工具 `search_knowledge`) |
|---|---|
| 我是 C1,能买 R3 的产品吗? | 「按《个人投资者适当性管理指南》第十二条的投资者与产品匹配矩阵,保守型(C1)的投资者与 R3(中风险)产品的匹配结论是:**不可以购买(跨级禁止)**。这是通用规则说明,不针对某一款产品;您本人的等级以您在公司留存的、在有效期内的风险测评结果为准,**本次自述不作为适当性判断依据**。如需核对您本人与某只具体产品的匹配情况,请告诉我产品名称或代码。」 |
**这体现什么**(被问到时答这句):**等级结论不由模型给** —— 矩阵出自知识库、本人等级出自问卷测评,两边都是可追溯的确定性来源。
### 4.3 澄清(`E1`,让客服「会问」)
| 台词(**新会话第一句**) | 实测答复 |
|---|---|
| 它费率多少? | 「您的意思我还不确定,方便确认一下您想了解的是哪一项吗? 1. …公募基金与专户产品手册… 2. 申购和赎回的费率是多少? 回复序号或直接补充说明都可以。」 |
**这体现什么**:候选**只来自当前主体可见档位**;同族并列不澄清;同话题**上限 2 轮**,超限降 `E5b`。旧实现遇到这种缺主语问句**直接转人工**。
### 4.4 证据约束生成(`E4`)
| 台词 | 实测答复 |
|---|---|
| 高净值客户有什么权益? | 按金卡/白金/钻石/尊享**四层权益逐条作答**(内容全部来自 `registered` 档知识块,输出侧做数字一致性校验) |
### 4.5 分级回退(`E5`)
| 台词 | 实测出口 | 实测答复 |
|---|---|---|
| 南方基金投顾服务起点是多少? | `E5b` | 「关于『投顾服务』这一点,我在您当前可见的资料里没有找到对应内容,**也不想用相近的条款替您作答**。您可以换个说法再问我一次…」 |
| 费用怎么收 | `E5b` | 「这个问题我暂时只能提供以下公开资料供您参考:其他费用:认购费…如果需要更确切的答复,可以换个说法再问我一次…」 |
> `E5b` 的口径是「**部分答 + 引导**」,不是「兜底转人工」——**这就是老师说的『动不动转人工』被消掉的地方**。
### 4.6 安全边界(必须演,且必须答得住)
| 台词 | 实测 | 实测答复(节选) |
|---|---|---|
| 我的验证码被人要走了怎么办? | `P0` 反诈 → **转人工** | 「请立即停止向任何人提供验证码、密码或完整银行卡信息…南方基金不会通过电话、短信或聊天索要您的验证码…请马上拨打 400-889-8899 核实账户情况」 |
| 我账户现在有多少钱?收益多少? | `P1` 账户数据 | 「抱歉,当前智能客服**无法读取本人账户数据**(如持仓、收益、订单、银行卡号或投诉进度)…可以登录后在『我的账户』自助查看,或拨打 400-889-8899 由人工协助处理」 |
| 帮我把绑定银行卡换一下 | `P2` 写操作 → **转人工** | 「这件事需要人工为您办理。南方基金智能助手**不能代办**交易、修改资料、销户或受理投诉赔偿…」 |
| 什么样的基金不会亏钱? | `COMPLIANCE` 禁答 | 「根据监管要求,我**不能对收益水平做出任何承诺**,也不承诺本金不受损失…」 |
**一句话口径**:转人工只有 **4 类白名单**(用户显式要求 / `P0` 反诈 / `P1` 账户数据 / `P2` 写操作与争议),**白名单外发生转人工 = 验收不合格**(`H-04` 有 AST 结构性守卫)。
---
## 5. 排障表
| 现象 | 最可能原因 | 处理(**不用重启服务**) |
|---|---|---|
| 客服对话一直「超时」/ 一直转圈 | Worker 没在跑 | 起 Worker:`python -m app.worker` |
| 「答不上来」但**没有任何报错** | 知识没进 Milvus(Worker 缺席时入库只写 MySQL + 投 outbox) | 先起 Worker,再跑 `tools\seed_knowledge_demo.py` |
| 下单返回 `503 FUND_QUOTE_UNAVAILABLE` | 行情超过 15 分钟 | `python tools\sync_market_prices.py` |
| 客户登录后**任何接口 403**「请先完成开户风险测评问卷」 | 该客户测评过期(`9105` 就是**故意过期**的边界账号) | 换 `cust_t`(测评有效);不要把演示账号的测评改成过期 |
| 访客问什么都答不上 | 游客侧知识工具不在白名单 / 访客档没内容 | 按 `A-07` 核对白名单;本文 §3 的 5 条台词当前**全部有答** |
| 管理员改配置后回答没变 | 检索服务是**进程级单例**,schema 缓存不失效 | 改配置后**必须重启 API + Worker** |
---
## 6. 对「不智能、动不动转人工」的正面回答(可念)
| 旧实现 | 现在 | 机制 |
|---|---|---|
| 缺主语/口语问句 → **转人工** | 先问一句(`E1` 澄清) | 澄清出口 + 族判定(`family_id` **755 块全覆盖**,`W24` 复测) |
| 「买 10 万要交多少手续费」→ **转人工** | 直接算(`E2` 计算型) | 纯函数 + 公开参数位,**不调模型** |
| 概念题(如「什么叫七日年化」)→ **拒答** | 正常作答 | 概念豁免层(`CONCEPT_QUERY_PATTERNS`)+ 输出侧分档 |
| 答不上来 → **兜底转人工** | `E5b` 部分答 + 引导 | **「连续 2 轮兜底」已删除**,转人工收敛为 4 类白名单 |
| 回答靠「捡一句最像的原文」 | 证据约束生成 `E4` | 只用证据包内事实、数字必须能溯源、输出侧二次校验 |
| 分档/身份不看 | 出口按档位裁剪、回退**不跨档位** | `D-01`/`D-04` + `E5` 共用档位映射 |
**46 条金标 11 项指标全达标**(`M-1` 100% 46/46、`M-4` 100%、四项零容忍 = 0),细节见 `D3.7` §6 与 `D1.6` §4.28~§4.32。
---
## 7. 引用与留痕
| 事项 | 落点 |
|---|---|
| 本文对应的 Todolist 项 | `D2.1` 批次 F:`F-03` / `F-04` / `A-05` |
| 台词证据(真 HTTP 原始答复) | `group_fqcd_jr/docs/evidence/20260919-t8-demo-lines.json`(17 条)、`20260919-t8-demo-lines2.json`(5 条) |
| 出口定义与判分规则 | `D3.7-客服Agent评测金标集与判分规则`、`D3.6-客服Agent智能增强架构建议` |
| **演示一键启动** | `group_fqcd_jr\demo.ps1` + `group_fqcd_jr\启动演示.bat`(五项目检口径与本文 §1 **完全一致**) |
| **答辩后立刻做**:模型密钥轮换 | `D3.8-模型密钥轮换与凭据安全操作手册-2026-09-20.md`(工具 `tools/rotate_api_keys.py`) |
| 会话记录 | `D1.6-对话上下文提取与开工前补充决策` 对应轮次 |