From 8a7eed3505791147ed4544d7ff56d95b45442d8e Mon Sep 17 00:00:00 2001 From: OoO Date: Wed, 13 May 2026 19:59:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86=E8=A7=80=E6=B8=AC=E5=8F=B0?= =?UTF-8?q?=E9=A1=AF=E7=A4=BA=E5=AD=97=E5=85=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.py | 2 +- templates/admin/_observability_labels.html | 144 ++++++++++++++++++++ templates/admin/ai_calls_dashboard.html | 9 +- templates/admin/budget.html | 14 +- templates/admin/business_intel.html | 35 +++-- templates/admin/observability_overview.html | 7 +- templates/admin/ppt_audit_history.html | 9 +- templates/admin/promotion_review.html | 9 +- templates/admin/quality_trend.html | 9 +- templates/admin/rag_queries.html | 4 +- 10 files changed, 204 insertions(+), 38 deletions(-) create mode 100644 templates/admin/_observability_labels.html diff --git a/config.py b/config.py index f5df2f5..4db4ffb 100644 --- a/config.py +++ b/config.py @@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.122" +SYSTEM_VERSION = "V10.123" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/templates/admin/_observability_labels.html b/templates/admin/_observability_labels.html new file mode 100644 index 0000000..aea0a18 --- /dev/null +++ b/templates/admin/_observability_labels.html @@ -0,0 +1,144 @@ +{% macro strategy(value, fallback='未分類策略') -%} + {%- set labels = { + 'product_pick': '選品攻擊', + 'price_recommendation': '價格建議', + 'price_adjustment': '價格調整', + 'price_reduction': '降價攻擊', + 'price_drop': '降價攻擊', + 'discount_attack': '降價攻擊', + 'price_increase': '漲價防守', + 'margin_repair': '毛利修復', + 'inventory_clearance': '庫存去化', + 'competitor_response': '競品回應', + 'competitor_check': '競品複核', + 'promotion': '活動促銷', + 'stockout': '缺貨處理', + 'repricing': '價格重定', + 'watch': '觀察', + 'maintain': '維持價格', + 'unknown': '未分類策略' + } -%} + {%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%} +{%- endmacro %} + +{% macro status(value, fallback='未分類') -%} + {%- set labels = { + 'pending': '待處理', + 'awaiting_review': '待審核', + 'approved': '已核准', + 'rejected': '已拒絕', + 'rejected_quality': '品質拒絕', + 'rejected_hallucination': '幻覺拒絕', + 'rejected_duplicate': '重複拒絕', + 'rejected_human': '人工拒絕', + 'expired': '已過期', + 'done': '已完成', + 'completed': '已完成', + 'running': '執行中', + 'success': '成功', + 'failed': '失敗', + 'matched': '已比對', + 'unmatched': '未比對', + 'ok': '正常', + 'cache_only': '只用快取', + 'passed': '已通過', + 'error': '錯誤', + 'skipped': '已跳過', + 'unknown': '未分類' + } -%} + {%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%} +{%- endmacro %} + +{% macro verdict(value, fallback='未分類') -%} + {%- set labels = { + 'effective': '有效', + 'success': '成功', + 'positive': '正向', + 'backfired': '反效果', + 'negative': '負向', + 'failed': '失敗', + 'neutral': '中性', + 'pending': '待回收', + 'inconclusive': '尚未定論', + 'no_data': '無資料', + 'unknown': '未分類' + } -%} + {%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%} +{%- endmacro %} + +{% macro plan_type(value, fallback='未分類計畫') -%} + {%- set labels = { + 'action_plan': '行動計畫', + 'price_adjustment': '價格調整', + 'product_pick': '選品攻擊', + 'promotion_review': '活動複核', + 'stockout_followup': '缺貨跟進', + 'competitor_check': '競品複核', + 'margin_repair': '毛利修復', + 'strategy_review': '策略複核', + 'quality_review': '品質複核' + } -%} + {%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%} +{%- endmacro %} + +{% macro metric(value, fallback='未分類指標') -%} + {%- set labels = { + 'sales': '銷售', + 'revenue': '業績', + 'margin': '毛利', + 'profit': '毛利', + 'conversion': '轉換', + 'price': '價格', + 'stock': '庫存', + 'orders': '訂單', + 'unknown': '未分類指標' + } -%} + {%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%} +{%- endmacro %} + +{% macro insight(value, fallback='未分類洞察') -%} + {%- set labels = { + 'product_pick': '選品攻擊', + 'price_recommendation': '價格建議', + 'competitor_price': '競品價格', + 'sales_anomaly': '業績異常', + 'budget_strategy': '預算策略', + 'rag_feedback': 'RAG 反饋', + 'ppt_audit': 'PPT 審核', + 'quality_issue': '品質問題', + 'promotion': '活動促銷', + 'market_signal': '市場訊號', + 'strategy': '策略洞察', + 'unknown': '未分類洞察' + } -%} + {%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%} +{%- endmacro %} + +{% macro provider(value, fallback='未分類供應商') -%} + {%- set labels = { + 'gcp_ollama': '主力 Ollama', + 'ollama_secondary': '備援 Ollama', + 'ollama_111': '111 Ollama', + 'nim_via_elephant': 'NIM Elephant', + 'gemini': 'Gemini', + 'claude': 'Claude', + 'nim': 'NIM', + 'openrouter': 'OpenRouter', + 'unknown': '未分類供應商' + } -%} + {%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%} +{%- endmacro %} + +{% macro source(value, fallback='未分類來源') -%} + {%- set labels = { + 'ai_insights': 'AI 知識庫', + 'ai_learning_episodes': '學習片段', + 'ai_price_recommendations': 'AI 價格建議', + 'action_plans': '行動計畫', + 'action_outcomes': '實際結果', + 'competitor_match_attempts': '競品比對', + 'competitor_price_history': '競品價格歷史', + 'ppt_audit_results': 'PPT 審核結果' + } -%} + {%- if value -%}{{ labels.get(value, value|replace('_', ' ')) }}{%- else -%}{{ fallback }}{%- endif -%} +{%- endmacro %} diff --git a/templates/admin/ai_calls_dashboard.html b/templates/admin/ai_calls_dashboard.html index 9e2cf10..b07bb6b 100644 --- a/templates/admin/ai_calls_dashboard.html +++ b/templates/admin/ai_calls_dashboard.html @@ -42,6 +42,7 @@ @media (max-width: 720px) { .calls-command, .calls-mini-grid { grid-template-columns:1fr; } } +{% import "admin/_observability_labels.html" as obs_label %} {% set total = summary.total_calls or 0 %} {% set errors = summary.error_calls or 0 %} {% set error_rate = (errors / total * 100) if total > 0 else 0 %} @@ -58,7 +59,7 @@
- +
@@ -98,7 +99,7 @@
{% for row in by_provider[:4] %} -
{{ row.provider }}{{ "{:,}".format(row.calls) }}${{ "%.2f"|format(row.cost) }} · {{ "{:,}".format(row.tokens) }} tk
+
{{ obs_label.provider(row.provider) }}{{ "{:,}".format(row.calls) }}${{ "%.2f"|format(row.cost) }} · {{ "{:,}".format(row.tokens) }} 權杖
{% else %}
尚無供應商資料
{% endfor %}
@@ -118,13 +119,13 @@ {% if by_model %}
模型成本

