168 lines
7.8 KiB
PowerShell
168 lines
7.8 KiB
PowerShell
<#
|
||||
|
|
.SYNOPSIS
|
|||
|
|
启动金融 Agent 平台:依赖检查 → API → Agent Worker。
|
|||
|
|
|
|||
|
|
.NOTES
|
|||
|
|
⚠️ 本文件必须保存为 **UTF-8 with BOM**。
|
|||
|
|
Windows PowerShell 5.1 在缺少 BOM 时按系统 ANSI 解析脚本(简体中文下是 GBK),
|
|||
|
|
中文注释与输出会变成乱码并直接抛语法错误(实测报 `Unexpected token '[璀﹀憡]'`)。
|
|||
|
|
用编辑器改完本文件后,务必另存为「UTF-8 带 BOM」。
|
|||
|
|
|
|||
|
|
.DESCRIPTION
|
|||
|
|
平台需要**两个进程**才能完整工作,本脚本把它们一起起在独立窗口里:
|
|||
|
|
|
|||
|
|
· API —— 所有 HTTP 接口与前端页面(/portal/)
|
|||
|
|
· Worker —— 消费队列:Agent 对话、知识向量同步、记忆抽取、风控扫描
|
|||
|
|
|
|||
|
|
**没有 Worker 的后果**(这也是最容易踩的坑):
|
|||
|
|
· 客服对话会一直停在 queued,前端显示"客服繁忙/超时";
|
|||
|
|
· 新灌的知识写不进 Milvus,客服照旧答不上,且**没有任何报错**。
|
|||
|
|
|
|||
|
|
.PARAMETER Port
|
|||
|
|
API 监听端口,默认 8000。
|
|||
|
|
|
|||
|
|
.PARAMETER SkipChecks
|
|||
|
|
跳过依赖端口检查(MySQL / Redis / Milvus)。
|
|||
|
|
|
|||
|
|
.PARAMETER ApiOnly
|
|||
|
|
只起 API,不起 Worker(仅在明确不需要 Agent 能力时使用)。
|
|||
|
|
|
|||
|
|
.PARAMETER SkipPriceSync
|
|||
|
|
跳过启动时的行情刷新。仅在外部数据源不可用、或不想联网时使用。
|
|||
|
|
|
|||
|
|
.EXAMPLE
|
|||
|
|
powershell -ExecutionPolicy Bypass -File start.ps1
|
|||
|
|
powershell -ExecutionPolicy Bypass -File start.ps1 -Port 8100
|
|||
|
|
#>
|
|||
|
|
param(
|
|||
|
|
[int]$Port = 8000,
|
|||
|
|
[switch]$SkipChecks,
|
|||
|
|
[switch]$ApiOnly,
|
|||
|
|
[switch]$SkipPriceSync
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
$ErrorActionPreference = "Stop"
|
|||
|
|
$Root = $PSScriptRoot
|
|||
|
|
Set-Location $Root
|
|||
|
|
|
|||
|
|
Write-Host "=== 金融 Agent 平台 · 启动 ===" -ForegroundColor Cyan
|
|||
|
|
Write-Host "工作目录:$Root"
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------- 解释器
|
|||
|
|
# 逐个尝试:项目虚拟环境 → 常见 conda 环境 → PATH 上的 python。
|
|||
|
|
# 不写死任何一个,因为各人机器的环境不同(.venv 被 gitignore,不进仓库)。
|
|||
|
|
$candidates = @(
|
|||
|
|
(Join-Path $Root ".venv\Scripts\python.exe"),
|
|||
|
|
"D:\conda\envs\jr_py313\python.exe",
|
|||
|
|
"python"
|
|||
|
|
)
|
|||
|
|
$Python = $null
|
|||
|
|
foreach ($candidate in $candidates) {
|
|||
|
|
try {
|
|||
|
|
if ($candidate -eq "python") {
|
|||
|
|
$version = & python --version 2>&1
|
|||
|
|
} else {
|
|||
|
|
if (-not (Test-Path $candidate)) { continue }
|
|||
|
|
$version = & $candidate --version 2>&1
|
|||
|
|
}
|
|||
|
|
if ($LASTEXITCODE -eq 0) {
|
|||
|
|
$Python = $candidate
|
|||
|
|
Write-Host "解释器:$candidate($version)" -ForegroundColor Green
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
} catch { continue }
|
|||
|
|
}
|
|||
|
|
if (-not $Python) {
|
|||
|
|
Write-Host "[失败] 找不到可用的 Python。请先建虚拟环境,或把解释器路径加进本脚本的候选列表。" -ForegroundColor Red
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------- 依赖检查
|
|||
|
|
if (-not $SkipChecks) {
|
|||
|
|
Write-Host "`n--- 依赖服务检查 ---" -ForegroundColor Cyan
|
|||
|
|
$services = @(
|
|||
|
|
@{ Name = "MySQL"; HostName = "127.0.0.1"; Port = 3306; Required = $true },
|
|||
|
|
@{ Name = "Redis"; HostName = "127.0.0.1"; Port = 6379; Required = $false },
|
|||
|
|
@{ Name = "Milvus"; HostName = "127.0.0.1"; Port = 19530; Required = $false }
|
|||
|
|
)
|
|||
|
|
foreach ($service in $services) {
|
|||
|
|
$reachable = $false
|
|||
|
|
try {
|
|||
|
|
$reachable = (Test-NetConnection -ComputerName $service.HostName -Port $service.Port `
|
|||
|
|
-InformationLevel Quiet -WarningAction SilentlyContinue)
|
|||
|
|
} catch { $reachable = $false }
|
|||
|
|
|
|||
|
|
if ($reachable) {
|
|||
|
|
Write-Host (" [OK] {0,-7} {1}:{2}" -f $service.Name, $service.HostName, $service.Port) -ForegroundColor Green
|
|||
|
|
} elseif ($service.Required) {
|
|||
|
|
Write-Host (" [缺失] {0,-7} {1}:{2} —— 平台起不来,请先启动它" -f $service.Name, $service.HostName, $service.Port) -ForegroundColor Red
|
|||
|
|
exit 1
|
|||
|
|
} else {
|
|||
|
|
Write-Host (" [警告] {0,-7} {1}:{2} 不可达:相关知识检索会降级、Docker 未运行时常见" -f $service.Name, $service.HostName, $service.Port) -ForegroundColor Yellow
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------- 刷新行情
|
|||
|
|
# ⚠️ 为什么放在启动流程里:下单要求行情快照落在 **15 分钟**有效期内
|
|||
|
|
# (`app/service/trade_service.py` 的 `MAX_QUOTE_AGE`),超时后**所有委托直接 503
|
|||
|
|
# 「行情已过期」**,而系统**没有任何自动刷新机制** —— 演示时讲到一半下单就会失败。
|
|||
|
|
# 所以默认在启动时刷一次:15 分钟窗口从此刻重新计时。
|
|||
|
|
# 同步是按产品 upsert 的幂等操作;失败只警告不阻断(仍可手动重跑)。
|
|||
|
|
if (-not $SkipPriceSync) {
|
|||
|
|
Write-Host "`n--- 刷新行情(下单前置,有效期 15 分钟)---" -ForegroundColor Cyan
|
|||
|
|
$syncOutput = & $Python "tools\sync_market_prices.py" 2>&1
|
|||
|
|
if ($LASTEXITCODE -eq 0) {
|
|||
|
|
Write-Host " [OK] 行情已刷新,15 分钟内可正常下单" -ForegroundColor Green
|
|||
|
|
} else {
|
|||
|
|
Write-Host " [警告] 行情刷新失败(退出码 $LASTEXITCODE):下单可能返回 503" -ForegroundColor Yellow
|
|||
|
|
Write-Host " 可手动重跑:$Python tools\sync_market_prices.py" -ForegroundColor Yellow
|
|||
|
|
$syncOutput | Select-Object -Last 8 | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------- 启动进程
|
|||
|
|
# 用独立窗口起,方便分别看两边的日志(Worker 的日志是排查问题的第一现场)。
|
|||
|
|
function Start-PlatformWindow {
|
|||
|
|
param([string]$Title, [string[]]$Arguments)
|
|||
|
|
$shell = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" }
|
|||
|
|
$command = "`$host.UI.RawUI.WindowTitle = '$Title'; & '$Python' " + ($Arguments -join " ")
|
|||
|
|
Start-Process -FilePath $shell -ArgumentList "-NoExit", "-Command", $command | Out-Null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Write-Host "`n--- 启动进程 ---" -ForegroundColor Cyan
|
|||
|
|
Start-PlatformWindow -Title "平台 API :$Port" -Arguments @(
|
|||
|
|
"-m", "uvicorn", "app.main:app", "--host", "127.0.0.1", "--port", "$Port"
|
|||
|
|
)
|
|||
|
|
Write-Host " [已启动] API 窗口(uvicorn,端口 $Port)" -ForegroundColor Green
|
|||
|
|
|
|||
|
|
if (-not $ApiOnly) {
|
|||
|
|
Start-PlatformWindow -Title "平台 Agent Worker" -Arguments @("-m", "app.worker")
|
|||
|
|
Write-Host " [已启动] Worker 窗口(Agent 对话 / 知识向量 / 记忆抽取)" -ForegroundColor Green
|
|||
|
|
} else {
|
|||
|
|
Write-Host " [跳过] Worker(-ApiOnly):客服对话会一直 queued、新知识不会进 Milvus" -ForegroundColor Yellow
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------- 访问入口
|
|||
|
|
Start-Sleep -Seconds 3
|
|||
|
|
Write-Host "`n=== 访问入口 ===" -ForegroundColor Cyan
|
|||
|
|
Write-Host " 门户首页 http://127.0.0.1:$Port/portal/"
|
|||
|
|
Write-Host " 访客页 http://127.0.0.1:$Port/portal/guest/home/"
|
|||
|
|
Write-Host " 客户登录 http://127.0.0.1:$Port/portal/customer/login/ (cust_t / 123456)"
|
|||
|
|
Write-Host " 员工登录 http://127.0.0.1:$Port/portal/employee-console/login/(见下)"
|
|||
|
|
Write-Host " 接口文档 http://127.0.0.1:$Port/docs"
|
|||
|
|
|
|||
|
|
Write-Host "`n=== 演示账号 ===" -ForegroundColor Cyan
|
|||
|
|
Write-Host " 客户 cust_t / 123456"
|
|||
|
|
Write-Host " 风控专员 risk_t / 666666"
|
|||
|
|
Write-Host " 管理员 admin_t / 88888888"
|
|||
|
|
Write-Host " 投顾 advisor_t / abc12345"
|
|||
|
|
Write-Host " 运营 offsite_t / offsite123"
|
|||
|
|
|
|||
|
|
Write-Host "`n提示:" -ForegroundColor Yellow
|
|||
|
|
Write-Host " · 首次使用先准备数据:python tools/seed_demo_data.py"
|
|||
|
|
Write-Host " · 行情有效期只有 15 分钟:演示中途下单若报 503,在新窗口重跑"
|
|||
|
|
Write-Host " python tools/sync_market_prices.py (立即生效,无需重启服务)"
|
|||
|
|
Write-Host " · 体检:python tools/e2e_smoke_test.py"
|
|||
|
|
Write-Host " · 演示流程见 docs/44-演示流程.md"
|