Skip to Content
📚 MyStoryFlow Docs — Your guide to preserving family stories

F011 - AI-Powered Coach Matching System

Objective

Implement an intelligent coach-manuscript matching system that pairs authors with the most suitable writing coaches based on manuscript analysis, genre expertise, coaching style preferences, and success metrics within the MyStoryFlow platform.

Quick Implementation

Using MyStoryFlow Components

  • User matching service from @mystoryflow/matching
  • Coach availability from @mystoryflow/scheduling
  • Review system from @mystoryflow/reviews
  • Communication platform from @mystoryflow/messaging

New Requirements

  • Coach profiling and expertise mapping
  • AI-powered compatibility scoring
  • Multi-factor matching algorithm
  • Success prediction model
  • Dynamic re-matching system

MVP Implementation

1. Coach Matching Engine

// packages/coach-matching/src/services/matching-engine.ts import { AIService } from '@mystoryflow/manuscript-analysis' import { Logger } from '@mystoryflow/logger' import { EmbeddingsService } from '@mystoryflow/ai-core' import { SchedulingService } from '@mystoryflow/scheduling' export class CoachMatchingEngine { private aiService: AIService private embeddings: EmbeddingsService private scheduling: SchedulingService private logger: Logger constructor() { this.aiService = new AIService() this.embeddings = new EmbeddingsService() this.scheduling = new SchedulingService() this.logger = new Logger('CoachMatchingEngine') } async findOptimalMatches(params: { manuscriptId: string userId: string analysis: ManuscriptAnalysis preferences: AuthorPreferences requirements: CoachingRequirements }): Promise<CoachMatches> { const { manuscriptId, userId, analysis, preferences, requirements } = params // Get available coaches const availableCoaches = await this.getAvailableCoaches(requirements) // Generate manuscript embedding const manuscriptEmbedding = await this.generateManuscriptEmbedding(analysis) // Calculate compatibility scores const scoredCoaches = await Promise.all( availableCoaches.map(async coach => { const score = await this.calculateCompatibilityScore({ coach, manuscriptAnalysis: analysis, manuscriptEmbedding, authorPreferences: preferences, requirements }) return { coach, score } }) ) // Rank and filter matches const topMatches = this.rankMatches(scoredCoaches) .filter(match => match.score.overall > 0.7) .slice(0, 5) // Enhance with detailed match reasons const enhancedMatches = await this.enhanceMatches(topMatches, analysis) // Predict success likelihood const matchesWithPredictions = await this.addSuccessPredictions( enhancedMatches, userId ) return { matches: matchesWithPredictions, alternativeOptions: this.generateAlternativeOptions(scoredCoaches), matchingInsights: this.generateMatchingInsights(matchesWithPredictions) } } private async calculateCompatibilityScore(params: { coach: Coach manuscriptAnalysis: ManuscriptAnalysis manuscriptEmbedding: number[] authorPreferences: AuthorPreferences requirements: CoachingRequirements }): Promise<CompatibilityScore> { const { coach, manuscriptAnalysis, manuscriptEmbedding, authorPreferences } = params // Calculate sub-scores const expertiseScore = await this.calculateExpertiseMatch( coach, manuscriptAnalysis ) const styleScore = await this.calculateStyleMatch( coach, manuscriptEmbedding ) const availabilityScore = this.calculateAvailabilityMatch( coach, params.requirements ) const personalityScore = await this.calculatePersonalityMatch( coach, authorPreferences ) const successScore = await this.calculateHistoricalSuccess( coach, manuscriptAnalysis.genre ) // Weighted combination const weights = { expertise: 0.35, style: 0.25, availability: 0.15, personality: 0.15, success: 0.10 } const overall = expertiseScore * weights.expertise + styleScore * weights.style + availabilityScore * weights.availability + personalityScore * weights.personality + successScore * weights.success return { overall, breakdown: { expertise: expertiseScore, style: styleScore, availability: availabilityScore, personality: personalityScore, historicalSuccess: successScore }, strengths: this.identifyMatchStrengths({ expertiseScore, styleScore, personalityScore }), concerns: this.identifyMatchConcerns({ expertiseScore, styleScore, availabilityScore }) } } private async calculateExpertiseMatch( coach: Coach, analysis: ManuscriptAnalysis ): Promise<number> { // Genre expertise const genreMatch = coach.expertise.genres.includes(analysis.genre) ? 1.0 : 0.5 // Specific skill matches const neededSkills = this.identifyNeededSkills(analysis) const skillMatches = neededSkills.filter(skill => coach.expertise.specializations.includes(skill) ).length / neededSkills.length // Experience level match const experienceMatch = this.matchExperienceLevel( coach.experience, analysis.complexity ) // Certification relevance const certificationBonus = coach.certifications .filter(cert => this.isRelevantCertification(cert, analysis)) .length * 0.05 return Math.min( 1.0, genreMatch * 0.4 + skillMatches * 0.3 + experienceMatch * 0.2 + certificationBonus + 0.1 ) } private async calculateStyleMatch( coach: Coach, manuscriptEmbedding: number[] ): Promise<number> { // Get coach's writing style embedding from their bio and sample edits const coachStyleEmbedding = await this.embeddings.generateEmbeddings([ coach.bio, ...coach.sampleEdits.map(edit => edit.content) ]) // Calculate cosine similarity const similarity = this.cosineSimilarity( manuscriptEmbedding, coachStyleEmbedding[0] ) // Adjust based on coaching approach const approachMultiplier = coach.coachingStyle === 'adaptive' ? 1.1 : 1.0 return Math.min(1.0, similarity * approachMultiplier) } private async calculatePersonalityMatch( coach: Coach, preferences: AuthorPreferences ): Promise<number> { let score = 0.5 // Base score // Communication style match if (coach.communicationStyle === preferences.preferredCommunicationStyle) { score += 0.2 } // Feedback style match if (coach.feedbackStyle === preferences.preferredFeedbackStyle) { score += 0.15 } // Session format preferences const formatMatch = coach.availableFormats.filter(format => preferences.preferredFormats.includes(format) ).length / preferences.preferredFormats.length score += formatMatch * 0.15 return Math.min(1.0, score) } private identifyNeededSkills(analysis: ManuscriptAnalysis): string[] { const skills: string[] = [] // Based on weaknesses if (analysis.scores.characterDevelopment < 0.7) { skills.push('character-development', 'character-arcs') } if (analysis.scores.dialogue < 0.7) { skills.push('dialogue-writing', 'voice-development') } if (analysis.scores.plotStructure < 0.7) { skills.push('plot-structure', 'story-architecture') } if (analysis.scores.pacing < 0.7) { skills.push('pacing', 'scene-structure') } // Genre-specific skills const genreSkills = this.getGenreSpecificSkills(analysis.genre) skills.push(...genreSkills) return [...new Set(skills)] } }