依模型細分

-
{% for m in by_model %}{% endfor %}
模型供應商呼叫權杖成本耗時錯誤
{{ m.model[:35] }}{{ m.provider }}{{ "{:,}".format(m.calls) }}{{ "{:,}".format(m.tokens) }}${{ "%.4f"|format(m.cost) }}{{ m.avg_ms }} ms{% if m.errors > 0 %}{{ m.errors }}{% else %}0{% endif %}
+
{% for m in by_model %}{% endfor %}
模型供應商呼叫權杖成本耗時錯誤
{{ m.model[:35] }}{{ obs_label.provider(m.provider) }}{{ "{:,}".format(m.calls) }}{{ "{:,}".format(m.tokens) }}${{ "%.4f"|format(m.cost) }}{{ m.avg_ms }} ms{% if m.errors > 0 %}{{ m.errors }}{% else %}0{% endif %}
{% endif %}
最近呼叫

最近呼叫 100 筆

-
{% for r in recent %}{% endfor %}
編號時間呼叫端供應商模型輸入輸出耗時狀態成本標記
{{ r.id }}{{ r.called_at }}{{ r.caller }}{{ r.provider }}{{ r.model[:25] }}{{ r.in_tokens }}{{ r.out_tokens }}{{ r.duration_ms }}{{ r.status }}${{ "%.4f"|format(r.cost) }}{% if r.cache_hit %}快取{% endif %}{% if r.rag_hit %}RAG{% endif %}
+
{% for r in recent %}{% endfor %}
編號時間呼叫端供應商模型輸入輸出耗時狀態成本標記
{{ r.id }}{{ r.called_at }}{{ r.caller }}{{ obs_label.provider(r.provider) }}{{ r.model[:25] }}{{ r.in_tokens }}{{ r.out_tokens }}{{ r.duration_ms }}{{ obs_label.status(r.status, '-') }}${{ "%.4f"|format(r.cost) }}{% if r.cache_hit %}快取{% endif %}{% if r.rag_hit %}RAG{% endif %}

