@claude-vector/core
Version:
Core vector search engine for code intelligence
818 lines (714 loc) • 24.1 kB
JavaScript
/**
* 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);
}
}