Files
group_fqcd_jr/demo.ps1
T
张胜宇 e0992c663e feat(demo): 新增演示一键启动脚本(demo.ps1 + 启动演示.bat)
演示真正要做的事只有一件:**把要看的前端页面按顺序打开,并确认环境真的就绪**。
原先只有 `start.ps1`(起服务),演示前还得手工跑行情刷新与五项自检,容易漏。

本次新增:

- `start.ps1`:不动(它已经幂等,缺什么补什么,重复执行不会起第二个 API/Worker)。
- `demo.ps1`:在 start.ps1 之上补三件事 ——
  ① **刷新行情**:下单要求行情快照落在 15 分钟有效期内(`trade_service.MAX_QUOTE_AGE`),
     过期后所有委托直接 503,而系统没有自动刷新机制;
  ② **五项自检**:口径与 `客服agent/D2.5` §1 完全一致(容器三件套 / 向量库三集合计数 /
     Worker 在跑 / 行情有效期 / `milvus_local_uri` 开关为空);
  ③ **打开演示页面**:访客页(客服浮窗主战场)+ 客户登录页。
  另把「**登录限流 10 次 / 60 秒**,不要反复登录」这条最容易造成假故障的提醒印在「开讲前必看」。
- `启动演示.bat`(GBK,与本仓库既有 .bat 同编码):双击即用,参数透传给 demo.ps1。

实测(本机):`启动演示.bat -SkipStart -NoBrowser` 与完整路径(含调用 start.ps1)
均 **五项自检全过、退出码 0**。

排错留痕(避免后人改回去):Milvus 探测代码原本用 `& $Python -c $probe` 传参,
Windows PowerShell 5.1 传原生命令参数时会把内嵌双引号吞掉(报 `unterminated string literal`)
⇒ 改为经 **stdin** 传给 python,并在脚本里写明原因。
2026-09-20 15:02:53 +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"):
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 "三个集合可查:"
$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 }