UNPKG

aiwf

Version:

AI Workflow Framework for Claude Code with multi-language support (Korean/English)

583 lines (513 loc) 16.7 kB
import { AggressiveCompressionStrategy, BalancedCompressionStrategy, MinimalCompressionStrategy } from './compression-strategies.js'; import { TextSummarizer } from './text-summarizer.js'; import { ContentNormalizer } from './content-normalizer.js'; import { ImportanceClassifier } from './importance-classifier.js'; import { InformationFilter } from './information-filter.js'; import { TokenCounter } from './token-counter.js'; import { TokenTracker } from './token-tracker.js'; /** * 통합 Context 압축 시스템 */ export class ContextCompressor { constructor(mode = 'balanced') { this.mode = mode; this.tokenCounter = new TokenCounter(); this.tokenTracker = new TokenTracker(); this.initializeStrategies(); this.initializeComponents(); } /** * 압축 전략을 초기화합니다 */ initializeStrategies() { this.strategies = { aggressive: new AggressiveCompressionStrategy(), balanced: new BalancedCompressionStrategy(), minimal: new MinimalCompressionStrategy() }; } /** * 컴포넌트를 초기화합니다 */ initializeComponents() { this.summarizer = new TextSummarizer(); this.normalizer = new ContentNormalizer(); this.classifier = new ImportanceClassifier(); this.filter = new InformationFilter(); } /** * 콘텐츠를 압축합니다 * @param {string} content - 압축할 콘텐츠 * @param {Object} options - 압축 옵션 * @returns {Object} 압축 결과 */ async compress(content, options = {}) { const startTime = Date.now(); const { strategy = this.mode, targetRatio = null, maxTokens = null, preserveStructure = true, enableSummarization = true, enableNormalization = true, enableFiltering = true, customFilters = {}, generateMetadata = true } = options; try { // 원본 토큰 수 계산 const originalTokens = this.tokenCounter.countTokens(content); // 압축 파이프라인 실행 const pipeline = this.createCompressionPipeline(strategy, options); const result = await this.executePipeline(pipeline, content); // 압축 결과 구성 const compressedTokens = this.tokenCounter.countTokens(result.content); const compressionRatio = ((originalTokens - compressedTokens) / originalTokens) * 100; // 토큰 추적 this.tokenTracker.trackCompression(originalTokens, compressedTokens); const compressionResult = { original: content, compressed: result.content, originalTokens, compressedTokens, compressionRatio, tokensReduced: originalTokens - compressedTokens, strategy, processingTime: Date.now() - startTime, success: true }; if (generateMetadata) { compressionResult.metadata = { ...result.metadata, compressionId: this.generateCompressionId(), timestamp: new Date().toISOString(), pipelineStages: result.stages, tokenTracker: this.tokenTracker.generateReport() }; } return compressionResult; } catch (error) { console.error('압축 중 오류 발생:', error); return { original: content, compressed: content, originalTokens: this.tokenCounter.countTokens(content), compressedTokens: this.tokenCounter.countTokens(content), compressionRatio: 0, tokensReduced: 0, strategy, processingTime: Date.now() - startTime, success: false, error: error.message }; } } /** * 압축 파이프라인을 생성합니다 * @param {string} strategy - 압축 전략 * @param {Object} options - 옵션 * @returns {Array<Object>} 파이프라인 스테이지 */ createCompressionPipeline(strategy, options) { const pipeline = []; // 1. 토큰 분석 스테이지 pipeline.push({ name: 'TokenAnalysis', processor: this.tokenAnalysisStage.bind(this), options: {} }); // 2. 중요도 분석 스테이지 pipeline.push({ name: 'ImportanceAnalysis', processor: this.importanceAnalysisStage.bind(this), options: {} }); // 3. 정규화 스테이지 (옵션) if (options.enableNormalization) { pipeline.push({ name: 'Normalization', processor: this.normalizationStage.bind(this), options: {} }); } // 4. 필터링 스테이지 (옵션) if (options.enableFiltering) { pipeline.push({ name: 'Filtering', processor: this.filteringStage.bind(this), options: { customFilters: options.customFilters || {}, maxTokens: options.maxTokens } }); } // 5. 요약 스테이지 (옵션) if (options.enableSummarization) { pipeline.push({ name: 'Summarization', processor: this.summarizationStage.bind(this), options: { targetRatio: options.targetRatio || 0.7 } }); } // 6. 전략 압축 스테이지 pipeline.push({ name: 'StrategyCompression', processor: this.strategyCompressionStage.bind(this), options: { strategy, preserveStructure: options.preserveStructure } }); // 7. 검증 스테이지 pipeline.push({ name: 'Validation', processor: this.validationStage.bind(this), options: {} }); return pipeline; } /** * 파이프라인을 실행합니다 * @param {Array<Object>} pipeline - 파이프라인 스테이지 * @param {string} content - 처리할 콘텐츠 * @returns {Object} 파이프라인 결과 */ async executePipeline(pipeline, content) { let result = { content, metadata: {}, stages: [] }; for (const stage of pipeline) { const stageStartTime = Date.now(); try { const stageResult = await stage.processor(result, stage.options); result = { ...result, ...stageResult, stages: [...result.stages, { name: stage.name, duration: Date.now() - stageStartTime, success: true, tokensAfter: this.tokenCounter.countTokens(stageResult.content) }] }; } catch (error) { console.error(`파이프라인 스테이지 ${stage.name}에서 오류 발생:`, error); result.stages.push({ name: stage.name, duration: Date.now() - stageStartTime, success: false, error: error.message }); } } return result; } /** * 토큰 분석 스테이지 * @param {Object} input - 입력 데이터 * @param {Object} options - 옵션 * @returns {Object} 스테이지 결과 */ async tokenAnalysisStage(input, options) { const tokenAnalysis = this.tokenCounter.analyzeTokenDistribution(input.content); return { content: input.content, metadata: { ...input.metadata, tokenAnalysis, originalTokens: this.tokenCounter.countTokens(input.content) } }; } /** * 중요도 분석 스테이지 * @param {Object} input - 입력 데이터 * @param {Object} options - 옵션 * @returns {Object} 스테이지 결과 */ async importanceAnalysisStage(input, options) { const importanceResult = this.classifier.analyzeImportance(input.content); return { content: input.content, metadata: { ...input.metadata, importanceAnalysis: importanceResult } }; } /** * 정규화 스테이지 * @param {Object} input - 입력 데이터 * @param {Object} options - 옵션 * @returns {Object} 스테이지 결과 */ async normalizationStage(input, options) { const normalizeResult = this.normalizer.normalize(input.content); return { content: normalizeResult.normalized, metadata: { ...input.metadata, normalization: normalizeResult.metadata } }; } /** * 필터링 스테이지 * @param {Object} input - 입력 데이터 * @param {Object} options - 옵션 * @returns {Object} 스테이지 결과 */ async filteringStage(input, options) { const filterResult = this.filter.filter(input.content, { customRules: options.customFilters, maxTokens: options.maxTokens }); return { content: filterResult.filtered, metadata: { ...input.metadata, filtering: filterResult.metadata } }; } /** * 요약 스테이지 * @param {Object} input - 입력 데이터 * @param {Object} options - 옵션 * @returns {Object} 스테이지 결과 */ async summarizationStage(input, options) { const summarizeResult = this.summarizer.summarize(input.content, { targetRatio: options.targetRatio }); return { content: summarizeResult.summary, metadata: { ...input.metadata, summarization: summarizeResult.metadata } }; } /** * 전략 압축 스테이지 * @param {Object} input - 입력 데이터 * @param {Object} options - 옵션 * @returns {Object} 스테이지 결과 */ async strategyCompressionStage(input, options) { const strategy = this.strategies[options.strategy]; if (!strategy) { throw new Error(`압축 전략을 찾을 수 없습니다: ${options.strategy}`); } const compressionResult = strategy.compress(input.content, { preserveStructure: options.preserveStructure }); return { content: compressionResult.content, metadata: { ...input.metadata, strategyCompression: compressionResult.metadata } }; } /** * 검증 스테이지 * @param {Object} input - 입력 데이터 * @param {Object} options - 옵션 * @returns {Object} 스테이지 결과 */ async validationStage(input, options) { const validationResult = this.validateCompression(input.content, input.metadata); return { content: input.content, metadata: { ...input.metadata, validation: validationResult } }; } /** * 압축 결과를 검증합니다 * @param {string} content - 압축된 콘텐츠 * @param {Object} metadata - 메타데이터 * @returns {Object} 검증 결과 */ validateCompression(content, metadata) { const validation = { isValid: true, warnings: [], errors: [], qualityScore: 0 }; // 1. 콘텐츠 유효성 검증 if (!content || content.trim().length === 0) { validation.isValid = false; validation.errors.push('압축된 콘텐츠가 비어있습니다.'); } // 2. 압축률 검증 if (metadata.originalTokens && metadata.originalTokens > 0) { const currentTokens = this.tokenCounter.countTokens(content); const compressionRatio = ((metadata.originalTokens - currentTokens) / metadata.originalTokens) * 100; if (compressionRatio < 5) { validation.warnings.push('압축률이 낮습니다 (5% 미만).'); } else if (compressionRatio > 90) { validation.warnings.push('압축률이 너무 높습니다 (90% 초과). 중요한 정보가 손실될 수 있습니다.'); } // 품질 점수 계산 validation.qualityScore = this.calculateQualityScore(compressionRatio, metadata); } // 3. 구조 유지 검증 if (metadata.importanceAnalysis) { const criticalSections = metadata.importanceAnalysis.sections.filter(s => s.importance === 'critical'); const preservedCritical = criticalSections.filter(s => content.includes(s.name) || content.includes(s.content.substring(0, 50)) ); if (preservedCritical.length < criticalSections.length) { validation.warnings.push('일부 중요한 섹션이 압축 과정에서 손실되었을 수 있습니다.'); } } return validation; } /** * 품질 점수를 계산합니다 * @param {number} compressionRatio - 압축률 * @param {Object} metadata - 메타데이터 * @returns {number} 품질 점수 (0-100) */ calculateQualityScore(compressionRatio, metadata) { let score = 0; // 압축률 점수 (40점 만점) if (compressionRatio >= 20 && compressionRatio <= 70) { score += 40; } else if (compressionRatio >= 10 && compressionRatio <= 80) { score += 30; } else if (compressionRatio >= 5 && compressionRatio <= 90) { score += 20; } else { score += 10; } // 정보 보존 점수 (30점 만점) if (metadata.importanceAnalysis) { const preservedImportant = metadata.importanceAnalysis.sections.filter(s => s.importance === 'critical' || s.importance === 'high' ).length; const totalImportant = metadata.importanceAnalysis.sections.filter(s => s.importance === 'critical' || s.importance === 'high' ).length; if (totalImportant > 0) { score += (preservedImportant / totalImportant) * 30; } else { score += 30; } } else { score += 20; // 기본 점수 } // 구조 유지 점수 (20점 만점) if (metadata.strategyCompression) { score += metadata.strategyCompression.preservedSections ? 20 : 10; } else { score += 15; // 기본 점수 } // 성능 점수 (10점 만점) score += 10; // 기본 점수 (실제로는 처리 시간 등을 고려) return Math.min(100, Math.max(0, score)); } /** * 압축된 콘텐츠를 복원합니다 * @param {string} compressedContent - 압축된 콘텐츠 * @param {Object} metadata - 복원 메타데이터 * @returns {Object} 복원 결과 */ async decompress(compressedContent, metadata) { const startTime = Date.now(); try { // 메타데이터에서 압축 정보 추출 const compressionStrategy = metadata.strategyCompression?.strategy || 'balanced'; const strategy = this.strategies[compressionStrategy]; if (!strategy) { throw new Error(`복원 전략을 찾을 수 없습니다: ${compressionStrategy}`); } // 전략별 복원 수행 const decompressed = strategy.decompress(compressedContent, metadata); const result = { compressed: compressedContent, decompressed, strategy: compressionStrategy, processingTime: Date.now() - startTime, success: true, metadata: { decompressionId: this.generateCompressionId(), timestamp: new Date().toISOString(), originalMetadata: metadata } }; return result; } catch (error) { console.error('복원 중 오류 발생:', error); return { compressed: compressedContent, decompressed: compressedContent, // 복원 실패 시 압축된 콘텐츠 반환 strategy: 'fallback', processingTime: Date.now() - startTime, success: false, error: error.message }; } } /** * 압축 ID를 생성합니다 * @returns {string} 압축 ID */ generateCompressionId() { const timestamp = Date.now(); const random = Math.random().toString(36).substr(2, 9); return `comp_${timestamp}_${random}`; } /** * 압축 통계를 반환합니다 * @returns {Object} 압축 통계 */ getStatistics() { return { tokenTracker: this.tokenTracker.getSessionStats(), filter: this.filter.getStatistics(), supportedStrategies: Object.keys(this.strategies), currentMode: this.mode }; } /** * 모드를 변경합니다 * @param {string} mode - 새로운 모드 */ setMode(mode) { if (this.strategies[mode]) { this.mode = mode; } else { throw new Error(`지원되지 않는 압축 모드: ${mode}`); } } /** * 사용자 정의 전략을 추가합니다 * @param {string} name - 전략 이름 * @param {Object} strategy - 전략 객체 */ addStrategy(name, strategy) { this.strategies[name] = strategy; } /** * 리소스를 정리합니다 */ cleanup() { if (this.tokenCounter) this.tokenCounter.cleanup(); if (this.tokenTracker) this.tokenTracker.cleanup(); if (this.summarizer) this.summarizer.cleanup(); if (this.normalizer) this.normalizer.cleanup(); if (this.classifier) this.classifier.cleanup(); if (this.filter) this.filter.cleanup(); // 전략들 정리 Object.values(this.strategies).forEach(strategy => { if (strategy.cleanup) strategy.cleanup(); }); } } export default ContextCompressor;