fix(heartbeat): KM向量化改用raw SQL + 格式優化去除空格對齊
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 29s

- KM vectorized 改用 raw SQL (ORM 無 embedding 欄位)
- 移除 {display:<18} 空格對齊(非等寬字體Telegram會錯位)
- 格式: Name: value 每行一項,清楚易讀
- KM向量化加狀態icon ( ≥90% / ⚠️ <90%)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-12 17:36:01 +08:00
parent dd1b5a4364
commit 38dddcc7a2

View File

@@ -340,12 +340,12 @@ class HeartbeatReportService:
stats.km_total = km_total or 0
# KM 向量化數embedding IS NOT NULL
# 注意knowledge_entries 無 vectorized 欄位,用 embedding 判斷
vec_count = await db.scalar(
select(func.count()).select_from(KnowledgeEntryRecord)
.where(KnowledgeEntryRecord.embedding.isnot(None))
# KnowledgeEntryRecord ORM 無 embedding 欄位,改用 raw SQL
from sqlalchemy import text as sa_text
vec_result = await db.execute(
sa_text("SELECT COUNT(*) FROM knowledge_entries WHERE embedding IS NOT NULL")
)
stats.km_vectorized = vec_count or 0
stats.km_vectorized = vec_result.scalar() or 0
# 24h 修復統計
since = datetime.utcnow() - timedelta(hours=24)
@@ -441,50 +441,42 @@ def report_to_telegram_html(report: HeartbeatReport) -> str:
# --- AI 服務 ---
lines.append("🤖 <b>AI 服務</b>")
ollama_probe = report.ai_services.get("ollama", ProbeResult(False, "❌ 無回應"))
latency_str = f" ({ollama_probe.latency_ms:.0f}ms)" if ollama_probe.latency_ms else ""
lines.append(f" Ollama: {ollama_probe.status}{latency_str}")
# 各模型狀態(縮排顯示)
lat = f" ({ollama_probe.latency_ms:.0f}ms)" if ollama_probe.latency_ms else ""
lines.append(f"Ollama: {ollama_probe.status}{lat}")
for model, loaded in report.ollama_models.items():
icon = "" if loaded else ""
short = model.split(":")[0]
lines.append(f" {icon} {html.escape(short)}")
lines.append(f" {icon} {html.escape(model.split(':')[0])}")
for svc_name, display in [("nemotron", "Nemotron NIM"), ("gemini", "Gemini API"), ("claude", "Claude API")]:
probe = report.ai_services.get(svc_name, ProbeResult(False, "❌ 無回應"))
latency_str = f" ({probe.latency_ms:.0f}ms)" if probe.latency_ms else ""
lines.append(f" {display:<18}{probe.status}{latency_str}")
lat = f" ({probe.latency_ms:.0f}ms)" if probe.latency_ms else ""
lines.append(f"{display}: {probe.status}{lat}")
lines.append("")
# --- MCP Provider ---
lines.append("🔌 <b>MCP Provider</b>")
mcp_display = {
"k8s": "K8s MCP",
"ssh": "SSH MCP",
"argocd": "ArgoCD MCP",
"sentry": "Sentry MCP",
}
for key, display in mcp_display.items():
for key, display in [("k8s", "K8s MCP"), ("ssh", "SSH MCP"), ("argocd", "ArgoCD MCP"), ("sentry", "Sentry MCP")]:
probe = report.mcp_providers.get(key, ProbeResult(False, "❌ 無回應"))
lines.append(f" {display:<18}{probe.status}")
lines.append(f"{display}: {probe.status}")
lines.append("")
# --- 飛輪狀態 ---
fw = report.flywheel
lines.append("🔄 <b>飛輪狀態24h</b>")
lines.append(f" Playbooks: {fw.playbook_count}")
lines.append(f"Playbooks: {fw.playbook_count}")
if fw.attempt_24h > 0:
rate = int(fw.success_24h / fw.attempt_24h * 100)
lines.append(f" 今日修復: {fw.success_24h}/{fw.attempt_24h} 次 ({rate}%)")
lines.append(f"今日修復: {fw.success_24h}/{fw.attempt_24h} 次 ({rate}%)")
else:
lines.append(f" 今日修復: 0 次")
lines.append("今日修復: 0 次")
if fw.km_total > 0:
vec_rate = int(fw.km_vectorized / fw.km_total * 100)
lines.append(f" KM 向量化: {fw.km_vectorized}/{fw.km_total} ({vec_rate}%)")
icon = "" if vec_rate >= 90 else "⚠️"
lines.append(f"KM 向量化: {icon} {fw.km_vectorized}/{fw.km_total} ({vec_rate}%)")
if fw.last_learning_at:
lines.append(f" 最後學習固化: {fw.last_learning_at.strftime('%H:%M')}")
lines.append(f"最後學習: {fw.last_learning_at.strftime('%H:%M')}")
lines.append("")
@@ -492,15 +484,15 @@ def report_to_telegram_html(report: HeartbeatReport) -> str:
lines.append("🚀 <b>基礎設施</b>")
argocd = report.infra.get("argocd_sync", ProbeResult(False, "❌ 無回應"))
velero = report.infra.get("velero", ProbeResult(False, "❌ 無回應"))
lines.append(f" ArgoCD: {argocd.status}")
lines.append(f" Velero 備份: {velero.status}")
lines.append(f"ArgoCD: {argocd.status}")
lines.append(f"Velero 備份: {velero.status}")
# --- Warnings ---
if report.warnings:
lines.append("")
lines.append(f"⚠️ <b>需關注({len(report.warnings)} 項)</b>")
for w in report.warnings:
lines.append(f" - {html.escape(w)}")
lines.append(f" {html.escape(w)}")
else:
lines.append("")
lines.append("✅ <b>全部正常</b>")