대시보드 폴드아웃/드릴다운 정리 + 페르소나 역린·misconduct 반응 + 게이트웨이 격리·RAG 비차단 수정

SSOT 대시보드:
- 한신대 기술분석 PDF(19쪽) 정합성 분석 + 이번 세션 발견 섹션 추가
- 섹션 폴드아웃(접기)·상단 목차(드릴다운)·모두 펼치기/접기 — 내용 보존, 레이아웃만 정리

페르소나 반응 강화('저항·반응 조절' 핵심 차별):
- PersonaCard.triggers(역린) 필드 + CCD 핵심상처 파생 역린 블록
- L0에 무례·모욕·조롱 시 현실적 동맹 균열 반응 지침

버그·성능 수정(라이브/E2E로 포착):
- 게이트웨이 페르소나 격리: --append-system-prompt를 --system-prompt(교체)로 + --exclude-dynamic-system-prompt-sections (내담자 캐릭터 붕괴·개발맥락 누출 차단)
- RAG: 임베더 동기 로드(약 7-13초)를 _warm_rag_caches 백그라운드 warm으로(세션 생성 블로킹 회귀 수정)
- voice TTS RMS 데드힌트 제거, init_state OpennessParams 파라미터객체화
- 한국어 PII(날짜·금액·주소) 마스킹 보강
- 레이아웃 시각 게이트: 폼 컨트롤 값 스크롤 오탐 제외(7/7)

검증: 백엔드 84/84, E2E 42(데스크톱 27·모바일 11·아바타 4), 시각 게이트 7/7
This commit is contained in:
Yun Chan 2026-06-27 02:30:46 +09:00
parent cb2aebd76c
commit 085460b5e0
327 changed files with 31226 additions and 1829 deletions

135
scripts/dev-up.ps1 Normal file
View file