2. Coach Profile Enhancement

// packages/coach-matching/src/services/coach-profiler.ts export class CoachProfiler { private aiService: AIService private analyticsService: AnalyticsService async buildEnhancedProfile(coachId: string): Promise<EnhancedCoachProfile> { const baseProfile = await this.getBaseProfile(coachId) const performanceData = await this.getPerformanceData(coachId) const clientFeedback = await this.getClientFeedback(coachId) // Analyze coaching samples const styleAnalysis = await this.analyzeCoachingStyle( baseProfile.sampleEdits, baseProfile.testimonials ) // Extract expertise signals const expertiseSignals = await this.extractExpertiseSignals( baseProfile, performanceData ) // Calculate success metrics const successMetrics = this.calculateSuccessMetrics( performanceData, clientFeedback ) // Generate coaching philosophy embedding const philosophyEmbedding = await this.generatePhilosophyEmbedding( baseProfile.coachingPhilosophy, styleAnalysis ) return { ...baseProfile, enhancedData: { styleProfile: styleAnalysis, expertiseDepth: expertiseSignals, successMetrics, philosophyEmbedding, strengthAreas: this.identifyStrengthAreas(performanceData), idealClientProfile: await this.generateIdealClientProfile( successMetrics, clientFeedback ), coachingPatterns: this.analyzeCoachingPatterns(performanceData) } } } private async analyzeCoachingStyle( sampleEdits: SampleEdit[], testimonials: Testimonial[] ): Promise<CoachingStyleProfile> { // Analyze editing patterns const editAnalysis = await this.aiService.analyzeManuscript( JSON.stringify(sampleEdits), 'coaching-style', { analysisType: 'editing-patterns', outputFormat: 'structured' } ) // Extract key characteristics return { primaryApproach: editAnalysis.approach, // 'nurturing', 'direct', 'collaborative' feedbackTone: editAnalysis.tone, // 'encouraging', 'constructive', 'challenging' focusAreas: editAnalysis.commonFocusAreas, editingDepth: editAnalysis.depth, // 'surface', 'structural', 'comprehensive' communicationStyle: this.extractCommunicationStyle(testimonials), coachingTechniques: editAnalysis.techniques, adaptability: this.assessAdaptability(sampleEdits) } } private calculateSuccessMetrics( performance: PerformanceData, feedback: ClientFeedback[] ): CoachSuccessMetrics { const satisfactionScore = feedback.reduce( (sum, f) => sum + f.satisfaction, 0 ) / feedback.length const completionRate = performance.completedProjects / performance.totalProjects const improvementMetrics = this.calculateAverageImprovement( performance.clientOutcomes ) const repeatClientRate = performance.repeatClients / performance.uniqueClients return { overallSuccess: this.calculateWeightedSuccess({ satisfaction: satisfactionScore, completion: completionRate, improvement: improvementMetrics.average, retention: repeatClientRate }), clientSatisfaction: satisfactionScore, projectCompletion: completionRate, averageImprovement: improvementMetrics, clientRetention: repeatClientRate, specialtySuccess: this.calculateSpecialtySuccess(performance), timeline: { avgResponseTime: performance.avgResponseHours, avgProjectDuration: performance.avgProjectDays, punctualityScore: performance.onTimeDelivery } } } }

