UNPKG

pm-orchestrator-enhancement

Version:

PM Orchestrator Enhancement - Multi-agent parallel execution system

212 lines 8.73 kB
"use strict"; /** * PM Orchestrator Enhancement - Trend Analyzer * * メトリクスのトレンド分析と改善提案を担当します。 */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TrendAnalyzer = void 0; const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const metrics_collector_1 = require("./metrics-collector"); /** * TrendAnalyzerクラス * * 指定期間のメトリクスを分析し、トレンドと改善提案を生成します。 */ class TrendAnalyzer { /** * コンストラクタ * * @param baseDir 分析ディレクトリのベースパス(デフォルト: カレントディレクトリ) */ constructor(baseDir = process.cwd()) { this.collector = new metrics_collector_1.MetricsCollector(baseDir); this.analysisDir = path_1.default.join(baseDir, '.pm-orchestrator', 'analysis'); } /** * 指定日数分のトレンド分析を実行します * * @param days 分析対象の日数 * @returns トレンド分析結果 */ async analyzeTrends(days) { // 分析期間を設定 const endDate = new Date(); const startDate = new Date(); startDate.setDate(startDate.getDate() - days); // 現在のメトリクスを取得 const currentMetrics = await this.collector.getMetrics(startDate, endDate); // 過去のメトリクスを取得(比較用) const pastEndDate = new Date(startDate); pastEndDate.setDate(pastEndDate.getDate() - 1); const pastStartDate = new Date(pastEndDate); pastStartDate.setDate(pastStartDate.getDate() - days); const pastMetrics = await this.collector.getMetrics(pastStartDate, pastEndDate); // トレンドを分析 const trends = this.calculateTrends(pastMetrics, currentMetrics); // 改善提案を生成 const suggestions = this.generateSuggestions(currentMetrics, trends); return { analyzed: true, period: { start: startDate.toISOString(), end: endDate.toISOString() }, trends, suggestions }; } /** * トレンド分析結果を保存します * * @param analysis トレンド分析結果 */ async saveAnalysis(analysis) { // 分析ディレクトリを作成 await fs_1.promises.mkdir(this.analysisDir, { recursive: true }); // ファイル名を生成 const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const fileName = `trend-analysis-${timestamp}.json`; const filePath = path_1.default.join(this.analysisDir, fileName); // JSON形式で保存 await fs_1.promises.writeFile(filePath, JSON.stringify(analysis, null, 2), 'utf-8'); } /** * トレンドを計算します(プライベートメソッド) * * @param pastMetrics 過去のメトリクス * @param currentMetrics 現在のメトリクス * @returns トレンドの配列 */ calculateTrends(pastMetrics, currentMetrics) { const trends = []; // 成功率のトレンド if (pastMetrics.totalTasks > 0 && currentMetrics.totalTasks > 0) { const successRateChange = currentMetrics.successRate - pastMetrics.successRate; trends.push({ metric: 'successRate', direction: this.getDirection(successRateChange), change: successRateChange, significance: this.getSignificance(Math.abs(successRateChange)) }); } // 平均実行時間のトレンド if (pastMetrics.totalTasks > 0 && currentMetrics.totalTasks > 0) { const durationChange = currentMetrics.averageDuration - pastMetrics.averageDuration; const durationChangePercent = (durationChange / pastMetrics.averageDuration) * 100; trends.push({ metric: 'averageDuration', direction: this.getDirection(-durationChange), // 実行時間は減少が良い change: durationChangePercent, significance: this.getSignificance(Math.abs(durationChangePercent)) }); } // 品質スコアのトレンド if (pastMetrics.totalTasks > 0 && currentMetrics.totalTasks > 0) { const qualityChange = currentMetrics.averageQualityScore - pastMetrics.averageQualityScore; trends.push({ metric: 'averageQualityScore', direction: this.getDirection(qualityChange), change: qualityChange, significance: this.getSignificance(Math.abs(qualityChange)) }); } return trends; } /** * 変化の方向を判定します * * @param change 変化量 * @returns 方向(increasing/decreasing/stable) */ getDirection(change) { if (change > 1) return 'increasing'; if (change < -1) return 'decreasing'; return 'stable'; } /** * 変化の重要度を判定します * * @param absChange 変化量の絶対値 * @returns 重要度(high/medium/low) */ getSignificance(absChange) { if (absChange >= 10) return 'high'; if (absChange >= 5) return 'medium'; return 'low'; } /** * 改善提案を生成します(プライベートメソッド) * * @param metrics メトリクス * @param trends トレンドの配列 * @returns 改善提案の配列 */ generateSuggestions(metrics, trends) { const suggestions = []; // 成功率が低い場合の提案 if (metrics.successRate < 80) { suggestions.push({ priority: 'high', title: '成功率の改善が必要', description: `現在の成功率は ${metrics.successRate.toFixed(1)}% です。80%以上を目標にしましょう。`, actions: [ 'エラーログを確認し、頻出エラーを特定する', 'テストカバレッジを向上させる', 'コードレビュープロセスを強化する' ] }); } // 実行時間が長い場合の提案 if (metrics.averageDuration > 300000) { // 5分以上 suggestions.push({ priority: 'medium', title: '実行時間の短縮を検討', description: `平均実行時間は ${(metrics.averageDuration / 1000).toFixed(1)}秒 です。`, actions: [ '並行実行可能なサブエージェントを特定する', 'ボトルネックとなっているサブエージェントを最適化する', 'キャッシュ機構の導入を検討する' ] }); } // 品質スコアが低い場合の提案 if (metrics.averageQualityScore < 70) { suggestions.push({ priority: 'high', title: '品質スコアの向上が必要', description: `現在の平均品質スコアは ${metrics.averageQualityScore.toFixed(1)} です。`, actions: [ 'Lintルールを厳格化する', 'コードフォーマットを統一する', 'テストカバレッジを90%以上にする' ] }); } // 成功率が下降トレンドの場合の提案 const successRateTrend = trends.find(t => t.metric === 'successRate'); if (successRateTrend && successRateTrend.direction === 'decreasing' && successRateTrend.significance === 'high') { suggestions.push({ priority: 'high', title: '成功率が低下傾向', description: `成功率が ${Math.abs(successRateTrend.change).toFixed(1)}% 低下しています。`, actions: [ '最近の変更内容をレビューする', 'テスト環境の安定性を確認する', 'リグレッションテストを強化する' ] }); } return suggestions; } } exports.TrendAnalyzer = TrendAnalyzer; //# sourceMappingURL=trend-analyzer.js.map