@ -0,0 +1,135 @@
<#
.SYNOPSIS
Vignette 로컬 개발 스택을 깔끔하게 ()기동한다: 엔진 게이트웨이(9099) + API(8000) + (5173).
.DESCRIPTION
- 기존(고아 포함) 프로세스를 커맨드라인 기준으로 정확히 정리한 새로 띄운다(--reload 미사용: 결정론적).
- API는 DB 미가용 in-memory degraded로 기동된다(Postgres 불필요). dev-login + seed 페르소나 활성.
- 로그는 .devlogs/ 남긴다. 종료는 scripts/dev-down.ps1.
.PARAMETER NoGateway
엔진 게이트웨이를 띄우지 않는다(AI 생성 불가, UI만 테스트 ).
.PARAMETER NoWeb
vite 서버를 띄우지 않는다(API만 필요할 ).
.EXAMPLE
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\dev-up.ps1
#>
param(
[switch]$NoGateway,
[switch]$NoWeb,
[switch]$NoDb
)
$ErrorActionPreference = 'Stop'
$repo = Split-Path -Parent $PSScriptRoot
$api = Join-Path $repo 'apps\api'
$web = Join-Path $repo 'apps\web'
$logs = Join-Path $repo '.devlogs'
New-Item -ItemType Directory -Force -Path $logs | Out-Null
# uvicorn 이 설치된 python 을 해석한다(시스템에 3.11/3.14 등 복수 python 공존 — 'python' 별칭이
# uvicorn 없는 인터프리터를 가리킬 수 있다). 후보를 순회해 import uvicorn 성공하는 것을 고른다.
$pyCandidates = @(
(Join-Path $env:LOCALAPPDATA 'Programs\Python\Python311\python.exe'),
(Join-Path $env:LOCALAPPDATA 'Programs\Python\Python312\python.exe'),
'py',
'python'
)
$Python = $null
foreach ($c in $pyCandidates) {
$exe = $c; $pre = @()
if ($c -eq 'py') { $pre = @('-3') }
try {
& $exe @pre '-c' 'import uvicorn' 2>$null
if ($LASTEXITCODE -eq 0) { $Python = $exe; $PyPre = $pre; break }
} catch {}
}
if (-not $Python) { Write-Host 'ERROR: uvicorn 설치된 python 을 못 찾음 (pip install -r apps/api/requirements.txt)'; exit 1 }
Write-Host ("python: {0} {1}" -f $Python, ($PyPre -join ' '))
function Stop-Stale([string]$pattern, [string]$label) {
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object { ($_.Name -eq 'python.exe' -or $_.Name -eq 'node.exe') -and $_.CommandLine -and $_.CommandLine -match $pattern } |
ForEach-Object {
Write-Host (" stop {0,-8} PID {1}" -f $label, $_.ProcessId)
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
}
}
function Wait-Health([string]$url, [string]$label, [int]$timeoutSec = 40) {
$deadline = (Get-Date).AddSeconds($timeoutSec)
while ((Get-Date) -lt $deadline) {
try {
$r = Invoke-WebRequest -Uri $url -TimeoutSec 4 -UseBasicParsing -ErrorAction Stop
if ($r.StatusCode -ge 200 -and $r.StatusCode -lt 500) { Write-Host " OK $label ($url)"; return $true }
} catch { Start-Sleep -Milliseconds 600 }
}
Write-Host " WARN $label 미응답 ($url) — .devlogs 로그 확인"; return $false
}
function Ensure-DevDb {
# Docker Postgres(pgvector)를 apps/api/.env 의 DATABASE_URL 자격증명에 맞춰 보장한다.
# DB가 있어야 페르소나 source=database 가 되어 UI 회기 시작이 열린다(무DB면 degraded로 막힘).
docker ps *> $null 2>&1
if ($LASTEXITCODE -ne 0) { Write-Host " WARN Docker 데몬 미응답 — DB 없이 degraded(UI 세션 시작 제한). Docker Desktop 실행 필요."; return }
$running = docker ps --filter name=vignette-dev-db --format "{{.Names}}" 2>$null
if ("$running" -match 'vignette-dev-db') { Write-Host " OK db (vignette-dev-db 실행 중)"; return }
docker rm -f vignette-dev-db *> $null 2>&1
$envLines = Get-Content (Join-Path $api '.env')
$dbUrl = (($envLines | Where-Object { $_ -match '^DATABASE_URL=' }) -replace '^DATABASE_URL=','').Trim()
if ($dbUrl -notmatch 'postgresql://([^:]+):([^@]+)@[^:]+:([0-9]+)/(\S+)') { Write-Host " WARN DATABASE_URL 파싱 실패 — DB 스킵"; return }
$u=$Matches[1]; $p=$Matches[2]; $port=$Matches[3]; $db=$Matches[4]
$initPath = Join-Path $repo 'infra\db\init'
docker run -d --name vignette-dev-db -p "$port`:5432" -e POSTGRES_USER=$u -e POSTGRES_PASSWORD=$p -e POSTGRES_DB=$db -e APP_DB_USER=vignette_app -e APP_DB_PASSWORD=vignette_app -v "$initPath`:/docker-entrypoint-initdb.d:ro" pgvector/pgvector:pg16 *> $null
for ($i=0; $i -lt 24; $i++) {
Start-Sleep -Seconds 2
docker exec vignette-dev-db pg_isready -U $u *> $null 2>&1
if ($LASTEXITCODE -eq 0) { Write-Host " OK db (Postgres 준비됨, init 스키마 적용)"; Start-Sleep -Seconds 1; return }
}
Write-Host " WARN db 준비 타임아웃"
}
Write-Host "[1/4] 기존 스택 정리..."
Stop-Stale 'engine_gateway\.gateway' 'gateway'
Stop-Stale 'app\.main:app' 'api'
Stop-Stale 'vite' 'web'
Start-Sleep -Seconds 2
if (-not $NoDb) {
Write-Host "[DB] Postgres(pgvector) 보장..."
Ensure-DevDb
}
if (-not $NoGateway) {
Write-Host "[2/4] 엔진 게이트웨이 :9099 (claude_cli)..."
Start-Process -FilePath $Python `
-ArgumentList (@($PyPre) + @('-m','uvicorn','engine_gateway.gateway:app','--host','127.0.0.1','--port','9099')) `
-WorkingDirectory $api -WindowStyle Hidden `
-RedirectStandardOutput (Join-Path $logs 'gateway.out.log') `
-RedirectStandardError (Join-Path $logs 'gateway.err.log')
} else { Write-Host "[2/4] (게이트웨이 건너뜀)" }
Write-Host "[3/4] API :8000 (degraded in-memory OK, seed 페르소나)..."
$env:AUTO_SEED_PERSONAS = 'true'
$env:ALLOW_SEED_PERSONA_FALLBACK = 'true'
Start-Process -FilePath $Python `
-ArgumentList (@($PyPre) + @('-m','uvicorn','app.main:app','--host','127.0.0.1','--port','8000')) `
-WorkingDirectory $api -WindowStyle Hidden `
-RedirectStandardOutput (Join-Path $logs 'api.out.log') `
-RedirectStandardError (Join-Path $logs 'api.err.log')
if (-not $NoWeb) {
Write-Host "[4/4] 웹 vite :5173..."
Start-Process -FilePath 'cmd.exe' -ArgumentList '/c','npm run dev' `
-WorkingDirectory $web -WindowStyle Hidden `
-RedirectStandardOutput (Join-Path $logs 'web.out.log') `
-RedirectStandardError (Join-Path $logs 'web.err.log')
} else { Write-Host "[4/4] (웹 건너뜀)" }
Start-Sleep -Seconds 3
Write-Host "`n헬스 체크:"
if (-not $NoGateway) { Wait-Health 'http://127.0.0.1:9099/health' 'gateway' | Out-Null }
Wait-Health 'http://127.0.0.1:8000/health' 'api' | Out-Null
if (-not $NoWeb) { Wait-Health 'http://localhost:5173/' 'web' | Out-Null }
Write-Host "`n준비 완료. 진입점: http://localhost:5173 (로그인 페이지에서 dev-login)"
Write-Host "종료: powershell -NoProfile -ExecutionPolicy Bypass -File scripts\dev-down.ps1"