Ollama 優先策略 v5.0 — AI 流量控制塔

diff --git a/templates/admin/budget.html b/templates/admin/budget.html index 08c014a..bb53b68 100644 --- a/templates/admin/budget.html +++ b/templates/admin/budget.html @@ -31,6 +31,7 @@ @media (max-width:720px){ .gov-command,.gov-mini-grid{grid-template-columns:1fr;} } +{% import "admin/_observability_labels.html" as obs_label %} {% set total_budget = namespace(value=0) %}{% set total_spent = namespace(value=0) %}{% set warn_count = namespace(value=0) %}{% set throttled_count = namespace(value=0) %} {% for r in rows %}{% set total_budget.value = total_budget.value + (r.budget_usd or 0) %}{% set total_spent.value = total_spent.value + (r.spent or 0) %}{% if r.ratio >= 0.8 %}{% set warn_count.value = warn_count.value + 1 %}{% endif %}{% if r.throttled %}{% set throttled_count.value = throttled_count.value + 1 %}{% endif %}{% endfor %} {% set total_ratio = (total_spent.value / total_budget.value * 100) if total_budget.value > 0 else 0 %} @@ -40,7 +41,7 @@
AI 成本治理 · 預算 / 節流 / RAG 策略

AI 成本治理艙

這頁回答一個問題:AI 中樞花錢是否仍在治理邊界內?預算、實際支出、月底推估、節流狀態與 RAG 策略建議集中在同一個工作台。

-
超過 110% 時不用等 排程,直接重算供應商節流。
+
超過 110% 時不用等排程,直接重算供應商節流。
當月花費
${{ "%.2f"|format(total_spent.value) }}
預算 ${{ "%.2f"|format(total_budget.value) }}
預算使用率
{{ "%.0f"|format(total_ratio) }}%
全供應商加總
@@ -55,7 +56,7 @@
預算線

預算線與節流狀態

-
{% for r in rows %}= 0.8 %}class="table-warning"{% endif %}>{% else %}{% endfor %}
週期供應商已花費預算閾值使用率狀態動作
{{ r.period }}{{ r.provider }}${{ "%.2f"|format(r.spent) }}{{ "%.0f"|format(r.ratio * 100) }}%{% if r.throttled %}已節流{% elif r.ratio >= 0.8 %}接近上限{% else %}正常{% endif %}
無預算資料(需先跑 migrations/025)
+
{% for r in rows %}= 0.8 %}class="table-warning"{% endif %}>{% else %}{% endfor %}
週期供應商已花費預算閾值使用率狀態動作
{{ r.period }}{{ obs_label.provider(r.provider) }}${{ "%.2f"|format(r.spent) }}{{ "%.0f"|format(r.ratio * 100) }}%{% if r.throttled %}已節流{% elif r.ratio >= 0.8 %}接近上限{% else %}正常{% endif %}
無預算資料(需先跑 migrations/025)
{% if cost_trend_30d %} @@ -75,11 +76,11 @@ {% if budget_strategies %} -
RAG 策略

RAG 自動策略建議

{% for s in budget_strategies %}
{{ s.insight_type }}相似度 {{ "%.2f"|format(s.similarity) }}{{ s.content }}{% if s.content|length >= 240 %}…{% endif %}
{% endfor %}
+
RAG 策略

