feat(telegram): AI 鏈路透明化 — 告警訊息顯示 OpenClaw + Tool Calling 模型/後端
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 12m12s

- nemotron.py: 偵測 OllamaToolProvider vs NvidiaProvider,記錄 tool_model/tool_backend
- openclaw.py: 傳播 nemotron_tool_model/nemotron_tool_backend 到 proposal
- decision_manager.py: 從 proposal_data 提取並傳給 send_approval_card()
- telegram_gateway.py: TelegramMessage 新增兩個欄位,format_with_nemotron 顯示
  "🔧 Tool Calling: llama3.1:8b (Ollama 本機)" 或 "NVIDIA 雲端"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-09 15:05:16 +08:00
parent aa2eb486ce
commit d8c2969341
4 changed files with 41 additions and 4 deletions

View File

@@ -220,10 +220,21 @@ class NemotronProvider:
validation_status = "✅ 驗證通過" if validation_passed and tools else "❌ 驗證失敗"
# 2026-04-09 Claude Sonnet 4.6: 記錄 tool calling 使用的模型和後端
from src.services.nvidia_provider import OllamaToolProvider
if isinstance(nvidia, OllamaToolProvider):
tool_model = settings.OLLAMA_TOOL_MODEL # e.g. "llama3.1:8b"
tool_backend = "Ollama 本機"
else:
tool_model = "nemotron-mini-4b-instruct"
tool_backend = "NVIDIA 雲端"
payload = {
"tools": tools,
"validation": validation_status,
"latency_ms": latency_ms,
"tool_model": tool_model,
"tool_backend": tool_backend,
}
logger.info(

View File

@@ -151,6 +151,9 @@ async def _push_decision_to_telegram(
nemotron_tools = proposal_data.get("nemotron_tools")
nemotron_validation = proposal_data.get("nemotron_validation", "")
nemotron_latency_ms = proposal_data.get("nemotron_latency_ms", 0.0)
# 2026-04-09 Claude Sonnet 4.6: Tool Calling 模型/後端
nemotron_tool_model = proposal_data.get("nemotron_tool_model", "")
nemotron_tool_backend = proposal_data.get("nemotron_tool_backend", "")
# 建立 approval_id (使用 incident_id 作為追蹤)
# 2026-03-27 ogt: 修復 INC-INC-INC- 重複前綴 bug
@@ -173,6 +176,8 @@ async def _push_decision_to_telegram(
nemotron_tools=nemotron_tools,
nemotron_validation=nemotron_validation,
nemotron_latency_ms=nemotron_latency_ms,
nemotron_tool_model=nemotron_tool_model,
nemotron_tool_backend=nemotron_tool_backend,
# 2026-04-05 Claude Code: 傳入 incident_id 以啟用 detail/reanalyze/history 按鈕
incident_id=incident.incident_id,
)

View File

@@ -1554,6 +1554,8 @@ Focus on:
proposal["nemotron_tools"] = nemotron_result.get("tools", [])
proposal["nemotron_validation"] = nemotron_result.get("validation", "⏳ 驗證中")
proposal["nemotron_latency_ms"] = nemotron_result.get("latency_ms", 0.0)
proposal["nemotron_tool_model"] = nemotron_result.get("tool_model", "")
proposal["nemotron_tool_backend"] = nemotron_result.get("tool_backend", "")
logger.info(
"nemotron_collaboration_complete",
@@ -1600,6 +1602,8 @@ Focus on:
proposal["nemotron_tools"] = gemini_fallback_result.get("tools", [])
proposal["nemotron_validation"] = gemini_fallback_result.get("validation", "⚠️ Gemini 代理")
proposal["nemotron_latency_ms"] = gemini_fallback_result.get("latency_ms", 0.0)
proposal["nemotron_tool_model"] = "gemini-fallback"
proposal["nemotron_tool_backend"] = "Gemini 雲端"
return proposal, provider, True

View File

@@ -170,6 +170,8 @@ class TelegramMessage:
# 2026-03-31 Claude Code: OpenClaw + Nemotron 雙軌顯示
# ==========================================================================
nemotron_enabled: bool = False # 是否啟用 Nemotron 協作
nemotron_tool_model: str = "" # Tool Calling 模型 (e.g. "llama3.1:8b")
nemotron_tool_backend: str = "" # Tool Calling 後端 (e.g. "Ollama 本機" / "NVIDIA 雲端")
nemotron_tools: list[dict] | None = None # Tool Calling 結果 [{"tool": str, "args": dict, "valid": bool}]
nemotron_validation: str = "" # "✅ 驗證通過" / "❌ 驗證失敗" / "⏳ 驗證中"
nemotron_latency_ms: float = 0.0 # Nemotron 呼叫延遲 (ms)
@@ -396,13 +398,13 @@ class TelegramMessage:
source_label = "⚙️ <b>規則匹配</b>"
# Nemotron 區塊
# 2026-04-09 Claude Sonnet 4.6: 顯示 AI 鏈路 — OpenClaw 用哪個模型Tool Calling 用哪個模型
nemotron_block = ""
if self.nemotron_enabled and self.nemotron_tools:
tools_lines = []
for t in self.nemotron_tools[:3]: # 最多顯示 3 個
valid_emoji = "" if t.get("valid", False) else ""
tool_name = html.escape(str(t.get("tool", "unknown"))[:20])
# 2026-04-05 ogt: 格式化 args 為可讀的 key=value而非 Python dict 字串
args = t.get("args", {})
if isinstance(args, dict) and args:
args_str = ", ".join(f"{k}={v}" for k, v in list(args.items())[:2])
@@ -414,14 +416,23 @@ class TelegramMessage:
tools_str = "\n".join(tools_lines)
validation_display = html.escape(self.nemotron_validation or "⏳ 驗證中")
# Tool Calling 模型/後端標籤
if self.nemotron_tool_model and self.nemotron_tool_backend:
tool_model_label = f"<code>{html.escape(self.nemotron_tool_model)}</code> ({html.escape(self.nemotron_tool_backend)})"
elif self.nemotron_tool_model:
tool_model_label = f"<code>{html.escape(self.nemotron_tool_model)}</code>"
else:
tool_model_label = "Nemotron"
latency_line = f"└ 延遲: {self.nemotron_latency_ms:.0f}ms\n" if self.nemotron_latency_ms > 0 else ""
nemotron_block = (
f"━━━━━━━━━━━━━━━━━━━\n"
f"🔧 <b>Nemotron 執行方案</b>\n"
f"🔧 <b>Tool Calling</b>: {tool_model_label}\n"
f"{tools_str}\n"
f"└ 驗證: {validation_display}\n"
f"{latency_line}"
)
if self.nemotron_latency_ms > 0:
nemotron_block += f"└ 延遲: {self.nemotron_latency_ms:.0f}ms\n"
# 2026-04-05 Claude Code: 重設計訊息格式,提升易讀性
# 組裝訊息
@@ -1327,6 +1338,9 @@ class TelegramGateway:
nemotron_tools: list[dict] | None = None,
nemotron_validation: str = "",
nemotron_latency_ms: float = 0.0,
# 2026-04-09 Claude Sonnet 4.6: Tool Calling 模型/後端顯示
nemotron_tool_model: str = "",
nemotron_tool_backend: str = "",
# 2026-04-05 Claude Code: incident_id 用於 detail/reanalyze/history 按鈕
incident_id: str = "",
) -> dict:
@@ -1399,6 +1413,9 @@ class TelegramGateway:
nemotron_tools=nemotron_tools,
nemotron_validation=nemotron_validation,
nemotron_latency_ms=nemotron_latency_ms,
# 2026-04-09 Claude Sonnet 4.6: Tool Calling 模型/後端
nemotron_tool_model=nemotron_tool_model,
nemotron_tool_backend=nemotron_tool_backend,
)
# 格式化訊息 — Phase 22: 如果 Nemotron 啟用,使用雙軌格式