All checks were successful
CD Pipeline / deploy (push) Successful in 2m30s
統帥質疑:6 頁觀測台只是 raw stats dashboard,沒展現 AI 自動化專業。 深度盤點 4 軸結果: - DB 利用率 22.7%(22 表只用 5 張) - MCP 整合 1/6(mcp_calls 表完全沒被讀) - RAG 整合 0/6(沒 import rag_service) - AI 自動化 L0 × 5 + L1 × 1(純讀 dashboard,無一鍵觸發) 本 commit 5 個增強: D-1: promotion_review 加 RAG「Top 3 相似已晉升」 - 對每筆 awaiting_review episode 跑 rag_service.query 找 ai_insights 中 cosine ≥ 0.7 的相似已晉升內容 - 輔助人工判斷:是否冗餘?是否新領域? - header 顯示 ai_insights 知識庫 size - fail-safe: 單筆 RAG 失敗不影響其餘 D-2: host_health 加 MCP 24h 工作量 widget - 從 mcp_calls 統計各 server 24h 呼叫次數 / 成功率 / cache 率 / 使用 tool 數 / 平均耗時 / cost - 展現「AI×MCP 編排規模」而非只「server 健康與否」 D-3: ai_calls × rag_query_log × mcp_calls 三表 JOIN - 新增「呼叫端 × RAG × MCP 編排矩陣」card - 每個 caller:總呼叫 / RAG 命中率 / MCP 編排率(透過 request_id 串接) / RAG 反饋分數 / 反饋筆數 - 展現「AI 自動化專業」核心指標 D-4: budget 加 RAG 自動策略建議 + 一鍵 force-throttle (L2) - ratio ≥ 0.8 時自動 RAG 召回 ai_insights 中的 budget_strategy 知識 - POST /budget/force_throttle endpoint:立即重算 cost_throttle 狀態 (不等下次每小時 cron)— 升級到 L2 自動化 - 對應頁面加「立即重算節流狀態」按鈕 D-5: host_health 加 incidents + heal_logs 7d 摘要 - 顯示 ADR-013 AutoHeal 閉環核心 KPI: 總事件 / 未解決 / 已解決 / P0+P1 / 自癒成功率 / 平均自癒耗時 - 展現「AIOps 自癒系統」運作實況 對應升級: - DB 利用率 22.7% → ~50%(新接 mcp_calls + rag_query_log JOIN + ai_insights + incidents + heal_logs) - MCP 整合 1/6 → 3/6(host_health + ai_calls + budget 都接 mcp_calls) - RAG 整合 0/6 → 3/6(promotion_review + budget + 待 quality_trend) - AI 自動化 L1 → L2 一鍵 force-throttle 一個(其餘按鈕待 D-6) 全部 fail-safe:DB 表/RAG/MCP 失敗都不擋頁面渲染。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
179 lines
7.9 KiB
HTML
179 lines
7.9 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}AI 呼叫總覽{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container-fluid mt-3">
|
||
<h2 class="mb-3"><i class="fas fa-chart-bar me-2"></i>AI 呼叫總覽
|
||
<small class="text-muted">過去 {{ hours }} 小時</small>
|
||
</h2>
|
||
|
||
{% if error %}
|
||
<div class="alert alert-warning"><strong><i class="fas fa-exclamation-triangle me-1"></i></strong> {{ error }}</div>
|
||
{% endif %}
|
||
|
||
<!-- 篩選 bar -->
|
||
<form method="get" class="row g-2 mb-3">
|
||
<div class="col-auto">
|
||
<select name="hours" class="form-select form-select-sm">
|
||
{% for h in [1, 6, 24, 72, 168] %}
|
||
<option value="{{ h }}" {% if hours == h %}selected{% endif %}>
|
||
{% if h < 24 %}{{ h }} 小時{% else %}{{ (h//24) }} 天{% endif %}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div class="col-auto">
|
||
<select name="caller" class="form-select form-select-sm">
|
||
<option value="">全部呼叫端</option>
|
||
{% for c in callers %}
|
||
<option value="{{ c }}" {% if caller_filter == c %}selected{% endif %}>{{ c }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div class="col-auto">
|
||
<select name="provider" class="form-select form-select-sm">
|
||
<option value="">全部供應商</option>
|
||
{% for p in ['gcp_ollama','ollama_secondary','ollama_111','gemini','claude','nim','openrouter','nim_via_elephant'] %}
|
||
<option value="{{ p }}" {% if provider_filter == p %}selected{% endif %}>{{ p }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div class="col-auto">
|
||
<button class="btn btn-primary btn-sm">篩選</button>
|
||
</div>
|
||
</form>
|
||
|
||
<!-- 總覽 KPI -->
|
||
<div class="row g-2 mb-3">
|
||
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>呼叫次數</small><h4>{{ "{:,}".format(summary.total_calls or 0) }}</h4></div></div>
|
||
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>Token 用量</small><h4>{{ "{:,}".format(summary.total_tokens or 0) }}</h4></div></div>
|
||
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>成本 (USD)</small><h4>${{ "%.2f"|format(summary.total_cost or 0) }}</h4></div></div>
|
||
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>平均耗時</small><h4>{{ summary.avg_duration or 0 }} ms</h4></div></div>
|
||
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>RAG 命中</small><h4 class="text-success">{{ summary.rag_hits or 0 }}</h4></div></div>
|
||
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>錯誤次數</small><h4 class="{% if (summary.error_calls or 0) > 0 %}text-danger{% endif %}">{{ summary.error_calls or 0 }}</h4></div></div>
|
||
</div>
|
||
|
||
<!-- Phase 39 D-3: caller × RAG × MCP 編排矩陣 -->
|
||
{% if caller_richness %}
|
||
<div class="card mb-3">
|
||
<div class="card-header"><strong><i class="fas fa-network-wired me-2"></i>呼叫端 × RAG × MCP 編排矩陣</strong>
|
||
<small class="text-muted">資料來源:ai_calls × mcp_calls × rag_query_log({{ hours }}h 內呼叫 ≥ 5 次的 caller)</small>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm mb-0">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>呼叫端</th>
|
||
<th class="text-end">總呼叫</th>
|
||
<th class="text-end">RAG 命中率</th>
|
||
<th class="text-end">MCP 編排率</th>
|
||
<th class="text-end">RAG 反饋分數</th>
|
||
<th class="text-end">反饋筆數</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for c in caller_richness %}
|
||
<tr>
|
||
<td><code>{{ c.caller }}</code></td>
|
||
<td class="text-end">{{ "{:,}".format(c.total_calls) }}</td>
|
||
<td class="text-end">
|
||
<strong class="{% if c.rag_hit_rate >= 50 %}text-success{% elif c.rag_hit_rate >= 20 %}text-warning{% else %}text-muted{% endif %}">
|
||
{{ "%.1f"|format(c.rag_hit_rate) }}%
|
||
</strong>
|
||
<small class="text-muted">({{ c.rag_hits }})</small>
|
||
</td>
|
||
<td class="text-end">
|
||
<strong class="{% if c.mcp_rate >= 30 %}text-info{% elif c.mcp_rate >= 10 %}text-warning{% endif %}">
|
||
{{ "%.1f"|format(c.mcp_rate) }}%
|
||
</strong>
|
||
<small class="text-muted">({{ c.mcp_orchestrated }})</small>
|
||
</td>
|
||
<td class="text-end">
|
||
{% if c.feedback_count > 0 %}
|
||
<strong class="{% if c.avg_rag_feedback >= 4 %}text-success{% elif c.avg_rag_feedback >= 3 %}text-warning{% else %}text-danger{% endif %}">
|
||
{{ "%.2f"|format(c.avg_rag_feedback) }}/5
|
||
</strong>
|
||
{% else %}
|
||
<small class="text-muted">—</small>
|
||
{% endif %}
|
||
</td>
|
||
<td class="text-end">{{ c.feedback_count }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="card-footer small text-muted">
|
||
<i class="fas fa-info-circle me-1"></i>
|
||
<strong>RAG 命中率</strong>:caller 在此期間有多少呼叫吃到 RAG 召回;
|
||
<strong>MCP 編排率</strong>:有多少呼叫透過 request_id 串接到 MCP tool;
|
||
<strong>反饋分數</strong>:RAG 召回後人工反饋(1-5)。
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- by provider -->
|
||
<div class="card mb-3">
|
||
<div class="card-header"><strong>依供應商分組</strong></div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm mb-0">
|
||
<thead class="table-light">
|
||
<tr><th>供應商</th><th class="text-end">呼叫數</th><th class="text-end">Token 用量</th><th class="text-end">成本 (USD)</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for row in by_provider %}
|
||
<tr>
|
||
<td><code>{{ row.provider }}</code></td>
|
||
<td class="text-end">{{ "{:,}".format(row.calls) }}</td>
|
||
<td class="text-end">{{ "{:,}".format(row.tokens) }}</td>
|
||
<td class="text-end">${{ "%.2f"|format(row.cost) }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- recent calls -->
|
||
<div class="card">
|
||
<div class="card-header"><strong>最近呼叫(最新 100 筆)</strong></div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm table-striped mb-0" style="font-size: 0.85em;">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>編號</th><th>時間</th><th>呼叫端</th><th>供應商</th><th>模型</th>
|
||
<th class="text-end">輸入</th><th class="text-end">輸出</th><th class="text-end">耗時 ms</th>
|
||
<th>狀態</th><th class="text-end">成本 $</th><th>標記</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for r in recent %}
|
||
<tr {% if r.status not in ['ok','cache_only'] %}class="table-warning"{% endif %}>
|
||
<td>{{ r.id }}</td>
|
||
<td><small>{{ r.called_at }}</small></td>
|
||
<td><code>{{ r.caller }}</code></td>
|
||
<td><small>{{ r.provider }}</small></td>
|
||
<td><small>{{ r.model[:25] }}</small></td>
|
||
<td class="text-end">{{ r.in_tokens }}</td>
|
||
<td class="text-end">{{ r.out_tokens }}</td>
|
||
<td class="text-end">{{ r.duration_ms }}</td>
|
||
<td><small>{{ r.status }}</small></td>
|
||
<td class="text-end">${{ "%.4f"|format(r.cost) }}</td>
|
||
<td>
|
||
{% if r.cache_hit %}<span class="badge bg-success">快取</span>{% endif %}
|
||
{% if r.rag_hit %}<span class="badge bg-info">RAG</span>{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="text-muted mt-2"><small>
|
||
<i class="fas fa-robot me-1"></i>Operation Ollama-First v5.0 / Phase 29 — AI 呼叫總覽
|
||
</small></p>
|
||
</div>
|
||
{% endblock %}
|