From e8c348ab219dc3395de34b27f340be8175e861ca Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Mon, 27 Apr 2026 08:29:53 +0900 Subject: [PATCH] =?UTF-8?q?feat(dashboard):=20Day=204=20=ED=8A=9C=EB=8B=9D?= =?UTF-8?q?=20=E2=80=94=20=EC=9E=84=EA=B3=84=EC=B9=98=20=EC=9E=AC=EC=A1=B0?= =?UTF-8?q?=EC=A0=95=20+=20deep=5Fsummary=20=EC=95=88=EC=A0=95=EC=84=B1=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3일 telemetry (599 triage / 555 deep) 기반 임계치 재평가: 1. 에스컬레이션 비율 — 임계치 의미 reframe - 기존: >20% 적색 (튜닝 필요) → 항상 적색 (운영 패턴 97%) - 신규: <80% 적색 (정책 매칭 실패 증가) - 메시지: "safety 정책상 95~100% 가 정상" 보조 표시 - safety_reference 99.7%, generic 100% (fallback risk_flag), msds 46.2% → 운영 정상 패턴 확인 2. Deep summary 안정성 — 신규 카드 추가 - mode='summary_deep' 의 error_code IS NOT NULL 비율 - 현재 5.2% (call_failed 21 + parse:ValidationError 8) - >5% 적색 임계 - MLX 호출 timeout / JSON 파싱 실패 모니터 3. triage JSON 건강도, Backlog Suppression — 임계치 유지 - 현재 0%, 1% — 매우 안정. 보수적 임계 유효. Backend: TierHealthStack 에 deep_total / deep_err_total 추가 Frontend: 카드 그리드 3열 → 4열 (lg), Day 4 신규 카드. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/api/dashboard.py | 20 +++++++++++++++----- frontend/src/lib/stores/system.ts | 3 +++ frontend/src/routes/+page.svelte | 29 +++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/app/api/dashboard.py b/app/api/dashboard.py index d591437..912fae6 100644 --- a/app/api/dashboard.py +++ b/app/api/dashboard.py @@ -50,12 +50,15 @@ class QueueLag(BaseModel): class TierHealthStack(BaseModel): - """PR-B B-3 — tier 관측성 3종 카드 소스 (24h 윈도우). + """PR-B B-3 — tier 관측성 카드 소스 (24h 윈도우). - 대시보드 카드: - - "에스컬레이션 비율": escalated_total / triage_total (>20% 적색, <1% 회색) + 대시보드 카드 (Day 4 튜닝 — 2026-04-27 임계치 재조정): + - "에스컬레이션 비율": escalated_total / triage_total + · <80% 적색 (정책 매칭 실패 증가 — 진짜 튜닝 필요) + · 80~99% 정상 (safety/health 정책 의도) - "triage JSON 건강도": triage_json_invalid / triage_total (>5% 적색) - "Backlog Suppression": suppressed_total / triage_total (>10% 주황) + - "Deep summary 안정성": deep_err_total / deep_total (>5% 적색) """ triage_total: int = 0 escalated_total: int = 0 @@ -63,6 +66,9 @@ class TierHealthStack(BaseModel): escalation_by_domain: dict[str, int] = {} # safety_reference / news_item / ... triage_json_invalid: int = 0 # error_code='triage_json_invalid' suppressed_total: int = 0 # suppressed_reason IS NOT NULL + # Day 4 튜닝 신규 — deep_summary 호출 안정성 + deep_total: int = 0 # mode='summary_deep' 전체 + deep_err_total: int = 0 # error_code IS NOT NULL (call_failed / parse:*) class DashboardResponse(BaseModel): @@ -216,13 +222,15 @@ async def get_dashboard( for row in lag_result.all() ] - # ─── PR-B B-3 — tier 관측성 (24h) ─── + # ─── PR-B B-3 — tier 관측성 (24h) + Day 4 deep_err 추가 ─── tier_rows = (await session.execute(text(""" SELECT COUNT(*) FILTER (WHERE mode = 'summary_triage') AS triage_total, COUNT(*) FILTER (WHERE mode = 'summary_triage' AND escalated_to_26b = true) AS escalated_total, COUNT(*) FILTER (WHERE mode = 'summary_triage' AND error_code = 'triage_json_invalid') AS json_invalid, - COUNT(*) FILTER (WHERE mode = 'summary_triage' AND suppressed_reason IS NOT NULL) AS suppressed_total + COUNT(*) FILTER (WHERE mode = 'summary_triage' AND suppressed_reason IS NOT NULL) AS suppressed_total, + COUNT(*) FILTER (WHERE mode = 'summary_deep') AS deep_total, + COUNT(*) FILTER (WHERE mode = 'summary_deep' AND error_code IS NOT NULL) AS deep_err_total FROM analyze_events WHERE created_at > NOW() - INTERVAL '24 hours' """))).one() @@ -253,6 +261,8 @@ async def get_dashboard( escalated_total=int(tier_rows.escalated_total or 0), triage_json_invalid=int(tier_rows.json_invalid or 0), suppressed_total=int(tier_rows.suppressed_total or 0), + deep_total=int(tier_rows.deep_total or 0), + deep_err_total=int(tier_rows.deep_err_total or 0), escalation_by_reason=escalation_by_reason, escalation_by_domain=escalation_by_domain, ) diff --git a/frontend/src/lib/stores/system.ts b/frontend/src/lib/stores/system.ts index 74bfa25..77a41f0 100644 --- a/frontend/src/lib/stores/system.ts +++ b/frontend/src/lib/stores/system.ts @@ -41,6 +41,9 @@ export interface TierHealthStack { escalation_by_domain: Record; triage_json_invalid: number; suppressed_total: number; + // Day 4 신규 — deep_summary 호출 안정성 + deep_total?: number; + deep_err_total?: number; } export interface DashboardSummary { diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 9d82145..2b8d6a1 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -357,10 +357,15 @@ {@const esc_rate = th.triage_total > 0 ? th.escalated_total / th.triage_total : 0} {@const json_rate = th.triage_total > 0 ? th.triage_json_invalid / th.triage_total : 0} {@const sup_rate = th.triage_total > 0 ? th.suppressed_total / th.triage_total : 0} - {@const esc_tone = esc_rate > 0.20 ? 'text-error' : (esc_rate < 0.01 ? 'text-dim' : 'text-text')} + {@const deep_total = th.deep_total ?? 0} + {@const deep_err_rate = deep_total > 0 ? (th.deep_err_total ?? 0) / deep_total : 0} + + {@const esc_tone = esc_rate < 0.80 ? 'text-error' : 'text-text'} {@const json_tone = json_rate > 0.05 ? 'text-error' : 'text-text'} {@const sup_tone = sup_rate > 0.10 ? 'text-warning' : 'text-text'} -
+ {@const deep_tone = deep_err_rate > 0.05 ? 'text-error' : 'text-text'} +
@@ -372,9 +377,9 @@

{th.escalated_total} / {th.triage_total} - {#if esc_rate > 0.20}(튜닝 필요){/if} - {#if esc_rate < 0.01}(false negative?){/if} + {#if esc_rate < 0.80}(매칭 실패 증가){/if}

+

safety 정책상 95~100% 가 정상

{#if Object.keys(th.escalation_by_reason).length > 0}
{#each Object.entries(th.escalation_by_reason).slice(0, 4) as [reason, n]} @@ -417,6 +422,22 @@

10% 초과 시 ratio/pending threshold 조정

+ + + +
+

Deep summary 안정성 (24h)

+ +
+

+ {(deep_err_rate * 100).toFixed(1)}% +

+

+ 실패 {th.deep_err_total ?? 0} / {deep_total} + {#if deep_err_rate > 0.05}(MLX 안정성 점검){/if} +

+

call_failed / parse:* 합계, 5% 초과 시 점검

+
{/if}