diff --git a/config.py b/config.py
index bc496ff..0dbdc1b 100644
--- a/config.py
+++ b/config.py
@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
-SYSTEM_VERSION = "V10.717"
+SYSTEM_VERSION = "V10.718"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示
diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md
index be4de98..88a96b6 100644
--- a/docs/AI_INTELLIGENCE_MODULE_SOT.md
+++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md
@@ -2,7 +2,7 @@
> **最後更新**: 2026-06-26 (台北時間)
> **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯;PChome 後台業績匯入韌性已補強;產品定位正名為「PChome 業績成長自動化作戰系統」;外部市場來源正規化層、自動同步、作戰清單與價格參考表優先讀取、CSV 備援預檢、前台操作入口、高可見頁面繁中化守門、比價/作戰 UI 工作台化、跨平台來源治理與商品身份 UI 契約已建立,GCP embedding 熔斷延後處理、110 proxy rescue 與 direct host health skip 已建立
-> **適用版本**: V10.717
+> **適用版本**: V10.718
---
@@ -802,3 +802,4 @@ POSTGRES_HOST=momo-db
| 2026-06-26 | AI 銷售建議前端不得保留 raw content 路徑 | V10.715 起 `/ai_recommend` 只使用已整理的外部訊號摘要與可用線索;搜尋或商品洞察結果若未整理成結構,前端只顯示可理解的待整理狀態,不再把 raw content 當上下文或 fallback 分支保留。 |
| 2026-06-26 | 首頁覆核候選必須標示為待確認商品 | V10.716 起首頁 PChome 覆核區不再使用「PChome 候選」作為可見主語,統一改為「PChome 待確認商品」與「開 PChome 待確認商品」,避免使用者把未確認同款誤認為正式比價結果。 |
| 2026-06-26 | 供貨風險頁不得使用資料表或英文模組名作為主語 | V10.717 起缺貨清單與補貨通知頁統一使用「供貨風險、缺貨處理清單、補貨通知紀錄」等營運語言,不再顯示「缺貨資料表、缺貨資料、Vendor Stockout」等資料庫或英文模組感文案。 |
+| 2026-06-26 | AI 觀測頁不得外露 caller key | V10.718 起 AI 品質診斷與知識召回頁使用「使用情境」作為可見主語,並透過 `obs_label.caller()` 顯示營運名稱;前台不得直接顯示 `{{ caller }}`、`top_k` 或「全部呼叫端」等工程語言。 |
diff --git a/routes/admin_observability_routes.py b/routes/admin_observability_routes.py
index cdbfaf9..c059882 100644
--- a/routes/admin_observability_routes.py
+++ b/routes/admin_observability_routes.py
@@ -588,7 +588,7 @@ def rag_queries_dashboard():
'queried_at': r[1].strftime('%Y-%m-%d %H:%M:%S') if r[1] else '',
'caller': r[2],
'query_text': r[3] or '',
- 'top_k': int(r[4] or 0),
+ 'take_count': int(r[4] or 0),
'threshold': round(float(r[5] or 0), 3),
'hit_count': int(r[6] or 0),
'used_results': used_ids,
diff --git a/templates/admin/quality_trend.html b/templates/admin/quality_trend.html
index ff4d289..53b7951 100644
--- a/templates/admin/quality_trend.html
+++ b/templates/admin/quality_trend.html
@@ -18,12 +18,12 @@
{% set rag_total = (rag_overall_dist | sum(attribute='count')) if rag_overall_dist else 0 %}
-
品質診斷 · {{ days }} 日視窗
AI 品質診斷台
用反饋、知識分數與行動閉環檢查 AI 建議是否可靠。
反饋總量
{{ total_feedback.value }}呼叫端反饋總量最差均分
{{ "%.2f"|format(worst_avg.value) }}最差呼叫端平均分蒸餾樣本
{{ episode_total }}蒸餾池 {{ days }} 日知識評分
{{ rag_total }}已回饋知識查詢
+
品質診斷 · {{ days }} 日視窗
AI 品質診斷台
用反饋、知識分數與行動閉環檢查 AI 建議是否可靠。
反饋總量
{{ total_feedback.value }}使用情境反饋總量最低均分
{{ "%.2f"|format(worst_avg.value) }}最低使用情境平均分蒸餾樣本
{{ episode_total }}蒸餾池 {{ days }} 日知識評分
{{ rag_total }}已回饋知識查詢
{% if error %}
{{ error }}
{% endif %}
-
| 呼叫端 | 平均 | 讚 | 倒讚 | 總數 | 趨勢 | 分布 |
{% for caller, info in trends %}{{ 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 %} | |
{% else %}| 無反饋資料 |
{% endfor %}
+
| 使用情境 | 平均 | 讚 | 倒讚 | 總數 | 趨勢 | 分布 |
{% for caller, info in trends %}| {{ obs_label.caller(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 %} | |
{% else %}| 無反饋資料 |
{% endfor %}
{% if action_plans_status %}
| 狀態 | 計畫類型 | 數量 |
{% for a in action_plans_status %}| {{ obs_label.status(a.status) }} | {{ obs_label.plan_type(a.plan_type) }} | {{ a.count }} |
{% endfor %}
{% endif %}
- {% if rag_root_causes %}
{% 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 rag_root_causes %}
{% for rc in rag_root_causes %}
{{ obs_label.caller(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 %}{{ obs_label.caller(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 %}
{{ obs_label.verdict(r.verdict) }}{{ r.count }}{{ "%.1f"|format(r.count / total_ao * 100) }}%
{% endfor %}
{% endif %}
AI 品質診斷台
diff --git a/templates/admin/rag_queries.html b/templates/admin/rag_queries.html
index 1419cc9..9ceac6b 100644
--- a/templates/admin/rag_queries.html
+++ b/templates/admin/rag_queries.html
@@ -15,23 +15,24 @@
{% set total = summary.total if summary else 0 %}
+{% import "admin/_observability_labels.html" as obs_label %}
知識召回雷達 · {{ hours }} 小時視窗
知識召回雷達
追蹤知識是否命中、是否省下模型呼叫,避免業績建議缺少根據。
-
- {% if summary and summary.total > 0 %}查詢數
{{ "{:,}".format(summary.total) }}{{ summary.distinct_callers }} 個呼叫端
命中率
{{ "%.1f"|format(summary.hit_rate) }}%{{ summary.with_hits }} 次命中 · {{ summary.no_hits }} 次未命中
省下呼叫
{{ "%.1f"|format(summary.saved_rate) }}%{{ summary.saved }} 次省下 LLM
反饋分
{{ "%.2f"|format(summary.avg_score) }}{{ summary.feedback_count }} 筆 · 平均 {{ summary.avg_hits }} 次命中
{% endif %}
+
+ {% if summary and summary.total > 0 %}查詢數
{{ "{:,}".format(summary.total) }}{{ summary.distinct_callers }} 個使用情境
命中率
{{ "%.1f"|format(summary.hit_rate) }}%{{ summary.with_hits }} 次命中 · {{ summary.no_hits }} 次未命中
省下模型
{{ "%.1f"|format(summary.saved_rate) }}%{{ summary.saved }} 次省下模型呼叫
反饋分
{{ "%.2f"|format(summary.avg_score) }}{{ summary.feedback_count }} 筆 · 平均 {{ summary.avg_hits }} 次命中
{% endif %}
{% if error %}
{{ error }}
{% endif %}
-
{% if queries %}
| 時間 | 呼叫端 | 查詢 | top_k | 門檻 | 命中 | 已省下 | 反饋 | 動作 |
{% for q in queries %}| {{ q.queried_at }} | {{ q.caller }} | {{ q.query_text }}{% if q.query_text|length >= 200 %}…{% endif %} | {{ q.top_k }} | {{ q.threshold }} | {% if q.hit_count > 0 %}{{ q.hit_count }}{% else %}0{% endif %} | {% if q.saved_call %}已省下{% else %}—{% endif %} | {% if q.feedback_score is not none %}{{ q.feedback_score }}/5{% else %}—{% endif %} | {% if q.hit_count > 0 %}{% endif %} |
{% endfor %}
{% else %}
過去 {{ hours }} 小時無符合條件的知識查詢紀錄。
{% endif %}
+
{% if queries %}
| 時間 | 使用情境 | 查詢 | 取用數 | 門檻 | 命中 | 已省下 | 反饋 | 動作 |
{% for q in queries %}| {{ q.queried_at }} | {{ obs_label.caller(q.caller) }} | {{ q.query_text }}{% if q.query_text|length >= 200 %}…{% endif %} | {{ q.take_count }} | {{ q.threshold }} | {% if q.hit_count > 0 %}{{ q.hit_count }}{% else %}0{% endif %} | {% if q.saved_call %}已省下{% else %}—{% endif %} | {% if q.feedback_score is not none %}{{ q.feedback_score }}/5{% else %}—{% endif %} | {% if q.hit_count > 0 %}{% endif %} |
{% endfor %}
{% else %}
過去 {{ hours }} 小時無符合條件的知識查詢紀錄。
{% endif %}
diff --git a/tests/test_frontend_v2_assets.py b/tests/test_frontend_v2_assets.py
index 89a266c..9dbf740 100644
--- a/tests/test_frontend_v2_assets.py
+++ b/tests/test_frontend_v2_assets.py
@@ -709,6 +709,8 @@ def test_utility_pages_keep_operator_copy_professional():
stockout_list = (ROOT / "templates/vendor_stockout_list_v2.html").read_text(encoding="utf-8")
stockout_send_email = (ROOT / "templates/vendor_stockout_send_email_v2.html").read_text(encoding="utf-8")
stockout_vendor_management = (ROOT / "templates/vendor_stockout_vendor_management_v2.html").read_text(encoding="utf-8")
+ quality_trend = (ROOT / "templates/admin/quality_trend.html").read_text(encoding="utf-8")
+ rag_queries = (ROOT / "templates/admin/rag_queries.html").read_text(encoding="utf-8")
ai_calls = (ROOT / "templates/admin/ai_calls_dashboard.html").read_text(encoding="utf-8")
observability_labels = (ROOT / "templates/admin/_observability_labels.html").read_text(encoding="utf-8")
host_health = (ROOT / "templates/admin/host_health.html").read_text(encoding="utf-8")
@@ -730,6 +732,8 @@ def test_utility_pages_keep_operator_copy_professional():
stockout_list,
stockout_send_email,
stockout_vendor_management,
+ quality_trend,
+ rag_queries,
ai_calls,
observability_labels,
host_health,
@@ -761,6 +765,17 @@ def test_utility_pages_keep_operator_copy_professional():
assert "供應商窗口" in stockout_vendor_management
assert "維護正確窗口" in stockout_vendor_management
assert "Vendor Stockout" not in stockout_vendor_management
+ assert "使用情境反饋分佈" in quality_trend
+ assert "最低使用情境平均分" in quality_trend
+ assert "
{{ caller }}" not in quality_trend
+ assert "
{{ rc.caller }}" not in quality_trend
+ assert "
{{ rec.caller }}" not in quality_trend
+ assert "全部使用情境" in rag_queries
+ assert "各使用情境知識表現" in rag_queries
+ assert "僅看已省下模型呼叫" in rag_queries
+ assert "top_k" not in rag_queries
+ assert "
{{ q.caller }}" not in rag_queries
+ assert "
{{ c.caller }}" not in rag_queries
assert "使用情境" in ai_calls
assert "全部使用情境" in ai_calls
assert "情境 × 知識命中矩陣" in ai_calls
@@ -807,6 +822,8 @@ def test_utility_pages_keep_operator_copy_professional():
"全部呼叫端",
"呼叫端 ×",
"
{{ c.caller }}",
+ "
{{ q.caller }}",
+ "
{{ caller }}",
"NIM Elephant",
"OpenRouter",
"GitLab 部署紀錄",