Files
ewoooc/templates/admin/agent_orchestration.html
OoO 347efb8ea1
All checks were successful
CD Pipeline / deploy (push) Successful in 2m30s
feat(p46): Agent 編排矩陣新頁 — OpenClaw/Hermes/NemoTron/EA × Ollama × Gemini × MCP × RAG
統帥要求:「好好把 OpenClaw/Hermes/NemoTron/ElephantAlpha + Ollama 多模型
+ 外部付費 Gemini + 內外 MCP + RAG 組合發揮出 AI 自動化新境界」

新頁面 /observability/agent_orchestration 一頁式呈現 4 Agent × 5 維度全景:

J-1: caller 自動分組
- OpenClaw: openclaw_qa/daily/meta/monthly/weekly/bot_main/bot_gemini/bot_nim
            + sales_copy + code_review_openclaw
- Hermes: hermes_analyst + hermes_intent + code_review_hermes
- NemoTron: nemotron_dispatch
- ElephantAlpha: ea_engine + code_review_elephant

J-2/3: 跨表 SQL JOIN(ai_calls × mcp_calls × rag_query_log)
每個 agent 顯示:
- 24h 呼叫 + Token + 成本
- 本地 Ollama 比例(細分 GCP-A/GCP-B/111)
- 付費 LLM 比例(細分 Gemini / 其他)
- MCP 編排率(透過 request_id 跨表 JOIN mcp_calls)
- RAG 命中率
- 錯誤率 + 平均耗時
- MCP server × caller 工作量明細

自動編排建議(5 條 rule-based):
1. 付費比例 > 50% 且 ollama < 20% → 改 Hermes-first 短路
2. 錯誤率 > 10% → 觸發 Code Review Pipeline
3. MCP 編排率 < 5% 但 calls > 50 → 擴大 MCP omnisearch/firecrawl
4. RAG 命中率 ≥ 40% → 推 Telegram 收 feedback 強化 promotion gate
5. 111 fallback 比例 > 20% → GCP 兩台異常,查 host_health AIOps

J-4: 入口
- sidebar AI 觀測 group 加「Agent 編排矩陣」(07b)
- /observability/overview 入口卡升級為 7 項,Agent 編排矩陣放第一

整體 KPI 卡片:
- 總呼叫 / 本地 Ollama 比例 / 付費 LLM 成本 / RAG 命中率
- 「組合發揮」一目瞭然

8 表跨 JOIN:ai_calls × mcp_calls × rag_query_log × ai_insights ×
learning_episodes × incidents × heal_logs × host_health_probes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 19:38:10 +08:00

