Files
group_fqcd_jr/demo.ps1
T
张胜宇 29da85d010 docs(W24): 交付前复核补正 + 场内基金演示线 + 一键启动端到端实跑
按甲方「按你建议的来 一次性搞定上边的建议」执行七项。

文档事实纠错(4 处,逐条实测核对后改)
- D2.4 §AC-04/AC-05:「当前 617 块全部为 public」与本文档自己的 v1.6/v1.8
  版本行(public 730 / registered 25)自相矛盾 ⇒ 标「设计当时」+ 补 v1.8 现状
  (755 块),写明 AC-04/AC-05 现已可证伪、不再需要「造一条测试块」
- 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)。已补口径更正段,定规:引用块数一律用 _chunks.jsonl
- D2.6 答辩报告:补 W24 状态 —— docs/43 已入库(702 → 755 块);出口经
  E2c-my 细分后共六个,转人工只在 E5c

演示脚本增强
- D2.5 新增 §4.7 场内基金演示线:5 条台词逐条真 HTTP 实测
  · 场内基金有哪些?             → 20 只清单(13 ETF + 7 LOF)
  · 科创债ETF南方怎么样?        → 单只产品行(R2 / 净值 101.8009)
  · 南方金利定开债券A的管理费率? → 0.50%/年 + 0.15%/年
  · 场内基金报价的最小变动单位?  → 0.001 元
  · 沪深300ETF怎么样?           → E4 生成式作答,且明示「非本公司发行」
  并附修复前/后对比表(修复前:命中另一只产品 / 返回 20 只整张表)
- demo.ps1 自检 2:补 fin_basic_collection ⇒ 四个集合计数(154/251/288/62=755)

_build 处置(不删,改为「把废弃做成一眼可见」)
- 与 D1.6 §4.22 四 已记录的裁定冲突(理由:删掉等于删留痕,也删掉
  「为什么不能重跑」的证据)⇒ 不删
- 三个正文源 _body_kb/_body_plan/_body_requirements.html 顶部加红框
  「已过期」横幅,逐文件列出已核实的滞后项(617 块全 public / 旧品牌
  XX科技·400-XXX·nanfangwm ×5 / 48 条·51 项·7 个批次 / US-CS-08·FR-CS-023)
- _build\README 追加「五、2026-09-21 补记」

端到端实跑(冷启动,非 -SkipStart)
- 停掉全部服务 → 启动演示.bat(demo.ps1 -NoBrowser)⇒ 五项自检全过、rc=0
- 8 个入口 URL 全部 200(portal 首页 / 访客页 / 客户登录 / 员工登录 /
  投顾工作台 / docs / openapi.json / health)
- 访客 token 真对话「科创债ETF南方怎么样?」⇒ succeeded、transfer=False、答单只产品

落档
- D2.1 v6.39 追加「交付前文档复核补正」表(7 项)+ 端到端实跑结论
- D1.6 §11.2 补 W24-8 / W24-9;§11.5 补第 5、6 条
2026-09-21 14:51:32 +08:00

