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 %}{% endfor %}
工具服務呼叫端工具呼叫快取快取率成本
{{ m.server }}{{ m.caller }}{{ "{:,}".format(m.calls) }}{{ m.cache_hits }}{{ "%.0f"|format(m.cache_rate) }}%${{ "%.4f"|format(m.cost) }}
{% endif %} + {% if mcp_matrix %}
工具協作明細

工具協作 × 使用情境

{% for m in mcp_matrix %}{% endfor %}
工具服務使用情境工具協作快取快取率成本
服務元件{{ obs_label.caller(m.caller) }}{{ "{:,}".format(m.calls) }}{{ m.cache_hits }}{{ "%.0f"|format(m.cache_rate) }}%${{ "%.4f"|format(m.cost) }}
{% 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 @@
預算線

預算線與節流狀態

-
{% 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)
+
{% 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 %}
尚未建立預算線。
{% if cost_trend_30d %} @@ -70,7 +70,7 @@ {% endif %} {% if top_cost_callers %} -
燃燒率

Top 5 燒錢呼叫端

{% 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 %}
+
成本集中

Top 5 成本使用情境

{% 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 @@
審查流程 - +
@@ -315,11 +315,10 @@
-
-
部署證據
+
上線證據
- 等待下次部署觸發... + 等待下次上線檢查...
@@ -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 建議。