3. Success Prediction Model

// packages/coach-matching/src/models/success-predictor.ts export class SuccessPredictor { private model: PredictionModel private historicalData: HistoricalDataService async predictMatchSuccess( match: CoachMatch, authorProfile: AuthorProfile, manuscriptAnalysis: ManuscriptAnalysis ): Promise<SuccessPrediction> { // Gather feature vectors const features = await this.extractFeatures({ coachProfile: match.coach.enhancedProfile, authorProfile, manuscriptAnalysis, compatibilityScore: match.score }) // Get historical similar matches const similarMatches = await this.findSimilarHistoricalMatches(features) // Make prediction const prediction = await this.model.predict(features) // Calculate confidence based on similar match data const confidence = this.calculateConfidence(similarMatches, features) // Generate specific success factors const successFactors = this.identifySuccessFactors( features, similarMatches ) // Identify potential challenges const challenges = this.identifyPotentialChallenges( match, authorProfile, manuscriptAnalysis ) return { likelihood: prediction.probability, confidence, successFactors, challenges, recommendedApproach: this.generateRecommendedApproach( successFactors, challenges ), expectedOutcomes: this.projectOutcomes( prediction, manuscriptAnalysis ), timelineEstimate: this.estimateTimeline( manuscriptAnalysis.wordCount, match.coach.enhancedProfile.successMetrics.timeline ) } } private async extractFeatures(params: { coachProfile: EnhancedCoachProfile authorProfile: AuthorProfile manuscriptAnalysis: ManuscriptAnalysis compatibilityScore: CompatibilityScore }): Promise<FeatureVector> { const { coachProfile, authorProfile, manuscriptAnalysis, compatibilityScore } = params return { // Coach features coachExperience: coachProfile.experience.years, coachSpecialtyMatch: compatibilityScore.breakdown.expertise, coachSuccessRate: coachProfile.enhancedData.successMetrics.overallSuccess, coachWorkload: coachProfile.currentWorkload, // Author features authorExperience: authorProfile.writingExperience, authorMotivation: authorProfile.motivationLevel, authorTimeCommitment: authorProfile.availableHoursPerWeek, authorLearningStyle: this.encodeLearningStyle(authorProfile.learningStyle), // Manuscript features manuscriptQuality: manuscriptAnalysis.scores.overall, manuscriptComplexity: manuscriptAnalysis.complexity, improvementPotential: manuscriptAnalysis.improvementPotential, genreAlignment: this.calculateGenreAlignment( coachProfile.expertise.genres, manuscriptAnalysis.genre ), // Match features styleCompatibility: compatibilityScore.breakdown.style, personalityMatch: compatibilityScore.breakdown.personality, scheduleAlignment: compatibilityScore.breakdown.availability, // Interaction features communicationMatch: this.calculateCommunicationMatch( coachProfile.enhancedData.styleProfile.communicationStyle, authorProfile.communicationPreferences ), expectationAlignment: this.calculateExpectationAlignment( authorProfile.goals, coachProfile.enhancedData.idealClientProfile ) } } private identifySuccessFactors( features: FeatureVector, historicalMatches: HistoricalMatch[] ): SuccessFactor[] { const factors: SuccessFactor[] = [] // High compatibility areas if (features.styleCompatibility > 0.8) { factors.push({ factor: 'Strong style alignment', impact: 'high', description: 'Coach\'s editing style aligns well with manuscript voice' }) } if (features.coachSpecialtyMatch > 0.85) { factors.push({ factor: 'Deep genre expertise', impact: 'high', description: 'Coach has extensive experience in this specific genre' }) } // Historical success patterns const successfulSimilar = historicalMatches.filter(m => m.outcome.success) if (successfulSimilar.length > 5) { factors.push({ factor: 'Proven track record', impact: 'medium', description: `${successfulSimilar.length} similar successful matches` }) } return factors } }

