Skip to main content
Glama
SailingCoder

grafana-mcp-analyzer

by SailingCoder
monitoring-analyzer.ts14.4 kB
import type { ExtractedData } from '../types/index.js'; const DEFAULT_SYSTEM_PROMPT = `您是一位资深的Grafana数据分析专家,具备丰富的数据可视化和洞察挖掘经验。您擅长从各类数据源中发现有价值的信息,并提供可执行的业务建议。请对数据进行专业分析,重点关注: ## 核心分析目标 - **数据洞察挖掘**:从数据中发现有价值的业务洞察和趋势 - **异常模式识别**:识别数据中的异常模式和潜在问题 - **业务影响评估**:分析数据变化对业务目标的影响 - **决策支持建议**:提供基于数据的决策建议和行动方案 ## 数据分析维度 1. **趋势分析**:识别长期趋势、季节性模式、周期性变化 2. **异常检测**:发现异常值、异常模式、偏离正常范围的数据点 3. **对比分析**:与历史数据、目标值、基准线进行对比 4. **关联分析**:分析不同指标间的关联性和影响关系 5. **预测分析**:基于历史数据预测未来趋势和可能的变化 ## 关键关注点 - **业务指标**:关注对业务目标有直接影响的关键指标 - **用户体验**:分析影响用户体验的相关数据 - **效率优化**:识别可以提升效率的优化机会 - **风险预警**:提前识别可能影响业务的风险信号 ## 建议输出标准 - **可执行性**:提供具体的行动建议和实施方案 - **优先级排序**:按照业务影响和紧急程度排序 - **量化分析**:提供具体的数值分析和量化指标 - **可视化建议**:建议创建的图表和仪表盘配置`; /** * 根据数据类型获取专门的分析指导 */ function getDataTypeSpecificGuidance(dataType: string): string { const guidanceMap: Record<string, string> = { 'timeseries': ` ## 时序数据专项分析指导 - **趋势识别**:分析数据的上升、下降、平稳趋势,识别关键拐点 - **周期性模式**:识别日周期、周周期、月周期等业务和用户行为模式 - **异常事件**:检测突发峰值、异常下降等重要事件 - **基线建立**:建立正常水位基线,计算P50/P90/P95/P99百分位数 - **预测分析**:基于历史趋势预测未来走向和可能的变化 - **目标对比**:与业务目标、KPI指标、SLA要求进行对比分析`, 'tables': ` ## 表格数据专项分析指导 - **数据质量**:检查数据完整性、准确性、一致性 - **分布特征**:分析数据分布,识别异常分布和离群值 - **排序分析**:按关键指标排序,识别Top N表现者和问题项 - **统计分析**:计算平均值、中位数、标准差等描述性统计 - **分组对比**:按不同维度分组,识别差异化表现 - **趋势对比**:与历史数据、目标值、行业基准对比`, 'elasticsearch': ` ## 日志数据专项分析指导 - **事件模式**:分析事件的时间分布和频率模式 - **用户行为**:分析用户访问模式、使用习惯和行为变化 - **业务流程**:跟踪业务流程的执行情况和效率 - **异常检测**:识别异常访问、错误模式、安全威胁 - **性能分析**:分析响应时间、处理效率等性能指标 - **业务洞察**:从日志中挖掘业务相关的洞察和机会`, 'default': ` ## 通用数据分析指导 - **数据探索**:全面了解数据的结构、范围和特征 - **质量评估**:评估数据的完整性、准确性、时效性 - **异常识别**:使用统计方法识别异常值和异常模式 - **关联发现**:分析不同指标间的关联关系和影响因素 - **价值挖掘**:从数据中发现业务价值和改进机会` }; return guidanceMap[dataType] || guidanceMap['default']; } /** * 生成结构化的分析报告模板 */ // eslint-disable-next-line @typescript-eslint/no-unused-vars function generateAnalysisTemplate(_prompt: string, _hasAggregateData: boolean = false): string { const baseTemplate = ` ## 分析报告结构要求 ### 1. 执行摘要 (Executive Summary) - **关键洞察**:最重要的3-5个数据洞察 - **影响评估**:对业务目标和用户体验的影响分析 - **优先级建议**:按重要性和紧急程度排序的行动建议 - **量化结果**:关键指标的量化分析结果 ### 2. 数据概览与模式 (Data Overview & Patterns) - **数据质量**:数据完整性、准确性、时效性评估 - **总体趋势**:整体数据趋势和发展方向 - **模式识别**:周期性模式、季节性变化、异常模式 - **关键指标**:最重要的业务指标表现 ### 3. 深度分析 (Deep Analysis) - **异常检测**:异常值、异常模式、异常时间段的识别 - **对比分析**:与历史数据、目标值、基准线的对比 - **关联分析**:不同指标间的关联关系和影响因素 - **细分分析**:按不同维度的细分表现 ### 4. 业务洞察与建议 (Business Insights & Recommendations) - **业务影响**:数据变化对业务目标的具体影响 - **机会识别**:基于数据发现的业务机会和优化点 - **风险预警**:潜在风险和问题的早期预警 - **行动建议**:具体的、可执行的改进建议 ### 5. 技术建议 (Technical Recommendations) - **数据质量改进**:数据收集、处理、存储的改进建议 - **监控优化**:监控策略和告警规则的优化建议 - **可视化建议**:仪表盘和图表的最佳实践建议 - **性能优化**:系统性能和数据处理效率的优化建议 ## 分析质量要求 - **准确性**:基于数据事实,避免主观臆断 - **可操作性**:提供具体、可执行的建议 - **优先级**:按重要性和紧急程度排序 - **量化**:提供具体的数值和指标支持 - **时效性**:关注当前和未来的业务影响`; return baseTemplate; } /** * 生成数据概览统计 */ export function generateDataOverview(data: ExtractedData): any { const safeGet = (obj: any, path: (string | number)[], defaultValue: any = 0): any => { return path.reduce((current, key) => current?.[key], obj) ?? defaultValue; }; // 基础统计 const overview = { dataType: 'unknown', totalSize: 0, recordCount: 0, timeRange: null as any, keyMetrics: {} as any, dataQuality: { completeness: 0, consistency: 0, accuracy: 0 } }; try { // 检测数据类型 if (data?.data?.results && typeof data.data.results === 'object') { overview.dataType = 'grafana-query'; const results = data.data.results; const resultKeys = Object.keys(results); overview.recordCount = resultKeys.length; // 统计frames和fields let totalFrames = 0; let totalFields = 0; let totalDataPoints = 0; resultKeys.forEach(key => { const result = results[key]; if (result?.frames && Array.isArray(result.frames)) { totalFrames += result.frames.length; result.frames.forEach((frame: any) => { if (frame?.fields && Array.isArray(frame.fields)) { totalFields += frame.fields.length; frame.fields.forEach((field: any) => { if (field?.values && Array.isArray(field.values)) { totalDataPoints += field.values.length; } }); } }); } }); overview.keyMetrics = { resultCount: resultKeys.length, totalFrames, totalFields, totalDataPoints }; } else if (data?.data?.series && Array.isArray(data.data.series)) { overview.dataType = 'timeseries'; overview.recordCount = data.data.series.length; // 统计时间序列数据 let totalDataPoints = 0; let timeRange = { start: null, end: null }; data.data.series.forEach((series: any) => { if (series?.datapoints && Array.isArray(series.datapoints)) { totalDataPoints += series.datapoints.length; series.datapoints.forEach((point: any) => { if (point && point.length >= 2) { const timestamp = point[1]; if (!timeRange.start || timestamp < timeRange.start) { timeRange.start = timestamp; } if (!timeRange.end || timestamp > timeRange.end) { timeRange.end = timestamp; } } }); } }); overview.timeRange = timeRange; overview.keyMetrics = { seriesCount: data.data.series.length, totalDataPoints }; } else if (data?.data?.tables && Array.isArray(data.data.tables)) { overview.dataType = 'tables'; overview.recordCount = data.data.tables.length; // 统计表格数据 let totalRows = 0; let totalColumns = 0; data.data.tables.forEach((table: any) => { if (table?.rows && Array.isArray(table.rows)) { totalRows += table.rows.length; } if (table?.columns && Array.isArray(table.columns)) { totalColumns = Math.max(totalColumns, table.columns.length); } }); overview.keyMetrics = { tableCount: data.data.tables.length, totalRows, totalColumns }; } else if (data?.data?.responses || data?.data?.hits || data?.data?.aggregations) { overview.dataType = 'elasticsearch'; // 统计Elasticsearch数据 const hits = safeGet(data.data, ['hits', 'hits'], []); const total = safeGet(data.data, ['hits', 'total'], { value: 0 }); const aggregations = data.data?.aggregations || {}; overview.recordCount = Array.isArray(hits) ? hits.length : 0; overview.keyMetrics = { totalHits: typeof total === 'object' ? total.value : total, returnedHits: hits.length, aggregationCount: Object.keys(aggregations).length }; } else if (Array.isArray(data)) { overview.dataType = 'array'; overview.recordCount = data.length; overview.keyMetrics = { arrayLength: data.length, itemTypes: [...new Set(data.map(item => typeof item))] }; } else if (typeof data === 'object' && data !== null) { overview.dataType = 'object'; overview.recordCount = 1; overview.keyMetrics = { propertyCount: Object.keys(data).length, propertyTypes: [...new Set(Object.values(data).map(value => typeof value))] }; } // 计算数据大小 overview.totalSize = Buffer.byteLength(JSON.stringify(data), 'utf8'); } catch (error) { console.error('生成数据概览时出错:', error); } return overview; } /** * 构建完整的分析指导 */ // eslint-disable-next-line @typescript-eslint/no-unused-vars function buildFullAnalysisGuidance( prompt: string, _requestId: string, dataOverview: any, storageResult: any, _queryConfig?: any, supportsResources: boolean = false ): string { const dataType = dataOverview?.dataType || 'unknown'; const dataTypeGuidance = getDataTypeSpecificGuidance(dataType); let resourceInstructions = ''; if (supportsResources && storageResult?.type === 'chunked') { resourceInstructions = ` ## 🎯 自动化数据获取工作流 数据已按${Math.round(storageResult.chunkingStrategy.split('-')[1])}KB分块存储,**请优先使用自动化工作流获取数据**: ### 🚀 推荐:自动化工作流(最简单) **请优先使用 \`chunk_workflow\` 工具自动获取所有分块,按顺序处理,直到complete为止。** **工作流步骤**: 1. 使用 \`chunk_workflow\` 工具执行 \`start\` 动作启动工作流(或直接执行 \`next\` 动作自动启动) 2. **自动连续执行**:重复执行 \`next\` 动作获取每个分块,直到所有分块获取完成 3. 执行 \`complete\` 动作完成工作流 4. 基于完整数据进行最终分析 **智能恢复**:如果工作流中断,直接执行 \`next\` 动作即可自动恢复并继续执行。 **自动连续**:AI可以自动连续调用 \`next\` 动作,无需等待用户确认。 ### 📋 备选:手动获取方式 - 使用 \`chunk_workflow\` 工具,执行 \`next\` 动作获取分块 - 例如:\`chunk-1\`, \`chunk-2\`, \`chunk-3\` 等 - **必须按顺序获取所有分块,不能跳过任何分块!** ### 📊 数据完整性要求 - 总共有 ${storageResult.chunks} 个分块 - 每个分块大小不超过${Math.round(storageResult.chunkingStrategy.split('-')[1])}KB - **必须获取所有${storageResult.chunks}个分块,不能遗漏任何分块!** - **跳过任何分块都会导致分析不完整!** **🚫 重要提醒**:这是唯一正确的数据获取方式,不要尝试使用curl或其他外部方法!`; } const analysisTemplate = generateAnalysisTemplate(prompt, false); return `${DEFAULT_SYSTEM_PROMPT} ${dataTypeGuidance} ${resourceInstructions} ${analysisTemplate} ## 当前数据信息 - **数据类型**: ${dataOverview?.dataType || 'unknown'} - **数据大小**: ${Math.round(dataOverview?.totalSize / 1024)}KB - **记录数量**: ${dataOverview?.recordCount || 0} - **存储方式**: ${storageResult?.type || 'full'} ${storageResult?.type === 'chunked' ? `- **分块数量**: ${storageResult.chunks}` : ''} ${storageResult?.type === 'chunked' ? `- **分块策略**: ${storageResult.chunkingStrategy}` : ''} ## 分析任务 **用户具体需求**: ${prompt} **重要提醒**: 请严格按照用户的具体分析需求执行,不要偏离用户的要求!如果用户要求分析特定内容(如支撑位和阻力位、技术指标等),请重点关注这些内容并提供详细分析。 请基于以上指导,对数据进行专业、深入的分析,并提供有价值的业务洞察和建议。`; } /** * 构建分析指导 */ export function buildAnalysisGuidance( prompt: string, requestId: string, dataOverview: any, storageResult: any, queryConfig?: any, supportsResources: boolean = false ): string { return buildFullAnalysisGuidance(prompt, requestId, dataOverview, storageResult, queryConfig, supportsResources); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SailingCoder/grafana-mcp-analyzer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server