UNPKG

@claude-vector/core

Version:

Core vector search engine for code intelligence

818 lines (714 loc) 24.1 kB
/** * Development Phase Detector - 開発フェーズ適応システム * * 機能: * - クエリ解析による開発フェーズ自動検出 * - フェーズ別最適化(探索→実装→デバッグ→リファクタリング) * - 動的チャンク戦略とメタデータ優先度調整 * - 開発コンテキストの学習と適応 */ import { EventEmitter } from 'events'; export class DevelopmentPhaseDetector extends EventEmitter { constructor(options = {}) { super(); this.options = { learningEnabled: options.learningEnabled ?? true, historySize: options.historySize || 50, confidenceThreshold: options.confidenceThreshold || 0.7, adaptiveWeighting: options.adaptiveWeighting ?? true, ...options }; // フェーズ定義と特徴 this.phases = { exploration: { description: '探索・理解フェーズ', keywords: [ 'overview', 'architecture', 'understand', 'how does', 'what is', 'explain', 'documentation', 'structure', 'design', 'concept', 'purpose', 'workflow', 'process', 'system', 'module' ], patterns: [ /how\s+does\s+.+\s+work/i, /what\s+is\s+.+\s+used\s+for/i, /explain\s+.+/i, /overview\s+of\s+.+/i, /architecture\s+of\s+.+/i ], chunkStrategy: { size: 'large', includeContext: true, preserveStructure: true, metadataFocus: ['overview', 'relationships', 'purpose', 'architecture'] }, searchOptimization: { expandResults: true, includeRelated: true, contextualBoost: 0.3, structuralWeight: 0.4 } }, implementation: { description: '実装・開発フェーズ', keywords: [ 'implement', 'create', 'build', 'develop', 'add', 'make', 'function', 'class', 'method', 'component', 'feature', 'example', 'template', 'pattern', 'code', 'write' ], patterns: [ /implement\s+.+/i, /create\s+.+\s+function/i, /build\s+.+\s+component/i, /add\s+.+\s+feature/i, /write\s+.+\s+code/i ], chunkStrategy: { size: 'medium', includeContext: true, preserveFunctions: true, metadataFocus: ['examples', 'patterns', 'dependencies', 'implementation'] }, searchOptimization: { exampleWeight: 0.4, patternMatching: true, implementationBoost: 0.3, dependencyAware: true } }, debugging: { description: 'デバッグ・修正フェーズ', keywords: [ 'error', 'bug', 'fix', 'problem', 'issue', 'wrong', 'broken', 'debug', 'troubleshoot', 'solve', 'TypeError', 'ReferenceError', 'undefined', 'null', 'exception', 'crash', 'fail' ], patterns: [ /error\s*:\s*.+/i, /TypeError\s*:\s*.+/i, /ReferenceError\s*:\s*.+/i, /fix\s+.+\s+bug/i, /debug\s+.+/i, /why\s+is\s+.+\s+not\s+working/i ], chunkStrategy: { size: 'small', includeContext: false, preserveBlocks: true, metadataFocus: ['error_history', 'test_cases', 'edge_cases', 'complexity'] }, searchOptimization: { errorPatternMatching: true, complexityAware: true, historyWeight: 0.4, precisionOverRecall: true } }, refactoring: { description: 'リファクタリング・改善フェーズ', keywords: [ 'refactor', 'improve', 'optimize', 'clean', 'restructure', 'performance', 'maintainability', 'simplify', 'organize', 'best practices', 'code quality', 'design pattern' ], patterns: [ /refactor\s+.+/i, /improve\s+.+\s+performance/i, /optimize\s+.+/i, /clean\s+up\s+.+/i, /make\s+.+\s+better/i ], chunkStrategy: { size: 'variable', includeContext: true, preserveStructure: true, metadataFocus: ['complexity', 'duplicates', 'patterns', 'quality'] }, searchOptimization: { qualityWeight: 0.4, patternRecognition: true, complexityAnalysis: true, bestPracticeBoost: 0.3 } }, testing: { description: 'テスト・検証フェーズ', keywords: [ 'test', 'spec', 'expect', 'assert', 'verify', 'validate', 'coverage', 'unit test', 'integration test', 'mock', 'jest', 'mocha', 'chai', 'cypress', 'testing' ], patterns: [ /test\s+.+/i, /write\s+test\s+for\s+.+/i, /how\s+to\s+test\s+.+/i, /unit\s+test\s+.+/i, /integration\s+test\s+.+/i ], chunkStrategy: { size: 'medium', includeContext: true, preserveFunctions: true, metadataFocus: ['test_cases', 'coverage', 'examples', 'edge_cases'] }, searchOptimization: { testPatternWeight: 0.4, exampleFocus: true, coverageAware: true, edgeCaseBoost: 0.2 } }, documentation: { description: 'ドキュメント・説明フェーズ', keywords: [ 'document', 'readme', 'guide', 'tutorial', 'manual', 'comment', 'explanation', 'describe', 'instruction', 'api doc', 'usage', 'example' ], patterns: [ /document\s+.+/i, /write\s+documentation\s+for\s+.+/i, /create\s+readme\s+for\s+.+/i, /usage\s+of\s+.+/i, /api\s+documentation\s+.+/i ], chunkStrategy: { size: 'large', includeContext: true, preserveStructure: true, metadataFocus: ['documentation', 'usage', 'examples', 'api'] }, searchOptimization: { documentationBoost: 0.4, usageExamples: true, structuralWeight: 0.3, clarityFocus: true } } }; // 学習データとコンテキスト this.queryHistory = []; this.phaseWeights = this.initializePhaseWeights(); this.contextualPatterns = new Map(); this.userBehaviorProfile = { preferredPhases: new Map(), queryPatterns: new Map(), timeBasedPatterns: new Map() }; } /** * 初期フェーズ重みの設定 */ initializePhaseWeights() { const weights = {}; for (const phase of Object.keys(this.phases)) { weights[phase] = 1.0; } return weights; } /** * 開発フェーズの検出 */ detectPhase(query, context = {}) { const startTime = Date.now(); // クエリの前処理 const normalizedQuery = this.normalizeQuery(query); // フェーズスコアの計算 const phaseScores = this.calculatePhaseScores(normalizedQuery, context); // 最適フェーズの決定 const detectedPhase = this.selectOptimalPhase(phaseScores, context); // 信頼度の計算 const confidence = this.calculateConfidence(phaseScores, detectedPhase); // 学習データの更新 if (this.options.learningEnabled) { this.updateLearningData(query, detectedPhase, confidence, context); } const result = { phase: detectedPhase, confidence, scores: phaseScores, strategy: this.phases[detectedPhase].chunkStrategy, optimization: this.phases[detectedPhase].searchOptimization, processingTime: Date.now() - startTime, context: this.buildPhaseContext(detectedPhase, context) }; this.emit('phase-detected', result); return result; } /** * クエリの正規化 */ normalizeQuery(query) { return { original: query, lowercase: query.toLowerCase(), words: query.toLowerCase().split(/\s+/).filter(word => word.length > 2), cleaned: query.toLowerCase().replace(/[^\w\s]/g, ' ').trim() }; } /** * フェーズスコアの計算 */ calculatePhaseScores(normalizedQuery, context) { const scores = {}; for (const [phaseName, phaseConfig] of Object.entries(this.phases)) { scores[phaseName] = this.calculatePhaseScore(normalizedQuery, phaseConfig, phaseName, context); } return scores; } /** * 個別フェーズスコアの計算 */ calculatePhaseScore(normalizedQuery, phaseConfig, phaseName, context) { let score = 0; // キーワードマッチング const keywordScore = this.calculateKeywordScore(normalizedQuery, phaseConfig.keywords); score += keywordScore * 0.4; // パターンマッチング const patternScore = this.calculatePatternScore(normalizedQuery, phaseConfig.patterns); score += patternScore * 0.3; // コンテキストスコア const contextScore = this.calculateContextScore(context, phaseName); score += contextScore * 0.2; // 学習ベースの重み付け const learningScore = this.calculateLearningScore(normalizedQuery, phaseName); score += learningScore * 0.1; // 適応的重み付け if (this.options.adaptiveWeighting) { score *= this.phaseWeights[phaseName] || 1.0; } return Math.max(0, Math.min(1, score)); } /** * キーワードスコアの計算 */ calculateKeywordScore(normalizedQuery, keywords) { let matches = 0; let totalWeight = 0; for (const keyword of keywords) { const weight = this.getKeywordWeight(keyword); totalWeight += weight; if (normalizedQuery.cleaned.includes(keyword)) { matches += weight; } } return totalWeight > 0 ? matches / totalWeight : 0; } /** * キーワード重みの取得 */ getKeywordWeight(keyword) { // 特定のキーワードに対する重み付け const specialWeights = { 'error': 2.0, 'TypeError': 2.0, 'implement': 1.5, 'refactor': 1.5, 'test': 1.5, 'overview': 1.3, 'architecture': 1.3 }; return specialWeights[keyword] || 1.0; } /** * パターンスコアの計算 */ calculatePatternScore(normalizedQuery, patterns) { let maxScore = 0; for (const pattern of patterns) { if (pattern.test(normalizedQuery.original)) { const score = this.getPatternWeight(pattern); maxScore = Math.max(maxScore, score); } } return maxScore; } /** * パターン重みの取得 */ getPatternWeight(pattern) { // パターンの複雑さに基づく重み付け const patternStr = pattern.toString(); if (patternStr.includes('Error')) return 0.9; if (patternStr.includes('implement|create|build')) return 0.8; if (patternStr.includes('how|what|explain')) return 0.7; if (patternStr.includes('refactor|improve')) return 0.8; if (patternStr.includes('test')) return 0.7; return 0.6; } /** * コンテキストスコアの計算 */ calculateContextScore(context, phaseName) { let score = 0; // 時間ベースのコンテキスト if (context.timeOfDay) { score += this.getTimeBasedScore(context.timeOfDay, phaseName); } // ファイルタイプベースのコンテキスト if (context.fileType) { score += this.getFileTypeScore(context.fileType, phaseName); } // プロジェクトコンテキスト if (context.projectPhase) { score += this.getProjectPhaseScore(context.projectPhase, phaseName); } // 過去のクエリコンテキスト if (this.queryHistory.length > 0) { score += this.getHistoryBasedScore(phaseName); } return Math.max(0, Math.min(1, score)); } /** * 時間ベースのスコア */ getTimeBasedScore(timeOfDay, phaseName) { // 時間帯による開発フェーズの傾向 const timeBasedWeights = { 'morning': { 'exploration': 0.3, 'implementation': 0.4, 'documentation': 0.2 }, 'afternoon': { 'implementation': 0.4, 'debugging': 0.3, 'testing': 0.2 }, 'evening': { 'debugging': 0.4, 'refactoring': 0.3, 'documentation': 0.2 }, 'night': { 'debugging': 0.5, 'exploration': 0.2, 'implementation': 0.1 } }; return timeBasedWeights[timeOfDay]?.[phaseName] || 0; } /** * ファイルタイプベースのスコア */ getFileTypeScore(fileType, phaseName) { const fileTypeWeights = { 'test': { 'testing': 0.5, 'debugging': 0.3 }, 'documentation': { 'documentation': 0.5, 'exploration': 0.3 }, 'config': { 'refactoring': 0.3, 'implementation': 0.2 }, 'component': { 'implementation': 0.4, 'refactoring': 0.3 } }; return fileTypeWeights[fileType]?.[phaseName] || 0; } /** * プロジェクトフェーズベースのスコア */ getProjectPhaseScore(projectPhase, phaseName) { const projectPhaseWeights = { 'initial': { 'exploration': 0.4, 'implementation': 0.3 }, 'development': { 'implementation': 0.4, 'testing': 0.3 }, 'testing': { 'testing': 0.5, 'debugging': 0.3 }, 'maintenance': { 'debugging': 0.4, 'refactoring': 0.3 }, 'documentation': { 'documentation': 0.5, 'exploration': 0.2 } }; return projectPhaseWeights[projectPhase]?.[phaseName] || 0; } /** * 履歴ベースのスコア */ getHistoryBasedScore(phaseName) { const recentQueries = this.queryHistory.slice(-5); const phaseCounts = recentQueries.reduce((counts, query) => { counts[query.detectedPhase] = (counts[query.detectedPhase] || 0) + 1; return counts; }, {}); const totalQueries = recentQueries.length; const phaseFrequency = phaseCounts[phaseName] || 0; // 最近のフェーズの連続性に基づくスコア return phaseFrequency / totalQueries * 0.2; } /** * 学習ベースのスコア */ calculateLearningScore(normalizedQuery, phaseName) { if (!this.options.learningEnabled) return 0; // ユーザーの行動パターンに基づくスコア const preferenceScore = this.userBehaviorProfile.preferredPhases.get(phaseName) || 0; // クエリパターンの学習 const patternScore = this.getLearnedPatternScore(normalizedQuery, phaseName); return (preferenceScore + patternScore) * 0.5; } /** * 学習済みパターンスコア */ getLearnedPatternScore(normalizedQuery, phaseName) { const patterns = this.userBehaviorProfile.queryPatterns.get(phaseName) || []; let maxScore = 0; for (const pattern of patterns) { const similarity = this.calculateSimilarity(normalizedQuery.words, pattern.words); maxScore = Math.max(maxScore, similarity * pattern.confidence); } return maxScore; } /** * 類似度計算 */ calculateSimilarity(words1, words2) { const set1 = new Set(words1); const set2 = new Set(words2); const intersection = new Set([...set1].filter(x => set2.has(x))); const union = new Set([...set1, ...set2]); return union.size > 0 ? intersection.size / union.size : 0; } /** * 最適フェーズの選択 */ selectOptimalPhase(phaseScores, context) { // スコアが閾値を超えるフェーズを特定 const candidatePhases = Object.entries(phaseScores) .filter(([phase, score]) => score >= this.options.confidenceThreshold) .sort(([, a], [, b]) => b - a); if (candidatePhases.length > 0) { return candidatePhases[0][0]; } // 閾値を超えるフェーズがない場合は最高スコアを選択 const maxPhase = Object.entries(phaseScores) .reduce(([maxPhase, maxScore], [phase, score]) => score > maxScore ? [phase, score] : [maxPhase, maxScore] ); return maxPhase[0]; } /** * 信頼度の計算 */ calculateConfidence(phaseScores, selectedPhase) { const scores = Object.values(phaseScores); const maxScore = phaseScores[selectedPhase]; const avgScore = scores.reduce((sum, score) => sum + score, 0) / scores.length; // 最高スコアと平均スコアの差による信頼度 return Math.max(0, Math.min(1, (maxScore - avgScore) / (1 - avgScore + 0.001))); } /** * フェーズコンテキストの構築 */ buildPhaseContext(phaseName, originalContext) { const phaseConfig = this.phases[phaseName]; return { ...originalContext, phase: phaseName, description: phaseConfig.description, chunkStrategy: phaseConfig.chunkStrategy, searchOptimization: phaseConfig.searchOptimization, adaptiveParameters: this.getAdaptiveParameters(phaseName) }; } /** * 適応的パラメータの取得 */ getAdaptiveParameters(phaseName) { return { searchThreshold: this.getPhaseSearchThreshold(phaseName), maxResults: this.getPhaseMaxResults(phaseName), contextWindow: this.getPhaseContextWindow(phaseName), rankingWeights: this.getPhaseRankingWeights(phaseName) }; } /** * フェーズ別検索閾値 */ getPhaseSearchThreshold(phaseName) { const thresholds = { 'exploration': 0.2, // 広範囲な検索 'implementation': 0.3, // 中程度の精度 'debugging': 0.5, // 高精度 'refactoring': 0.3, // 中程度の精度 'testing': 0.4, // やや高精度 'documentation': 0.2 // 広範囲な検索 }; return thresholds[phaseName] || 0.3; } /** * フェーズ別最大結果数 */ getPhaseMaxResults(phaseName) { const maxResults = { 'exploration': 30, // 多くの結果 'implementation': 20, // 標準的な結果数 'debugging': 10, // 精密な結果 'refactoring': 25, // やや多めの結果 'testing': 15, // 中程度の結果数 'documentation': 30 // 多くの結果 }; return maxResults[phaseName] || 20; } /** * フェーズ別コンテキストウィンドウ */ getPhaseContextWindow(phaseName) { const contextWindows = { 'exploration': 'large', // 大きなコンテキスト 'implementation': 'medium', // 中程度のコンテキスト 'debugging': 'small', // 小さく精密なコンテキスト 'refactoring': 'medium', // 中程度のコンテキスト 'testing': 'medium', // 中程度のコンテキスト 'documentation': 'large' // 大きなコンテキスト }; return contextWindows[phaseName] || 'medium'; } /** * フェーズ別ランキング重み */ getPhaseRankingWeights(phaseName) { const weights = { 'exploration': { semantic: 0.3, structural: 0.4, contextual: 0.2, temporal: 0.1 }, 'implementation': { semantic: 0.4, structural: 0.2, contextual: 0.2, temporal: 0.2 }, 'debugging': { semantic: 0.5, structural: 0.1, contextual: 0.3, temporal: 0.1 }, 'refactoring': { semantic: 0.3, structural: 0.3, contextual: 0.2, temporal: 0.2 }, 'testing': { semantic: 0.4, structural: 0.2, contextual: 0.3, temporal: 0.1 }, 'documentation': { semantic: 0.3, structural: 0.3, contextual: 0.3, temporal: 0.1 } }; return weights[phaseName] || weights['implementation']; } /** * 学習データの更新 */ updateLearningData(query, detectedPhase, confidence, context) { const normalizedQuery = this.normalizeQuery(query); // クエリ履歴の更新 this.queryHistory.push({ query: normalizedQuery.original, detectedPhase, confidence, timestamp: Date.now(), context }); // 履歴サイズの制限 if (this.queryHistory.length > this.options.historySize) { this.queryHistory.shift(); } // ユーザー行動プロファイルの更新 this.updateUserBehaviorProfile(normalizedQuery, detectedPhase, confidence); // 適応的重みの更新 if (this.options.adaptiveWeighting) { this.updatePhaseWeights(detectedPhase, confidence); } } /** * ユーザー行動プロファイルの更新 */ updateUserBehaviorProfile(normalizedQuery, detectedPhase, confidence) { // フェーズ優先度の更新 const currentPreference = this.userBehaviorProfile.preferredPhases.get(detectedPhase) || 0; this.userBehaviorProfile.preferredPhases.set( detectedPhase, currentPreference + confidence * 0.1 ); // クエリパターンの学習 const patterns = this.userBehaviorProfile.queryPatterns.get(detectedPhase) || []; patterns.push({ words: normalizedQuery.words, confidence, timestamp: Date.now() }); // パターン数の制限 if (patterns.length > 10) { patterns.shift(); } this.userBehaviorProfile.queryPatterns.set(detectedPhase, patterns); } /** * フェーズ重みの更新 */ updatePhaseWeights(detectedPhase, confidence) { // 成功したフェーズの重みを増加 this.phaseWeights[detectedPhase] = Math.min( this.phaseWeights[detectedPhase] + confidence * 0.05, 2.0 ); // 他のフェーズの重みを軽微に減少 for (const phase of Object.keys(this.phaseWeights)) { if (phase !== detectedPhase) { this.phaseWeights[phase] = Math.max( this.phaseWeights[phase] - 0.01, 0.5 ); } } } /** * 統計情報の取得 */ getStats() { const phaseDistribution = this.queryHistory.reduce((dist, query) => { dist[query.detectedPhase] = (dist[query.detectedPhase] || 0) + 1; return dist; }, {}); return { totalQueries: this.queryHistory.length, phaseDistribution, averageConfidence: this.queryHistory.reduce((sum, q) => sum + q.confidence, 0) / this.queryHistory.length, phaseWeights: { ...this.phaseWeights }, learningEnabled: this.options.learningEnabled, lastUpdate: this.queryHistory.length > 0 ? this.queryHistory[this.queryHistory.length - 1].timestamp : null }; } /** * 学習データのリセット */ resetLearning() { this.queryHistory = []; this.phaseWeights = this.initializePhaseWeights(); this.userBehaviorProfile = { preferredPhases: new Map(), queryPatterns: new Map(), timeBasedPatterns: new Map() }; this.emit('learning-reset'); } /** * 学習データのエクスポート */ exportLearningData() { return { queryHistory: this.queryHistory, phaseWeights: this.phaseWeights, userBehaviorProfile: { preferredPhases: Object.fromEntries(this.userBehaviorProfile.preferredPhases), queryPatterns: Object.fromEntries(this.userBehaviorProfile.queryPatterns), timeBasedPatterns: Object.fromEntries(this.userBehaviorProfile.timeBasedPatterns) }, exportedAt: Date.now() }; } /** * 学習データのインポート */ importLearningData(data) { if (data.queryHistory) { this.queryHistory = data.queryHistory; } if (data.phaseWeights) { this.phaseWeights = data.phaseWeights; } if (data.userBehaviorProfile) { this.userBehaviorProfile = { preferredPhases: new Map(Object.entries(data.userBehaviorProfile.preferredPhases || {})), queryPatterns: new Map(Object.entries(data.userBehaviorProfile.queryPatterns || {})), timeBasedPatterns: new Map(Object.entries(data.userBehaviorProfile.timeBasedPatterns || {})) }; } this.emit('learning-imported', data); } }