Files
awoooi/apps/api/src/services/chat_manager.py
OG T 8b7f99b5fa
All checks were successful
E2E Health Check / e2e-health (push) Successful in 18s
fix(telegram): fix chat_id routing and llm result unpacking
2026-03-31 15:56:58 +08:00

118 lines
3.8 KiB
Python
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.
"""
AWOOOI Chat Manager - 統帥對話核心
===================================
Phase 21.5: 實作 Telegram 互動對話功能
職責:
1. 整合系統上下文 (K3s 狀態, 最近告警, 目前時間)
2. 決定對話風格 (OpenClaw 專業風 vs Nemo 參謀風)
3. 調用 LLM (Nemo-4B / Gemini) 產出回應
4. 遵守 SOUL.md Nothing.tech 純淨美學
2026-03-31 ogt: 初版建立
"""
import structlog
from datetime import datetime
from src.utils.timezone import now_taipei
from src.services.nvidia_provider import get_nvidia_provider
from src.repositories.k8s_repository import get_k8s_repository
from src.repositories.incident_repository import get_incident_repository
logger = structlog.get_logger(__name__)
class ChatManager:
"""
AWOOOI 對話管理器 - 系統的大腦與聲帶
"""
def __init__(self):
self.nvidia = get_nvidia_provider()
self.k8s = get_k8s_repository()
self.incidents = get_incident_repository()
async def get_system_context(self) -> str:
"""
收集系統即時上下文,供 LLM 參考
"""
now = now_taipei()
# 1. K3s 狀態
k8s_status = await self.k8s.get_pod_status_summary(namespace="awoooi-prod")
cluster_info = f"Cluster: {k8s_status['running']}/{k8s_status['total']} Pods Running"
if k8s_status['problem_pods']:
cluster_info += f", {len(k8s_status['problem_pods'])} anomalies detected."
# 2. 最近告警 (取 3 筆)
active_incidents = await self.incidents.get_active()
incident_summary = "None"
if active_incidents:
lines = []
for inc in active_incidents[:3]:
lines.append(f"- {inc.incident_id}: {inc.status.value} (Severity: {inc.severity.value})")
incident_summary = "\n".join(lines)
context = f"""
## Current System Context (Taipei Time: {now.strftime('%Y-%m-%d %H:%M:%S')})
- Environment: AWOOOI Production (K3s)
- {cluster_info}
- Active Incidents:
{incident_summary}
"""
return context
async def generate_response(
self,
user_id: int,
username: str,
message_text: str
) -> str:
"""
根據統帥訊息產生回覆
"""
system_context = await self.get_system_context()
# 判定是否在跟 Nemo 對話
is_asking_nemo = "nemo" in message_text.lower()
role_description = "You are OpenClaw, the AI operations assistant for AWOOOI platform."
if is_asking_nemo:
role_description = "You are Nemo-4B, the elite AI tactical advisor for AWOOOI. Address the user as 'Supreme Commander' (統帥)."
system_prompt = f"""{role_description}
{system_context}
## Guidelines:
1. Keep responses extremely concise and professional (Nothing.tech aesthetic).
2. For status queries, provide precise data.
3. For general chat, be supportive but focused on operations.
4. Language: Preferred Traditional Chinese (繁體中文).
5. No emojis except for functional ones (🚨, ✅, 📊).
"""
try:
# 優先使用 NVIDIA Nemo-4B
response, success, tokens, cost = await self.nvidia.chat(
prompt=f"{system_prompt}\n\nCommander's Message: {message_text}",
model="nvidia/nemotron-mini-4b-instruct",
max_tokens=1024
)
if not success:
return "🛸 抱歉統帥Nemo 參謀暫時離線。請稍後再試。"
return response.strip()
except Exception as e:
logger.exception("chat_generation_error", error=str(e))
return "⚠️ 通訊鏈路異常,無法聯繫 AI 腦區。"
# Singleton
_chat_manager = None
def get_chat_manager() -> ChatManager:
global _chat_manager
if _chat_manager is None:
_chat_manager = ChatManager()
return _chat_manager