630 lines
23 KiB
HTML
630 lines
23 KiB
HTML
{% extends "ewoooc_base.html" %}
|
||
|
||
{% block title %}AI 觀測戰情室{% endblock %}
|
||
|
||
{% block ewooo_content %}
|
||
<style>
|
||
.obs-war-room {
|
||
--obs-ink: var(--momo-ink, #302720);
|
||
--obs-muted: var(--momo-muted, #8b8077);
|
||
--obs-paper: var(--momo-paper, #fff8ef);
|
||
--obs-card: var(--momo-bg-surface, #faf7f0);
|
||
--obs-line: rgba(86, 64, 48, 0.14);
|
||
--obs-accent: var(--momo-accent, #c96442);
|
||
--obs-accent-soft: rgba(201, 100, 66, 0.12);
|
||
--obs-green: #4f8a5b;
|
||
--obs-amber: #b8792f;
|
||
--obs-red: #b94b45;
|
||
--obs-blue: #4f6f8f;
|
||
color: var(--obs-ink);
|
||
font-family: var(--momo-font-family, "Inter", "Noto Sans TC", system-ui, sans-serif);
|
||
}
|
||
|
||
.obs-hero {
|
||
position: relative;
|
||
overflow: hidden;
|
||
border: 1px solid var(--obs-line);
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
padding: clamp(1.05rem, 2.2vw, 1.65rem);
|
||
background-color: var(--momo-bg-surface, #faf7f0);
|
||
background-image: var(--obs-dot);
|
||
background-size: 13px 13px;
|
||
box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06));
|
||
}
|
||
|
||
.obs-hero::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: auto 1rem 1rem auto;
|
||
width: 8.5rem;
|
||
height: 8.5rem;
|
||
border: 1px solid color-mix(in srgb, var(--obs-accent) 22%, transparent);
|
||
background-image: var(--obs-dot);
|
||
background-size: 10px 10px;
|
||
opacity: 0.46;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.obs-kicker {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding: 0.36rem 0.58rem;
|
||
border: 1px solid rgba(201, 100, 66, 0.22);
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
background: var(--momo-bg-elevated, #fdfaf3);
|
||
color: var(--obs-accent);
|
||
font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
|
||
font-size: var(--momo-text-label, 0.6875rem);
|
||
font-weight: 800;
|
||
letter-spacing: 0.06em;
|
||
}
|
||
|
||
.obs-kicker-date {
|
||
color: var(--obs-muted);
|
||
}
|
||
|
||
.obs-title {
|
||
margin: 0.7rem 0 0.35rem;
|
||
max-width: 820px;
|
||
font-family: var(--momo-font-display, "JetBrains Mono", "Noto Sans TC", ui-monospace, monospace);
|
||
font-size: var(--obs-title-size, 1.8rem);
|
||
line-height: 1.18;
|
||
letter-spacing: 0;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.obs-lede {
|
||
max-width: 760px;
|
||
margin: 0;
|
||
color: var(--obs-muted);
|
||
font-size: var(--momo-text-body, 0.875rem);
|
||
line-height: 1.75;
|
||
}
|
||
|
||
.obs-command-strip {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
gap: 0.75rem;
|
||
margin-top: 1.45rem;
|
||
}
|
||
|
||
.obs-signal {
|
||
position: relative;
|
||
z-index: 1;
|
||
min-height: 104px;
|
||
padding: 0.92rem;
|
||
border: 1px solid var(--obs-line);
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
background: var(--momo-bg-surface, #faf7f0);
|
||
backdrop-filter: blur(8px);
|
||
}
|
||
|
||
.obs-signal-label,
|
||
.obs-section-eyebrow,
|
||
.obs-route-code {
|
||
color: color-mix(in srgb, var(--obs-accent) 76%, var(--obs-muted));
|
||
font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
|
||
font-size: var(--momo-text-label, 0.6875rem);
|
||
font-weight: 800;
|
||
letter-spacing: 0.06em;
|
||
}
|
||
|
||
.obs-signal-value {
|
||
margin-top: 0.45rem;
|
||
font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
|
||
font-size: var(--obs-value-size, 1.85rem);
|
||
font-weight: 850;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.obs-signal-note {
|
||
margin-top: 0.25rem;
|
||
color: var(--obs-muted);
|
||
font-size: 0.82rem;
|
||
}
|
||
|
||
.obs-grid {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1.35fr) minmax(320px, 0.65fr);
|
||
gap: 1rem;
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
.obs-panel {
|
||
border: 1px solid var(--obs-line);
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
background: var(--obs-card);
|
||
box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06));
|
||
}
|
||
|
||
.obs-panel-head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 1rem;
|
||
padding: 1.1rem 1.15rem 0.25rem;
|
||
}
|
||
|
||
.obs-panel-title {
|
||
margin: 0.18rem 0 0;
|
||
font-size: var(--momo-text-title, 1.0625rem);
|
||
font-weight: 800;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.obs-panel-body {
|
||
padding: 1rem 1.15rem 1.15rem;
|
||
}
|
||
|
||
.obs-host-list {
|
||
display: grid;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.obs-host-card {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) 150px;
|
||
gap: 0.85rem;
|
||
align-items: center;
|
||
padding: 0.9rem;
|
||
border: 1px solid var(--obs-line);
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
background: var(--momo-bg-elevated, #fdfaf3);
|
||
}
|
||
|
||
.obs-host-card.is-good { border-left: 4px solid var(--obs-green); }
|
||
.obs-host-card.is-warn { border-left: 4px solid var(--obs-amber); }
|
||
.obs-host-card.is-bad { border-left: 4px solid var(--obs-red); }
|
||
|
||
.obs-host-top {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 0.7rem;
|
||
align-items: baseline;
|
||
}
|
||
|
||
.obs-host-name {
|
||
font-weight: 800;
|
||
}
|
||
|
||
.obs-host-pct {
|
||
font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
|
||
font-size: 1.35rem;
|
||
font-weight: 850;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.obs-host-meta,
|
||
.obs-microcopy {
|
||
color: var(--obs-muted);
|
||
font-size: 0.82rem;
|
||
}
|
||
|
||
.obs-sparkline {
|
||
height: 54px;
|
||
}
|
||
|
||
.obs-alert-table {
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0 0.48rem;
|
||
}
|
||
|
||
.obs-alert-table th {
|
||
color: var(--obs-muted);
|
||
font-size: 0.7rem;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.obs-alert-table td {
|
||
padding: 0.62rem;
|
||
background: rgba(255, 255, 255, 0.68);
|
||
border-top: 1px solid var(--obs-line);
|
||
border-bottom: 1px solid var(--obs-line);
|
||
}
|
||
|
||
.obs-alert-table td:first-child {
|
||
border-left: 1px solid var(--obs-line);
|
||
border-radius: 14px 0 0 14px;
|
||
}
|
||
|
||
.obs-alert-table td:last-child {
|
||
border-right: 1px solid var(--obs-line);
|
||
border-radius: 0 14px 14px 0;
|
||
text-align: right;
|
||
}
|
||
|
||
.obs-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.35rem;
|
||
padding: 0.32rem 0.55rem;
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
background: var(--obs-accent-soft);
|
||
color: var(--obs-accent);
|
||
font-size: 0.76rem;
|
||
font-weight: 750;
|
||
}
|
||
|
||
.obs-stack {
|
||
display: grid;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.obs-metric-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 0.7rem;
|
||
}
|
||
|
||
.obs-mini {
|
||
padding: 0.85rem;
|
||
border: 1px solid var(--obs-line);
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
background: var(--momo-bg-elevated, #fdfaf3);
|
||
}
|
||
|
||
.obs-mini strong {
|
||
display: block;
|
||
margin-top: 0.25rem;
|
||
font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
|
||
font-size: 1.45rem;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.obs-status-good { color: var(--obs-green); }
|
||
.obs-status-warn { color: var(--obs-amber); }
|
||
.obs-status-bad { color: var(--obs-red); }
|
||
.obs-status-blue { color: var(--obs-blue); }
|
||
|
||
.obs-link-button {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.45rem;
|
||
padding: 0.58rem 0.78rem;
|
||
border: 1px solid rgba(201, 100, 66, 0.25);
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
background: var(--momo-bg-elevated, #fdfaf3);
|
||
color: var(--obs-accent);
|
||
text-decoration: none;
|
||
font-size: 0.82rem;
|
||
font-weight: 750;
|
||
transition: transform 160ms ease, box-shadow 160ms ease, background 160ms ease;
|
||
}
|
||
|
||
.obs-link-button:hover {
|
||
transform: translateY(-1px);
|
||
background: rgba(255, 252, 247, 0.96);
|
||
box-shadow: 0 10px 24px rgba(201, 100, 66, 0.14);
|
||
color: var(--obs-accent);
|
||
}
|
||
|
||
.obs-route-map {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 0.9rem;
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
.obs-route-group {
|
||
border: 1px solid var(--obs-line);
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
padding: 1rem;
|
||
background-color: var(--momo-bg-surface, #faf7f0);
|
||
background-image: var(--obs-dot);
|
||
background-size: 13px 13px;
|
||
}
|
||
|
||
.obs-route-group h3 {
|
||
margin: 0.25rem 0 0.85rem;
|
||
font-size: var(--momo-text-title, 1.0625rem);
|
||
font-weight: 850;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.obs-route-list {
|
||
display: grid;
|
||
gap: 0.55rem;
|
||
}
|
||
|
||
.obs-route-card {
|
||
display: grid;
|
||
grid-template-columns: 2rem minmax(0, 1fr) auto;
|
||
gap: 0.72rem;
|
||
align-items: center;
|
||
padding: 0.72rem;
|
||
border: 1px solid transparent;
|
||
border-radius: var(--momo-radius-lg, 8px);
|
||
color: var(--obs-ink);
|
||
text-decoration: none;
|
||
background: var(--momo-bg-elevated, #fdfaf3);
|
||
transition: border-color 160ms ease, transform 160ms ease, background 160ms ease;
|
||
}
|
||
|
||
.obs-route-card:hover {
|
||
border-color: rgba(201, 100, 66, 0.28);
|
||
background: rgba(255, 252, 247, 0.96);
|
||
color: var(--obs-ink);
|
||
transform: translateX(3px);
|
||
}
|
||
|
||
.obs-route-icon {
|
||
display: inline-grid;
|
||
place-items: center;
|
||
width: 2rem;
|
||
height: 2rem;
|
||
border-radius: 6px;
|
||
background: var(--obs-accent-soft);
|
||
color: var(--obs-accent);
|
||
}
|
||
|
||
.obs-route-title {
|
||
display: block;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.obs-route-desc {
|
||
display: block;
|
||
margin-top: 0.08rem;
|
||
color: var(--obs-muted);
|
||
font-size: 0.76rem;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
.obs-empty {
|
||
padding: 1rem;
|
||
border: 1px dashed rgba(184, 121, 47, 0.38);
|
||
border-radius: 18px;
|
||
background: rgba(184, 121, 47, 0.08);
|
||
color: var(--obs-amber);
|
||
}
|
||
|
||
@media (max-width: 1180px) {
|
||
.obs-command-strip,
|
||
.obs-route-map {
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
}
|
||
|
||
.obs-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 720px) {
|
||
.obs-command-strip,
|
||
.obs-route-map,
|
||
.obs-metric-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.obs-host-card {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
{% import "admin/_observability_labels.html" as obs_label %}
|
||
{% set ai = summary.ai_calls if summary.ai_calls else none %}
|
||
{% set host_count = summary.hosts|length if summary.hosts else 0 %}
|
||
{% set host_bad = namespace(value=0) %}
|
||
{% if summary.hosts %}
|
||
{% for h in summary.hosts %}
|
||
{% if h.uptime_pct < 90 %}{% set host_bad.value = host_bad.value + 1 %}{% endif %}
|
||
{% endfor %}
|
||
{% endif %}
|
||
{% set risk_count = (summary.budget_alerts|length if summary.budget_alerts else 0) + host_bad.value + (1 if ai and ai.error_rate >= 15 else 0) + (1 if summary.episodes and summary.episodes.pending > 0 else 0) %}
|
||
|
||
<div class="obs-war-room">
|
||
<section class="obs-hero">
|
||
<span class="obs-kicker"><i class="fas fa-satellite-dish"></i> 01 指揮總覽 <span class="obs-kicker-date">· {{ today }}</span></span>
|
||
<h1 class="obs-title">AI 觀測戰情室</h1>
|
||
<p class="obs-lede">
|
||
私有 AI 中樞的第一入口:三主機、AI 呼叫、RAG 學習、MCP、AIOps、預算與 PPT 視覺審核收斂到同一張工作台。所有數字只讀正式資料來源;缺資料時呈現可診斷空狀態。
|
||
</p>
|
||
|
||
<div class="obs-command-strip">
|
||
<div class="obs-signal">
|
||
<div class="obs-signal-label">即時風險</div>
|
||
<div class="obs-signal-value {% if risk_count == 0 %}obs-status-good{% elif risk_count <= 2 %}obs-status-warn{% else %}obs-status-bad{% endif %}">{{ risk_count }}</div>
|
||
<div class="obs-signal-note">主機、預算、錯誤率、待審核的即時風險數</div>
|
||
</div>
|
||
<div class="obs-signal">
|
||
<div class="obs-signal-label">24 小時呼叫</div>
|
||
<div class="obs-signal-value">{{ "{:,}".format(ai.total) if ai else '—' }}</div>
|
||
<div class="obs-signal-note">用量:{{ "{:,}".format(ai.tokens) if ai else '—' }}</div>
|
||
</div>
|
||
<div class="obs-signal">
|
||
<div class="obs-signal-label">成本水位</div>
|
||
<div class="obs-signal-value">${{ "%.2f"|format(ai.cost_24h) if ai else '0.00' }}</div>
|
||
<div class="obs-signal-note">當月累計 ${{ "%.2f"|format(summary.month_cost|default(0)) }}</div>
|
||
</div>
|
||
<div class="obs-signal">
|
||
<div class="obs-signal-label">RAG 命中率</div>
|
||
<div class="obs-signal-value obs-status-blue">{{ "%.1f"|format(ai.rag_rate) if ai else '—' }}{% if ai %}%{% endif %}</div>
|
||
<div class="obs-signal-note">快取命中 {{ "%.0f"|format(ai.cache_rate) if ai else '—' }}{% if ai %}%{% endif %}</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="obs-grid">
|
||
<div class="obs-stack">
|
||
<article class="obs-panel">
|
||
<div class="obs-panel-head">
|
||
<div>
|
||
<div class="obs-section-eyebrow">02 主機級聯</div>
|
||
<h2 class="obs-panel-title">三主機生命線</h2>
|
||
</div>
|
||
<a class="obs-link-button" href="/observability/host_health"><i class="fas fa-arrow-right"></i>主機健康</a>
|
||
</div>
|
||
<div class="obs-panel-body">
|
||
{% if summary.hosts %}
|
||
<div class="obs-host-list">
|
||
{% for h in summary.hosts %}
|
||
<div class="obs-host-card {% if h.uptime_pct >= 99 %}is-good{% elif h.uptime_pct >= 90 %}is-warn{% else %}is-bad{% endif %}">
|
||
<div>
|
||
<div class="obs-host-top">
|
||
<span class="obs-host-name">{{ h.label }}</span>
|
||
<span class="obs-host-pct {% if h.uptime_pct >= 99 %}obs-status-good{% elif h.uptime_pct >= 90 %}obs-status-warn{% else %}obs-status-bad{% endif %}">{{ "%.1f"|format(h.uptime_pct) }}%</span>
|
||
</div>
|
||
<div class="obs-host-meta">{{ h.up }}/{{ h.total }} 次探測 · 平均 {{ h.avg_ms }} ms · 24 小時視窗</div>
|
||
</div>
|
||
<div class="obs-sparkline">
|
||
{% if host_sparkline.get(h.label) %}
|
||
<canvas data-host-sparkline="{{ h.label }}"></canvas>
|
||
{% else %}
|
||
<div class="obs-microcopy">尚無趨勢資料</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% else %}
|
||
<div class="obs-empty">
|
||
<i class="fas fa-triangle-exclamation me-1"></i>
|
||
主機探測尚無資料。請確認第 029 號資料遷移與排程探測任務是否已啟動。
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</article>
|
||
|
||
{% if summary.budget_alerts %}
|
||
<article class="obs-panel">
|
||
<div class="obs-panel-head">
|
||
<div>
|
||
<div class="obs-section-eyebrow">預算守門</div>
|
||
<h2 class="obs-panel-title">預算告警</h2>
|
||
</div>
|
||
<a class="obs-link-button" href="/observability/budget"><i class="fas fa-bolt"></i>處理預算</a>
|
||
</div>
|
||
<div class="obs-panel-body">
|
||
<table class="obs-alert-table">
|
||
<thead>
|
||
<tr><th>週期</th><th>供應商</th><th>已花費</th><th>預算</th><th>使用率</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for b in summary.budget_alerts %}
|
||
<tr>
|
||
<td><span class="obs-pill">{{ b.period }}</span></td>
|
||
<td><code>{{ obs_label.provider(b.provider) }}</code></td>
|
||
<td>${{ "%.2f"|format(b.spent) }}</td>
|
||
<td>${{ "%.2f"|format(b.budget) }}</td>
|
||
<td><strong class="{% if b.ratio >= 1.0 %}obs-status-bad{% else %}obs-status-warn{% endif %}">{{ "%.0f"|format(b.ratio * 100) }}%</strong></td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</article>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<aside class="obs-stack">
|
||
<article class="obs-panel">
|
||
<div class="obs-panel-head">
|
||
<div>
|
||
<div class="obs-section-eyebrow">AI 呼叫</div>
|
||
<h2 class="obs-panel-title">呼叫品質</h2>
|
||
</div>
|
||
<a class="obs-link-button" href="/observability/ai_calls"><i class="fas fa-chart-bar"></i>詳情</a>
|
||
</div>
|
||
<div class="obs-panel-body">
|
||
{% if ai %}
|
||
<div class="obs-metric-grid">
|
||
<div class="obs-mini"><span class="obs-microcopy">錯誤率</span><strong class="{% if ai.error_rate >= 15 %}obs-status-bad{% elif ai.error_rate >= 5 %}obs-status-warn{% else %}obs-status-good{% endif %}">{{ "%.1f"|format(ai.error_rate) }}%</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">失敗</span><strong>{{ ai.errors }}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">RAG 命中</span><strong class="obs-status-blue">{{ ai.rag_hits }}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">快取命中</span><strong>{{ ai.cache_hits }}</strong></div>
|
||
</div>
|
||
{% else %}
|
||
<div class="obs-empty">ai_calls 無 24h 資料。</div>
|
||
{% endif %}
|
||
</div>
|
||
</article>
|
||
|
||
<article class="obs-panel">
|
||
<div class="obs-panel-head">
|
||
<div>
|
||
<div class="obs-section-eyebrow">學習閉環</div>
|
||
<h2 class="obs-panel-title">RAG 學習閘</h2>
|
||
</div>
|
||
<a class="obs-link-button" href="/observability/promotion_review"><i class="fas fa-brain"></i>審核</a>
|
||
</div>
|
||
<div class="obs-panel-body">
|
||
{% if summary.episodes %}
|
||
<div class="obs-metric-grid">
|
||
<div class="obs-mini"><span class="obs-microcopy">待審核</span><strong class="{% if summary.episodes.pending > 0 %}obs-status-warn{% else %}obs-status-good{% endif %}">{{ summary.episodes.pending }}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">30 日樣本</span><strong>{{ summary.episodes.total_30d }}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">已晉升</span><strong class="obs-status-good">{{ summary.episodes.approved_30d }}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">晉升率</span><strong>{{ "%.0f"|format(summary.episodes.approval_rate) }}%</strong></div>
|
||
</div>
|
||
{% else %}
|
||
<div class="obs-empty">learning_episodes 無 30d 資料。</div>
|
||
{% endif %}
|
||
</div>
|
||
</article>
|
||
|
||
<article class="obs-panel">
|
||
<div class="obs-panel-head">
|
||
<div>
|
||
<div class="obs-section-eyebrow">自動化後勤</div>
|
||
<h2 class="obs-panel-title">自動化後勤</h2>
|
||
</div>
|
||
</div>
|
||
<div class="obs-panel-body">
|
||
<div class="obs-metric-grid">
|
||
<div class="obs-mini"><span class="obs-microcopy">AIOps 未解</span><strong class="{% if summary.aiops and summary.aiops.incidents_open > 0 %}obs-status-bad{% else %}obs-status-good{% endif %}">{{ summary.aiops.incidents_open if summary.aiops else '—' }}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">自癒成功率</span><strong>{{ "%.0f"|format(summary.aiops.heal_rate) if summary.aiops else '—' }}{% if summary.aiops %}%{% endif %}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">MCP 呼叫</span><strong>{{ "{:,}".format(summary.mcp.total) if summary.mcp else '—' }}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">PPT 通過率</span><strong>{{ "%.0f"|format(summary.ppt.pass_rate) if summary.ppt and summary.ppt.total > 0 else '—' }}{% if summary.ppt and summary.ppt.total > 0 %}%{% endif %}</strong></div>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
</aside>
|
||
</section>
|
||
|
||
<section class="obs-route-map" aria-label="AI 觀測台子頁入口">
|
||
<div class="obs-route-group">
|
||
<div class="obs-section-eyebrow">03 戰情指揮</div>
|
||
<h3>戰情室</h3>
|
||
<div class="obs-route-list">
|
||
<a class="obs-route-card" href="/observability/agent_orchestration"><span class="obs-route-icon"><i class="fas fa-network-wired"></i></span><span><span class="obs-route-title">Agent 編排矩陣</span><span class="obs-route-desc">四 Agent × 模型 × MCP × RAG 分工。</span></span><span class="obs-route-code">02</span></a>
|
||
<a class="obs-route-card" href="/observability/business_intel"><span class="obs-route-icon"><i class="fas fa-briefcase"></i></span><span><span class="obs-route-title">商業面 × AI</span><span class="obs-route-desc">AI 決策是否真的轉成生意動作。</span></span><span class="obs-route-code">03</span></a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="obs-route-group">
|
||
<div class="obs-section-eyebrow">系統成本</div>
|
||
<h3>系統與成本</h3>
|
||
<div class="obs-route-list">
|
||
<a class="obs-route-card" href="/observability/host_health"><span class="obs-route-icon"><i class="fas fa-heartbeat"></i></span><span><span class="obs-route-title">主機健康</span><span class="obs-route-desc">三主機、MCP、AIOps、AutoHeal。</span></span><span class="obs-route-code">04</span></a>
|
||
<a class="obs-route-card" href="/observability/ai_calls"><span class="obs-route-icon"><i class="fas fa-chart-bar"></i></span><span><span class="obs-route-title">AI 呼叫</span><span class="obs-route-desc">用量、成本、錯誤、知識與工具矩陣。</span></span><span class="obs-route-code">05</span></a>
|
||
<a class="obs-route-card" href="/observability/budget"><span class="obs-route-icon"><i class="fas fa-wallet"></i></span><span><span class="obs-route-title">預算控管</span><span class="obs-route-desc">供應商成本與強制節流。</span></span><span class="obs-route-code">06</span></a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="obs-route-group">
|
||
<div class="obs-section-eyebrow">RAG 品質</div>
|
||
<h3>RAG 與品質</h3>
|
||
<div class="obs-route-list">
|
||
<a class="obs-route-card" href="/observability/promotion_review"><span class="obs-route-icon"><i class="fas fa-brain"></i></span><span><span class="obs-route-title">RAG 晉升審核</span><span class="obs-route-desc">晉升守門與人工審核。</span></span><span class="obs-route-code">07</span></a>
|
||
<a class="obs-route-card" href="/observability/rag_queries"><span class="obs-route-icon"><i class="fas fa-magnifying-glass-chart"></i></span><span><span class="obs-route-title">RAG 召回詳情</span><span class="obs-route-desc">查詢命中、節省呼叫、反饋追蹤。</span></span><span class="obs-route-code">08</span></a>
|
||
<a class="obs-route-card" href="/observability/quality_trend"><span class="obs-route-icon"><i class="fas fa-comments"></i></span><span><span class="obs-route-title">反饋趨勢</span><span class="obs-route-desc">呼叫端品質、蒸餾池、根因建議。</span></span><span class="obs-route-code">09</span></a>
|
||
<a class="obs-route-card" href="/observability/ppt_audit_history"><span class="obs-route-icon"><i class="fas fa-search"></i></span><span><span class="obs-route-title">PPT 視覺審核</span><span class="obs-route-desc">PPT 審核、RAG 修法、AiderHeal。</span></span><span class="obs-route-code">10</span></a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<p class="obs-microcopy mt-3">
|
||
<i class="fas fa-database me-1"></i>
|
||
資料來源:主機探測、AI 呼叫、預算、學習事件、RAG 查詢、MCP 呼叫、事件與自癒、PPT 審核。
|
||
</p>
|
||
</div>
|
||
|
||
<template id="obs-host-sparkline-data">{{ host_sparkline | tojson }}</template>
|
||
<script src="{{ url_for('static', filename='js/analysis-chart-theme.js') }}"></script>
|
||
<script src="{{ url_for('static', filename='js/observability-charts.js') }}"></script>
|
||
{% endblock %}
|