From 42a330c4c92f93599f8566d18ef5bed1ae6b740b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=BF=E4=BA=91=E7=A7=8B=E6=9C=88?= <15273589815@163.com> Date: Tue, 15 Sep 2026 00:14:20 +0800 Subject: [PATCH] =?UTF-8?q?docs/44=20=E6=BC=94=E7=A4=BA=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E6=8C=89=202026-09-14=20=E5=AE=9E=E6=B5=8B=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=EF=BC=9A=E4=BB=8A=E6=97=A5=E7=9B=88=E4=BA=8F=E5=8F=AF=E8=AE=B2?= =?UTF-8?q?=E3=80=81=E6=8A=95=E9=A1=BE=E8=87=AA=E5=8A=A9=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E3=80=81=E7=AE=A1=E7=90=86=E5=91=98=E5=85=AB=E9=A1=B5=E7=AD=BE?= =?UTF-8?q?=E3=80=81=E6=9D=83=E9=99=90=E6=95=B0=E4=B8=8E=E5=B7=B2=E7=9F=A5?= =?UTF-8?q?=E7=A9=BA=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/44-演示流程.md | 181 +++++++++++++++++++++++++++++--------------- 1 file changed, 122 insertions(+), 59 deletions(-) diff --git a/docs/44-演示流程.md b/docs/44-演示流程.md index b10d3fd..8a66585 100644 --- a/docs/44-演示流程.md +++ b/docs/44-演示流程.md @@ -3,7 +3,16 @@ > **读者**:负责演示的人。本文按"操作 → 看到什么 → 这体现什么"三段式写, > 可以直接照着念。 > **主线时长**:约 8 分钟;含讲解约 15 分钟。 -> **全部内容均已实测**(2026-09-13);标 ⚠️ 的地方是容易翻车的点。 +> **全部内容均已实测**;标 ⚠️ 的地方是容易翻车的点。 +> +> **2026-09-14 复核**:本文档已按当天实测数据更新四处(「今日盈亏」已实现、投顾推荐方案 +> 已开放自助审核发布、管理员工作台为八个标签页、角色权限数与已知空数据)。数字类结论都标了 +> 实测日期——**换环境或换库后要重查**。 +> +> **配套文档**(想看"为什么"而不是"怎么演"就看这两份): +> - `docs/演示用/全功能流程-大白话版.md` —— 从登入讲到六个门户的全部流程,不用术语; +> - `docs/演示用/记忆架构与Worker工作流程-大白话版.md` —— 记忆链路与后台 Worker 的详细工作流程; +> - `docs/演示用/今日盈亏实现说明-2026-09-14.md` —— 「今日盈亏」的口径、数据坑与复算命令。 --- @@ -34,7 +43,7 @@ python tools/seed_demo_data.py python tools/sync_market_prices.py ``` -它按产品 upsert(幂等),几秒内把 20 只产品行情全部刷新,15 分钟窗口重新计时。 +它按产品 upsert(幂等),几秒内把**全部产品(当前库里 26 只)**行情刷新,15 分钟窗口重新计时。 > 不想让启动脚本联网刷行情:`start.ps1 -SkipPriceSync` @@ -72,9 +81,14 @@ powershell -ExecutionPolicy Bypass -File start.ps1 -Port 8100 # 换端口(80 | 窗口 | 作用 | 少了它会怎样 | |---|---|---| | API | 所有接口与页面 | 什么都没有 | -| **Worker** | Agent 对话、知识向量同步、记忆抽取、风控扫描 | 客服对话一直"超时";新知识不进 Milvus 且**无任何报错** | +| **Worker** | Agent 真正执行、领域事件投递、知识向量同步、记忆抽取与画像投影、会话片段聚合、**场外收件** | 客服对话一直"超时";新知识不进 Milvus(**无任何报错**);记忆不更新;场外收不到邮件 | > ⚠️ 两个窗口都别关。启动脚本自己的窗口跑完就可以关(服务在独立窗口里)。 +> +> 注:**风控定时扫描不在这个 Worker 里** —— 它是独立进程 +> (`python -m app.worker.risk_scan_scheduler`)且默认关闭;演示时用的是风控工作台上的 +> **「扫描」按钮**(同步执行,最长 60 秒)。 +> 详细分工见 `docs/演示用/记忆架构与Worker工作流程-大白话版.md`。 ### 0.4 开场前自检(务必做) @@ -85,7 +99,8 @@ python tools/e2e_smoke_test.py --read-only # 业务链路:登录→下单 python tools/portal_api_check.py # 接口契约:按前端的方式核对每个端点的状态码与字段 ``` -看最后一行:**40/40** 与 **41 项全通过**才开始演示。有 FAIL 就按 §4 排查。 +看最后一行:**40/40** 与 **41 项全通过**才开始演示(用例数会随开发增减, +判断健康的准则是 **0 FAIL**,不是绝对条数)。有 FAIL 就按 §4 排查。 > 两者的分工:`e2e_smoke_test.py` 回答"**这条业务走得通吗**", > `portal_api_check.py` 回答"**接口返回的东西前端能不能正确显示**"。 @@ -107,7 +122,12 @@ python tools/portal_api_check.py # 接口契约:按前端的方 |---|---| | **打开** | | | **操作** | 点右下角客服浮窗 → 输入「场内基金的管理费率是多少」→ 发送 | -| **预期** | **约 3–4 秒**返回:本平台 20 只场内基金的管理费率 0.15%~1.20%/年,并列出 ETF 与 LOF 的差异 | +| **预期** | **约 3–4 秒**返回:本平台场内基金的管理费率区间(0.15%~1.20%/年),并列出 ETF 与 LOF 的差异 | + +> ⚠️ 知识库文案是**人工审核入库时的快照**(当前 active 知识 24 份、元数据 199 行), +> 里面写的产品数量**可能与产品列表的当前数量不一致**。 +> 被问到时如实说:"这是知识库里的内容,知识是人工入库的,产品列表取的是实时数据。" +> 不要现场改知识库文案来对齐数字。 **这体现什么**:客服不是"模型随便聊",而是**检索已发布知识后作答**,答案可追溯; 延迟稳定在 3–4 秒(一次意图分类 + 一次向量检索)。 @@ -137,18 +157,25 @@ python tools/portal_api_check.py # 接口契约:按前端的方 **这体现什么**:持仓按**真实行情**计价(不是写死的假数);页面底部有数据来源说明。 -点左侧「我的持仓」「资金流水」「成交明细」各看一眼即可。 +点顶部导航「我的持仓」「资金流水」「成交明细」「盈亏分析」各看一眼即可。 -> ⚠️ **刻意不要指着"今日盈亏"讲** —— 该字段目前是**占位 0**(硬编码,见 §3 同名问答与 SRS Q22)。 -> 讲「持有盈亏」「总市值」「可用资金」这三项,它们都是真实计算的。 -> 被追问时按 §3 的话术回答,不要现编。 +> ✅ **「今日盈亏」现在可以直接讲**(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 @@ -160,15 +187,19 @@ Invoke-RestMethod -Uri http://127.0.0.1:8000/api/v1/users/me/orders -Method Post -Body $body -ContentType application/json | ConvertTo-Json -Depth 4 ``` -**预期**:`status=已成交`、`executed_price=4.579`(**真实行情价**)、 -成交金额 457.90、手续费 0.05、实际扣款 457.95(`net_amount`)。 +**预期**:`status=已成交`;`executed_price` = **当时的最新行情价**(演示前先刷一次行情, +按页面/返回里的真实数字念,**别背固定价**);成交金额 = 100 × 该价格; +再扣一笔手续费(按费率规则),`net_amount` 就是实际扣款。 **这体现什么**: - **成交价来自外部行情**,不是模拟随机数; - 下单前会做**适当性校验**(风险等级匹配),不匹配直接拒绝; -- 成交后持仓、资金流水、成交明细**三处同时落账**(可当场刷新场景 2 的页面看变化)。 +- 成交后委托、成交、资金流水、持仓**四处同时落账**(可当场刷新场景 2 的页面看变化)。 > ⚠️ 如果返回 `503 FUND_QUOTE_UNAVAILABLE` → 跑 §0.2 的行情同步。 +> +> ⚠️ 幂等键只防"同一次请求被重发",**在页面上连点两下仍会下两笔**(前端每次提交生成新键, +> 属已知前端缺口)。演示时点一次就好。 ### 场景 4 · 客服对话与转人工(1 min) @@ -185,18 +216,25 @@ Invoke-RestMethod -Uri http://127.0.0.1:8000/api/v1/users/me/orders -Method Post | | | |---|---| | **打开** | → `risk_t` / `666666` | -| **预期** | 概览:未闭环预警数、高风险数、待处理、已超时 | +| **预期** | 概览:未闭环预警数、高风险数、待处理、已超时(**2026-09-14 实测:未闭环 7 条 / 预警总数 32 条**) | + +> 队列空或想现场造一条:页面上的**「扫描」按钮**会同步跑一次风控扫描(最长 60 秒)。 +> 也可以跑 `python tools/e2e_smoke_test.py` —— 它会自动造一条待处理预警并跑完整闭环。 **操作顺序(这是核心闭环)**: 1. 点预警队列里的某条 → 打开详情; 2. 详情里有**八类证据**入口:客户、产品、交易、资金流水、持仓、登录记录、预警、通知; -3. 点「确认接收」→ 状态变为已确认; -4. 点「进入调查」→ 状态变为调查中; -5. 点「完成结案」→ 状态变为**已结案**,并且**按风险等级扣减客户行为分**。 +3. 点「确认接收」→ **处置状态不变**,只把"确认状态"置为已确认(记录谁、何时看到了这条预警); +4. 点「进入调查」→ 处置状态:待处理 → 调查中; +5. 点「完成结案」→ 状态变为**已结案**(只有"调查中"的才能结案),并且**按风险等级扣客户行为分**: + 低 3 / 中 5 / 高 20(行为分 0–20 封顶)。 + +> 另有两个动作可以按需插入:**关闭误报**(→ 已排除,必须填理由)与**升级处理** +> (只打"已升级"标记,**不改处置状态**)。 **这体现什么**:预警处置是**有状态机、有留痕、有连带影响**的 —— -不是改个字段。每一步都记审计(谁、何时、做了什么)。 +不是改个字段。每一步都记审计(谁、何时、做了什么),结案还会影响客户行为分。 > 若队列里没有「待处理」的预警(都已被处置过),跑 > `python tools/e2e_smoke_test.py` —— 它会自动造一条待处理预警并跑完整闭环。 @@ -215,24 +253,36 @@ Invoke-RestMethod -Uri http://127.0.0.1:8000/api/v1/users/me/orders -Method Post | | | |---|---| | **操作** | 风控工作台 → 日报 → 生成 | -| **预期** | 文本**流式**逐段出现(SSE),完成后可填多个邮箱发送 | +| **预期** | 文本**流式**逐段出现(SSE),一共 9 段;完成后可填多个邮箱发送 | **这体现什么**:长任务走 SSE 流式,而不是让页面转圈等 30 秒。 +> 结尾会标来源:模型可用时是"大模型生成",不可用时**自动降级为规则模板并如实标注**—— +> 降级不是报错,被问到就这么讲。 +> ⚠️ **发送邮件默认是"演练"**:本机 SMTP 开关默认关着、dry-run 默认开着,所以流程会走完、 +> 状态会返回,但**不会真的投递**。要真发得先配 SMTP。 + ### 场景 7 · 投顾工作台(1 min) | | | |---|---| | **打开** | | | **账号** | `advisor_t` / `abc12345` | -| **预期** | 一张「投资目标方案书 · 客户 9001」卡片,含发布时间与方案内容 | +| **预期** | 概览四张卡 + 客户区(**页面内置 4 位演示客户**:张总/李阿姨/王工/陈医生,对应真实客户号 9101–9104)+ 7 个动作按钮 + 「已发布交付物」 | -**这体现什么**:投顾看到的是**自己服务的客户**的已发布交付物。 -数据范围按 `sys_customer_assignment` 归属关系判定(不是按 `data_scope` —— -那会把 all 级权限放大成"看全部客户")。 +**这体现什么**:投顾看到的是**自己服务的客户**的交付物。 +数据范围按 `sys_customer_assignment` 归属关系判定(不是按 `data_scope` —— 那会把 all 级权限放大成"看全部客户"), +并且有**灰度开关**:没开放的账号直接 403。 -> 方案书的**审核与发布都要求管理员**(`admin=True`),投顾自己发不出来。 -> 这是有意的复核环节,不是缺陷。 +选一个客户点「生成推荐方案」,结果区会给出方案与**六步流水线**进度。 +若候选池为空,页面会**把原因写清楚**(证据缺失/流动性不够/适当性不匹配/没有客户目标)+ 计数 + 两条出路。 + +> **审核与发布的口径(2026-09-14 更新,别再照旧话术讲)**: +> - **产品推荐方案**:**投顾可以自助审核 / 驳回 / 发布给客户**(本次已开放),管理员复核队列同样能做; +> - **投资目标方案书**:投顾做到"录入目标 → 确认目标 → 查看方案书"为止,**审核与发布仍归管理员**。 +> +> 没有后端也能演:网址加 `?demo=1` 进**离线演示模式**(本地引擎、无令牌、页面明确标注 +> "演示模式 · 本地引擎"、方案标"规则模拟,不构成真实推荐")。**用它时务必主动说明这是演示模式。** ### 场景 8 · 管理员治理(1.5 min) @@ -240,21 +290,27 @@ Invoke-RestMethod -Uri http://127.0.0.1:8000/api/v1/users/me/orders -Method Post |---|---| | **打开** | | | **账号** | `admin_t` / `88888888` | -| **预期** | 四个指标卡 + 六个标签页 | +| **预期** | 四张指标卡(有效角色 / 生效配置版本 / 可用模型端点 / 待人工复核)+ **八个标签页** | -**挨个点一遍,每处一句话**: +**八个标签页,挨个点一遍,每处一句话**: | 标签页 | 讲解要点 | |---|---| -| 角色权限 | 五个角色,权限数分别是 admin 59 / advisor 28 / customer 26 / risk_operator 10 / operator 2 | -| 配置发布 | 工具白名单、提示词**走发布状态机**(校验→审核→激活),不是改配置文件 | -| 审计 | 每一次权限判定、工具调用都有记录 | -| 转人工工单 | 场景 4 建的工单在这里;**不返回客户标识与原始对话**(脱敏) | -| 模型端点 | 当前用 `deepseek-flash`(意图分类)+ `qwen` 向量(embedding) | +| 角色与权限 | 五个角色的权限数(**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. 备选场景(时间充足时) @@ -262,9 +318,11 @@ Invoke-RestMethod -Uri http://127.0.0.1:8000/api/v1/users/me/orders -Method Post | 场景 | 怎么演 | 体现什么 | |---|---|---| | 配置发布四态 | 管理员工作台建一个草稿版本 → 提交校验 → 审核 → 激活 | 改配置是走流程的,可回滚 | -| 五角色权限对比 | 用五个账号分别登录,看入口守卫各跳哪里 | 越权在**前端就被挡**,服务端还有一层 | +| 五角色权限对比 | 用五个账号分别登录,看入口守卫各跳哪里 | 前端只是体验层,真正的拦截在服务端权限码与数据范围 | | 知识入库 | `python tools/seed_knowledge_demo.py` 后复测场景 1 | 知识是**可运营**的,不是写死的 | | 平台体检 | `python tools/e2e_smoke_test.py` | 40 项全绿,交付质量可自证 | +| 记忆链路 | 客户连问几轮 → 看 `memory_unit` 里新增长期记忆 → Worker 投影到 Neo4j/Milvus | "记住客户"是可查的,不是模型自己说记住了(见 `docs/演示用/记忆架构与Worker工作流程-大白话版.md`) | +| 后台排障 | 停掉 Worker → 客服变"繁忙" → 查 `agent_run.status='queued'` → 起回 Worker 自动恢复 | 异步三段式的可观测性 | --- @@ -290,27 +348,25 @@ A:三层 —— 前端按权限隐藏入口;服务端每次请求**重新解 **Q:为什么不让管理员直接改权限?** A:金融场景要求权限变更留痕可追溯,所以统一走配置发布流程(草稿→校验→审核→激活)。 -**Q:客户看板上的"今日盈亏"为什么是 0?** -A:**这项本期是占位值,不是真实算出来的**,不回避这一点。 -代码里 `today_profit_loss` / `today_profit_loss_ratio` 目前是**硬编码 `ZERO`** -(`app/service/trade_service.py` 的持仓列表 L678、账户看板 L748–749); -而它旁边的**"持有盈亏"是真算的**(`market_value − cost_amount`,L660 / L726)。 -所以「持有盈亏 / 总市值 / 可用资金」可以照着讲,**"今日盈亏"不要当成真实数字念**。 +**Q:客户看板上的"今日盈亏"是怎么算的?** +A:**2026-09-14 起是真实计算的**(此前是占位 0,已修)。一句话口径: -是否本期实现**已列为待业务确认项**(`docs/软件需求文档-2026-09-14.md` **Q22**), -并且已经实测过两条口径的可行性: +``` +今日盈亏 = 今日市值 − 昨日持仓市值 − 今日买入金额 + 今日卖出金额 (不含交易费用) +昨日持仓数量 = 今日持仓数量 − 今日买入份额 + 今日卖出份额 (由当日成交反推) +``` -| 口径 | 数据现状 | 结论 | -|---|---|---| -| 用行情算(今收 − 昨收) | `fin_market_price` **只在同步时才写行**,实测每个产品**只有 2 行** | ❌ 取不到连续"昨收" | -| 用净值算(今净值 − 上一交易日净值) | `fin_nav_history` 每个产品 **120~160 行连续交易日净值** | ✅ 建议按这个口径做 | +基准**优先用行情**(最近两个交易日收盘价,与同页"最新价/市值"同源、客户能自己核对), +行情只有一天的产品(演示数据里 `15911` / `159991-159995`)**回退用净值**。 +语义是「**最近一个交易日**相对前一交易日」的盈亏,**不是盘中实时** —— 这句一定要讲, +否则会被当成 bug。 -> ⚠️ 同时要讲清语义:净值是**日终**数据,所以它其实是"**最近一个交易日**的盈亏", -> **不是盘中实时** —— 这一点业务确认时要一并定下来。 -> -> 💡 时间紧、不想被追问的话:**场景 2 讲解时避开"今日盈亏"这一栏**, -> 只讲持有盈亏、总市值、可用资金。万一被直接问到,就按上面这段答, -> **不要含糊过去也不要现编** —— 它属于"已知且已登记的范围边界",不是缺陷。 +**现场可自证**:`python tools/check_today_profit_loss.py`(纯 SQL 独立复算,与接口逐只比对, +不一致会以退出码 1 结束)。实测:`cust_t` = −132.70(−0.2528%)。 +细节见 `docs/演示用/今日盈亏实现说明-2026-09-14.md`。 + +> 💡 当天买卖会让"今日盈亏"包含当日已实现部分(卖出份额仍算"昨收→卖出价", +> 当日买入的份额只算"买入价→今收")。这是刻意的,不是算错。 --- @@ -323,6 +379,8 @@ A:**这项本期是占位值,不是真实算出来的**,不回避这一点 | **所有页面一起变慢/卡住**(不是某一个请求超时,而是客服、风控、看板**同时**迟钝) | 检索层用的是**同步** Milvus 客户端,单次 `search` 卡住会**阻塞整个 API 进程**(连 Worker 心跳一起停)—— 已知项,见 `docs/演示用/代码库全面审查报告-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`(自动造一条) | @@ -363,15 +421,20 @@ python tools/e2e_smoke_test.py 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 # 全量体检(40 项) +python tools/e2e_smoke_test.py # 全量体检(6 条线 40 项) 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 实测**) -| 位置 | 现状 | -|---|---| -| 运营工作台 | 场外邮件 0 条、邮箱未初始化(需要真实邮件源) | -| 管理员 · 画像候选 | 0 条(需要客户对话触发画像抽取) | +| 位置 | 现状 | 原因 | +|---|---|---| +| 运营 · 场外邮件 | **当前 4 封**(不是 0 了),但**不会有新的** | 收件依赖真实可用的邮箱配置与常驻 Worker;发件还依赖 SMTP | +| 运营 · 通知发送 | 只"演练",不真发 | SMTP 开关默认关闭 + dry-run 默认开启 | +| 管理员 · 画像候选 | 0 条 | 需要客户对话触发候选抽取,且要管理员批准才进正式记忆 | +| 投顾 · 客户列表 | 页面内置 4 位演示客户 | 前端暂无"查我的客户"接口,真实范围在服务端按归属判定 | +| 风控 · 未闭环预警 | **7 条**(总数 32) | 想造新的就点页面上的「扫描」 | -**端口**:API `8000` / MySQL `3306` / Redis `6379` / Milvus `19530` +**端口**:API `8000` / MySQL `3306` / Redis `6379` / Milvus `19530` / Neo4j `7687`