All checks were successful
CD Pipeline / deploy (push) Successful in 2m36s
統帥質疑:「6 頁內容太空洞,要更貼近資料庫裡所有數據」
盤點:DB 有 22 表,6 頁原本只用 5 表(22.7% 利用率)。
本 commit 新接 12 張既有但未用的表。
K-1 host_health(接 +5 表 → 8 表)
- 加 incidents 最近 10 筆詳細(task_name/error_type/嚴重度/狀態/重試/錯誤訊息)
- 加 heal_logs 最近 10 筆(action_type/result/耗時/incident 關聯)
- 加 playbooks 庫排行 TOP 12(success_count/fail_count/成功率/啟用狀態)
- 加 backup_log 7 日歷史(type/status/size/duration/error)
- 加 embedding_retry_queue pending/failed 警示
K-2 ai_calls(接 +3 表)
- 加 24h 每小時呼叫趨勢 bucket(含成本+錯誤+流量分布條)
- 加 by model 細分(不只 provider,到 model 版本級別)TOP 15
- 加 agent_context 最近 10 筆(OpenClaw/Hermes 對話 session preview)
K-3 budget(接 +3 表)
- 加當月 Top 5 燒錢呼叫端(caller × cost ranking)
- 加過去 30 日每日成本 by provider 趨勢表
- 加 ai_price_recommendations 7 日統計(strategy 分布 + 平均信心度)
K-4 promotion_review(接 +2 表)
- 加蒸餾池 30 日 status 分布(不只 awaiting,看 8 種完整流動)
- 加 ai_insights 最近 10 筆已晉升內容預覽
- 加 agent_strategy_weights TOP 12(OpenClaw 學習權重 + 成功率)
K-5 quality_trend(接 +3 表)
- 加 RAG 整體 feedback 1-5 分分布(過去 N 日,星等視覺)
- 加 action_plans status 分布(pending/approved/executed/rejected)
- 加 action_outcomes verdict 分布(effective/neutral/backfired
— ADR-012 閉環學習核心 KPI)
K-6 ppt_audit_history(接 +0 表,但 deeper SQL)
- 加 30 日統計卡(total/passed/failed/error/通過率/總 issue 數/平均信心度)
- 加 Top 10 反覆失敗檔案(30d 失敗次數 + total issues)
- 加 empty state 說明(PPT_VISION_ENABLED=false 時顯示啟用步驟)
DB 利用率對應:
- Phase 38 起點:5 表(22.7%)
- Phase 39-46 累計:12 表(54.5%)
- Phase 47 收官:17 表(77.3%)
新接:playbooks / backup_log / embedding_retry_queue / agent_context /
ai_price_recommendations / agent_strategy_weights / action_plans /
action_outcomes(之前已接:incidents/heal_logs/ai_insights/learning_episodes/
rag_query_log/mcp_calls/ai_calls/host_health_probes/ppt_audit_results)
每頁 widget 數對應:
- host_health: 5 卡 → 11 卡
- ai_calls: 5 卡 → 8 卡
- budget: 4 卡 → 7 卡
- promotion_review: 2 卡 → 5 卡
- quality_trend: 4 卡 → 7 卡
- ppt_audit: 3 卡 → 6 卡
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
258 lines
11 KiB
HTML
258 lines
11 KiB
HTML
{% extends "ewoooc_base.html" %}
|
||
|
||
{% block title %}PPT 視覺審核歷史{% endblock %}
|
||
|
||
{% block ewooo_content %}
|
||
<div class="container-fluid mt-3">
|
||
<h2 class="mb-3"><i class="fas fa-search me-2"></i>PPT 視覺審核歷史
|
||
<small class="text-muted">reports/ 目錄過去 7 日 .pptx</small>
|
||
</h2>
|
||
|
||
{% if error %}<div class="alert alert-warning"><strong><i class="fas fa-exclamation-triangle me-1"></i></strong> {{ error }}</div>{% endif %}
|
||
|
||
<div class="alert {% if vision_enabled %}alert-success{% else %}alert-secondary{% endif %} small">
|
||
<strong>PPT_VISION_ENABLED:</strong>
|
||
{% if vision_enabled %}
|
||
<i class="fas fa-check me-1"></i>已啟用 — 每日 22:00 cron 自動跑 minicpm-v 視覺檢查,發現問題推送 Telegram
|
||
{% else %}
|
||
<i class="fas fa-pause me-1"></i>未啟用 — 設 PPT_VISION_ENABLED=true 並在 188 主機安裝 LibreOffice 即生效
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="card mb-3">
|
||
<div class="card-header"><strong><i class="fas fa-folder-open me-2"></i>過去 7 日 PPT 檔案</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">大小 (KB)</th>
|
||
<th>修改時間</th><th>動作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for f in files %}
|
||
<tr>
|
||
<td><code>{{ f.name }}</code></td>
|
||
<td class="text-end">{{ f.size_kb }}</td>
|
||
<td><small>{{ f.mtime }}</small></td>
|
||
<td>
|
||
<small class="text-muted">由 audit cron 22:00 自動執行</small>
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="4" class="text-center text-muted">過去 7 日無 PPT 生成</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card mb-3">
|
||
<div class="card-header"><strong><i class="fas fa-history me-2"></i>視覺審核歷史紀錄(最近 100 筆)</strong></div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm mb-0">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<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>動作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for r in audit_records %}
|
||
<tr>
|
||
<td><small>{{ r.audited_at }}</small></td>
|
||
<td><code>{{ r.pptx_filename }}</code></td>
|
||
<td>
|
||
{% if r.audit_status == 'passed' %}
|
||
<span class="badge bg-success"><i class="fas fa-check me-1"></i>通過</span>
|
||
{% elif r.audit_status == 'failed' %}
|
||
<span class="badge bg-warning"><i class="fas fa-exclamation me-1"></i>有問題</span>
|
||
{% elif r.audit_status == 'skipped' %}
|
||
<span class="badge bg-secondary"><i class="fas fa-pause me-1"></i>跳過</span>
|
||
{% elif r.audit_status == 'error' %}
|
||
<span class="badge bg-danger"><i class="fas fa-times me-1"></i>錯誤</span>
|
||
{% else %}
|
||
<span class="badge bg-light text-dark">{{ r.audit_status }}</span>
|
||
{% endif %}
|
||
</td>
|
||
<td class="text-end">{{ r.issues_count }}</td>
|
||
<td class="text-end">{{ "%.2f"|format(r.confidence) }}</td>
|
||
<td class="text-end">{{ r.duration_ms }}</td>
|
||
<td><small class="text-muted">{{ (r.error_msg or '')[:80] }}</small></td>
|
||
<td>
|
||
{% if r.audit_status in ('failed', 'error') %}
|
||
<button class="btn btn-sm btn-outline-warning"
|
||
onclick="triggerAiderHeal({{ r.pptx_filename|tojson }}, {{ (r.error_msg or '')|tojson }})">
|
||
<i class="fas fa-wrench me-1"></i>AiderHeal
|
||
</button>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="8" class="text-center text-muted">尚無審核紀錄(migration 030 跑過後即會累積)</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{% if rag_fixes %}
|
||
<div class="card mb-3" style="border-left: 4px solid #6f42c1;">
|
||
<div class="card-header bg-light">
|
||
<strong><i class="fas fa-magic me-2"></i>RAG 自動修法建議</strong>
|
||
<small class="text-muted">— 對最近 3 筆 failed audit 從 ai_insights 召回相似案例修法</small>
|
||
</div>
|
||
<div class="card-body p-2">
|
||
{% for fix in rag_fixes %}
|
||
<div class="mb-2 p-2" style="background: #fafafa; border-radius: 6px;">
|
||
<strong><code>{{ fix.pptx_filename }}</code></strong>
|
||
<small class="text-muted ms-2">{{ fix.audited_at }}</small>
|
||
<div class="small text-danger mt-1">{{ fix.error_msg }}</div>
|
||
<ul class="list-unstyled mt-2 mb-0 small">
|
||
{% for h in fix.hits %}
|
||
<li class="mb-1">
|
||
<span class="badge bg-info text-dark me-1">{{ h.insight_type }}</span>
|
||
<span class="badge bg-light text-dark me-1">相似度 {{ "%.2f"|format(h.similarity) }}</span>
|
||
{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<p class="text-muted mt-2 small">
|
||
審核結果:<strong>有問題才推 Telegram</strong>(避免靜默無問題洗版)。
|
||
手動觸發單檔審核需 SSH 到 188 主機執行:
|
||
<code>python3 -c "from services.ppt_vision_service import ppt_vision_service; print(ppt_vision_service.check_ppt_file('reports/xxx.pptx'))"</code>
|
||
</p>
|
||
|
||
<!-- Phase 47 K-6: 30d 統計 -->
|
||
{% if audit_30d_stats and audit_30d_stats.total > 0 %}
|
||
<div class="card mb-3">
|
||
<div class="card-header"><strong><i class="fas fa-chart-pie me-2"></i>過去 30 日 PPT 審核統計</strong>
|
||
<small class="text-muted">資料來源:ppt_audit_results 全表聚合</small>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row g-2">
|
||
<div class="col-md-2 col-sm-4">
|
||
<div class="border rounded p-2 text-center">
|
||
<small class="text-muted d-block">總筆數</small>
|
||
<strong style="font-size: 1.4em;">{{ audit_30d_stats.total }}</strong>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-2 col-sm-4">
|
||
<div class="border rounded p-2 text-center">
|
||
<small class="text-muted d-block">通過</small>
|
||
<strong class="text-success" style="font-size: 1.4em;">{{ audit_30d_stats.passed }}</strong>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-2 col-sm-4">
|
||
<div class="border rounded p-2 text-center">
|
||
<small class="text-muted d-block">失敗</small>
|
||
<strong class="{% if audit_30d_stats.failed > 0 %}text-warning{% endif %}" style="font-size: 1.4em;">{{ audit_30d_stats.failed }}</strong>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-2 col-sm-4">
|
||
<div class="border rounded p-2 text-center">
|
||
<small class="text-muted d-block">錯誤</small>
|
||
<strong class="{% if audit_30d_stats.error > 0 %}text-danger{% endif %}" style="font-size: 1.4em;">{{ audit_30d_stats.error }}</strong>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-2 col-sm-4">
|
||
<div class="border rounded p-2 text-center">
|
||
<small class="text-muted d-block">通過率</small>
|
||
<strong class="{% if audit_30d_stats.pass_rate >= 80 %}text-success{% elif audit_30d_stats.pass_rate >= 60 %}text-warning{% else %}text-danger{% endif %}" style="font-size: 1.4em;">{{ "%.0f"|format(audit_30d_stats.pass_rate) }}%</strong>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-2 col-sm-4">
|
||
<div class="border rounded p-2 text-center">
|
||
<small class="text-muted d-block">總 issue 數</small>
|
||
<strong style="font-size: 1.4em;">{{ audit_30d_stats.total_issues }}</strong>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="mt-2 small text-muted">
|
||
<i class="fas fa-info-circle me-1"></i>
|
||
通過 audit 平均信心度:<strong>{{ "%.2f"|format(audit_30d_stats.avg_confidence) }}</strong>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Phase 47 K-6: Top 10 failure files -->
|
||
{% if top_failure_files %}
|
||
<div class="card mb-3" style="border-left: 4px solid #ffc107;">
|
||
<div class="card-header bg-light">
|
||
<strong><i class="fas fa-exclamation-triangle me-2"></i>Top 10 失敗檔案(30d)</strong>
|
||
<small class="text-muted">— 反覆失敗的 PPT 檔案,需手動處理或調整 generator</small>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm mb-0" style="font-size: 0.9em;">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>檔名</th>
|
||
<th class="text-end">失敗次數</th>
|
||
<th class="text-end">總 issue</th>
|
||
<th>最近審核</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for f in top_failure_files %}
|
||
<tr>
|
||
<td><code>{{ f.filename }}</code></td>
|
||
<td class="text-end"><span class="badge bg-warning text-dark">{{ f.attempts }}</span></td>
|
||
<td class="text-end">{{ f.total_issues }}</td>
|
||
<td><small>{{ f.last_audit }}</small></td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if (not audit_30d_stats or audit_30d_stats.total == 0) and not vision_enabled %}
|
||
<div class="alert alert-info mt-3">
|
||
<i class="fas fa-info-circle me-1"></i>
|
||
<strong>為什麼這頁空?</strong>
|
||
<ul class="mb-0 small mt-2">
|
||
<li>PPT_VISION_ENABLED=false(在 .env 設為 true 啟用)</li>
|
||
<li>188 主機需安裝 LibreOffice:<code>apt install libreoffice</code></li>
|
||
<li>需 Ollama 拉取 minicpm-v 模型(用於 PPT 視覺檢查)</li>
|
||
<li>啟用後每日 22:00 cron 自動掃當天新生 .pptx,審核結果寫入 ppt_audit_results 累積歷史</li>
|
||
</ul>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<p class="text-muted mt-3"><small>
|
||
<i class="fas fa-robot me-1"></i>Operation Ollama-First v5.0 / Phase 47 — PPT 視覺審核歷史
|
||
(3 表深挖:ppt_audit_results / reports/ 檔案系統 / ai_insights RAG)
|
||
</small></p>
|
||
</div>
|
||
|
||
<script>
|
||
async function triggerAiderHeal(pptxFilename, errorMsg) {
|
||
if (!confirm(`觸發 AiderHeal 自動修復?\n\n檔案:${pptxFilename}\n錯誤:${(errorMsg || '').substring(0, 200)}\n\nAiderHeal 會嘗試修 services/ppt_generator.py 並 git push 到 main 觸發 CD。`)) return;
|
||
try {
|
||
const r = await fetch('/observability/ppt_audit/trigger_aider_heal', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify({pptx_filename: pptxFilename, error_msg: errorMsg || ''}),
|
||
});
|
||
const d = await r.json();
|
||
if (d.ok) {
|
||
alert(`✅ AiderHeal 已派出\n動作:${d.action || '—'}\n訊息:${d.message || ''}`);
|
||
} else {
|
||
alert('❌ ' + (d.error || d.message || '觸發失敗'));
|
||
}
|
||
} catch (e) {
|
||
alert('Error: ' + e);
|
||
}
|
||
}
|
||
</script>
|
||
{% endblock %}
|