RAG 自動策略建議

{% for s in budget_strategies %}
{{ obs_label.insight(s.insight_type) }}相似度 {{ "%.2f"|format(s.similarity) }}{{ s.content }}{% if s.content|length >= 240 %}…{% endif %}
{% endfor %}
{% endif %} {% if price_rec_7d %} -
商業產出

AI 價格決策 7 日

{% for p in price_rec_7d %}
{{ p.strategy }}{{ p.count }}信心 {{ "%.2f"|format(p.avg_confidence) }}
{% endfor %}
+
商業產出

AI 價格決策 7 日

{% for p in price_rec_7d %}
{{ obs_label.strategy(p.strategy) }}{{ p.count }}信心 {{ "%.2f"|format(p.avg_confidence) }}
{% endfor %}
{% endif %}

Ollama 優先策略 v5.0 — AI 成本治理艙

@@ -87,8 +88,9 @@ diff --git a/templates/admin/business_intel.html b/templates/admin/business_intel.html index d4104a2..178c5c8 100644 --- a/templates/admin/business_intel.html +++ b/templates/admin/business_intel.html @@ -417,6 +417,7 @@ {% endblock %} {% block ewooo_content %} +{% import "admin/_observability_labels.html" as obs_label %} {% set rec_total = rec_by_strategy|sum(attribute='count') if rec_by_strategy else 0 %} {% set ns = namespace(conf_total=0, effective=0, backfired=0, neutral=0, verdict_total=0) %} {% for r in rec_by_strategy %} @@ -497,7 +498,7 @@ {% if unfollowed_count > 0 %}
-
{{ unfollowed_count }} 筆高信心 AI 價格建議尚未跟進,建議優先轉為 action_plan 或標記原因。
+
{{ unfollowed_count }} 筆高信心 AI 價格建議尚未跟進,建議優先轉為行動計畫或標記原因。
需人工決策
{% endif %} @@ -508,7 +509,7 @@

策略族群雷達

-

把 AI 價格建議依 strategy 聚合,快速判斷目前主攻降價、防守或毛利修復。

+

把 AI 價格建議依策略類型聚合,快速判斷目前主攻降價、防守或毛利修復。