4. Dynamic Matching API

// apps/story-analyzer/src/app/api/coaches/match/route.ts import { NextRequest, NextResponse } from 'next/server' import { CoachMatchingEngine } from '@mystoryflow/coach-matching' import { withAuth } from '@mystoryflow/auth' import { trackEvent } from '@mystoryflow/analytics' export async function POST(req: NextRequest) { try { const session = await withAuth(req) if (!session) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const { manuscriptId, preferences, requirements, urgency = 'normal' } = await req.json() // Get manuscript analysis const analysis = await getManuscriptAnalysis(manuscriptId, session.user.id) if (!analysis) { return NextResponse.json( { error: 'Manuscript analysis required' }, { status: 400 } ) } // Get author profile const authorProfile = await getAuthorProfile(session.user.id) // Find matches const matchingEngine = new CoachMatchingEngine() const matches = await matchingEngine.findOptimalMatches({ manuscriptId, userId: session.user.id, analysis, preferences: { ...authorProfile.preferences, ...preferences }, requirements: { ...requirements, urgency } }) // Add success predictions const predictor = new SuccessPredictor() const matchesWithPredictions = await Promise.all( matches.matches.map(async match => ({ ...match, prediction: await predictor.predictMatchSuccess( match, authorProfile, analysis ) })) ) // Track matching event await trackEvent({ userId: session.user.id, event: 'coach_matching_completed', properties: { manuscriptId, matchCount: matchesWithPredictions.length, topMatchScore: matchesWithPredictions[0]?.score.overall, urgency } }) return NextResponse.json({ matches: matchesWithPredictions, alternativeOptions: matches.alternativeOptions, insights: matches.matchingInsights }) } catch (error) { console.error('Coach matching failed:', error) return NextResponse.json( { error: 'Failed to find coach matches' }, { status: 500 } ) } } // Re-match endpoint for when initial match doesn't work out export async function PUT(req: NextRequest) { try { const session = await withAuth(req) if (!session) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const { manuscriptId, previousMatchId, feedback, newPreferences } = await req.json() // Learn from feedback await updateMatchingPreferences(session.user.id, previousMatchId, feedback) // Get updated analysis and preferences const analysis = await getManuscriptAnalysis(manuscriptId, session.user.id) const updatedPreferences = await mergePreferences( session.user.id, newPreferences, feedback ) // Find new matches excluding previous const matchingEngine = new CoachMatchingEngine() const matches = await matchingEngine.findOptimalMatches({ manuscriptId, userId: session.user.id, analysis, preferences: updatedPreferences, requirements: { excludeCoaches: [previousMatchId], learningFromFeedback: feedback } }) return NextResponse.json({ matches: matches.matches, adjustmentsApplied: feedback.reasons }) } catch (error) { console.error('Re-matching failed:', error) return NextResponse.json( { error: 'Failed to find new matches' }, { status: 500 } ) } }

5. Coach Performance Tracking

// packages/coach-matching/src/services/performance-tracker.ts export class CoachPerformanceTracker { async updateCoachMetrics( coachId: string, projectId: string, outcome: ProjectOutcome ): Promise<void> { // Calculate improvement metrics const improvementScore = this.calculateImprovement( outcome.beforeScores, outcome.afterScores ) // Update coach statistics await this.updateCoachStats(coachId, { projectsCompleted: 1, avgImprovement: improvementScore, clientSatisfaction: outcome.clientFeedback.satisfaction, onTimeDelivery: outcome.deliveredOnTime, genrePerformance: { [outcome.genre]: improvementScore } }) // Update matching algorithm weights await this.updateMatchingWeights(coachId, outcome) // Generate performance insights const insights = await this.generatePerformanceInsights( coachId, projectId, outcome ) // Notify coach of performance update await this.notifyCoachOfPerformance(coachId, insights) } private async updateMatchingWeights( coachId: string, outcome: ProjectOutcome ): Promise<void> { // Identify successful patterns const successPatterns = this.identifySuccessPatterns(outcome) // Update coach expertise weights if (outcome.success) { await this.reinforceExpertiseAreas(coachId, successPatterns) } else { await this.adjustExpertiseWeights(coachId, outcome.challenges) } // Update ideal client profile await this.refineIdealClientProfile(coachId, outcome) } private identifySuccessPatterns(outcome: ProjectOutcome): SuccessPattern[] { const patterns: SuccessPattern[] = [] // Analyze which areas improved most const improvements = Object.entries(outcome.afterScores).map( ([dimension, afterScore]) => ({ dimension, improvement: afterScore - (outcome.beforeScores[dimension] || 0) }) ).sort((a, b) => b.improvement - a.improvement) // Top improvements indicate coach strengths patterns.push(...improvements.slice(0, 3).map(imp => ({ type: 'dimension-improvement', dimension: imp.dimension, strength: imp.improvement }))) // Analyze successful techniques if (outcome.techniquesUsed) { patterns.push(...outcome.techniquesUsed .filter(t => t.effectiveness > 0.8) .map(t => ({ type: 'technique-success', technique: t.name, strength: t.effectiveness })) ) } return patterns } }

