落地并存测服全端:智能体 Graph/路由、后台切 8180、主账与部署脚本齐备,便于线上重发与验收。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
linmianxun
2026-09-13 21:15:58 +08:00
co-authored by Cursor
parent 0eb1adecc8
commit 8103b3b651
143 changed files with 8968 additions and 277 deletions
+74 -61
View File
@@ -1,33 +1,28 @@
# 安全发布:新 release + 切 agent\current,重启 8910;禁止改 Nginx / C:\yutongda
# 入参:incoming\agent-release.zip 已就位;可选 env.file 覆盖 E:\wwwroot\.env
# Safe agent deploy: new release, switch junction, restart 8910+worker. No nginx / no C:\yutongda.
$ErrorActionPreference = 'Stop'
$ReleaseId = 'agent-' + (Get-Date -Format 'yyyyMMdd-HHmmss')
$Root = 'E:\wwwroot'
$Release = Join-Path $Root "agent\releases\$ReleaseId"
$Release = Join-Path $Root ("agent\releases\" + $ReleaseId)
$AgentCurrent = Join-Path $Root 'agent\current'
$Python = 'C:\yutongda\tools\Python312\python.exe'
$Zip = 'C:\Users\ytd_extdev_0903\incoming\agent-release.zip'
$EnvSrc = 'C:\Users\ytd_extdev_0903\incoming\env.file'
Write-Output "=== create dirs release=$ReleaseId ==="
New-Item -ItemType Directory -Force -Path $Release, "$Root\logs", "$Root\ops", "$Root\agent\releases" | Out-Null
if (-not (Test-Path $Zip)) { throw "missing $Zip" }
if (-not (Test-Path $Python)) { throw "missing $Python" }
Write-Output ("=== create dirs release=" + $ReleaseId + " ===")
New-Item -ItemType Directory -Force -Path $Release, (Join-Path $Root 'logs'), (Join-Path $Root 'ops'), (Join-Path $Root 'agent\releases') | Out-Null
if (-not (Test-Path $Zip)) { throw 'missing agent-release.zip' }
if (-not (Test-Path $Python)) { throw 'missing python' }
Write-Output '=== expand ==='
Expand-Archive -Path $Zip -DestinationPath $Release -Force
$AgentHome = Join-Path $Release 'agent'
if (-not (Test-Path (Join-Path $AgentHome 'agent\http_app.py'))) {
throw "http_app.py not found under $AgentHome"
}
$HttpApp = Join-Path $AgentHome 'agent\http_app.py'
if (-not (Test-Path $HttpApp)) { throw 'http_app.py not found' }
Write-Output '=== env ==='
if (Test-Path $EnvSrc) {
Copy-Item $EnvSrc (Join-Path $Root '.env') -Force
}
if (-not (Test-Path (Join-Path $Root '.env'))) { throw 'missing E:\wwwroot\.env' }
Copy-Item (Join-Path $Root '.env') (Join-Path $Release '.env') -Force
Copy-Item (Join-Path $Root '.env') (Join-Path $AgentHome '.env') -Force
$EnvFile = Join-Path $Root '.env'
if (-not (Test-Path $EnvFile)) { throw 'missing E:\wwwroot\.env' }
Copy-Item $EnvFile (Join-Path $Release '.env') -Force
Copy-Item $EnvFile (Join-Path $AgentHome '.env') -Force
Write-Output '=== venv + pip ==='
$venvDir = Join-Path $AgentHome '.venv'
@@ -37,93 +32,111 @@ if (-not (Test-Path $venvPy)) {
& $Python -m venv $venvDir
if ($LASTEXITCODE -ne 0) { throw 'venv failed' }
}
& $venvPip install --default-timeout=180 -i https://pypi.tuna.tsinghua.edu.cn/simple -r (Join-Path $AgentHome 'requirements.txt')
# Windows Chinese locale: force UTF-8 so requirements.txt comments decode
$env:PYTHONUTF8 = '1'
$env:PYTHONIOENCODING = 'utf-8'
$Req = Join-Path $AgentHome 'requirements.txt'
& $venvPip install --default-timeout=180 -i https://pypi.tuna.tsinghua.edu.cn/simple -r $Req
if ($LASTEXITCODE -ne 0) { throw 'pip install failed' }
Write-Output '=== switch junction ==='
if (Test-Path $AgentCurrent) {
cmd /c "rmdir `"$AgentCurrent`"" | Out-Null
cmd /c ("rmdir `"" + $AgentCurrent + "`"") | Out-Null
if (Test-Path $AgentCurrent) { Remove-Item $AgentCurrent -Recurse -Force }
}
cmd /c "mklink /J `"$AgentCurrent`" `"$AgentHome`""
cmd /c ("mklink /J `"" + $AgentCurrent + "`" `"" + $AgentHome + "`"")
if (-not (Test-Path (Join-Path $AgentCurrent 'agent\http_app.py'))) { throw 'agent current missing http_app' }
Write-Output '=== stop old 8910 only ==='
Get-NetTCPConnection -LocalPort 8910 -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty OwningProcess -Unique |
ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }
# 停旧 worker(仅匹配 wwwroot agent)
Get-CimInstance Win32_Process -Filter "Name='python.exe'" | ForEach-Object {
if ($_.CommandLine -and $_.CommandLine -like '*E:\wwwroot\agent*' -and $_.CommandLine -like '*worker_app*') {
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
$cl = $_.CommandLine
if ($null -ne $cl) {
if (($cl -like '*wwwroot*agent*') -and ($cl -like '*worker_app*')) {
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
}
}
}
Start-Sleep -Seconds 2
Write-Output '=== write run scripts ==='
$runHttp = @"
@echo off
cd /d "$AgentCurrent"
set YTD_ENV=test
set AGENT_HTTP_PORT=8910
:loop
"$AgentCurrent\.venv\Scripts\python.exe" -m agent.http_app >> "$Root\logs\agent-http.out.log" 2>> "$Root\logs\agent-http.err.log"
echo agent exited, restart in 3s >> "$Root\logs\agent-http.err.log"
timeout /t 3 /nobreak >nul
goto loop
"@
Set-Content -Path (Join-Path $Root 'ops\run-agent-http.cmd') -Value $runHttp -Encoding ASCII
$runHttpPath = Join-Path $Root 'ops\run-agent-http.cmd'
$runWorkerPath = Join-Path $Root 'ops\run-agent-worker.cmd'
$logHttpOut = Join-Path $Root 'logs\agent-http.out.log'
$logHttpErr = Join-Path $Root 'logs\agent-http.err.log'
$logWorkerOut = Join-Path $Root 'logs\agent-worker.out.log'
$logWorkerErr = Join-Path $Root 'logs\agent-worker.err.log'
$runWorker = @"
@echo off
cd /d "$AgentCurrent"
set YTD_ENV=test
:loop
"$AgentCurrent\.venv\Scripts\python.exe" -m agent.worker_app >> "$Root\logs\agent-worker.out.log" 2>> "$Root\logs\agent-worker.err.log"
echo worker exited, restart in 3s >> "$Root\logs\agent-worker.err.log"
timeout /t 3 /nobreak >nul
goto loop
"@
Set-Content -Path (Join-Path $Root 'ops\run-agent-worker.cmd') -Value $runWorker -Encoding ASCII
$runHttp = @(
'@echo off',
('cd /d "' + $AgentCurrent + '"'),
'set YTD_ENV=test',
'set AGENT_HTTP_PORT=8910',
':loop',
('"' + $venvPy + '" -m agent.http_app >> "' + $logHttpOut + '" 2>> "' + $logHttpErr + '"'),
('echo agent exited, restart in 3s >> "' + $logHttpErr + '"'),
'timeout /t 3 /nobreak >nul',
'goto loop'
) -join "`r`n"
Set-Content -Path $runHttpPath -Value $runHttp -Encoding ASCII
$runWorker = @(
'@echo off',
('cd /d "' + $AgentCurrent + '"'),
'set YTD_ENV=test',
':loop',
('"' + $venvPy + '" -m agent.worker_app >> "' + $logWorkerOut + '" 2>> "' + $logWorkerErr + '"'),
('echo worker exited, restart in 3s >> "' + $logWorkerErr + '"'),
'timeout /t 3 /nobreak >nul',
'goto loop'
) -join "`r`n"
Set-Content -Path $runWorkerPath -Value $runWorker -Encoding ASCII
Write-Output '=== start http + worker via WMI ==='
$w1 = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{
CommandLine = "cmd.exe /c `"$(Join-Path $Root 'ops\run-agent-http.cmd')`""
CommandLine = ('cmd.exe /c "' + $runHttpPath + '"')
}
Write-Output "HTTP_WMI=$($w1.ReturnValue) pid=$($w1.ProcessId)"
Write-Output ("HTTP_WMI=" + $w1.ReturnValue + " pid=" + $w1.ProcessId)
$w2 = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{
CommandLine = "cmd.exe /c `"$(Join-Path $Root 'ops\run-agent-worker.cmd')`""
CommandLine = ('cmd.exe /c "' + $runWorkerPath + '"')
}
Write-Output "WORKER_WMI=$($w2.ReturnValue) pid=$($w2.ProcessId)"
Write-Output ("WORKER_WMI=" + $w2.ReturnValue + " pid=" + $w2.ProcessId)
Start-Sleep -Seconds 8
try {
$h = Invoke-WebRequest -Uri 'http://127.0.0.1:8910/health' -UseBasicParsing -TimeoutSec 8
Write-Output "HEALTH=$($h.StatusCode) $($h.Content)"
Write-Output ("HEALTH=" + $h.StatusCode + " " + $h.Content)
} catch {
Get-Content (Join-Path $Root 'logs\agent-http.err.log') -Tail 40 -ErrorAction SilentlyContinue
throw "agent health failed: $_"
if (Test-Path $logHttpErr) { Get-Content $logHttpErr -Tail 40 }
throw ('agent health failed: ' + $_)
}
$listenPid = 0
$listen = Get-NetTCPConnection -LocalPort 8910 -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1
if ($listen) { $listenPid = $listen.OwningProcess }
$prevAdmin = $null
if (Test-Path (Join-Path $Root 'ops\active-release.json')) {
try { $prevAdmin = (Get-Content (Join-Path $Root 'ops\active-release.json') -Raw | ConvertFrom-Json).adminCurrent } catch {}
$prevAdmin = Join-Path $Root 'admin\current'
$ptrPath = Join-Path $Root 'ops\active-release.json'
if (Test-Path $ptrPath) {
try {
$old = Get-Content $ptrPath -Raw | ConvertFrom-Json
if ($old.adminCurrent) { $prevAdmin = $old.adminCurrent }
} catch {}
}
@{
$ptr = @{
root = $Root
releaseId = $ReleaseId
releaseRoot = $Release
agentHome = $AgentHome
agentCurrent = $AgentCurrent
adminCurrent = $(if ($prevAdmin) { $prevAdmin } else { Join-Path $Root 'admin\current' })
adminCurrent = $prevAdmin
pid = $listenPid
updatedAt = (Get-Date).ToString('o')
note = 'safe deploy: no nginx change, no C:\yutongda touch'
} | ConvertTo-Json | Set-Content (Join-Path $Root 'ops\active-release.json') -Encoding UTF8
note = 'safe deploy: no nginx change'
}
$ptr | ConvertTo-Json | Set-Content $ptrPath -Encoding UTF8
Write-Output 'AGENT_SAFE_DEPLOY_OK'