Spaces:
Sleeping
Sleeping
| // RFP Analysis Engine - Analyzes current RFP questions and provides structured insights | |
| // Combines previous answers with AI-powered gap analysis and suggestions | |
| import { vectorStorage, type SearchResult } from './vectorStorage'; | |
| import { backendService } from './backendService'; | |
| export interface AnalyzedAnswer { | |
| questionId: string; | |
| question: string; | |
| previousAnswers: StructuredPreviousAnswer[]; | |
| aiGeneratedAnswer: AIAnalyzedAnswer; | |
| gapAnalysis: GapAnalysis; | |
| suggestions: string[]; | |
| confidence: number; | |
| } | |
| export interface StructuredPreviousAnswer { | |
| id: string; | |
| source: string; | |
| content: string; | |
| relevanceScore: number; | |
| category: string; | |
| pageNumber?: number; | |
| keyPoints: string[]; | |
| metrics: ExtractedMetric[]; | |
| dateAnswered?: string; | |
| projectContext?: string; | |
| } | |
| export interface AIAnalyzedAnswer { | |
| answer: string; | |
| wordCount: number; | |
| addressedPoints: string[]; | |
| strengths: string[]; | |
| citations: string[]; | |
| tone: 'technical' | 'business' | 'mixed'; | |
| comprehensiveness: number; // 0-1 score | |
| } | |
| export interface GapAnalysis { | |
| coveredPoints: CoveredPoint[]; | |
| missingPoints: MissingPoint[]; | |
| weakPoints: WeakPoint[]; | |
| recommendations: string[]; | |
| overallCoverage: number; // 0-1 score | |
| } | |
| export interface CoveredPoint { | |
| point: string; | |
| source: string; | |
| confidence: number; | |
| evidence: string; | |
| } | |
| export interface MissingPoint { | |
| point: string; | |
| importance: 'critical' | 'high' | 'medium' | 'low'; | |
| suggestion: string; | |
| examples?: string[]; | |
| } | |
| export interface WeakPoint { | |
| point: string; | |
| currentCoverage: string; | |
| improvementNeeded: string; | |
| suggestions: string[]; | |
| } | |
| export interface ExtractedMetric { | |
| type: 'number' | 'percentage' | 'currency' | 'duration'; | |
| value: string; | |
| context: string; | |
| } | |
| export interface StressPoint { | |
| keyword: string; | |
| weight: number; | |
| category: string; | |
| expectedResponseType: string; | |
| } | |
| /** | |
| * Main RFP Analysis function | |
| * Analyzes current RFP question against previous answers and generates comprehensive insights | |
| */ | |
| export async function analyzeRFPQuestion( | |
| question: string, | |
| currentRFPContext?: string | |
| ): Promise<AnalyzedAnswer> { | |
| console.log('π Starting RFP Question Analysis...'); | |
| console.log(`π Question: ${question.substring(0, 100)}...`); | |
| // Step 1: Identify stress points and key requirements in the question | |
| const stressPoints = identifyStressPoints(question, currentRFPContext); | |
| console.log(`π― Identified ${stressPoints.length} stress points:`, stressPoints.map(sp => sp.keyword)); | |
| // Step 2: Retrieve and structure previous answers | |
| const previousAnswers = await retrieveStructuredAnswers(question, stressPoints); | |
| console.log(`π Found ${previousAnswers.length} relevant previous answers`); | |
| // Step 3: Generate AI answer considering stress points | |
| const aiAnswer = await generateAIAnswerWithStressPoints(question, previousAnswers, stressPoints, currentRFPContext); | |
| console.log(`π€ Generated AI answer: ${aiAnswer.wordCount} words`); | |
| // Step 4: Perform gap analysis | |
| const gapAnalysis = performGapAnalysis(question, previousAnswers, aiAnswer, stressPoints); | |
| console.log(`π Gap analysis complete: ${gapAnalysis.overallCoverage * 100}% coverage`); | |
| // Step 5: Generate actionable suggestions | |
| const suggestions = generateActionableSuggestions(gapAnalysis, stressPoints); | |
| console.log(`π‘ Generated ${suggestions.length} suggestions`); | |
| return { | |
| questionId: generateQuestionId(question), | |
| question, | |
| previousAnswers, | |
| aiGeneratedAnswer: aiAnswer, | |
| gapAnalysis, | |
| suggestions, | |
| confidence: calculateOverallConfidence(previousAnswers, aiAnswer, gapAnalysis) | |
| }; | |
| } | |
| /** | |
| * Identify stress points and key requirements in the RFP question | |
| */ | |
| function identifyStressPoints(question: string, context?: string): StressPoint[] { | |
| const stressPoints: StressPoint[] = []; | |
| const textToAnalyze = `${question} ${context || ''}`.toLowerCase(); | |
| // Define stress point patterns with weights | |
| const patterns: Record<string, { keywords: string[], weight: number, category: string, responseType: string }> = { | |
| experience: { | |
| keywords: ['experience', 'years', 'track record', 'history', 'previous', 'past projects', 'portfolio'], | |
| weight: 0.9, | |
| category: 'Qualification', | |
| responseType: 'Specific examples with metrics and timeframes' | |
| }, | |
| technical: { | |
| keywords: ['technical', 'technology', 'platform', 'architecture', 'infrastructure', 'system', 'tools'], | |
| weight: 0.85, | |
| category: 'Technical', | |
| responseType: 'Detailed technical specifications and capabilities' | |
| }, | |
| methodology: { | |
| keywords: ['approach', 'methodology', 'process', 'framework', 'method', 'procedure', 'workflow'], | |
| weight: 0.8, | |
| category: 'Approach', | |
| responseType: 'Step-by-step process with phases and deliverables' | |
| }, | |
| team: { | |
| keywords: ['team', 'staff', 'personnel', 'resources', 'qualifications', 'certifications', 'expertise'], | |
| weight: 0.85, | |
| category: 'Resources', | |
| responseType: 'Team structure, qualifications, and availability' | |
| }, | |
| timeline: { | |
| keywords: ['timeline', 'schedule', 'deadline', 'duration', 'timeframe', 'delivery', 'milestones'], | |
| weight: 0.75, | |
| category: 'Schedule', | |
| responseType: 'Detailed timeline with milestones and dependencies' | |
| }, | |
| cost: { | |
| keywords: ['cost', 'price', 'budget', 'rate', 'fee', 'pricing', 'financial'], | |
| weight: 0.8, | |
| category: 'Financial', | |
| responseType: 'Pricing structure with justification and options' | |
| }, | |
| quality: { | |
| keywords: ['quality', 'standards', 'assurance', 'compliance', 'certification', 'audit'], | |
| weight: 0.7, | |
| category: 'Quality', | |
| responseType: 'QA processes, standards, and compliance measures' | |
| }, | |
| risk: { | |
| keywords: ['risk', 'mitigation', 'contingency', 'backup', 'disaster recovery', 'security'], | |
| weight: 0.75, | |
| category: 'Risk Management', | |
| responseType: 'Risk identification, assessment, and mitigation strategies' | |
| }, | |
| innovation: { | |
| keywords: ['innovative', 'innovation', 'cutting-edge', 'advanced', 'modern', 'latest'], | |
| weight: 0.65, | |
| category: 'Innovation', | |
| responseType: 'Innovative approaches and competitive advantages' | |
| }, | |
| scalability: { | |
| keywords: ['scalable', 'scalability', 'growth', 'expansion', 'flexible', 'adaptable'], | |
| weight: 0.7, | |
| category: 'Scalability', | |
| responseType: 'Scalability measures and future-proofing strategies' | |
| }, | |
| metrics: { | |
| keywords: ['metrics', 'kpi', 'measurement', 'success criteria', 'performance', 'roi'], | |
| weight: 0.75, | |
| category: 'Performance', | |
| responseType: 'Specific metrics, KPIs, and success measurements' | |
| }, | |
| references: { | |
| keywords: ['references', 'case study', 'client', 'similar project', 'comparable', 'example'], | |
| weight: 0.8, | |
| category: 'Proof Points', | |
| responseType: 'Specific client examples with measurable outcomes' | |
| } | |
| }; | |
| // Analyze text for stress points | |
| Object.entries(patterns).forEach(([key, pattern]) => { | |
| const matches = pattern.keywords.filter(keyword => | |
| textToAnalyze.includes(keyword) | |
| ); | |
| if (matches.length > 0) { | |
| stressPoints.push({ | |
| keyword: key, | |
| weight: pattern.weight * (matches.length / pattern.keywords.length), // Adjust weight by match density | |
| category: pattern.category, | |
| expectedResponseType: pattern.responseType | |
| }); | |
| } | |
| }); | |
| // Sort by weight (most important first) | |
| return stressPoints.sort((a, b) => b.weight - a.weight); | |
| } | |
| /** | |
| * Retrieve and structure previous answers with detailed extraction | |
| */ | |
| async function retrieveStructuredAnswers( | |
| question: string, | |
| stressPoints: StressPoint[] | |
| ): Promise<StructuredPreviousAnswer[]> { | |
| // Use enhanced semantic search | |
| const searchResults = await backendService.searchDocuments(question, { | |
| topK: 15, | |
| useSemanticSearch: true | |
| }); | |
| const structuredAnswers: StructuredPreviousAnswer[] = []; | |
| for (let i = 0; i < searchResults.length && i < 10; i++) { | |
| const result = searchResults[i]; | |
| const structured: StructuredPreviousAnswer = { | |
| id: `prev_${i + 1}`, | |
| source: result.source, | |
| content: result.content, | |
| relevanceScore: result.relevanceScore, | |
| category: result.category || 'general', | |
| keyPoints: extractKeyPoints(result.content, stressPoints), | |
| metrics: extractMetrics(result.content), | |
| projectContext: extractProjectContext(result.content) | |
| }; | |
| structuredAnswers.push(structured); | |
| } | |
| return structuredAnswers; | |
| } | |
| /** | |
| * Extract key points from content based on stress points | |
| */ | |
| function extractKeyPoints(content: string, stressPoints: StressPoint[]): string[] { | |
| const keyPoints: string[] = []; | |
| const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 20); | |
| // Look for sentences containing stress point keywords | |
| stressPoints.forEach(sp => { | |
| const relevantSentences = sentences.filter(sentence => | |
| sentence.toLowerCase().includes(sp.keyword) | |
| ); | |
| relevantSentences.slice(0, 2).forEach(sentence => { | |
| const cleaned = sentence.trim(); | |
| if (cleaned && !keyPoints.includes(cleaned)) { | |
| keyPoints.push(cleaned); | |
| } | |
| }); | |
| }); | |
| // Also extract bullet points | |
| const bulletPattern = /[β’\-\*]\s*(.+?)(?=[β’\-\*\n]|$)/g; | |
| let match; | |
| while ((match = bulletPattern.exec(content)) !== null) { | |
| const point = match[1].trim(); | |
| if (point.length > 15 && point.length < 200 && !keyPoints.includes(point)) { | |
| keyPoints.push(point); | |
| } | |
| } | |
| return keyPoints.slice(0, 10); // Return top 10 key points | |
| } | |
| /** | |
| * Extract metrics (numbers, percentages, etc.) from content | |
| */ | |
| function extractMetrics(content: string): ExtractedMetric[] { | |
| const metrics: ExtractedMetric[] = []; | |
| // Extract percentages | |
| const percentagePattern = /(\d+(?:\.\d+)?)\s*%/g; | |
| let match; | |
| while ((match = percentagePattern.exec(content)) !== null) { | |
| const context = content.substring(Math.max(0, match.index - 50), Math.min(content.length, match.index + 100)); | |
| metrics.push({ | |
| type: 'percentage', | |
| value: `${match[1]}%`, | |
| context: context.trim() | |
| }); | |
| } | |
| // Extract currency | |
| const currencyPattern = /\$\s*(\d+(?:,\d{3})*(?:\.\d{2})?)\s*(million|billion|M|B)?/gi; | |
| while ((match = currencyPattern.exec(content)) !== null) { | |
| const context = content.substring(Math.max(0, match.index - 50), Math.min(content.length, match.index + 100)); | |
| metrics.push({ | |
| type: 'currency', | |
| value: match[0], | |
| context: context.trim() | |
| }); | |
| } | |
| // Extract durations | |
| const durationPattern = /(\d+)\s*(years?|months?|weeks?|days?)/gi; | |
| while ((match = durationPattern.exec(content)) !== null) { | |
| const context = content.substring(Math.max(0, match.index - 50), Math.min(content.length, match.index + 100)); | |
| metrics.push({ | |
| type: 'duration', | |
| value: match[0], | |
| context: context.trim() | |
| }); | |
| } | |
| // Extract general numbers with context | |
| const numberPattern = /(\d{2,})\s*([+]|\w+)/g; | |
| while ((match = numberPattern.exec(content)) !== null && metrics.length < 20) { | |
| const context = content.substring(Math.max(0, match.index - 50), Math.min(content.length, match.index + 100)); | |
| if (!context.toLowerCase().includes('page') && !context.toLowerCase().includes('section')) { | |
| metrics.push({ | |
| type: 'number', | |
| value: match[1], | |
| context: context.trim() | |
| }); | |
| } | |
| } | |
| return metrics.slice(0, 15); // Limit to top 15 metrics | |
| } | |
| /** | |
| * Extract project context from content | |
| */ | |
| function extractProjectContext(content: string): string | undefined { | |
| const projectPatterns = [ | |
| /project for ([^.]+)/i, | |
| /client:?\s*([^.]+)/i, | |
| /implemented ([^.]+)/i, | |
| /delivered ([^.]+)/i | |
| ]; | |
| for (const pattern of projectPatterns) { | |
| const match = content.match(pattern); | |
| if (match) { | |
| return match[1].trim().substring(0, 150); | |
| } | |
| } | |
| return undefined; | |
| } | |
| /** | |
| * Generate AI answer with stress points consideration | |
| */ | |
| async function generateAIAnswerWithStressPoints( | |
| question: string, | |
| previousAnswers: StructuredPreviousAnswer[], | |
| stressPoints: StressPoint[], | |
| currentRFPContext?: string | |
| ): Promise<AIAnalyzedAnswer> { | |
| // Build comprehensive context for AI | |
| const referenceContext = previousAnswers.map((ans, idx) => ({ | |
| title: `Reference ${idx + 1}: ${ans.source}`, | |
| content: ans.content, | |
| keyPoints: ans.keyPoints, | |
| metrics: ans.metrics.map(m => `${m.value} (${m.context})`).join('; ') | |
| })); | |
| // Add stress points to prompt | |
| const stressPointsContext = stressPoints.map(sp => | |
| `- ${sp.category}: ${sp.expectedResponseType} (Priority: ${Math.round(sp.weight * 100)}%)` | |
| ).join('\n'); | |
| try { | |
| const result = await backendService.comprehensiveAnswer( | |
| `${question}\n\nKEY REQUIREMENTS TO ADDRESS:\n${stressPointsContext}\n\n${currentRFPContext || ''}` | |
| ); | |
| const addressedPoints = analyzeAddressedPoints(result.generatedAnswer, stressPoints); | |
| const strengths = identifyAnswerStrengths(result.generatedAnswer); | |
| const tone = detectTone(result.generatedAnswer); | |
| return { | |
| answer: result.generatedAnswer, | |
| wordCount: result.wordCount, | |
| addressedPoints, | |
| strengths, | |
| citations: result.citations?.map(c => c.citation || c.raw) || [], | |
| tone, | |
| comprehensiveness: calculateComprehensiveness(result.generatedAnswer, stressPoints) | |
| }; | |
| } catch (error) { | |
| console.error('Error generating AI answer:', error); | |
| // Fallback answer | |
| return { | |
| answer: generateFallbackAnswer(question, previousAnswers, stressPoints), | |
| wordCount: 0, | |
| addressedPoints: [], | |
| strengths: [], | |
| citations: [], | |
| tone: 'business', | |
| comprehensiveness: 0.5 | |
| }; | |
| } | |
| } | |
| /** | |
| * Analyze which stress points are addressed in the answer | |
| */ | |
| function analyzeAddressedPoints(answer: string, stressPoints: StressPoint[]): string[] { | |
| const addressed: string[] = []; | |
| const answerLower = answer.toLowerCase(); | |
| stressPoints.forEach(sp => { | |
| if (answerLower.includes(sp.keyword) || | |
| answerLower.includes(sp.category.toLowerCase())) { | |
| addressed.push(`${sp.category}: ${sp.keyword}`); | |
| } | |
| }); | |
| return addressed; | |
| } | |
| /** | |
| * Identify strengths in the generated answer | |
| */ | |
| function identifyAnswerStrengths(answer: string): string[] { | |
| const strengths: string[] = []; | |
| // Check for specific examples | |
| if (answer.match(/for example|such as|specifically|instance/i)) { | |
| strengths.push('Includes specific examples'); | |
| } | |
| // Check for metrics | |
| if (answer.match(/\d+%|\d+ years?|\$\d+/)) { | |
| strengths.push('Contains quantifiable metrics'); | |
| } | |
| // Check for structured format | |
| if (answer.match(/^#{1,3}\s|\*\*[^*]+\*\*/m)) { | |
| strengths.push('Well-structured with headers'); | |
| } | |
| // Check for citations | |
| if (answer.match(/\[Doc\d+\]|\[Ref\d+\]/)) { | |
| strengths.push('Includes citations to source documents'); | |
| } | |
| // Check length | |
| if (answer.split(/\s+/).length > 500) { | |
| strengths.push('Comprehensive and detailed'); | |
| } | |
| return strengths; | |
| } | |
| /** | |
| * Detect the tone of the answer | |
| */ | |
| function detectTone(answer: string): 'technical' | 'business' | 'mixed' { | |
| const technicalTerms = (answer.match(/\b(system|architecture|infrastructure|API|database|server|cloud|deployment)\b/gi) || []).length; | |
| const businessTerms = (answer.match(/\b(client|customer|value|ROI|business|stakeholder|strategic|partnership)\b/gi) || []).length; | |
| if (technicalTerms > businessTerms * 1.5) return 'technical'; | |
| if (businessTerms > technicalTerms * 1.5) return 'business'; | |
| return 'mixed'; | |
| } | |
| /** | |
| * Calculate how comprehensive the answer is | |
| */ | |
| function calculateComprehensiveness(answer: string, stressPoints: StressPoint[]): number { | |
| let score = 0; | |
| const answerLower = answer.toLowerCase(); | |
| stressPoints.forEach(sp => { | |
| if (answerLower.includes(sp.keyword)) { | |
| score += sp.weight; | |
| } | |
| }); | |
| return Math.min(score / stressPoints.reduce((sum, sp) => sum + sp.weight, 0), 1); | |
| } | |
| /** | |
| * Perform gap analysis comparing AI answer against requirements | |
| */ | |
| function performGapAnalysis( | |
| question: string, | |
| previousAnswers: StructuredPreviousAnswer[], | |
| aiAnswer: AIAnalyzedAnswer, | |
| stressPoints: StressPoint[] | |
| ): GapAnalysis { | |
| const coveredPoints = identifyCoveredPoints(aiAnswer, stressPoints, previousAnswers); | |
| const missingPoints = identifyMissingPoints(stressPoints, coveredPoints, question); | |
| const weakPoints = identifyWeakPoints(aiAnswer, stressPoints); | |
| const recommendations = generateRecommendations(missingPoints, weakPoints); | |
| const overallCoverage = coveredPoints.length / (coveredPoints.length + missingPoints.length); | |
| return { | |
| coveredPoints, | |
| missingPoints, | |
| weakPoints, | |
| recommendations, | |
| overallCoverage | |
| }; | |
| } | |
| /** | |
| * Identify covered points in the answer | |
| */ | |
| function identifyCoveredPoints( | |
| aiAnswer: AIAnalyzedAnswer, | |
| stressPoints: StressPoint[], | |
| previousAnswers: StructuredPreviousAnswer[] | |
| ): CoveredPoint[] { | |
| const covered: CoveredPoint[] = []; | |
| const answerLower = aiAnswer.answer.toLowerCase(); | |
| stressPoints.forEach(sp => { | |
| if (answerLower.includes(sp.keyword)) { | |
| // Find evidence | |
| const sentences = aiAnswer.answer.split(/[.!?]+/); | |
| const evidence = sentences.find(s => s.toLowerCase().includes(sp.keyword)) || ''; | |
| // Find source | |
| const source = previousAnswers.find(ans => | |
| ans.content.toLowerCase().includes(sp.keyword) | |
| ); | |
| covered.push({ | |
| point: sp.category, | |
| source: source?.source || 'AI Generated', | |
| confidence: sp.weight, | |
| evidence: evidence.trim().substring(0, 200) | |
| }); | |
| } | |
| }); | |
| return covered; | |
| } | |
| /** | |
| * Identify missing points that should be addressed | |
| */ | |
| function identifyMissingPoints( | |
| stressPoints: StressPoint[], | |
| coveredPoints: CoveredPoint[], | |
| question: string | |
| ): MissingPoint[] { | |
| const missing: MissingPoint[] = []; | |
| const coveredCategories = new Set(coveredPoints.map(cp => cp.point)); | |
| stressPoints.forEach(sp => { | |
| if (!coveredCategories.has(sp.category)) { | |
| const importance = sp.weight > 0.8 ? 'critical' : | |
| sp.weight > 0.7 ? 'high' : | |
| sp.weight > 0.6 ? 'medium' : 'low'; | |
| missing.push({ | |
| point: sp.category, | |
| importance: importance as 'critical' | 'high' | 'medium' | 'low', | |
| suggestion: `Add ${sp.expectedResponseType.toLowerCase()}`, | |
| examples: generateExamplesForPoint(sp) | |
| }); | |
| } | |
| }); | |
| return missing.sort((a, b) => { | |
| const order = { critical: 0, high: 1, medium: 2, low: 3 }; | |
| return order[a.importance] - order[b.importance]; | |
| }); | |
| } | |
| /** | |
| * Identify weak points that need improvement | |
| */ | |
| function identifyWeakPoints( | |
| aiAnswer: AIAnalyzedAnswer, | |
| stressPoints: StressPoint[] | |
| ): WeakPoint[] { | |
| const weak: WeakPoint[] = []; | |
| // Check if answer lacks specific examples | |
| if (!aiAnswer.answer.match(/for example|such as|specifically/i)) { | |
| weak.push({ | |
| point: 'Specific Examples', | |
| currentCoverage: 'Generic statements without concrete examples', | |
| improvementNeeded: 'Add 2-3 specific project examples with outcomes', | |
| suggestions: [ | |
| 'Include client names and project types', | |
| 'Add measurable results (percentages, cost savings, etc.)', | |
| 'Provide timeline context (when projects were completed)' | |
| ] | |
| }); | |
| } | |
| // Check if answer lacks metrics | |
| if ((aiAnswer.answer.match(/\d+/g) || []).length < 5) { | |
| weak.push({ | |
| point: 'Quantifiable Metrics', | |
| currentCoverage: 'Few or no specific numbers provided', | |
| improvementNeeded: 'Include specific metrics and KPIs', | |
| suggestions: [ | |
| 'Add years of experience', | |
| 'Include number of projects completed', | |
| 'Provide team size and certifications count', | |
| 'Add performance metrics (%, time savings, cost reductions)' | |
| ] | |
| }); | |
| } | |
| // Check comprehensiveness | |
| if (aiAnswer.comprehensiveness < 0.7) { | |
| weak.push({ | |
| point: 'Overall Comprehensiveness', | |
| currentCoverage: `Only ${Math.round(aiAnswer.comprehensiveness * 100)}% of key points addressed`, | |
| improvementNeeded: 'Address all critical stress points in the question', | |
| suggestions: [ | |
| 'Review the question requirements carefully', | |
| 'Ensure all key aspects are covered', | |
| 'Add more detail to superficial sections' | |
| ] | |
| }); | |
| } | |
| return weak; | |
| } | |
| /** | |
| * Generate examples for a stress point | |
| */ | |
| function generateExamplesForPoint(stressPoint: StressPoint): string[] { | |
| const examples: Record<string, string[]> = { | |
| experience: [ | |
| '"Over 15 years of experience with 200+ completed projects"', | |
| '"Successfully delivered similar projects for Fortune 500 clients"', | |
| '"Maintained 98% client satisfaction rate across all engagements"' | |
| ], | |
| technical: [ | |
| '"Expertise in AWS, Azure, and GCP with 50+ certified architects"', | |
| '"Implemented microservices architecture for 30+ enterprise clients"', | |
| '"24/7 monitoring using Datadog, Prometheus, and Grafana"' | |
| ], | |
| methodology: [ | |
| '"Agile/Scrum methodology with 2-week sprints and daily standups"', | |
| '"Comprehensive QA process including automated and manual testing"', | |
| '"Phased delivery approach: Discovery β Design β Build β Deploy"' | |
| ], | |
| team: [ | |
| '"15 PMP-certified project managers with 10+ years experience"', | |
| '"50+ AWS/Azure certified cloud architects and engineers"', | |
| '"Dedicated team of 5-7 resources assigned per project"' | |
| ] | |
| }; | |
| return examples[stressPoint.keyword] || [ | |
| 'Add specific details relevant to this requirement', | |
| 'Include measurable data and timeframes', | |
| 'Provide concrete examples from past projects' | |
| ]; | |
| } | |
| /** | |
| * Generate actionable recommendations | |
| */ | |
| function generateRecommendations( | |
| missingPoints: MissingPoint[], | |
| weakPoints: WeakPoint[] | |
| ): string[] { | |
| const recommendations: string[] = []; | |
| // Critical missing points | |
| const critical = missingPoints.filter(mp => mp.importance === 'critical'); | |
| if (critical.length > 0) { | |
| recommendations.push( | |
| `π¨ CRITICAL: Address ${critical.length} critical missing point(s): ${critical.map(c => c.point).join(', ')}` | |
| ); | |
| } | |
| // High priority missing points | |
| const high = missingPoints.filter(mp => mp.importance === 'high'); | |
| if (high.length > 0) { | |
| recommendations.push( | |
| `β οΈ HIGH PRIORITY: Include ${high.map(h => h.point).join(', ')}` | |
| ); | |
| } | |
| // Weak points | |
| if (weakPoints.length > 0) { | |
| recommendations.push( | |
| `πͺ STRENGTHEN: Improve ${weakPoints.map(wp => wp.point).join(', ')}` | |
| ); | |
| } | |
| // General recommendations | |
| if (missingPoints.length === 0 && weakPoints.length === 0) { | |
| recommendations.push('β Response covers all key requirements well'); | |
| } else { | |
| recommendations.push('π Review and enhance the answer before submission'); | |
| } | |
| return recommendations; | |
| } | |
| /** | |
| * Generate actionable suggestions for improvement | |
| */ | |
| function generateActionableSuggestions( | |
| gapAnalysis: GapAnalysis, | |
| stressPoints: StressPoint[] | |
| ): string[] { | |
| const suggestions: string[] = []; | |
| // Suggestions for missing points | |
| gapAnalysis.missingPoints.forEach(mp => { | |
| suggestions.push(`β ADD ${mp.point}: ${mp.suggestion}`); | |
| if (mp.examples && mp.examples.length > 0) { | |
| suggestions.push(` Example: ${mp.examples[0]}`); | |
| } | |
| }); | |
| // Suggestions for weak points | |
| gapAnalysis.weakPoints.forEach(wp => { | |
| suggestions.push(`π§ IMPROVE ${wp.point}: ${wp.improvementNeeded}`); | |
| if (wp.suggestions.length > 0) { | |
| suggestions.push(` β ${wp.suggestions[0]}`); | |
| } | |
| }); | |
| // Overall suggestions | |
| if (gapAnalysis.overallCoverage < 0.8) { | |
| suggestions.push('π Overall coverage is below 80% - review all question requirements'); | |
| } | |
| if (suggestions.length === 0) { | |
| suggestions.push('β Response is comprehensive - ready for review'); | |
| } | |
| return suggestions; | |
| } | |
| /** | |
| * Generate fallback answer when AI fails | |
| */ | |
| function generateFallbackAnswer( | |
| question: string, | |
| previousAnswers: StructuredPreviousAnswer[], | |
| stressPoints: StressPoint[] | |
| ): string { | |
| let answer = `## Response to: ${question}\n\n`; | |
| answer += '**Based on Previous RFP Responses:**\n\n'; | |
| previousAnswers.slice(0, 3).forEach((ans, idx) => { | |
| answer += `### Reference ${idx + 1}: ${ans.source}\n`; | |
| if (ans.keyPoints.length > 0) { | |
| answer += ans.keyPoints.slice(0, 3).map(kp => `β’ ${kp}`).join('\n'); | |
| answer += '\n\n'; | |
| } | |
| }); | |
| answer += '**Key Requirements to Address:**\n'; | |
| stressPoints.forEach(sp => { | |
| answer += `β’ ${sp.category}: ${sp.expectedResponseType}\n`; | |
| }); | |
| answer += '\n*Please review and enhance this response with specific details from your organization.*'; | |
| return answer; | |
| } | |
| // Helper functions | |
| function generateQuestionId(question: string): string { | |
| return `q_${Date.now()}_${question.substring(0, 20).replace(/\W/g, '_')}`; | |
| } | |
| function calculateOverallConfidence( | |
| previousAnswers: StructuredPreviousAnswer[], | |
| aiAnswer: AIAnalyzedAnswer, | |
| gapAnalysis: GapAnalysis | |
| ): number { | |
| const hasGoodReferences = previousAnswers.length >= 3 ? 0.3 : previousAnswers.length * 0.1; | |
| const aiQuality = aiAnswer.comprehensiveness * 0.4; | |
| const coverage = gapAnalysis.overallCoverage * 0.3; | |
| return Math.min(hasGoodReferences + aiQuality + coverage, 1); | |
| } | |