diff --git a/AGENTS.md b/AGENTS.md
index 6e4f67c..6595fcc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -141,6 +141,7 @@
- AI 自動化 Session SOP: `docs/guides/ai_automation_session_sop.md`
- Browse.sh 爬蟲診斷手冊: `docs/guides/browse_sh_crawler_playbook.md`
- Webcrumbs 共用 UI Runtime: `docs/guides/webcrumbs_shared_runtime.md`
+- PChome 業績成長 UI/UX 守門: `docs/guides/pchome_growth_ui_ux_guardrails.md`
- 外部專業 Benchmark: `docs/guides/external_professional_benchmark.md`
- AI 競價情報 SOT: `docs/AI_INTELLIGENCE_MODULE_SOT.md`
- Agent 角色矩陣: `docs/guides/codex_agent_roles.md`
diff --git a/config.py b/config.py
index ce5e2ef..01b1073 100644
--- a/config.py
+++ b/config.py
@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
-SYSTEM_VERSION = "V10.703"
+SYSTEM_VERSION = "V10.704"
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 9f6bba3..07097e5 100644
--- a/docs/AI_INTELLIGENCE_MODULE_SOT.md
+++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md
@@ -780,3 +780,5 @@ POSTGRES_HOST=momo-db
| 2026-06-26 | 靜態資源部署必須保持容器可讀 | V10.701 起部署 SOP 改用 Git 物件打包差異檔,並以測試檢查 `web/static` 檔案可被容器讀取,避免 macOS / iCloud 工作目錄權限造成正式 `/static/*` 500,讓全站 CSS/JS 不再因檔案模式回歸而失效。 |
| 2026-06-26 | 治理頁也要用營運情境語,不得渲染 raw caller、服務代碼或錯誤日誌 | V10.702 起 AI 流量頁把 caller/provider/上下文改成「使用情境、建議路徑、作戰素材」;服務更新監控頁隱藏 branch/sha/error log/pod 名稱等工程細節,改用「更新流程、服務元件、診斷線索」語言;缺貨頁移除英文 Vendor Stockout。 |
| 2026-06-26 | 關鍵決策框架不得因資料不足整段消失 | V10.703 起 AI 流量頁的「情境 × 知識命中矩陣」改為永遠顯示;資料不足時顯示營運空狀態,避免使用者看不到頁面應該如何判讀。 |
+| 2026-06-26 | 使用者頁不得把提交、分支、JSON/API 或 raw caller 當主訊息 | V10.704 起成本治理、AI 分工、AI 健康檢查與登入頁再收斂:raw caller/server 改成使用情境與服務元件,JSONL/API/PostgreSQL/Session/CSRF 等工程語改成檢查紀錄、健康檢查服務、資料服務與工作階段。 |
+| 2026-06-26 | 全站 UI/UX 工作重點必須文件化並納入入口索引 | V10.704 起新增 `docs/guides/pchome_growth_ui_ux_guardrails.md` 並由 `AGENTS.md` 索引;所有前台頁面以「提升 PChome 業績、快速判斷、直接下一步」為共同目標,避免後續工作再偏回局部文案修補。 |
diff --git a/docs/guides/pchome_growth_ui_ux_guardrails.md b/docs/guides/pchome_growth_ui_ux_guardrails.md
new file mode 100644
index 0000000..39c2583
--- /dev/null
+++ b/docs/guides/pchome_growth_ui_ux_guardrails.md
@@ -0,0 +1,26 @@
+# PChome Growth UI/UX Guardrails
+
+本專案所有使用者可見頁面,都必須服務同一個產品目標:
+
+> 讓營運者更快判斷如何提升 PChome 業績,並能直接採取下一步。
+
+## 不可偏離的工作重點
+
+1. 每個頁面都要有清楚的營運目的、目前狀態與下一步。
+2. 使用者不應看到工程實作細節,例如資料庫欄位、raw caller、commit、branch、JSON、API、endpoint、模型名稱或錯誤堆疊。
+3. 比價、缺貨、匯入、AI 建議、觀測與服務健康頁,都要用同一套 PChome 業績成長語言:主推、守價、補比價、供貨風險、成長缺口、建議路徑、使用情境、服務元件。
+4. 資料不足時不能整段消失,要顯示可理解的空狀態與下一步。
+5. 不得把工作視窗溝通、部署交接、工程判斷或維護工作摘要搬到前台。
+
+## 每次 UI/UX 修改的驗收
+
+至少要完成:
+
+- 本地測試:`tests/test_frontend_v2_assets.py`
+- 相關業務測試:`tests/test_pchome_revenue_growth_service.py`
+- 正式 smoke:檢查 `/health` 版本、核心頁 HTTP 200、可見文案無 raw terms、靜態資源 HTTP 200
+- 如果頁面有 PChome/MOMO 商品比較,必須能一眼看到兩平台價格與可同時開啟的外部賣場連結
+
+## 判斷標準
+
+如果頁面需要使用者理解「這是什麼欄位、哪個模型、哪個 pipeline、哪個 commit、哪個 API」才知道下一步,這個 UI 就是不合格。
diff --git a/scripts/observability_contract.py b/scripts/observability_contract.py
index 13b1987..6981937 100644
--- a/scripts/observability_contract.py
+++ b/scripts/observability_contract.py
@@ -41,7 +41,7 @@ OBSERVABILITY_PAGES = (
"/observability/agent_orchestration",
"AI 分工矩陣",
"分工",
- ("AI 分工矩陣", "本地模型", "知識"),
+ ("AI 分工矩陣", "建議路徑", "知識"),
),
ObservabilityPage(
"templates/admin/business_intel.html",
@@ -57,7 +57,7 @@ OBSERVABILITY_PAGES = (
"/observability/host_health",
"主機健康",
"主機",
- ("主機健康", "AI 模型", "自癒"),
+ ("主機健康", "AI 建議服務", "自癒"),
),
ObservabilityPage(
"templates/admin/ai_calls_dashboard.html",
diff --git a/templates/admin/agent_orchestration.html b/templates/admin/agent_orchestration.html
index e400fd6..af7cbe5 100644
--- a/templates/admin/agent_orchestration.html
+++ b/templates/admin/agent_orchestration.html
@@ -8,6 +8,8 @@
.agent-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))}.agent-kicker{color:var(--obs-accent);font-size:.76rem;letter-spacing:.13em;text-transform:uppercase;font-weight:850}.agent-title{margin:.45rem 0 .25rem;font-family: var(--momo-font-display, "Inter", "Noto Sans TC", system-ui, sans-serif);font-size:var(--obs-title-size);letter-spacing: 0;line-height:.98}.agent-subtitle{color:var(--obs-muted);max-width:870px;line-height:1.7}.agent-filter{display:flex;gap:.55rem;flex-wrap:wrap;margin-top:1rem;padding:.8rem;border:1px solid var(--obs-line);border-radius:20px;background:rgba(255,255,255,.58)}.agent-command{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.75rem;margin-top:1rem}.agent-signal{padding:.95rem;border:1px solid var(--obs-line);border-radius:20px;background:rgba(255,255,255,.62)}.agent-label{color:var(--obs-muted);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase}.agent-value{display:block;margin-top:.28rem;font-size:var(--obs-value-size);font-weight:880;letter-spacing: 0}.agent-grid{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(330px,.8fr);gap:1rem;margin-top:1rem}.agent-stack{display:grid;gap:1rem}.agent-panel-head,.agent-table-title{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;padding:1.05rem 1.1rem .25rem}.agent-panel-title,.agent-table-title h3{margin:.15rem 0 0;font-size:1.1rem;font-weight:850;letter-spacing: 0}.agent-panel-body{padding:1rem 1.1rem 1.1rem}.agent-table-shell{overflow:hidden;margin-top:1rem}.agent-card{padding:.9rem;border:1px solid var(--obs-line);border-radius:20px;background:rgba(255,255,255,.58);margin-bottom:.75rem}.agent-card-top{display:flex;justify-content:space-between;gap:.8rem;align-items:start}.agent-meter{height:7px;border-radius:999px;background:rgba(86,64,48,.1);overflow:hidden;margin-top:.65rem}.agent-meter span{display:block;height:100%;background:var(--obs-accent)}.rec-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){.agent-command{grid-template-columns:repeat(2,minmax(0,1fr))}.agent-grid{grid-template-columns:1fr}}@media(max-width:720px){.agent-command{grid-template-columns:1fr}}
+{% import "admin/_observability_labels.html" as obs_label %}
+
AI 分工指揮台 · {{ hours }} 小時視窗
AI 分工指揮台
確認 AI 分工、建議路徑、知識命中與工具編排是否支撐業績決策。
{% if overall %}呼叫總量
{{ "{:,}".format(overall.total_calls) }}{{ "{:,}".format(overall.total_tokens) }} 用量主力路徑占比
{{ "%.0f"|format(overall.local_pct) }}%{{ "{:,}".format(overall.local_calls) }} 次主力呼叫付費成本
${{ "%.2f"|format(overall.total_cost) }}{{ "{:,}".format(overall.paid_calls) }} 次付費呼叫知識命中率
{{ "%.0f"|format(overall.rag_rate) }}%{{ "{:,}".format(overall.rag_hits) }} 次命中 {% endif %}
{% if error %}
{{ error }}
{% endif %}
@@ -22,7 +24,7 @@
{% if recommendations %}
{% for r in recommendations %}
{{ r.severity|upper }}{{ r.agent }}發現:{{ r.finding }}
建議:{{ r.suggestion }}
{% endfor %}
{% endif %}
- {% if mcp_matrix %}
| 工具服務 | 呼叫端 | 工具呼叫 | 快取 | 快取率 | 成本 |
{% for m in mcp_matrix %}{{ m.server }} | {{ m.caller }} | {{ "{:,}".format(m.calls) }} | {{ m.cache_hits }} | {{ "%.0f"|format(m.cache_rate) }}% | ${{ "%.4f"|format(m.cost) }} |
{% endfor %}
{% endif %}
+ {% if mcp_matrix %}
| 工具服務 | 使用情境 | 工具協作 | 快取 | 快取率 | 成本 |
{% for m in mcp_matrix %}| 服務元件 | {{ obs_label.caller(m.caller) }} | {{ "{:,}".format(m.calls) }} | {{ m.cache_hits }} | {{ "%.0f"|format(m.cache_rate) }}% | ${{ "%.4f"|format(m.cost) }} |
{% endfor %}
{% endif %}
AI 分工指揮台
{% endblock %}
diff --git a/templates/admin/budget.html b/templates/admin/budget.html
index 3b47bc9..7c4c702 100644
--- a/templates/admin/budget.html
+++ b/templates/admin/budget.html
@@ -56,7 +56,7 @@
-
+
{% if cost_trend_30d %}
@@ -70,7 +70,7 @@
{% endif %}
{% if top_cost_callers %}
-
{% set max_cost = (top_cost_callers | map(attribute='cost') | max) or 1 %}{% for c in top_cost_callers %}
{{ c.caller }}${{ "%.2f"|format(c.cost) }}
{{ "{:,}".format(c.calls) }} 次呼叫 · {{ "{:,}".format(c.tokens) }} 用量{% endfor %}
+
{% set max_cost = (top_cost_callers | map(attribute='cost') | max) or 1 %}{% for c in top_cost_callers %}
{{ obs_label.caller(c.caller) }}${{ "%.2f"|format(c.cost) }}
{{ "{:,}".format(c.calls) }} 次 · {{ "{:,}".format(c.tokens) }} 用量{% endfor %}
{% endif %}
diff --git a/templates/ai_automation_smoke.html b/templates/ai_automation_smoke.html
index 2c1c18e..f27b864 100644
--- a/templates/ai_automation_smoke.html
+++ b/templates/ai_automation_smoke.html
@@ -138,7 +138,7 @@
重新檢查
- 匯出 JSONL
+ 下載檢查紀錄
${escapeHtml(item.summary)}
- ${escapeHtml(JSON.stringify(item.details || {}, null, 2))}
+ 已保留診斷細節,請由維護者查看。
`).join('');
@@ -282,15 +282,15 @@ async function loadSmoke() {
btn.querySelector('i').classList.add('fa-spin');
try {
const res = await fetchWithCSRF('/api/ai-automation/smoke');
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ if (!res.ok) throw new Error('smoke-unavailable');
renderSmoke(await res.json());
} catch (err) {
document.getElementById('overallStatus').innerHTML = badge('critical');
document.getElementById('checkGrid').innerHTML = `
-
健康檢查 API
-
讀取失敗:${escapeHtml(err.message)}
+
健康檢查服務
+
讀取失敗,請稍後重試。
`;
} finally {
diff --git a/templates/code_review.html b/templates/code_review.html
index d4e9a39..d60bc02 100644
--- a/templates/code_review.html
+++ b/templates/code_review.html
@@ -289,7 +289,7 @@
-
-
+
- 等待下次部署觸發...
+ 等待下次上線檢查...
@@ -490,17 +489,15 @@ function renderEA(ea, autoFix) {
`;
}
-// ── Commit info ───────────────────────────────────────────────────
+// ── 上線證據 ─────────────────────────────────────────────────────
function renderCommitInfo(state) {
const el = document.getElementById('commitInfo');
if (!state.commit_sha) return;
- const files = (state.changed_files||[]).slice(0,5).map(f=>`${f.split('/').pop()}`).join(' ');
- const more = (state.changed_files||[]).length > 5 ? `+${state.changed_files.length-5}` : '';
+ const changedCount = (state.changed_files||[]).length;
el.innerHTML = `
- 提交 ${state.commit_sha.slice(0,8)}
- 分支 ${state.branch||'?'}
+ 狀態 已收到上線檢查資料
模式 ${DEPLOY_TYPE_LABEL[state.deploy_type] || state.deploy_type || '同步部署'}
- 變更 ${files} ${more}
`;
+ 變更 ${changedCount} 項待檢查
`;
}
// ── Status bar ────────────────────────────────────────────────────
@@ -514,7 +511,7 @@ function renderStatusBar(state) {
const msgs = {
running: `⟳ 流程執行中 — 第 ${state.current_step}/5 步`,
completed: `✅ 程式碼審查完成 — ${state.message||''}`,
- error: `❌ 流程失敗 — ${escHtml(state.message||'')}`,
+ error: `❌ 流程需確認 — 請查看風險清單`,
skipped: `⏭ 已略過 — ${escHtml(state.message||'')}`,
};
el.innerHTML = msgs[state.status] || '';
@@ -533,10 +530,10 @@ function renderHistory(items) {
const sev = h.severity_summary || {};
return `
- ${h.commit_sha}
+ 上線檢查
${h.created_at.slice(0,16).replace('T',' ')}
-
🌿 ${h.branch} • ${(h.changed_files||[]).length} 檔案${h.auto_fix?' • 🔧 已自動修復':''}
+
${(h.changed_files||[]).length} 項變更${h.auto_fix?' • 已完成修復流程':''}
${sev.critical?`
🔴 ${sev.critical}`:''}
${sev.high?`
🟠 ${sev.high}`:''}
@@ -555,18 +552,16 @@ function loadHistoryItem(idx) {
el.style.borderColor = i === idx ? 'var(--blue)' : '';
});
renderSeverity(h.severity_summary);
- const files = (h.changed_files||[]).slice(0,5).map(f=>`
${f.split('/').pop()}`).join(' ');
- const more = (h.changed_files||[]).length > 5 ? `
+${h.changed_files.length-5}` : '';
+ const changedCount = (h.changed_files||[]).length;
document.getElementById('commitInfo').innerHTML = `
-
提交 ${h.commit_sha}
-
分支 ${h.branch||'?'}
+
狀態 已完成上線檢查
時間 ${h.created_at.slice(0,16).replace('T',' ')}
-
變更 ${files} ${more}
`;
- document.getElementById('pipelineId').textContent = (h.pipeline_id||'').slice(-14);
+
變更 ${changedCount} 項已檢查
`;
+ document.getElementById('pipelineId').textContent = '最近一次';
const sBar = document.getElementById('statusBar');
sBar.style.display = 'block';
sBar.className = 'completed';
- sBar.innerHTML = `✅
歷史記錄 — 提交 ${h.commit_sha}${h.auto_fix ? ' 🔧 已自動修復' : ''}`;
+ sBar.innerHTML = `✅
歷史記錄 — 已完成上線檢查${h.auto_fix ? ',並完成修復流程' : ''}`;
renderFindings(h.findings || []);
renderArchitectureReport(h.openclaw_report || '');
renderEA(h.ea_decision || {}, h.auto_fix || false);
@@ -593,7 +588,7 @@ async function poll() {
renderArchitectureReport(state.openclaw_report);
renderEA(state.ea_decision, state.auto_fix_triggered);
renderCommitInfo(state);
- document.getElementById('pipelineId').textContent = (state.pipeline_id||'').slice(-14);
+ document.getElementById('pipelineId').textContent = state.pipeline_id ? '執行中' : '';
// 每 3s 輪詢(running)/ 30s(idle)
const interval = state.status === 'running' ? 3000 : 30000;
diff --git a/templates/login.html b/templates/login.html
index 48103e4..0284b0b 100644
--- a/templates/login.html
+++ b/templates/login.html
@@ -282,9 +282,9 @@
營運數據工作台
登入後先看 PChome 業績、價差、缺貨與 AI 建議。
- CSRF 防護
- Session 2h
- PostgreSQL
+ 安全登入
+ 工作階段
+ 資料服務