diff --git a/frontend/src/components/aiops/AIOpsAssistant.tsx b/frontend/src/components/aiops/AIOpsAssistant.tsx index 2bf6895..f21cc72 100644 --- a/frontend/src/components/aiops/AIOpsAssistant.tsx +++ b/frontend/src/components/aiops/AIOpsAssistant.tsx @@ -23,7 +23,7 @@ import { } from '@/components/ui/select'; import { Switch } from '@/components/ui/switch'; import { toast } from 'sonner'; -import { aiApi, type LLMConfigData, type ChatHistoryItem } from '@/services/api'; +import { aiApi, alertApi, agentApi, dashboardStatsApi, type LLMConfigData, type ChatHistoryItem } from '@/services/api'; import { Bot, Send, @@ -63,12 +63,68 @@ const defaultConfig: LLMConfigData = { enabled: false, }; -// 快捷操作(填入输入框供用户发送) // Logan.Li -const quickActions = [ - { id: 'alerts', name: '分析今日告警', icon: AlertTriangle, prompt: '帮我分析最近的告警事件,给出根因分析和处置建议' }, - { id: 'performance', name: '性能瓶颈分析', icon: Activity, prompt: '分析当前系统的性能瓶颈,找出 CPU/内存/磁盘使用率最高的服务' }, - { id: 'report', name: '生成运维报告', icon: FileText, prompt: '生成今日运维报告,包括资源使用、告警、发布情况' }, - { id: 'optimize', name: '资源优化建议', icon: TrendingUp, prompt: '根据当前资源使用情况提供成本优化和容量规划建议' }, +// ============ 快捷操作平台数据加载器(注入真实运维上下文) // Logan.Li ============ +// 拉取最近告警事件,格式化为紧凑列表供 LLM 分析 +async function loadAlertsContext(): Promise { + const res = await alertApi.getAlerts({ page: 1, page_size: 20 }); + const alerts: any[] = (res as any)?.list || []; + if (!alerts.length) return '(当前无告警事件)'; + const lines = alerts.slice(0, 15).map((a, i) => + `${i + 1}. [${a.severity || '未知'}|${a.status || '-'}] ${a.rule_name || '告警'} → 目标:${a.target || '-'} 值:${a.value ?? '-'} 时间:${(a.started_at || '').slice(0, 16)}`, + ); + const firing = alerts.filter((a) => a.status === 'firing').length; + return `共 ${alerts.length} 条告警(firing ${firing} 条),前 ${Math.min(15, alerts.length)} 条:\n${lines.join('\n')}`; +} + +// 拉取 Agent 实时主机指标,识别高负载主机 +async function loadMetricsContext(): Promise { + const res = await agentApi.getLatest(); + const hosts: any[] = Array.isArray(res) ? res : (res as any)?.list || []; + if (!hosts.length) return '(当前无 Agent 上报数据,可能未部署监控 Agent)'; + const lines = hosts.slice(0, 20).map((h, i) => { + const cpu = h.cpu_usage ?? '-'; + const mem = h.mem_usage ?? '-'; + return `${i + 1}. ${h.hostname || '-'} | CPU:${cpu}% | 内存:${mem}% (${h.mem_used ?? '-'}/${h.mem_total ?? '-'}GB) | 核数:${h.cpu_cores ?? '-'} | 上报:${(h.report_time || '').slice(0, 16)}`; + }); + const hot = hosts.filter((h) => (h.cpu_usage ?? 0) >= 80 || (h.mem_usage ?? 0) >= 80); + const note = hot.length + ? `\n⚠️ 高负载主机 ${hot.length} 台:${hot.map((h) => `${h.hostname}(CPU${h.cpu_usage}%/MEM${h.mem_usage}%)`).join('、')}` + : ''; + return `Agent 监控的 ${hosts.length} 台主机实时指标:\n${lines.join('\n')}${note}`; +} + +// 拉取仪表盘综合数据,生成运维报告上下文 +async function loadReportContext(): Promise { + const [stats, alerts, releases] = await Promise.all([ + dashboardStatsApi.getStats().catch(() => null), + dashboardStatsApi.getAlerts(10).catch(() => null), + dashboardStatsApi.getReleases(10).catch(() => null), + ]); + const s: any = stats || {}; + const ss = s.server_stats || {}, ks = s.k8s_stats || {}, as = s.alert_stats || {}, ps = s.pipeline_stats || {}, cs = s.cost_stats || {}; + const overview = `服务器:${ss.total ?? 0}(运行${ss.running ?? 0}) | K8s集群:${ks.total ?? 0} 节点:${ks.total_nodes ?? 0} Pod:${ks.total_pods ?? 0} | 告警:${as.total ?? 0}(firing ${as.firing ?? 0}) | 流水线:${ps.total ?? 0}(成功率${ps.success_rate ?? 0}%) | 本月成本:¥${cs.month_cost ?? 0}`; + const alertList: any[] = Array.isArray(alerts) ? alerts : (alerts as any)?.list || []; + const releaseList: any[] = Array.isArray(releases) ? releases : (releases as any)?.list || []; + const alertLines = alertList.slice(0, 10).map((a, i) => `${i + 1}.[${a.severity || '?'}|${a.status || '?'}] ${a.rule_name || '-'} → ${a.target || '-'}`); + const releaseLines = releaseList.slice(0, 10).map((r, i) => `${i + 1}.${r.app_name || '-'} ${r.version || ''} [${r.environment || '-'}/${r.status || '-'}] by ${r.author || '-'}`); + return `平台概览:${overview}\n\n最近告警:\n${alertLines.join('\n') || '(无)'}\n\n最近发布:\n${releaseLines.join('\n') || '(无)'}`; +} + +// 快捷操作类型(contextLoader 可选,支持未来无数据加载的纯文本操作) // Logan.Li +interface QuickAction { + id: string; + name: string; + icon: typeof AlertTriangle; + prompt: string; + contextLoader?: () => Promise; +} + +// 快捷操作(已配置 LLM 时自动拉取真实平台数据注入上下文后发送) // Logan.Li +const quickActions: QuickAction[] = [ + { id: 'alerts', name: '分析今日告警', icon: AlertTriangle, prompt: '帮我分析以下最近的告警事件,给出根因分析和处置建议(按严重程度排序,给出具体处置步骤)', contextLoader: loadAlertsContext }, + { id: 'performance', name: '性能瓶颈分析', icon: Activity, prompt: '分析以下主机实时指标,找出 CPU/内存使用率最高的主机并给出性能优化建议', contextLoader: loadMetricsContext }, + { id: 'report', name: '生成运维报告', icon: FileText, prompt: '基于以下平台实时数据,生成一份简洁的今日运维报告,包括资源概览、告警情况和发布状态,用 Markdown 格式输出', contextLoader: loadReportContext }, + { id: 'optimize', name: '资源优化建议', icon: TrendingUp, prompt: '根据以下主机资源使用数据,提供成本优化和容量规划建议(识别闲置/过载主机,给出缩容/扩容建议)', contextLoader: loadMetricsContext }, ]; interface Message { @@ -94,6 +150,7 @@ export function AIOpsAssistant() { const [testing, setTesting] = useState(false); const [config, setConfig] = useState(defaultConfig); const [configReady, setConfigReady] = useState(false); // 已配置且启用 + const [contextLoading, setContextLoading] = useState(null); // 快捷操作拉取平台数据中 const messagesEndRef = useRef(null); // 滚动到底部 // Logan.Li @@ -177,10 +234,8 @@ export function AIOpsAssistant() { } }; - // 发送消息(真实 LLM 对话) // Logan.Li - const handleSend = async () => { - const text = input.trim(); - if (!text || isLoading) return; + // 发送一条消息的核心逻辑(供输入框发送和快捷操作复用) // Logan.Li + const sendMessage = async (text: string) => { const userMsg: Message = { id: Date.now().toString(), role: 'user', @@ -189,7 +244,6 @@ export function AIOpsAssistant() { }; const newMessages = [...messages, userMsg]; setMessages(newMessages); - setInput(''); setIsLoading(true); try { // 仅保留最近 10 条上下文避免 token 超限 // Logan.Li @@ -210,6 +264,36 @@ export function AIOpsAssistant() { } }; + // 输入框发送 // Logan.Li + const handleSend = async () => { + const text = input.trim(); + if (!text || isLoading) return; + setInput(''); + await sendMessage(text); + }; + + // 快捷操作:已配置 LLM 时拉取真实平台数据注入上下文后自动发送;未配置则仅填入输入框 // Logan.Li + const handleQuickAction = async (action: QuickAction) => { + if (isLoading || contextLoading) return; + // 未配置或无数据加载器时,回退到填入输入框 + if (!configReady || !action.contextLoader) { + setInput(action.prompt); + return; + } + // 拉取真实平台数据,失败时注入错误说明而非中断 + setContextLoading(action.id); + let context = ''; + try { + context = await action.contextLoader(); + } catch { + context = '(平台数据加载失败,请稍后重试或检查后端服务)'; + } finally { + setContextLoading(null); + } + const fullPrompt = `${action.prompt}\n\n【平台实时数据】\n${context}`; + await sendMessage(fullPrompt); + }; + // 新对话(清空本地 + 后端持久化历史) // Logan.Li const handleNewChat = async () => { try { @@ -268,10 +352,18 @@ export function AIOpsAssistant() { key={action.id} variant="ghost" className="w-full justify-start text-slate-300 hover:text-slate-100 hover:bg-slate-700" - onClick={() => setInput(action.prompt)} + disabled={isLoading || contextLoading !== null} + onClick={() => handleQuickAction(action)} > - + {contextLoading === action.id ? ( + + ) : ( + + )} {action.name} + {configReady && action.contextLoader && ( + 实时 + )} ))}