258 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "ewoooc_base.html" %}
{% block title %}Agent 編排矩陣{% endblock %}
{% block ewooo_content %}
<div class="container-fluid mt-3">
<h2 class="mb-3"><i class="fas fa-network-wired me-2"></i>Agent 編排矩陣
<small class="text-muted">{{ hours }}h 內 4 Agent × Ollama × Gemini × MCP × RAG 協作全景</small>
</h2>
{% if error %}
<div class="alert alert-warning"><strong><i class="fas fa-exclamation-triangle me-1"></i></strong> {{ error }}</div>
{% endif %}
<!-- 時間範圍 -->
<form method="get" class="row g-2 mb-3">
<div class="col-auto">
<select name="hours" class="form-select form-select-sm" onchange="this.form.submit()">
{% for h in [1, 6, 24, 72, 168] %}
<option value="{{ h }}" {% if hours == h %}selected{% endif %}>
{% if h < 24 %}過去 {{ h }} 小時{% else %}過去 {{ (h//24) }} {% endif %}
</option>
{% endfor %}
</select>
</div>
</form>
<!-- 整體 KPI -->
{% if overall %}
<div class="row g-3 mb-3">
<div class="col-lg-3 col-md-6">
<div class="card h-100">
<div class="card-body">
<small class="text-muted d-block"><i class="fas fa-network-wired me-1"></i>總呼叫</small>
<h3 class="mb-0">{{ "{:,}".format(overall.total_calls) }}</h3>
<small class="text-muted">Token{{ "{:,}".format(overall.total_tokens) }}</small>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="card h-100" style="border-left: 4px solid #198754;">
<div class="card-body">
<small class="text-muted d-block"><i class="fas fa-server me-1"></i>本地 Ollama 比例</small>
<h3 class="mb-0 text-success">{{ "%.0f"|format(overall.local_pct) }}<small>%</small></h3>
<small class="text-muted">{{ "{:,}".format(overall.local_calls) }} 呼叫</small>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="card h-100" style="border-left: 4px solid #ffc107;">
<div class="card-body">
<small class="text-muted d-block"><i class="fas fa-coins me-1"></i>付費 LLM 成本</small>
<h3 class="mb-0">${{ "%.2f"|format(overall.total_cost) }}</h3>
<small class="text-muted">{{ "{:,}".format(overall.paid_calls) }} 付費呼叫({{ "%.0f"|format(overall.paid_pct) }}%</small>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="card h-100" style="border-left: 4px solid #6f42c1;">
<div class="card-body">
<small class="text-muted d-block"><i class="fas fa-magnifying-glass-chart me-1"></i>RAG 命中率</small>
<h3 class="mb-0" style="color: #6f42c1;">{{ "%.0f"|format(overall.rag_rate) }}<small>%</small></h3>
<small class="text-muted">{{ "{:,}".format(overall.rag_hits) }} hits</small>
</div>
</div>
</div>
</div>
{% endif %}
<!-- 4 Agent 矩陣 -->
<div class="card mb-3">
<div class="card-header">
<strong><i class="fas fa-th me-2"></i>4 Agent × LLM × MCP × RAG 矩陣</strong>
<small class="text-muted">資料來源ai_calls × mcp_calls × rag_query_logcaller 自動分組)</small>
</div>
<div class="card-body p-0">
<table class="table mb-0" style="font-size: 0.92em;">
<thead class="table-light">
<tr>
<th>Agent</th>
<th class="text-end">呼叫</th>
<th class="text-end">成本</th>
<th class="text-end">本地 Ollama</th>
<th class="text-end">付費 LLM</th>
<th class="text-end">MCP 編排</th>
<th class="text-end">RAG 命中</th>
<th class="text-end">錯誤率</th>
<th class="text-end">耗時</th>
</tr>
</thead>
<tbody>
{% for ag in agent_matrix %}
<tr>
<td>
<strong>{{ ag.label }}</strong>
<small class="d-block text-muted">{{ ag.desc }}</small>
</td>
<td class="text-end">
{% if ag.calls > 0 %}
<strong>{{ "{:,}".format(ag.calls) }}</strong>
<small class="d-block text-muted">{{ "{:,}".format(ag.tokens) }} tk</small>
{% else %}
<small class="text-muted"></small>
{% endif %}
</td>
<td class="text-end">
{% if ag.calls > 0 %}
${{ "%.2f"|format(ag.cost) }}
{% else %}
<small class="text-muted"></small>
{% endif %}
</td>
<td class="text-end">
{% if ag.calls > 0 %}
<strong class="text-success">{{ "%.0f"|format(ag.ollama_pct) }}%</strong>
<small class="d-block text-muted">
GCP-A {{ ag.ollama_gcp_a }} ·
GCP-B {{ ag.ollama_gcp_b }} ·
111 {{ ag.ollama_111 }}
</small>
{% else %}
<small class="text-muted"></small>
{% endif %}
</td>
<td class="text-end">
{% if ag.calls > 0 %}
<strong class="{% if ag.paid_pct > 50 %}text-danger{% elif ag.paid_pct > 20 %}text-warning{% endif %}">
{{ "%.0f"|format(ag.paid_pct) }}%
</strong>
<small class="d-block text-muted">
Gemini {{ ag.gemini }}{% if ag.other_paid %} · 其他 {{ ag.other_paid }}{% endif %}
</small>
{% else %}
<small class="text-muted"></small>
{% endif %}
</td>
<td class="text-end">
{% if ag.calls > 0 %}
<strong class="{% if ag.mcp_rate >= 30 %}text-info{% elif ag.mcp_rate >= 10 %}text-warning{% else %}text-muted{% endif %}">
{{ "%.1f"|format(ag.mcp_rate) }}%
</strong>
<small class="d-block text-muted">{{ ag.mcp_calls }} request_id</small>
{% else %}
<small class="text-muted"></small>
{% endif %}
</td>
<td class="text-end">
{% if ag.calls > 0 %}
<strong style="color: #6f42c1;">{{ "%.1f"|format(ag.rag_rate) }}%</strong>
<small class="d-block text-muted">{{ ag.rag_hits }} hits</small>
{% else %}
<small class="text-muted"></small>
{% endif %}
</td>
<td class="text-end">
{% if ag.calls > 0 %}
<strong class="{% if ag.error_rate >= 15 %}text-danger{% elif ag.error_rate >= 5 %}text-warning{% else %}text-success{% endif %}">
{{ "%.1f"|format(ag.error_rate) }}%
</strong>
<small class="d-block text-muted">{{ ag.errors }} 次</small>
{% else %}
<small class="text-muted"></small>
{% endif %}
</td>
<td class="text-end">
{% if ag.calls > 0 %}{{ ag.avg_ms }} ms{% else %}<small class="text-muted"></small>{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer small text-muted">
<i class="fas fa-info-circle me-1"></i>
<strong>本地 Ollama</strong> = GCP-A + GCP-B + 111 三主機級聯(免費);
<strong>付費 LLM</strong> = Gemini / Claude / NIM / OpenRouter
<strong>MCP 編排率</strong> = caller 透過 ai_calls.request_id 串接到 mcp_calls 的比例;
<strong>RAG 命中</strong> = ai_calls.rag_hit=true 的 caller-level 比例。
</div>
</div>
<!-- 自動編排建議 -->
{% if recommendations %}
<div class="card mb-3" style="border-left: 4px solid #6f42c1;">
<div class="card-header bg-light">
<strong><i class="fas fa-lightbulb me-2"></i>編排策略自動建議</strong>
<small class="text-muted">— rule-based 規則引擎5 條判斷</small>
</div>
<div class="card-body p-2">
<ul class="list-unstyled mb-0">
{% for r in recommendations %}
<li class="mb-2 p-2" style="background: #fafafa; border-radius: 6px;
{% if r.severity == 'high' %}border-left: 3px solid #dc3545;
{% elif r.severity == 'med' %}border-left: 3px solid #ffc107;
{% else %}border-left: 3px solid #0dcaf0;{% endif %}">
<span class="badge {% if r.severity == 'high' %}bg-danger{% elif r.severity == 'med' %}bg-warning text-dark{% else %}bg-info text-dark{% endif %} me-1">{{ r.severity|upper }}</span>
<strong>{{ r.agent }}</strong>
<div class="small mt-1">
<i class="fas fa-search me-1"></i><strong>發現:</strong>{{ r.finding }}
</div>
<div class="small text-muted">
<i class="fas fa-arrow-right me-1"></i><strong>建議:</strong>{{ r.suggestion }}
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
<!-- MCP server × caller 細節 -->
{% if mcp_matrix %}
<div class="card mb-3">
<div class="card-header">
<strong><i class="fas fa-bolt me-2"></i>MCP server × caller 工作量明細</strong>
<small class="text-muted">資料來源mcp_calls過去 {{ hours }}h前 30 筆)</small>
</div>
<div class="card-body p-0">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr>
<th>MCP Server</th>
<th>呼叫端 (caller)</th>
<th class="text-end">tool 呼叫</th>
<th class="text-end">cache 命中</th>
<th class="text-end">cache 率</th>
<th class="text-end">成本</th>
</tr>
</thead>
<tbody>
{% for m in mcp_matrix %}
<tr>
<td><code>{{ m.server }}</code></td>
<td><code>{{ m.caller }}</code></td>
<td class="text-end">{{ "{:,}".format(m.calls) }}</td>
<td class="text-end">{{ m.cache_hits }}</td>
<td class="text-end">
<span class="{% if m.cache_rate >= 50 %}text-success{% elif m.cache_rate >= 20 %}text-warning{% endif %}">
{{ "%.0f"|format(m.cache_rate) }}%
</span>
</td>
<td class="text-end">${{ "%.4f"|format(m.cost) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<p class="text-muted mt-3"><small>
<i class="fas fa-robot me-1"></i>Operation Ollama-First v5.0 / Phase 46 — Agent 編排矩陣
8 表跨 JOINai_calls × mcp_calls × rag_query_log × ai_insights × learning_episodes
× incidents × heal_logs × host_health_probes
</small></p>
</div>
{% endblock %}