{{ rec_by_strategy|length }} 類策略
@@ -517,7 +518,7 @@
{% for r in rec_by_strategy %}
-
{{ r.strategy or '未分類策略' }}
+
{{ obs_label.strategy(r.strategy) }}
{{ r.count }}建議數
{{ '%.0f'|format((r.avg_confidence or 0) * 100) }}%信心
@@ -553,7 +554,7 @@
{{ r.name or '-' }}
- {{ r.strategy or '-' }} + {{ obs_label.strategy(r.strategy, '-') }} {{ '%.0f'|format((r.confidence or 0) * 100) }}%
@@ -596,12 +597,12 @@ {% for r in loop_records %} - #{{ r.plan_id }}
{{ r.plan_type or '-' }} + #{{ r.plan_id }}
{{ obs_label.plan_type(r.plan_type, '-') }} {{ r.sku }} - {{ r.status or '-' }} + {{ obs_label.status(r.status, '-') }} {{ r.created_at or '-' }} / {{ r.executed_at or '-' }} - {{ r.verdict or '-' }} - {{ r.metric_type or '-' }}
{{ r.before or '-' }} → {{ r.after or '-' }} + {{ obs_label.verdict(r.verdict, '-') }} + {{ obs_label.metric(r.metric_type, '-') }}
{{ r.before or '-' }} → {{ r.after or '-' }} {{ '%.1f'|format(r.change_pct or 0) }}% {% endfor %} @@ -629,7 +630,7 @@ 結論數量平均變化 {% for v in verdict_stats %} - {{ v.verdict or '未分類' }}{{ v.count }}{{ '%.1f'|format(v.avg_delta or 0) }}% + {{ obs_label.verdict(v.verdict) }}{{ v.count }}{{ '%.1f'|format(v.avg_delta or 0) }}% {% endfor %} @@ -653,7 +654,7 @@ {% for m in match_stats %} - {{ m.status or '-' }} + {{ obs_label.status(m.status, '-') }} {{ m.count }} {{ '%.1f'|format(m.avg_candidates or 0) }} {{ '%.2f'|format(m.avg_score or 0) }} @@ -708,11 +709,23 @@ const verdictRows = {{ verdict_stats|tojson }}; const canvas = document.getElementById('verdictPieChart'); if (!canvas || !verdictRows || verdictRows.length === 0) return; + const verdictLabelMap = { + effective: '有效', + success: '成功', + positive: '正向', + backfired: '反效果', + negative: '負向', + failed: '失敗', + neutral: '中性', + pending: '待回收', + inconclusive: '尚未定論', + no_data: '無資料' + }; new Chart(canvas, { type: 'doughnut', data: { - labels: verdictRows.map(row => row.verdict || '未分類'), + labels: verdictRows.map(row => verdictLabelMap[row.verdict] || row.verdict || '未分類'), datasets: [{ data: verdictRows.map(row => row.count || 0), backgroundColor: ['#2f8f6b', '#c96442', '#f1b45a', '#6d4b3f', '#d9a06f'], diff --git a/templates/admin/observability_overview.html b/templates/admin/observability_overview.html index 7c337f0..5bd6748 100644 --- a/templates/admin/observability_overview.html +++ b/templates/admin/observability_overview.html @@ -406,6 +406,7 @@ } +{% 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) %} @@ -507,7 +508,7 @@ {% for b in summary.budget_alerts %} {{ b.period }} - {{ b.provider }} + {{ obs_label.provider(b.provider) }} ${{ "%.2f"|format(b.spent) }} ${{ "%.2f"|format(b.budget) }} {{ "%.0f"|format(b.ratio * 100) }}% @@ -609,9 +610,9 @@

RAG 與品質

diff --git a/templates/admin/ppt_audit_history.html b/templates/admin/ppt_audit_history.html index a587e03..f592263 100644 --- a/templates/admin/ppt_audit_history.html +++ b/templates/admin/ppt_audit_history.html @@ -8,14 +8,15 @@ .ppt-hero{padding:clamp(1.2rem,2.4vw,2rem);background:radial-gradient(circle at 12% 14%,rgba(201,100,66,.18),transparent 24rem),radial-gradient(circle at 88% 8%,rgba(79,111,143,.14),transparent 22rem),linear-gradient(135deg,rgba(255,248,239,.98),rgba(255,255,255,.74))}.ppt-kicker{color:var(--obs-accent);font-size:.76rem;letter-spacing:.13em;text-transform:uppercase;font-weight:850}.ppt-title{margin:.45rem 0 .25rem;font-family:'Noto Sans TC','Inter',sans-serif;font-size:var(--obs-title-size);letter-spacing:-.055em;line-height:.98}.ppt-subtitle{color:var(--obs-muted);max-width:860px;line-height:1.7}.ppt-command{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.75rem;margin-top:1rem}.ppt-signal{padding:.95rem;border:1px solid var(--obs-line);border-radius:20px;background:rgba(255,255,255,.62)}.ppt-label{color:var(--obs-muted);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase}.ppt-value{display:block;margin-top:.28rem;font-size:var(--obs-value-size);font-weight:880;letter-spacing:-.045em}.ppt-grid{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(330px,.8fr);gap:1rem;margin-top:1rem}.ppt-stack{display:grid;gap:1rem}.ppt-panel-head,.ppt-table-title{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;padding:1.05rem 1.1rem .25rem}.ppt-panel-title,.ppt-table-title h3{margin:.15rem 0 0;font-size:1.1rem;font-weight:850;letter-spacing:-.025em}.ppt-panel-body{padding:1rem 1.1rem 1.1rem}.ppt-table-shell{overflow:hidden;margin-top:1rem}.ppt-mini-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.7rem}.ppt-mini{padding:.85rem;border:1px solid var(--obs-line);border-radius:18px;background:rgba(255,255,255,.58)}.ppt-mini strong{display:block;margin-top:.24rem;font-size:1.35rem;letter-spacing:-.04em}.fix-card{padding:.85rem;border:1px solid var(--obs-line);border-radius:18px;background:rgba(255,255,255,.58);margin-bottom:.7rem}.status-good{color:var(--obs-green)}.status-warn{color:var(--obs-amber)}.status-bad{color:var(--obs-red)}.status-blue{color:var(--obs-blue)}@media(max-width:1100px){.ppt-command{grid-template-columns:repeat(2,minmax(0,1fr))}.ppt-grid{grid-template-columns:1fr}}@media(max-width:720px){.ppt-command,.ppt-mini-grid{grid-template-columns:1fr}} +{% import "admin/_observability_labels.html" as obs_label %}
-
PPT 視覺 QA 產線 · minicpm-v / AiderHeal / RAG 修法

PPT 視覺 QA 產線

這頁追蹤每份自動簡報是否通過視覺審核:檔案產出、minicpm-v 審核、Telegram 推送、RAG 修法建議與 AiderHeal 自動修 generator。

視覺模型
{{ '啟用' if vision_enabled else '停用' }}PPT_VISION_ENABLED
30 日總量
{{ audit_30d_stats.total if audit_30d_stats else 0 }}審核紀錄
通過率
{{ "%.0f"|format(audit_30d_stats.pass_rate) if audit_30d_stats else '—' }}{% if audit_30d_stats %}%{% endif %}過去 30 日
問題數
{{ audit_30d_stats.total_issues if audit_30d_stats else 0 }}視覺問題數
+
PPT 視覺 QA 產線 · minicpm-v / AiderHeal / RAG 修法

PPT 視覺 QA 產線

這頁追蹤每份自動簡報是否通過視覺審核:檔案產出、minicpm-v 審核、Telegram 推送、RAG 修法建議與 AiderHeal 自動修產生器。

視覺模型
{{ '啟用' if vision_enabled else '停用' }}PPT_VISION_ENABLED
30 日總量
{{ audit_30d_stats.total if audit_30d_stats else 0 }}審核紀錄
通過率
{{ "%.0f"|format(audit_30d_stats.pass_rate) if audit_30d_stats else '—' }}{% if audit_30d_stats %}%{% endif %}過去 30 日
問題數
{{ audit_30d_stats.total_issues if audit_30d_stats else 0 }}視覺問題數
{% if error %}
{{ error }}
{% endif %}
審核歷史

視覺審核歷史 100 筆

{% for r in audit_records %}{% else %}{% endfor %}
時間檔名結果問題信心耗時錯誤動作
{{ r.audited_at }}{{ r.pptx_filename }}{% if r.audit_status == 'passed' %}通過{% elif r.audit_status == 'failed' %}有問題{% elif r.audit_status == 'error' %}錯誤{% elif r.audit_status == 'skipped' %}跳過{% else %}{{ r.audit_status }}{% endif %}{{ r.issues_count }}{{ "%.2f"|format(r.confidence) }}{{ r.duration_ms }}{{ (r.error_msg or '')[:80] }}{% if r.audit_status in ('failed','error') %}{% endif %}
尚無審核紀錄
-
已產檔案

過去 7 日 PPT 檔案

{% for f in files %}{% else %}{% endfor %}
檔名KB修改時間狀態
{{ f.name }}{{ f.size_kb }}{{ f.mtime }}22:00 cron 自動審核
過去 7 日無 PPT 生成
+
已產檔案

過去 7 日 PPT 檔案

{% for f in files %}{% else %}{% endfor %}
檔名KB修改時間狀態
{{ f.name }}{{ f.size_kb }}{{ f.mtime }}22:00 排程自動審核
過去 7 日無 PPT 生成
- {% if rag_fixes %}
RAG 修法建議

RAG 自動修法建議

{% for fix in rag_fixes %}
{{ fix.pptx_filename }}{{ fix.audited_at }}
{{ fix.error_msg }}
    {% for h in fix.hits %}
  • {{ h.insight_type }}相似度 {{ "%.2f"|format(h.similarity) }}{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
  • {% endfor %}
{% endfor %}
{% endif %} - {% if (not audit_30d_stats or audit_30d_stats.total == 0) and not vision_enabled %}
為什麼這頁空?
  • PPT_VISION_ENABLED=false
  • 188 主機需安裝 LibreOffice
  • 需 Ollama 拉取 minicpm-v 模型
  • 啟用後每日 22:00 cron 寫入 ppt_audit_results
{% endif %} + {% if rag_fixes %}
RAG 修法建議

RAG 自動修法建議

{% for fix in rag_fixes %}
{{ fix.pptx_filename }}{{ fix.audited_at }}
{{ fix.error_msg }}
    {% for h in fix.hits %}
  • {{ obs_label.insight(h.insight_type) }}相似度 {{ "%.2f"|format(h.similarity) }}{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
  • {% endfor %}
{% endfor %}
{% endif %} + {% if (not audit_30d_stats or audit_30d_stats.total == 0) and not vision_enabled %}
為什麼這頁空?
  • PPT_VISION_ENABLED=false
  • 188 主機需安裝 LibreOffice
  • 需 Ollama 拉取 minicpm-v 模型
  • 啟用後每日 22:00 排程寫入 ppt_audit_results
{% endif %}

Ollama 優先策略 v5.0 — PPT 視覺 QA 產線

diff --git a/templates/admin/promotion_review.html b/templates/admin/promotion_review.html index 70c35a9..ebb0467 100644 --- a/templates/admin/promotion_review.html +++ b/templates/admin/promotion_review.html @@ -32,6 +32,7 @@ @media (max-width:720px){ .gate-command,.gate-mini-grid{grid-template-columns:1fr;} .episode-head{display:block;} } +{% import "admin/_observability_labels.html" as obs_label %} {% set total_dist = (episode_distribution_30d.values() | sum) if episode_distribution_30d else 0 %} {% set approved_30d = episode_distribution_30d.get('approved', 0) if episode_distribution_30d else 0 %} {% set rejected_30d = namespace(value=0) %} @@ -66,12 +67,12 @@ {% for ep in episodes %}
-
學習片段 #{{ ep.id }} {{ ep.episode_type }}{% if ep.source_table %}{{ ep.source_table }}#{{ ep.source_id }}{% endif %}權重 {{ "%.2f"|format(ep.weight) }}品質 {{ "%.2f"|format(ep.quality_score) }}
+
學習片段 #{{ ep.id }} {{ obs_label.insight(ep.episode_type) }}{% if ep.source_table %}{{ obs_label.source(ep.source_table) }} #{{ ep.source_id }}{% endif %}權重 {{ "%.2f"|format(ep.weight) }}品質 {{ "%.2f"|format(ep.quality_score) }}
{{ ep.created_at }}
{{ ep.distilled_text }}
- {% if ep.similar_insights %}
Top 3 相似已晉升知識(用來判斷是否重複)
    {% for sim in ep.similar_insights %}
  • #{{ sim.id }}{{ sim.insight_type }}相似度 {{ "%.2f"|format(sim.similarity) }}{{ sim.content }}{% if sim.content|length >= 180 %}…{% endif %}
  • {% endfor %}
{% else %}
知識庫無 cosine ≥ 0.7 相似內容,可能是新領域知識。
{% endif %} + {% if ep.similar_insights %}
Top 3 相似已晉升知識(用來判斷是否重複)
    {% for sim in ep.similar_insights %}
  • #{{ sim.id }}{{ obs_label.insight(sim.insight_type) }}相似度 {{ "%.2f"|format(sim.similarity) }}{{ sim.content }}{% if sim.content|length >= 180 %}…{% endif %}
  • {% endfor %}
{% else %}
知識庫無相似度 ≥ 0.7 的相似內容,可能是新領域知識。
{% endif %}
@@ -86,7 +87,7 @@
蒸餾池

30 日狀態分布

{% endif %} {% if strategy_weights %} -
OpenClaw 權重

策略權重 Top

{% for s in strategy_weights[:6] %}
{{ s.strategy_key[:22] }}{{ "%.2f"|format(s.weight) }}成功 {{ s.success }} · 失敗 {{ s.fail }}
{% endfor %}
+
OpenClaw 權重

策略權重 Top

{% for s in strategy_weights[:6] %}
{{ obs_label.strategy(s.strategy_key) }}{{ "%.2f"|format(s.weight) }}成功 {{ s.success }} · 失敗 {{ s.fail }}
{% endfor %}
{% endif %} @@ -102,6 +103,6 @@ {% endblock %} diff --git a/templates/admin/quality_trend.html b/templates/admin/quality_trend.html index 07186cc..9a7a790 100644 --- a/templates/admin/quality_trend.html +++ b/templates/admin/quality_trend.html @@ -12,6 +12,7 @@ @media(max-width:1100px){.quality-command{grid-template-columns:repeat(2,minmax(0,1fr))}.quality-grid{grid-template-columns:1fr}}@media(max-width:720px){.quality-command,.quality-mini-grid{grid-template-columns:1fr}} +{% import "admin/_observability_labels.html" as obs_label %} {% set total_feedback = namespace(value=0) %}{% set worst_avg = namespace(value=5) %}{% for caller, info in trends %}{% set total_feedback.value = total_feedback.value + (info.total_feedback or 0) %}{% if info.avg_score < worst_avg.value %}{% set worst_avg.value = info.avg_score %}{% endif %}{% endfor %} {% set episode_total = (episode_distribution.values() | sum) if episode_distribution else 0 %} {% set rag_total = (rag_overall_dist | sum(attribute='count')) if rag_overall_dist else 0 %} @@ -23,17 +24,17 @@
呼叫端反饋

呼叫端 × 反饋分佈

{% for caller, info in trends %}{% else %}{% endfor %}
呼叫端平均倒讚總數趨勢分布
{{ caller }}{{ "%.2f"|format(info.avg_score) }}/5{{ info.thumbs_up }}{{ info.thumbs_down }}{{ info.total_feedback }}{% if info.trend == 'positive' %}正向{% elif info.trend == 'negative' %}負向{% elif info.trend == 'neutral' %}中性{% else %}無資料{% endif %}
無反饋資料
- {% if action_plans_status %}
行動計畫

行動計畫 狀態分布

{% for a in action_plans_status %}{% endfor %}
狀態計畫類型數量
{{ a.status }}{{ a.plan_type }}{{ a.count }}
{% endif %} + {% if action_plans_status %}
行動計畫

行動計畫狀態分布

{% for a in action_plans_status %}{% endfor %}
狀態計畫類型數量
{{ obs_label.status(a.status) }}{{ obs_label.plan_type(a.plan_type) }}{{ a.count }}
{% endif %}
- {% if rag_root_causes %}
根因分析

RAG 自動根因建議

{% for rc in rag_root_causes %}
{{ rc.caller }}{{ "%.2f"|format(rc.avg_score) }}/5{{ rc.feedback_n }} 筆
    {% for h in rc.hits %}
  • {{ h.insight_type }}相似度 {{ "%.2f"|format(h.similarity) }}{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
  • {% endfor %}
{% endfor %}
{% endif %} + {% if rag_root_causes %}
根因分析

RAG 自動根因建議

{% for rc in rag_root_causes %}
{{ rc.caller }}{{ "%.2f"|format(rc.avg_score) }}/5{{ rc.feedback_n }} 筆
    {% for h in rc.hits %}
  • {{ obs_label.insight(h.insight_type) }}相似度 {{ "%.2f"|format(h.similarity) }}{{ h.content }}{% if h.content|length >= 200 %}…{% endif %}
  • {% endfor %}
{% endfor %}
{% endif %} {% if recommendations %}
智能建議

智能建議

    {% for rec in recommendations %}
  • {% if rec.action == 'review' %}{% else %}{% endif %}{{ rec.caller }}:{{ rec.reason }}
  • {% endfor %}
{% endif %} - {% if action_outcomes_stats %}
動作成效

實際動作成效

{% set total_ao = (action_outcomes_stats | sum(attribute='count')) or 1 %}{% for r in action_outcomes_stats %}
{{ r.verdict }}{{ r.count }}{{ "%.1f"|format(r.count / total_ao * 100) }}%
{% endfor %}
{% endif %} + {% if action_outcomes_stats %}
動作成效

實際動作成效

{% set total_ao = (action_outcomes_stats | sum(attribute='count')) or 1 %}{% for r in action_outcomes_stats %}
{{ obs_label.verdict(r.verdict) }}{{ r.count }}{{ "%.1f"|format(r.count / total_ao * 100) }}%
{% endfor %}
{% endif %}

Ollama 優先策略 v5.0 — AI 品質診斷台

diff --git a/templates/admin/rag_queries.html b/templates/admin/rag_queries.html index fe25f29..63c1ca9 100644 --- a/templates/admin/rag_queries.html +++ b/templates/admin/rag_queries.html @@ -40,7 +40,9 @@ {% endblock %}