652 lines
23 KiB
HTML
652 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: rgba(255, 252, 246, 0.92);
|
||
--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);
|
||
}
|
||
|
||
.obs-hero {
|
||
position: relative;
|
||
overflow: hidden;
|
||
border: 1px solid var(--obs-line);
|
||
border-radius: 28px;
|
||
padding: clamp(1.35rem, 3vw, 2.3rem);
|
||
background:
|
||
radial-gradient(circle at 12% 18%, rgba(201, 100, 66, 0.2), transparent 28%),
|
||
radial-gradient(circle at 84% 12%, rgba(79, 111, 143, 0.18), transparent 30%),
|
||
linear-gradient(135deg, #fff8ed 0%, #f7eadb 48%, #fffdf8 100%);
|
||
box-shadow: 0 22px 55px rgba(70, 46, 28, 0.1);
|
||
}
|
||
|
||
.obs-hero::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: auto -8% -42% 42%;
|
||
height: 260px;
|
||
background: repeating-linear-gradient(90deg, rgba(201, 100, 66, 0.1) 0 1px, transparent 1px 18px);
|
||
transform: rotate(-7deg);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.obs-kicker {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding: 0.42rem 0.72rem;
|
||
border: 1px solid rgba(201, 100, 66, 0.22);
|
||
border-radius: 999px;
|
||
background: rgba(255, 255, 255, 0.58);
|
||
color: var(--obs-accent);
|
||
font-size: 0.78rem;
|
||
letter-spacing: 0.12em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.obs-title {
|
||
margin: 0.85rem 0 0.4rem;
|
||
max-width: 820px;
|
||
font-family:'Noto Sans TC','Inter',sans-serif;
|
||
font-size: clamp(1.9rem, 3.2vw, 2.75rem);
|
||
line-height: 0.95;
|
||
letter-spacing: -0.055em;
|
||
}
|
||
|
||
.obs-lede {
|
||
max-width: 760px;
|
||
margin: 0;
|
||
color: var(--obs-muted);
|
||
font-size: 1rem;
|
||
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: 112px;
|
||
padding: 1rem;
|
||
border: 1px solid var(--obs-line);
|
||
border-radius: 20px;
|
||
background: rgba(255, 255, 255, 0.66);
|
||
backdrop-filter: blur(8px);
|
||
}
|
||
|
||
.obs-signal-label,
|
||
.obs-section-eyebrow,
|
||
.obs-route-code {
|
||
color: var(--obs-muted);
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.11em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.obs-signal-value {
|
||
margin-top: 0.45rem;
|
||
font-size: clamp(1.65rem, 3vw, 2.35rem);
|
||
font-weight: 800;
|
||
letter-spacing: -0.04em;
|
||
}
|
||
|
||
.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: 24px;
|
||
background: var(--obs-card);
|
||
box-shadow: 0 14px 36px rgba(70, 46, 28, 0.07);
|
||
}
|
||
|
||
.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: 1.1rem;
|
||
font-weight: 800;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
.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: 18px;
|
||
background: rgba(255, 255, 255, 0.62);
|
||
}
|
||
|
||
.obs-host-card.is-good { border-left: 5px solid var(--obs-green); }
|
||
.obs-host-card.is-warn { border-left: 5px solid var(--obs-amber); }
|
||
.obs-host-card.is-bad { border-left: 5px 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-size: 1.35rem;
|
||
font-weight: 850;
|
||
letter-spacing: -0.04em;
|
||
}
|
||
|
||
.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: 999px;
|
||
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: 18px;
|
||
background: rgba(255, 255, 255, 0.58);
|
||
}
|
||
|
||
.obs-mini strong {
|
||
display: block;
|
||
margin-top: 0.25rem;
|
||
font-size: 1.45rem;
|
||
letter-spacing: -0.04em;
|
||
}
|
||
|
||
.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: 999px;
|
||
background: rgba(255, 255, 255, 0.65);
|
||
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: 24px;
|
||
padding: 1rem;
|
||
background:
|
||
linear-gradient(180deg, rgba(255, 255, 255, 0.74), rgba(255, 248, 239, 0.82));
|
||
}
|
||
|
||
.obs-route-group h3 {
|
||
margin: 0.25rem 0 0.85rem;
|
||
font-size: 1.1rem;
|
||
font-weight: 850;
|
||
}
|
||
|
||
.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: 16px;
|
||
color: var(--obs-ink);
|
||
text-decoration: none;
|
||
background: rgba(255, 255, 255, 0.58);
|
||
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: 12px;
|
||
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>
|
||
|
||
{% 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> AI Observability Command Room · {{ today }}</span>
|
||
<h1 class="obs-title">AI 觀測戰情室</h1>
|
||
<p class="obs-lede">
|
||
這裡是 momo-pro 私有 AI 中樞的第一入口:三主機、AI 呼叫、RAG 學習、MCP、AIOps、預算與 PPT 視覺審核全部收斂到同一張作戰圖。數字只讀正式資料來源;沒有資料時顯示診斷空狀態,不用假 KPI 撐場面。
|
||
</p>
|
||
|
||
<div class="obs-command-strip">
|
||
<div class="obs-signal">
|
||
<div class="obs-signal-label">Risk Signals</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">AI Calls / 24h</div>
|
||
<div class="obs-signal-value">{{ "{:,}".format(ai.total) if ai else '—' }}</div>
|
||
<div class="obs-signal-note">Token {{ "{:,}".format(ai.tokens) if ai else '—' }}</div>
|
||
</div>
|
||
<div class="obs-signal">
|
||
<div class="obs-signal-label">Cost</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 Hit Rate</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">Cache {{ "%.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">Host Cascade</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 }} probes · 平均 {{ h.avg_ms }} ms · 24h 視窗</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>
|
||
host_health_probes 無資料。請確認 migration 029 與 scheduler probe job 是否已啟動。
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</article>
|
||
|
||
{% if summary.budget_alerts %}
|
||
<article class="obs-panel">
|
||
<div class="obs-panel-head">
|
||
<div>
|
||
<div class="obs-section-eyebrow">Budget Guard</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>{{ 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 Runtime</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 hits</span><strong class="obs-status-blue">{{ ai.rag_hits }}</strong></div>
|
||
<div class="obs-mini"><span class="obs-microcopy">Cache hits</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">Learning Loop</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">30d episodes</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">AIOps / MCP / PPT</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 calls</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">Command</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">Runtime</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">Token、成本、錯誤、RAG × MCP 矩陣。</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">供應商成本與 force-throttle。</span></span><span class="obs-route-code">06</span></a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="obs-route-group">
|
||
<div class="obs-section-eyebrow">Quality</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">Promotion Gate 與人工審核。</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">Query hits、節省呼叫、反饋追蹤。</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">Caller 品質、蒸餾池、根因建議。</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 audit、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>
|
||
資料來源:host_health_probes / ai_calls / ai_call_budgets / learning_episodes / rag_query_log / mcp_calls / incidents / heal_logs / ppt_audit_results。
|
||
</p>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
||
<script>
|
||
(function() {
|
||
const data = {{ host_sparkline | tojson }};
|
||
document.querySelectorAll('canvas[data-host-sparkline]').forEach(el => {
|
||
const label = el.getAttribute('data-host-sparkline');
|
||
const sp = data[label];
|
||
if (!sp || !sp.hours || !sp.hours.length) return;
|
||
new Chart(el, {
|
||
type: 'line',
|
||
data: {
|
||
labels: sp.hours,
|
||
datasets: [{
|
||
data: sp.uptime_pct,
|
||
borderColor: '#c96442',
|
||
backgroundColor: 'rgba(201, 100, 66, 0.14)',
|
||
borderWidth: 1.8,
|
||
fill: true,
|
||
tension: 0.42,
|
||
pointRadius: 0,
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
plugins: {
|
||
legend: { display: false },
|
||
tooltip: {
|
||
displayColors: false,
|
||
callbacks: { label: c => `${c.label}: ${c.parsed.y.toFixed(0)}% uptime` }
|
||
}
|
||
},
|
||
scales: {
|
||
x: { display: false },
|
||
y: { display: false, min: 0, max: 100 }
|
||
}
|
||
}
|
||
});
|
||
});
|
||
})();
|
||
</script>
|
||
{% endblock %}
|