221 lines
10 KiB
PowerShell
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.
<#
.SYNOPSIS
客服 Agent 演示 · 前端一键启动(服务拉起 + 五项自检 + 打开演示页面)
.DESCRIPTION
真正的服务拉起交给同目录的 `start.ps1`(**幂等**:缺什么补什么,重复执行不会起出
第二个 API/Worker)。本脚本在它之上补三件事:
1. **刷新行情** —— 下单要求行情快照落在 **15 分钟**有效期内
(`app/service/trade_service.py` 的 `MAX_QUOTE_AGE`),过期后所有委托直接
503「行情已过期」,而系统**没有自动刷新机制**;所以每次启动都刷一次。
2. **五项自检** —— 口径与 `客服agent\D2.5` §1 一致(容器 / 向量库 / Worker / 行情 / 向量库开关)。
3. **打开前端页面** —— 访客页(客服浮窗主战场)与客户登录页。
⚠️ **登录限流**:`.env` 的 `RATE_LIMIT_*` 是 **10 次 / 60 秒**。演示时不要反复登录 ——
连续登录会让后续用例整片报红,看起来像「客服坏了」,其实是限流。
.PARAMETER Port
API 端口,默认 8000(与 `start.ps1 -Port` 同义)。
.PARAMETER SkipStart
不调 `start.ps1`,只做自检 + 开页面(服务已经在跑时用)。
.PARAMETER NoBrowser
不自动打开浏览器。
.PARAMETER KeepPrices
不刷新行情(只看自检结果时用)。
.EXAMPLE
双击 `启动演示.bat`
powershell -ExecutionPolicy Bypass -File demo.ps1 -SkipStart -NoBrowser
.NOTES
本文件必须保存为 **UTF-8 with BOM**(理由同 `start.ps1`:缺 BOM 时 Windows PowerShell 5.1
按 ANSI 解析,中文会乱码并抛语法错误)。
#>
param(
[int]$Port = 8000,
[switch]$SkipStart,
[switch]$NoBrowser,
[switch]$KeepPrices
)
$ErrorActionPreference = "Continue"
$Root = $PSScriptRoot
Set-Location $Root
$Python = Join-Path $Root ".venv\Scripts\python.exe"
if (-not (Test-Path $Python)) { $Python = "python" }
$Base = "http://127.0.0.1:$Port"
$script:Fails = 0
function Section($text) { Write-Host ""; Write-Host "--- $text ---" -ForegroundColor Cyan }
function Ok($text) { Write-Host " [OK] $text" -ForegroundColor Green }
function Warn($text) { Write-Host " [警告] $text" -ForegroundColor Yellow; $script:Fails++ }
function Bad($text) { Write-Host " [失败] $text" -ForegroundColor Red; $script:Fails++ }
function Test-ApiRunning {
try {
$null = Invoke-WebRequest -Uri "$Base/internal/health/ready" -TimeoutSec 2 -UseBasicParsing -ErrorAction Stop
return $true
} catch { return $false }
}
function Wait-ApiReady {
for ($i = 1; $i -le 30; $i++) {
if (Test-ApiRunning) { return $true }
Start-Sleep -Seconds 1
}
return $false
}
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " 客服 Agent 演示 · 前端一键启动" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host "工作目录:$Root"
Write-Host "Python :$Python"
$apiWasRunning = Test-ApiRunning
# ---------------------------------------------------------------- 0. 拉起服务
if ($SkipStart) {
Section "0 服务拉起(-SkipStart:跳过 start.ps1)"
if ($apiWasRunning) { Ok "API 已在跑,直接进入自检" } else { Bad "API 未就绪,且按 -SkipStart 未拉起服务" }
} else {
Section "0 服务拉起(调用 start.ps1,幂等)"
& (Join-Path $Root "start.ps1") -Port $Port -NoBrowser -SkipPriceSync
if ($LASTEXITCODE -eq 1) { Bad "start.ps1 判定依赖缺失(常见:MySQL 未启动)—— 详见上方输出" }
}
Section "等待 API 就绪"
if (Wait-ApiReady) { Ok "API 已就绪:$Base/internal/health/ready" }
else { Bad "等了 30 秒仍未就绪:切到 API 窗口看报错(常见原因是数据库连不上)" }
# ---------------------------------------------------------------- 1~5. 五项自检
Section "自检 1/5 · 容器运行时(Milvus 三件套)"
if (Get-Command docker -ErrorAction SilentlyContinue) {
$containers = @(& docker ps --format "{{.Names}} {{.Status}}" 2>$null)
$milvus = @($containers | Where-Object { $_ -like "*milvus*" })
if ($milvus.Count -ge 3) { Ok "milvus 容器 $($milvus.Count) 个在跑:$($milvus -join " | ")" }
elseif ($milvus.Count -gt 0) { Warn "只看到 $($milvus.Count) 个 milvus 容器(期望 3 个:standalone / etcd / minio)" }
else { Warn "没看到 milvus 容器:知识检索会静默降级,先起 Docker Desktop" }
} else {
Warn "没找到 docker 命令,本项跳过"
}
Section "自检 2/5 · 向量库可查(四个集合计数)"
$probe = @'
import sys
sys.path.insert(0, '.')
from app.core.config import get_settings as g
from pymilvus import MilvusClient
client = MilvusClient(uri=g().resolved_milvus_uri)
for name in ("fin_faq_collection", "fin_product_collection", "fin_policy_collection", "fin_basic_collection"):
print(name, client.query(collection_name=name, filter='', output_fields=['count(*)']))
'@
# 经 stdin 送给 python,不走命令行参数:PowerShell 5.1 传原生命令参数时会吞掉内嵌双引号,
# 而这段探测代码必须保留字符串字面量(集合名与 output_fields)。
$probeOut = $probe | & $Python - 2>&1
if ($LASTEXITCODE -eq 0 -and (($probeOut | Out-String) -like "*count*")) {
Ok "四个集合可查(含 fin_basic_collection 补充语料):"
$probeOut | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
} else {
Warn "向量库探测失败:知识题会走 E5b 兜底(答不出但不会编)"
$probeOut | Select-Object -Last 4 | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
}
Section "自检 3/5 · Agent Worker 在跑"
$pythonProcs = Get-CimInstance Win32_Process -Filter "Name like '%python%'" -ErrorAction SilentlyContinue
$workerProcs = @($pythonProcs | Where-Object { $_.CommandLine -like "*app.worker*" })
if ($workerProcs.Count -gt 0) { Ok "Worker 在跑(PID $($workerProcs[0].ProcessId))" }
else { Bad "没有 Worker:客服对话会一直 queued,前端显示「繁忙 / 超时」" }
Section "自检 4/5 · 行情有效期(下单前置,15 分钟)"
if ($KeepPrices) {
Warn "按 -KeepPrices 跳过刷新:若距上次刷新超过 15 分钟,下单会 503"
} else {
$syncOut = & $Python "tools\sync_market_prices.py" 2>&1
if ($LASTEXITCODE -eq 0) {
Ok "行情已刷新,15 分钟窗口从此刻重新计时"
$syncOut | Where-Object { $_ -like "*落库*" } | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
} else {
Warn "行情刷新失败(退出码 $LASTEXITCODE):下单可能返回 503"
$syncOut | Select-Object -Last 5 | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
}
}
Section "自检 5/5 · 本地向量库开关(应为空:非空会绕过服务端 Milvus)"
$switchOut = & $Python -c "import sys; sys.path.insert(0, '.'); from app.core.config import get_settings as g; print(repr(g().milvus_local_uri)); print(g().resolved_milvus_uri)" 2>&1
if ($LASTEXITCODE -eq 0) {
$localUri = ($switchOut | Select-Object -First 1)
if ($localUri -eq "''") { Ok "milvus_local_uri 为空(符合预期);resolved = $($switchOut | Select-Object -Last 1)" }
else { Warn "milvus_local_uri 不为空:$localUri —— 演示前请清空该开关" }
} else {
Warn "配置读取失败"
$switchOut | Select-Object -Last 4 | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
}
# ---------------------------------------------------------------- 演示入口
$guestUrl = "$Base/portal/guest/home/"
$customerUrl = "$Base/portal/customer/login/"
$staffUrl = "$Base/portal/employee-console/login/"
Section "演示入口"
Write-Host " 访客页(客服浮窗主战场) $guestUrl"
Write-Host " 客户登录 $customerUrl"
Write-Host " 员工登录(风控/运营/管理员)$staffUrl"
Write-Host " 投顾工作台 $Base/portal/employee-advisor/dashboard/"
Write-Host " 接口文档 $Base/docs"
if ($NoBrowser) {
Write-Host " (-NoBrowser:未自动打开浏览器)" -ForegroundColor DarkGray
} elseif (Test-ApiRunning) {
try {
Start-Process $guestUrl | Out-Null
Start-Sleep -Milliseconds 600
Start-Process $customerUrl | Out-Null
Ok "已打开两个页面(访客页 / 客户登录)"
} catch {
Warn "自动打开浏览器失败,请手动访问上面的地址"
}
}
# ---------------------------------------------------------------- 账号与提醒
Section "演示账号(实测可登录)"
Write-Host " 客户 cust_t / 123456"
Write-Host " 风控专员 risk_t / 666666"
Write-Host " 管理员 admin_t / 88888888"
Write-Host " 运营 offsite_t / offsite123"
Write-Host " 投顾 advisor_t / abc12345 (2026-09-20 恢复)"
Write-Host " review_t :登录返回 401 是设计如此(只作权限夹具)" -ForegroundColor DarkGray
Section "开讲前必看"
Write-Host " · 登录限流 10 次 / 60 秒:不要反复登录,否则后续整片报红(假故障)"
Write-Host " · 行情 15 分钟:中途下单报 503 就在新窗口重跑"
Write-Host " & `"$Python`" tools\sync_market_prices.py"
Write-Host " · 演示台词与话术:客服agent\D2.5-客服Agent演示脚本与账号速查-2026-09-19.md"
Write-Host " · 答辩问答准备 :客服agent\D2.6-客服Agent答辩报告-2026-09-19.md"
if ($apiWasRunning -and -not $SkipStart) {
Section "提示:服务已在跑,本次没有新开日志窗口"
Write-Host " 当前 API/Worker 可能是无窗口方式启动的。想要可见日志窗口,先停掉再双击本脚本:"
$stopCmd = @'
Get-CimInstance Win32_Process -Filter "Name like '%python%'" | Where-Object { $_.CommandLine -match 'uvicorn app.main:app|app.worker' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
'@
Write-Host $stopCmd -ForegroundColor DarkGray
}
Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
if ($script:Fails -eq 0) {
Write-Host " 五项自检全过,演示环境就绪。" -ForegroundColor Green
} else {
Write-Host " 自检有 $script:Fails 项需要处理,见上方 [警告] / [失败] 行。" -ForegroundColor Yellow
}
Write-Host " 服务在独立窗口里运行,关掉本窗口不影响它们。" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
if ($script:Fails -gt 0) { exit 1 } else { exit 0 }