按甲方「按你建议的来 一次性搞定上边的建议」执行七项。 文档事实纠错(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 条
221 lines
10 KiB
PowerShell
221 lines
10 KiB
PowerShell
<#
|
||
.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 }
|