UNPKG

@claude-vector/core

Version:

Core vector search engine for code intelligence

836 lines (705 loc) 25.7 kB
/** * Claude Prompt Optimizer - Claude向け最適化プロンプトテンプレート * * 機能: * - 構造化出力生成(絵文字、構造化マークダウン) * - コンテキスト情報の階層化 * - 開発タスク別プロンプトテンプレート * - トークン効率性の最適化 */ export class ClaudePromptOptimizer { constructor(options = {}) { this.options = { useEmojis: options.useEmojis ?? true, useStructuredOutput: options.useStructuredOutput ?? true, maxTokensPerChunk: options.maxTokensPerChunk || 2000, includeMetrics: options.includeMetrics ?? true, includeContext: options.includeContext ?? true, adaptToPhase: options.adaptToPhase ?? true, ...options }; // プロンプトテンプレート定義 this.templates = { class_structure: { emoji: '🏗️', title: 'CLASS STRUCTURE', sections: ['overview', 'properties', 'methods', 'inheritance', 'usage'], priority: 'high' }, function_implementation: { emoji: '⚙️', title: 'FUNCTION', sections: ['signature', 'purpose', 'parameters', 'implementation', 'examples'], priority: 'high' }, module_export: { emoji: '📦', title: 'MODULE EXPORT', sections: ['exports', 'dependencies', 'usage', 'api'], priority: 'medium' }, module_import: { emoji: '📥', title: 'MODULE IMPORT', sections: ['source', 'imported_items', 'usage_context'], priority: 'low' }, variable_declaration: { emoji: '📊', title: 'VARIABLE', sections: ['type', 'value', 'scope', 'usage'], priority: 'medium' }, error_handling: { emoji: '🚨', title: 'ERROR HANDLING', sections: ['error_type', 'cause', 'solution', 'prevention'], priority: 'critical' }, test_case: { emoji: '🧪', title: 'TEST CASE', sections: ['test_type', 'scenario', 'expectations', 'implementation'], priority: 'high' }, documentation: { emoji: '📚', title: 'DOCUMENTATION', sections: ['description', 'usage', 'examples', 'api_reference'], priority: 'medium' }, configuration: { emoji: '⚙️', title: 'CONFIGURATION', sections: ['purpose', 'options', 'defaults', 'examples'], priority: 'medium' }, general_code: { emoji: '💻', title: 'CODE', sections: ['overview', 'implementation', 'context'], priority: 'medium' } }; // 開発フェーズ別最適化設定 this.phaseOptimizations = { exploration: { emphasize: ['overview', 'purpose', 'architecture', 'relationships'], includeContext: true, expandReferences: true, structuralWeight: 0.4 }, implementation: { emphasize: ['implementation', 'examples', 'parameters', 'usage'], includeContext: true, expandReferences: false, codeWeight: 0.5 }, debugging: { emphasize: ['error_type', 'cause', 'solution', 'implementation'], includeContext: false, expandReferences: false, precisionWeight: 0.6 }, refactoring: { emphasize: ['structure', 'quality', 'patterns', 'improvements'], includeContext: true, expandReferences: true, qualityWeight: 0.4 }, testing: { emphasize: ['test_type', 'scenario', 'expectations', 'coverage'], includeContext: true, expandReferences: false, testWeight: 0.5 }, documentation: { emphasize: ['description', 'usage', 'examples', 'api_reference'], includeContext: true, expandReferences: true, clarityWeight: 0.4 } }; } /** * Claude最適化フォーマットの生成 */ formatForClaude(chunk, searchContext = {}) { // 安全性チェック:chunkとmetadataの存在確認 if (!chunk || !chunk.metadata) { console.warn('⚠️ Invalid chunk structure in formatForClaude, using fallback'); return this.createFallbackPrompt(chunk, searchContext); } const metadata = chunk.metadata; const template = this.selectTemplate(metadata); const phaseOptimization = this.getPhaseOptimization(searchContext.phase); // トークン効率性の計算 const tokenBudget = this.calculateTokenBudget(chunk, searchContext); // 構造化プロンプトの生成 const formattedPrompt = this.buildStructuredPrompt( chunk, template, phaseOptimization, tokenBudget, searchContext ); return formattedPrompt; } /** * フォールバック用のプロンプト生成 */ createFallbackPrompt(chunk, searchContext = {}) { const content = chunk?.content || 'No content available'; const filename = chunk?.metadata?.file || 'unknown_file'; return { content: `## 🔍 Code Fragment\n\n**File:** ${filename}\n\n\`\`\`\n${content}\n\`\`\`\n\n*Note: This is a fallback format due to incomplete metadata.*`, metadata: { template: 'fallback', quality: 'basic', tokens: Math.ceil(content.length / 4), fallback: true } }; } /** * テンプレートの選択 */ selectTemplate(metadata) { // 安全性チェック:metadataの存在確認 if (!metadata) { return this.templates.general_code; } // AI最適化メタデータからプロンプトテンプレートを取得 if (metadata.aiContext?.promptTemplate) { return this.templates[metadata.aiContext.promptTemplate] || this.templates.general_code; } // セマンティックタイプベースの選択 const semanticType = metadata.semantic?.type || metadata.semanticType; switch (semanticType) { case 'class': return this.templates.class_structure; case 'function': return this.templates.function_implementation; case 'export': return this.templates.module_export; case 'import': return this.templates.module_import; case 'variable': return this.templates.variable_declaration; case 'error': return this.templates.error_handling; case 'test': return this.templates.test_case; case 'documentation': return this.templates.documentation; case 'config': return this.templates.configuration; default: return this.templates.general_code; } } /** * フェーズ最適化の取得 */ getPhaseOptimization(phase) { return this.phaseOptimizations[phase] || this.phaseOptimizations.implementation; } /** * トークン予算の計算 */ calculateTokenBudget(chunk, searchContext) { const baseTokens = chunk.metadata.tokens || this.estimateTokens(chunk.content); const maxTokens = this.options.maxTokensPerChunk; // 利用可能トークン予算の配分 return { content: Math.min(baseTokens, maxTokens * 0.7), metadata: maxTokens * 0.2, context: maxTokens * 0.1, total: maxTokens }; } /** * 構造化プロンプトの構築 */ buildStructuredPrompt(chunk, template, phaseOptimization, tokenBudget, searchContext) { let prompt = ''; // ヘッダーの生成 prompt += this.buildHeader(chunk, template, searchContext); // メタデータセクションの生成 if (this.options.includeMetrics) { prompt += this.buildMetricsSection(chunk, tokenBudget); } // コンテキストセクションの生成 if (this.options.includeContext && phaseOptimization.includeContext) { prompt += this.buildContextSection(chunk, phaseOptimization, searchContext); } // メインコンテンツの生成 prompt += this.buildMainContent(chunk, template, phaseOptimization, tokenBudget); // 開発ノートの生成 prompt += this.buildDevelopmentNotes(chunk, phaseOptimization, searchContext); // フッターの生成 prompt += this.buildFooter(chunk, searchContext); return prompt; } /** * ヘッダーの構築 */ buildHeader(chunk, template, searchContext) { const metadata = chunk.metadata; const emoji = this.options.useEmojis ? template.emoji : ''; const title = template.title; // ファイルパスの相対化 const relativePath = this.getRelativePath(metadata.file, searchContext.projectRoot); // 行番号の情報 const lineInfo = metadata.startLine && metadata.endLine ? `:${metadata.startLine}${metadata.endLine !== metadata.startLine ? `-${metadata.endLine}` : ''}` : ''; let header = `\n${emoji} **${title}** | 📍 \`${relativePath}${lineInfo}\`\n`; // 重要度とメトリクスの追加 if (metadata.aiContext?.importance) { const importance = Math.round(metadata.aiContext.importance * 100); header += `⭐ **Importance**: ${importance}% | `; } if (metadata.complexity?.cognitive) { header += `🧠 **Complexity**: ${metadata.complexity.cognitive}/10 | `; } if (metadata.semantic?.name && metadata.semantic.name !== 'anonymous') { header += `🎯 **Name**: \`${metadata.semantic.name}\``; } header += '\n'; return header; } /** * メトリクスセクションの構築 */ buildMetricsSection(chunk, tokenBudget) { const metadata = chunk.metadata; let metrics = '\n📊 **Metrics & Quality**:\n'; // 複雑度メトリクス if (metadata.complexity) { metrics += `- **Cognitive Complexity**: ${metadata.complexity.cognitive || 'N/A'}\n`; metrics += `- **Cyclomatic Complexity**: ${metadata.complexity.cyclomatic || 'N/A'}\n`; if (metadata.complexity.maintainabilityIndex) { metrics += `- **Maintainability**: ${Math.round(metadata.complexity.maintainabilityIndex)}/100\n`; } } // 品質メトリクス if (metadata.quality) { if (metadata.quality.codeQuality) { metrics += `- **Code Quality**: ${Math.round(metadata.quality.codeQuality * 100)}%\n`; } if (metadata.quality.testCoverage) { metrics += `- **Test Coverage**: ${Math.round(metadata.quality.testCoverage * 100)}%\n`; } if (metadata.quality.documentation) { metrics += `- **Documentation**: ${Math.round(metadata.quality.documentation * 100)}%\n`; } } // AIコンテキスト if (metadata.aiContext) { metrics += `- **AI Searchability**: ${Math.round(metadata.aiContext.searchability * 100)}%\n`; if (metadata.aiContext.tokenEfficiency) { metrics += `- **Token Efficiency**: ${Math.round(metadata.aiContext.tokenEfficiency * 100)}%\n`; } } return metrics + '\n'; } /** * コンテキストセクションの構築 */ buildContextSection(chunk, phaseOptimization, searchContext) { const metadata = chunk.metadata; let context = '🔗 **Context & Relationships**:\n'; // 依存関係 if (metadata.relationships?.dependencies?.length > 0) { const deps = metadata.relationships.dependencies.slice(0, 5).join(', '); context += `- **Dependencies**: ${deps}\n`; } // 呼び出し関係 if (metadata.relationships?.callGraph?.length > 0) { const calls = metadata.relationships.callGraph.slice(0, 3).join(', '); context += `- **Calls**: ${calls}\n`; } // セマンティック情報 if (metadata.semantic) { context += `- **Type**: ${metadata.semantic.type}\n`; if (metadata.semantic.purpose) { context += `- **Purpose**: ${metadata.semantic.purpose}\n`; } if (metadata.semantic.patterns?.length > 0) { context += `- **Patterns**: ${metadata.semantic.patterns.join(', ')}\n`; } } // 開発コンテキスト if (metadata.development) { context += `- **Stability**: ${Math.round(metadata.development.stability * 100)}%\n`; if (metadata.development.changeFrequency) { context += `- **Change Frequency**: ${Math.round(metadata.development.changeFrequency * 100)}%\n`; } } return context + '\n'; } /** * メインコンテンツの構築 */ buildMainContent(chunk, template, phaseOptimization, tokenBudget) { const metadata = chunk.metadata; let content = ''; // コンテンツの前処理(トークン制限に基づく) const processedContent = this.processContentForTokens(chunk.content, tokenBudget.content); // 言語に基づくシンタックスハイライト const language = metadata.language || 'text'; // フェーズ最適化に基づく注釈の追加 const annotatedContent = this.addPhaseBasedAnnotations( processedContent, metadata, phaseOptimization ); content += `\`\`\`${language}\n${annotatedContent}\n\`\`\`\n\n`; return content; } /** * 開発ノートの構築 */ buildDevelopmentNotes(chunk, phaseOptimization, searchContext) { const metadata = chunk.metadata; let notes = '💡 **Development Notes**:\n'; // フェーズ特有のノート if (searchContext.phase === 'debugging') { notes += this.buildDebuggingNotes(metadata); } else if (searchContext.phase === 'implementation') { notes += this.buildImplementationNotes(metadata); } else if (searchContext.phase === 'refactoring') { notes += this.buildRefactoringNotes(metadata); } else { notes += this.buildGeneralNotes(metadata); } // コードスメルの警告 if (metadata.quality?.codeSmells?.length > 0) { notes += `\n⚠️ **Code Smells**: ${metadata.quality.codeSmells.join(', ')}\n`; } // ベストプラクティスの提案 if (metadata.quality?.bestPractices?.length > 0) { notes += `✅ **Best Practices**: ${metadata.quality.bestPractices.join(', ')}\n`; } return notes + '\n'; } /** * デバッグ用ノート */ buildDebuggingNotes(metadata) { let notes = ''; if (metadata.complexity?.cognitive > 5) { notes += '- High complexity - potential source of bugs\n'; } if (metadata.development?.bugHistory?.length > 0) { const recentBugs = metadata.development.bugHistory.filter(bug => !bug.resolved); if (recentBugs.length > 0) { notes += `- Known issues: ${recentBugs.length} unresolved\n`; } } if (metadata.quality?.testCoverage < 0.5) { notes += '- Low test coverage - consider adding tests\n'; } return notes; } /** * 実装用ノート */ buildImplementationNotes(metadata) { let notes = ''; if (metadata.relationships?.dependencies?.length > 0) { notes += `- Required dependencies: ${metadata.relationships.dependencies.slice(0, 3).join(', ')}\n`; } if (metadata.semantic?.patterns?.includes('async_pattern')) { notes += '- Async pattern - handle promises carefully\n'; } if (metadata.complexity?.maintainabilityIndex < 50) { notes += '- Consider simplifying for better maintainability\n'; } return notes; } /** * リファクタリング用ノート */ buildRefactoringNotes(metadata) { let notes = ''; if (metadata.complexity?.cognitive > 10) { notes += '- High cognitive load - good candidate for refactoring\n'; } if (metadata.quality?.codeSmells?.includes('long_method')) { notes += '- Long method - consider breaking into smaller functions\n'; } if (metadata.development?.changeFrequency > 0.7) { notes += '- Frequently changed - stability improvements recommended\n'; } return notes; } /** * 一般的なノート */ buildGeneralNotes(metadata) { let notes = ''; if (metadata.development?.lastModified) { const lastModified = new Date(metadata.development.lastModified); const daysAgo = Math.floor((Date.now() - lastModified.getTime()) / (1000 * 60 * 60 * 24)); notes += `- Last modified: ${daysAgo} days ago\n`; } if (metadata.aiContext?.importance > 0.8) { notes += '- High importance component in codebase\n'; } if (metadata.quality?.documentation > 0.8) { notes += '- Well documented code\n'; } return notes; } /** * フッターの構築 */ buildFooter(chunk, searchContext) { let footer = '\n---\n'; // 検索スコア情報 if (chunk.score) { footer += `🎯 **Relevance Score**: ${(chunk.score * 100).toFixed(1)}%`; } // AI最適化情報 const metadata = chunk.metadata; if (metadata.aiContext?.claudeOptimization?.priority) { footer += ` | 🤖 **AI Priority**: ${Math.round(metadata.aiContext.claudeOptimization.priority * 100)}%`; } // タイムスタンプ if (searchContext.timestamp) { footer += ` | ⏰ **Retrieved**: ${new Date(searchContext.timestamp).toLocaleTimeString()}`; } footer += '\n'; return footer; } /** * コンテンツのトークン制限処理 */ processContentForTokens(content, maxTokens) { const estimatedTokens = this.estimateTokens(content); if (estimatedTokens <= maxTokens) { return content; } // トークン制限を超える場合の処理 const lines = content.split('\n'); const targetLines = Math.floor(lines.length * (maxTokens / estimatedTokens)); const truncatedContent = lines.slice(0, targetLines).join('\n'); if (targetLines < lines.length) { return truncatedContent + '\n\n// ... (truncated for token limit)'; } return truncatedContent; } /** * フェーズベース注釈の追加 */ addPhaseBasedAnnotations(content, metadata, phaseOptimization) { if (!phaseOptimization.emphasize) { return content; } let annotatedContent = content; // デバッグフェーズの場合 if (phaseOptimization.emphasize.includes('error_type')) { annotatedContent = this.addErrorAnnotations(annotatedContent); } // 実装フェーズの場合 if (phaseOptimization.emphasize.includes('implementation')) { annotatedContent = this.addImplementationAnnotations(annotatedContent, metadata); } // リファクタリングフェーズの場合 if (phaseOptimization.emphasize.includes('quality')) { annotatedContent = this.addQualityAnnotations(annotatedContent, metadata); } return annotatedContent; } /** * エラー注釈の追加 */ addErrorAnnotations(content) { return content .replace(/(try\s*{)/g, '// Error handling start\n$1') .replace(/(catch\s*\([^)]*\)\s*{)/g, '// Error caught here\n$1') .replace(/(throw\s+)/g, '// Error thrown\n$1'); } /** * 実装注釈の追加 */ addImplementationAnnotations(content, metadata) { let annotated = content; // 関数の場合 if (metadata.semantic?.type === 'function') { annotated = annotated.replace( /(function\s+\w+|const\s+\w+\s*=\s*(?:async\s+)?\([^)]*\)\s*=>)/, '// Function implementation\n$1' ); } // 非同期パターンの場合 if (metadata.semantic?.patterns?.includes('async_pattern')) { annotated = annotated.replace(/(await\s+)/g, '// Async operation\n$1'); } return annotated; } /** * 品質注釈の追加 */ addQualityAnnotations(content, metadata) { let annotated = content; // 複雑なロジックの注釈 if (metadata.complexity?.cognitive > 5) { annotated = '// Complex logic - consider refactoring\n' + annotated; } // ベストプラクティスの注釈 if (metadata.quality?.bestPractices?.includes('use_const')) { annotated = annotated.replace(/(const\s+)/g, '// Good: using const\n$1'); } return annotated; } /** * 相対パスの取得 */ getRelativePath(fullPath, projectRoot) { if (!projectRoot || !fullPath.startsWith(projectRoot)) { return fullPath; } return fullPath.substring(projectRoot.length + 1); } /** * トークン数の推定 */ estimateTokens(content) { // 簡略化されたトークン推定 return Math.ceil(content.length / 4); } /** * 複数チャンクの最適化フォーマット */ formatMultipleChunks(chunks, searchContext = {}) { // 安全性チェック:chunksの有効性確認 if (!chunks || chunks.length === 0) { return '# Search Results\n\nNo valid results found.'; } let formattedOutput = ''; try { // 検索サマリーの追加 formattedOutput += this.buildSearchSummary(chunks, searchContext); } catch (error) { console.warn('⚠️ Failed to build search summary:', error.message); formattedOutput += '# Search Results\n\n'; } // 各チャンクのフォーマット chunks.forEach((chunk, index) => { try { formattedOutput += `\n## Result ${index + 1}\n`; formattedOutput += this.formatForClaude(chunk, searchContext); } catch (error) { console.warn(`⚠️ Failed to format chunk ${index + 1}:`, error.message); formattedOutput += `\n## Result ${index + 1}\n**Error**: Unable to format this result.\n`; } }); // 関連性の分析 if (chunks.length > 1) { formattedOutput += this.buildRelationshipAnalysis(chunks); } return formattedOutput; } /** * 検索サマリーの構築 */ buildSearchSummary(chunks, searchContext) { // 安全性チェック:chunksの有効性確認 if (!chunks || chunks.length === 0) { return '# 🔍 Search Results Summary\n\nNo results found.\n\n'; } const totalChunks = chunks.length; const avgScore = chunks.reduce((sum, chunk) => sum + (chunk?.score || 0), 0) / totalChunks; let summary = `# 🔍 Search Results Summary\n\n`; summary += `**Query**: "${searchContext?.query || 'N/A'}"\n`; summary += `**Phase**: ${searchContext?.phase || 'general'}\n`; summary += `**Results**: ${totalChunks} chunks found\n`; summary += `**Average Relevance**: ${(avgScore * 100).toFixed(1)}%\n\n`; // ファイル分布(安全性チェック付き) const fileDistribution = chunks.reduce((dist, chunk) => { const file = chunk?.metadata?.file || 'unknown_file'; dist[file] = (dist[file] || 0) + 1; return dist; }, {}); summary += `**File Distribution**:\n`; Object.entries(fileDistribution).forEach(([file, count]) => { const relativePath = this.getRelativePath(file, searchContext.projectRoot); summary += `- ${relativePath}: ${count} chunks\n`; }); return summary + '\n'; } /** * 関係性分析の構築 */ buildRelationshipAnalysis(chunks) { let analysis = `\n## 🔗 Relationship Analysis\n\n`; // 共通の依存関係(既存インデックスとの互換性確保) const allDependencies = chunks.flatMap(chunk => { const metadata = chunk?.metadata || {}; // 新しい形式と既存形式の両方をサポート return metadata.relationships?.dependencies || metadata.dependencies || []; }); const commonDeps = this.findCommonElements(allDependencies); if (commonDeps.length > 0) { analysis += `**Common Dependencies**: ${commonDeps.join(', ')}\n`; } // 複雑度分布(既存インデックスとの互換性確保) const complexities = chunks .map(chunk => chunk?.metadata?.complexity?.cognitive) .filter(c => c !== undefined); if (complexities.length > 0) { const avgComplexity = complexities.reduce((sum, c) => sum + c, 0) / complexities.length; analysis += `**Average Complexity**: ${avgComplexity.toFixed(1)}/10\n`; } // セマンティックタイプ分布(既存インデックスとの互換性確保) const typeDistribution = chunks.reduce((dist, chunk) => { const metadata = chunk?.metadata || {}; // 新しい形式と既存形式の両方をサポート const type = metadata.semantic?.type || metadata.semanticType || 'unknown'; dist[type] = (dist[type] || 0) + 1; return dist; }, {}); analysis += `**Type Distribution**: ${Object.entries(typeDistribution) .map(([type, count]) => `${type}(${count})`) .join(', ')}\n`; return analysis; } /** * 共通要素の検出 */ findCommonElements(array) { const counts = array.reduce((acc, item) => { acc[item] = (acc[item] || 0) + 1; return acc; }, {}); return Object.entries(counts) .filter(([, count]) => count > 1) .map(([item]) => item); } /** * 設定の更新 */ updateOptions(newOptions) { this.options = { ...this.options, ...newOptions }; } /** * テンプレートの追加 */ addTemplate(name, template) { this.templates[name] = template; } /** * 統計情報の取得 */ getStats() { return { templatesCount: Object.keys(this.templates).length, phaseOptimizations: Object.keys(this.phaseOptimizations).length, options: this.options, version: '1.0.0' }; } }