diff --git a/config.py b/config.py
index d0f285b..f5df2f5 100644
--- a/config.py
+++ b/config.py
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
-SYSTEM_VERSION = "V10.121"
+SYSTEM_VERSION = "V10.122"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示
diff --git a/scripts/check_observability_ui.py b/scripts/check_observability_ui.py
index 51de3c3..8cb0cfd 100644
--- a/scripts/check_observability_ui.py
+++ b/scripts/check_observability_ui.py
@@ -25,7 +25,6 @@ WEB_CSS_PATH = Path("web/static/css/observability-system.css")
SHELL_PATH = Path("templates/components/_ewoooc_shell.html")
BASE_PATH = Path("templates/ewoooc_base.html")
ROUTE_PATH = Path("routes/admin_observability_routes.py")
-OVERVIEW_PATH = Path("templates/admin/observability_overview.html")
@dataclass(frozen=True)
@@ -79,6 +78,7 @@ REQUIRED_CSS_SNIPPETS = [
"--obs-title-size",
"--obs-value-size",
"--obs-matrix-dot",
+ "v3.11 V2 workbench normalization",
"v3.10 terminal dot-matrix layer",
".momo-observability-mode",
".obs-chart-frame",
@@ -137,16 +137,55 @@ FORBIDDEN_BASE_PATTERNS = [
),
]
-FORBIDDEN_OVERVIEW_COPY = [
+FORBIDDEN_OBSERVABILITY_COPY = [
"AI Observability Command Room",
+ "Business Intelligence",
+ "Agent Command Matrix",
+ "AI Traffic Control",
+ "AI Cost Governance",
+ "RAG Recall Radar",
+ "Quality Diagnostics",
+ "PPT Visual QA Pipeline",
+ "RAG Promotion Gate",
+ "Infrastructure Lifeline",
"Risk Signals",
"AI Calls / 24h",
"Host Cascade",
"AI Runtime",
"Learning Loop",
- ">Command<",
- ">Runtime<",
- ">Quality<",
+ "Total Calls",
+ "Ollama Share",
+ "Paid Cost",
+ "Provider Split",
+ "Caller Orchestration",
+ "Model Economics",
+ "Recent Calls",
+ "Budget Ratio",
+ "Budget Lines",
+ "Provider Mix",
+ "Burn Rate",
+ "Saved Call",
+ "Query Stream",
+ "Caller Quality",
+ "Worst Avg",
+ "RAG Scores",
+ "Caller Feedback",
+ "RAG Feedback",
+ "Learning Pool",
+ "Root Cause",
+ "Action Outcomes",
+ "Audit History",
+ "Generated Files",
+ "30d Audit Mix",
+ "Failure Hotspots",
+ "Awaiting Review",
+ "Review Queue",
+ "Ollama Down",
+ "AIOps Open",
+ "Heal Rate",
+ "Cost Throttle",
+ "MCP Workload",
+ "Operation Ollama-First",
"RAG hits",
"Cache hits",
"30d episodes",
@@ -258,18 +297,20 @@ def scan_base_topbar() -> list[str]:
return findings
-def scan_overview_copy() -> list[str]:
- path = ROOT / OVERVIEW_PATH
- if not path.exists():
- return [f"{OVERVIEW_PATH}: missing required overview page"]
-
- text = path.read_text(encoding="utf-8")
+def scan_observability_copy() -> list[str]:
findings: list[str] = []
- for snippet in FORBIDDEN_OVERVIEW_COPY:
- if snippet in text:
- findings.append(
- f"{OVERVIEW_PATH}: legacy English overview copy `{snippet}` must be localized to the V2 workbench language"
- )
+ for template_path in TEMPLATE_PATHS:
+ path = ROOT / Path(template_path)
+ if not path.exists():
+ findings.append(f"{template_path}: missing required observability page")
+ continue
+
+ text = path.read_text(encoding="utf-8")
+ for snippet in FORBIDDEN_OBSERVABILITY_COPY:
+ if snippet in text:
+ findings.append(
+ f"{template_path}: legacy English observability copy `{snippet}` must be localized to the V2 workbench language"
+ )
return findings
@@ -357,7 +398,7 @@ def main() -> int:
findings.extend(scan_css())
findings.extend(scan_shell())
findings.extend(scan_base_topbar())
- findings.extend(scan_overview_copy())
+ findings.extend(scan_observability_copy())
findings.extend(scan_nav_contract())
if findings:
@@ -371,7 +412,7 @@ def main() -> int:
print(f"- css guardrails checked: {len(REQUIRED_CSS_SNIPPETS)}")
print(f"- sidebar/nav guardrails checked: {len(REQUIRED_SHELL_SNIPPETS)}")
print(f"- base/topbar guardrails checked: {len(REQUIRED_BASE_SNIPPETS) + len(FORBIDDEN_BASE_PATTERNS)}")
- print(f"- overview copy guardrails checked: {len(FORBIDDEN_OVERVIEW_COPY)}")
+ print(f"- observability copy guardrails checked: {len(TEMPLATE_PATHS)} pages × {len(FORBIDDEN_OBSERVABILITY_COPY)} terms")
print(f"- nav contract checked: {len(OBSERVABILITY_PAGES)} pages")
return 0
diff --git a/scripts/observability_contract.py b/scripts/observability_contract.py
index 9099f90..0abb514 100644
--- a/scripts/observability_contract.py
+++ b/scripts/observability_contract.py
@@ -65,7 +65,7 @@ OBSERVABILITY_PAGES = (
"/observability/ai_calls",
"AI 呼叫",
"AI 呼叫",
- ("AI 呼叫", "Provider", "RAG"),
+ ("AI 呼叫", "供應商", "RAG"),
),
ObservabilityPage(
"templates/admin/budget.html",
@@ -73,7 +73,7 @@ OBSERVABILITY_PAGES = (
"/observability/budget",
"預算控管",
"預算",
- ("預算控管", "force", "throttle"),
+ ("預算控管", "預算線", "節流"),
),
ObservabilityPage(
"templates/admin/promotion_review.html",
@@ -81,7 +81,7 @@ OBSERVABILITY_PAGES = (
"/observability/promotion_review",
"RAG 晉升審核",
"晉升",
- ("RAG 晉升審核", "Promotion", "ai_insights"),
+ ("RAG 晉升審核", "晉升", "ai_insights"),
),
ObservabilityPage(
"templates/admin/rag_queries.html",
@@ -89,7 +89,7 @@ OBSERVABILITY_PAGES = (
"/observability/rag_queries",
"RAG 召回詳情",
"RAG",
- ("RAG 召回詳情", "最近 50", "hits"),
+ ("RAG 召回詳情", "最近 50", "命中"),
),
ObservabilityPage(
"templates/admin/quality_trend.html",
@@ -97,7 +97,7 @@ OBSERVABILITY_PAGES = (
"/observability/quality_trend",
"反饋趨勢",
"品質",
- ("反饋趨勢", "Caller", "蒸餾"),
+ ("反饋趨勢", "呼叫端", "蒸餾"),
),
ObservabilityPage(
"templates/admin/ppt_audit_history.html",
@@ -105,7 +105,7 @@ OBSERVABILITY_PAGES = (
"/observability/ppt_audit_history",
"PPT 視覺審核",
"PPT",
- ("PPT 視覺審核", "AiderHeal", "audit"),
+ ("PPT 視覺審核", "AiderHeal", "審核"),
),
)
diff --git a/static/css/observability-system.css b/static/css/observability-system.css
index 5eba739..fac4482 100644
--- a/static/css/observability-system.css
+++ b/static/css/observability-system.css
@@ -2379,6 +2379,136 @@
}
}
+/* v3.11 V2 workbench normalization: unify legacy observability page skins before the terminal dot layer. */
+.momo-observability-mode :is(
+ .obs-hero,
+ .agent-hero,
+ .biz-command,
+ .runtime-hero,
+ .calls-hero,
+ .gov-hero,
+ .gate-hero,
+ .rag-hero,
+ .qa-hero,
+ .quality-hero,
+ .ppt-hero
+) {
+ border-color: var(--obs-line) !important;
+ border-radius: var(--momo-radius-lg, 8px) !important;
+ background-color: var(--momo-bg-surface, #faf7f0) !important;
+ background-image: var(--obs-matrix-dot) !important;
+ background-size: var(--obs-matrix-size) !important;
+ box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06)) !important;
+}
+
+.momo-observability-mode :is(
+ .obs-panel,
+ .agent-panel,
+ .biz-panel,
+ .runtime-panel,
+ .calls-panel,
+ .gov-panel,
+ .gate-panel,
+ .rag-panel,
+ .qa-panel,
+ .quality-panel,
+ .ppt-panel,
+ .obs-signal,
+ .agent-signal,
+ .biz-signal,
+ .runtime-signal,
+ .calls-signal,
+ .gov-signal,
+ .gate-signal,
+ .rag-signal,
+ .qa-signal,
+ .quality-signal,
+ .ppt-signal,
+ .agent-card,
+ .rec-card,
+ .host-lane,
+ .runtime-mini,
+ .calls-mini,
+ .gov-mini,
+ .gate-mini,
+ .quality-mini,
+ .ppt-mini,
+ .strategy-card,
+ .episode-card,
+ .similar-box,
+ .fix-card,
+ .root-card,
+ .caller-card,
+ .biz-filter-card,
+ .biz-alert-strip,
+ .biz-chart-shell,
+ .biz-strategy-card,
+ .biz-mini-metric,
+ .biz-decision-card,
+ .obs-route-card
+) {
+ border-color: var(--obs-line) !important;
+ border-radius: var(--momo-radius-lg, 8px) !important;
+ background-color: var(--momo-bg-elevated, #fdfaf3) !important;
+ background-image: var(--obs-matrix-dot-soft) !important;
+ background-size: var(--obs-matrix-size) !important;
+ box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06)) !important;
+}
+
+.momo-observability-mode :is(
+ .obs-kicker,
+ .agent-kicker,
+ .biz-kicker,
+ .runtime-kicker,
+ .calls-kicker,
+ .gov-kicker,
+ .gate-kicker,
+ .rag-kicker,
+ .qa-kicker,
+ .quality-kicker,
+ .ppt-kicker,
+ .obs-signal-label,
+ .agent-label,
+ .biz-signal .label,
+ .runtime-label,
+ .calls-label,
+ .gov-label,
+ .gate-label,
+ .rag-label,
+ .qa-label,
+ .quality-label,
+ .ppt-label,
+ .obs-section-eyebrow
+) {
+ font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace) !important;
+ font-size: var(--momo-text-label, 0.6875rem) !important;
+ letter-spacing: 0.06em !important;
+ text-transform: none !important;
+ color: color-mix(in srgb, var(--obs-accent) 76%, var(--obs-muted)) !important;
+}
+
+.momo-observability-mode :is(
+ .btn,
+ .badge,
+ .obs-pill,
+ .biz-badge,
+ [class$="-pill"],
+ .model-chip
+) {
+ border-radius: var(--momo-radius-lg, 8px) !important;
+}
+
+.momo-observability-mode :is(
+ .agent-meter,
+ .caller-meter,
+ .progress,
+ .progress-bar,
+ .obs-progress-xs,
+ .obs-progress-sm
+) {
+ border-radius: var(--momo-radius-sm, 3px) !important;
+}
+
/* v3.10 terminal dot-matrix layer: this must stay at EOF to win the cascade. */
.momo-observability-mode {
--obs-matrix-dot: radial-gradient(color-mix(in srgb, var(--obs-accent) 14%, transparent) 0.85px, transparent 0.95px);
diff --git a/templates/admin/agent_orchestration.html b/templates/admin/agent_orchestration.html
index a4fe229..127a2da 100644
--- a/templates/admin/agent_orchestration.html
+++ b/templates/admin/agent_orchestration.html
@@ -9,20 +9,20 @@
-
Agent Command Matrix · {{ hours }}h Window
Agent 指揮矩陣
這頁回答 AI 中樞如何分工:誰在用 Ollama、誰還在吃付費 LLM、哪些 Agent 有 RAG 命中、哪些工作流已經接上 MCP。這不是列表,是指揮官視角。
{% if overall %}Total Calls
{{ "{:,}".format(overall.total_calls) }}{{ "{:,}".format(overall.total_tokens) }} tokensOllama Share
{{ "%.0f"|format(overall.local_pct) }}%{{ "{:,}".format(overall.local_calls) }} local callsPaid Cost
${{ "%.2f"|format(overall.total_cost) }}{{ "{:,}".format(overall.paid_calls) }} paid callsRAG Rate
{{ "%.0f"|format(overall.rag_rate) }}%{{ "{:,}".format(overall.rag_hits) }} hits {% endif %}
+
Agent 指揮矩陣 · {{ hours }} 小時視窗
Agent 指揮矩陣
這頁回答 AI 中樞如何分工:誰在用 Ollama、誰還在吃付費 LLM、哪些 Agent 有 RAG 命中、哪些工作流已經接上 MCP。這不是列表,是指揮官視角。
{% if overall %}呼叫總量
{{ "{:,}".format(overall.total_calls) }}{{ "{:,}".format(overall.total_tokens) }} 權杖Ollama 占比
{{ "%.0f"|format(overall.local_pct) }}%{{ "{:,}".format(overall.local_calls) }} 次本地呼叫付費成本
${{ "%.2f"|format(overall.total_cost) }}{{ "{:,}".format(overall.paid_calls) }} 次付費呼叫RAG 命中率
{{ "%.0f"|format(overall.rag_rate) }}%{{ "{:,}".format(overall.rag_hits) }} 次命中 {% endif %}
{% if error %}
{{ error }}
{% endif %}
-
4 Agent Matrix
LLM × MCP × RAG 編排矩陣
| Agent | 呼叫 | 成本 | Ollama | 付費 | MCP | RAG | 錯誤 | 耗時 |
{% for ag in agent_matrix %}| {{ ag.label }}{{ ag.desc }} | {% if ag.calls > 0 %}{{ "{:,}".format(ag.calls) }}{{ "{:,}".format(ag.tokens) }} tk{% else %}—{% endif %} | {% if ag.calls > 0 %}${{ "%.2f"|format(ag.cost) }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.0f"|format(ag.ollama_pct) }}%A {{ ag.ollama_gcp_a }} · B {{ ag.ollama_gcp_b }} · 111 {{ ag.ollama_111 }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.0f"|format(ag.paid_pct) }}%Gemini {{ ag.gemini }}{% if ag.other_paid %} · 其他 {{ ag.other_paid }}{% endif %}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.1f"|format(ag.mcp_rate) }}%{{ ag.mcp_calls }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.1f"|format(ag.rag_rate) }}%{{ ag.rag_hits }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.1f"|format(ag.error_rate) }}%{{ ag.errors }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ ag.avg_ms }} ms{% else %}—{% endif %} |
{% endfor %}
+
四 Agent 矩陣
LLM × MCP × RAG 編排矩陣
| Agent | 呼叫 | 成本 | Ollama | 付費 | MCP | RAG | 錯誤 | 耗時 |
{% for ag in agent_matrix %}| {{ ag.label }}{{ ag.desc }} | {% if ag.calls > 0 %}{{ "{:,}".format(ag.calls) }}{{ "{:,}".format(ag.tokens) }} 權杖{% else %}—{% endif %} | {% if ag.calls > 0 %}${{ "%.2f"|format(ag.cost) }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.0f"|format(ag.ollama_pct) }}%A {{ ag.ollama_gcp_a }} · B {{ ag.ollama_gcp_b }} · 111 {{ ag.ollama_111 }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.0f"|format(ag.paid_pct) }}%Gemini {{ ag.gemini }}{% if ag.other_paid %} · 其他 {{ ag.other_paid }}{% endif %}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.1f"|format(ag.mcp_rate) }}%{{ ag.mcp_calls }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.1f"|format(ag.rag_rate) }}%{{ ag.rag_hits }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ "%.1f"|format(ag.error_rate) }}%{{ ag.errors }}{% else %}—{% endif %} | {% if ag.calls > 0 %}{{ ag.avg_ms }} ms{% else %}—{% endif %} |
{% endfor %}
- {% if recommendations %}
{% for r in recommendations %}
{{ r.severity|upper }}{{ r.agent }}發現:{{ r.finding }}
建議:{{ r.suggestion }}
{% endfor %}
{% endif %}
- {% if mcp_matrix %}
MCP Detail
MCP server × caller 工作量
| MCP Server | Caller | tool 呼叫 | cache | cache 率 | 成本 |
{% 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 %}
-
Operation Ollama-First v5.0 — Agent 指揮矩陣
+ {% if recommendations %}
{% for r in recommendations %}
{{ r.severity|upper }}{{ r.agent }}發現:{{ r.finding }}
建議:{{ r.suggestion }}
{% endfor %}
{% endif %}
+ {% if mcp_matrix %}
| MCP 服務 | 呼叫端 | tool 呼叫 | 快取 | 快取率 | 成本 |
{% 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 %}
+
Ollama 優先策略 v5.0 — Agent 指揮矩陣
{% endblock %}
diff --git a/templates/admin/ai_calls_dashboard.html b/templates/admin/ai_calls_dashboard.html
index 10f6b88..9e2cf10 100644
--- a/templates/admin/ai_calls_dashboard.html
+++ b/templates/admin/ai_calls_dashboard.html
@@ -50,11 +50,11 @@
- AI Traffic Control · {{ hours }}h Window
+ AI 流量管制 · {{ hours }} 小時視窗
AI 流量控制塔
- 這裡不是流水帳,而是 AI 中樞的飛航管制台:看呼叫量、Token、成本、錯誤率、RAG 命中與 MCP 編排,並在異常時一鍵派出 Code Review Pipeline。
+ 這裡不是流水帳,而是 AI 中樞的飛航管制台:看呼叫量、權杖量、成本、錯誤率、RAG 命中與 MCP 編排,並在異常時一鍵派出程式碼審查管線。
-
+