Database Schema

-- Enhanced coach profiles CREATE TABLE coaches.enhanced_profiles ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), coach_id UUID REFERENCES coaches.profiles(id), style_profile JSONB, expertise_depth JSONB, success_metrics JSONB, philosophy_embedding vector(1536), strength_areas TEXT[], ideal_client_profile JSONB, coaching_patterns JSONB, last_updated TIMESTAMP DEFAULT NOW(), INDEX idx_enhanced_coach (coach_id), INDEX idx_philosophy_embedding USING ivfflat (philosophy_embedding vector_cosine_ops) ); -- Coach-manuscript matches CREATE TABLE coaches.matches ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), manuscript_id UUID REFERENCES analyzer.manuscripts(id), author_id UUID REFERENCES auth.users(id), coach_id UUID REFERENCES coaches.profiles(id), compatibility_score JSONB, match_reasons JSONB, success_prediction JSONB, status VARCHAR(50) DEFAULT 'proposed', created_at TIMESTAMP DEFAULT NOW(), INDEX idx_matches_manuscript (manuscript_id), INDEX idx_matches_author (author_id), INDEX idx_matches_coach (coach_id), INDEX idx_matches_score ((compatibility_score->>'overall')::numeric DESC) ); -- Match feedback and learning CREATE TABLE coaches.match_feedback ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), match_id UUID REFERENCES coaches.matches(id), author_id UUID REFERENCES auth.users(id), feedback_type VARCHAR(50), satisfaction_score INTEGER CHECK (satisfaction_score BETWEEN 1 AND 5), reasons JSONB, outcome JSONB, created_at TIMESTAMP DEFAULT NOW(), INDEX idx_feedback_match (match_id) ); -- Coach performance metrics CREATE TABLE coaches.performance_metrics ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), coach_id UUID REFERENCES coaches.profiles(id), period_start DATE, period_end DATE, projects_completed INTEGER, avg_improvement DECIMAL(3,2), client_satisfaction DECIMAL(3,2), on_time_delivery DECIMAL(3,2), genre_performance JSONB, success_patterns JSONB, UNIQUE(coach_id, period_start, period_end), INDEX idx_performance_coach (coach_id) ); -- Success prediction model data CREATE TABLE coaches.match_outcomes ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), match_id UUID REFERENCES coaches.matches(id), project_id UUID, before_scores JSONB, after_scores JSONB, improvement_metrics JSONB, client_feedback JSONB, coach_feedback JSONB, success BOOLEAN, completed_at TIMESTAMP, INDEX idx_outcomes_match (match_id), INDEX idx_outcomes_success (success) );

MVP Acceptance Criteria

  • AI-powered coach matching algorithm
  • Multi-factor compatibility scoring
  • Enhanced coach profiling
  • Success prediction model
  • Dynamic re-matching system
  • Performance tracking
  • API endpoints for matching
  • Feedback learning system
  • Alternative options generation

Post-MVP Enhancements

  • Real-time coach availability updates
  • Group coaching match optimization
  • Personality assessment integration
  • Video introduction matching
  • Trial session scheduling
  • Multi-coach team matching
  • Mentorship program matching
  • AI-powered chemistry prediction

Implementation Time

  • Matching Engine: 1.5 days
  • Coach Profiler: 1 day
  • Success Predictor: 1 day
  • API Integration: 0.5 days
  • Performance Tracking: 0.5 days
  • Testing: 1 day
  • Total: 5.5 days

Dependencies

  • F007 - Manuscript Analysis (for matching criteria)
  • F009 - Scoring System (for quality metrics)
  • F010 - Recommendations (for coaching focus areas)
  • Coach profiles and availability data

Next Feature

After completion, proceed to F012-ADVANCED-ANALYTICS for deep analytical insights.