F010 - AI-Powered Recommendations Engine
Objective
Build an intelligent recommendations system that provides personalized, actionable improvement suggestions for manuscripts based on AI analysis, genre conventions, market trends, and individual author goals within the MyStoryFlow platform.
Quick Implementation
Using MyStoryFlow Components
- Personalization engine from
@mystoryflow/personalization - User preferences from
@mystoryflow/user-data - Analytics tracking from
@mystoryflow/analytics - Notification system from
@mystoryflow/notifications
New Requirements
- Recommendation generation algorithms
- Priority ranking system
- Implementation tracking
- Success measurement
- Adaptive learning system
MVP Implementation
1. Recommendation Engine Core
// packages/manuscript-analysis/src/recommendations/recommendation-engine.ts
import { AIService } from '../services/ai-service'
import { Logger } from '@mystoryflow/logger'
import { PersonalizationService } from '@mystoryflow/personalization'
import { UserPreferencesService } from '@mystoryflow/user-data'
export class RecommendationEngine {
private aiService: AIService
private personalization: PersonalizationService
private userPreferences: UserPreferencesService
private logger: Logger
constructor() {
this.aiService = new AIService()
this.personalization = new PersonalizationService()
this.userPreferences = new UserPreferencesService()
this.logger = new Logger('RecommendationEngine')
}
async generateRecommendations(params: {
manuscriptId: string
userId: string
analysis: ComprehensiveAnalysis
scores: ManuscriptScore
userGoals: AuthorGoals
}): Promise<PersonalizedRecommendations> {
const { analysis, scores, userGoals } = params
// Get user preferences and history
const userProfile = await this.getUserProfile(params.userId)
// Generate base recommendations
const baseRecommendations = await this.generateBaseRecommendations(
analysis,
scores
)
// Personalize based on author goals
const personalizedRecs = await this.personalizeRecommendations(
baseRecommendations,
userGoals,
userProfile
)
// Prioritize recommendations
const prioritizedRecs = this.prioritizeRecommendations(
personalizedRecs,
scores,
userGoals
)
// Generate implementation plans
const implementationPlans = await this.createImplementationPlans(
prioritizedRecs,
analysis.metadata
)
return {
recommendations: prioritizedRecs,
implementationPlans,
estimatedImpact: this.calculateEstimatedImpact(prioritizedRecs, scores),
timeline: this.generateTimeline(implementationPlans),
trackingMetrics: this.defineTrackingMetrics(prioritizedRecs)
}
}
private async generateBaseRecommendations(
analysis: ComprehensiveAnalysis,
scores: ManuscriptScore
): Promise<BaseRecommendation[]> {
const recommendations: BaseRecommendation[] = []
// Character development recommendations
if (scores.dimensions.characterDevelopment.score < 0.8) {
const charRecs = await this.generateCharacterRecommendations(
analysis.overall.analysis.character,
analysis.chapters
)
recommendations.push(...charRecs)
}
// Plot structure recommendations
if (scores.dimensions.plotStructure.score < 0.8) {
const plotRecs = await this.generatePlotRecommendations(
analysis.overall.analysis.plot,
analysis.specialized.plotAnalysis
)
recommendations.push(...plotRecs)
}
// Writing style recommendations
if (scores.dimensions.writingStyle.score < 0.85) {
const styleRecs = await this.generateStyleRecommendations(
analysis.overall.analysis.style,
analysis.metadata.genre
)
recommendations.push(...styleRecs)
}
// Dialogue recommendations
if (analysis.specialized.dialogueAnalysis &&
scores.dimensions.dialogue.score < 0.8) {
const dialogueRecs = await this.generateDialogueRecommendations(
analysis.specialized.dialogueAnalysis
)
recommendations.push(...dialogueRecs)
}
// Pacing recommendations
if (scores.dimensions.pacing.score < 0.75) {
const pacingRecs = await this.generatePacingRecommendations(
analysis.overall.analysis.pacing,
analysis.chapters
)
recommendations.push(...pacingRecs)
}
return recommendations
}
private async generateCharacterRecommendations(
characterAnalysis: any,
chapters: ChapterAnalysis[]
): Promise<BaseRecommendation[]> {
const prompt = `
Based on this character analysis, generate specific, actionable recommendations:
${JSON.stringify(characterAnalysis)}
Focus on:
1. Deepening character motivations
2. Strengthening character arcs
3. Improving character distinctiveness
4. Enhancing emotional depth
5. Fixing consistency issues
Return as JSON array with structure:
{
type: "character",
title: "Brief title",
description: "Detailed description",
impact: "high|medium|low",
effort: "high|medium|low",
specificActions: ["action1", "action2"],
examples: ["example from text"],
location: "chapter/page references"
}
`
const aiRecommendations = await this.aiService.analyzeManuscript(
prompt,
'recommendations',
{ outputFormat: 'json' }
)
return aiRecommendations.recommendations.map(rec => ({
...rec,
category: 'character-development',
generatedAt: new Date()
}))
}
private async personalizeRecommendations(
recommendations: BaseRecommendation[],
userGoals: AuthorGoals,
userProfile: UserProfile
): Promise<PersonalizedRecommendation[]> {
const personalized = await Promise.all(
recommendations.map(async rec => {
// Calculate relevance to user goals
const goalRelevance = this.calculateGoalRelevance(rec, userGoals)
// Adjust based on user preferences
const preferenceScore = await this.personalization.scoreRecommendation(
rec,
userProfile
)
// Check if similar recommendations were successful before
const historicalSuccess = await this.checkHistoricalSuccess(
rec.type,
userProfile.id
)
return {
...rec,
relevanceScore: goalRelevance * preferenceScore,
personalizedReason: this.explainPersonalization(
rec,
userGoals,
preferenceScore
),
historicalContext: historicalSuccess,
alternativeApproaches: await this.generateAlternatives(rec, userProfile)
}
})
)
return personalized.filter(rec => rec.relevanceScore > 0.5)
}
private prioritizeRecommendations(
recommendations: PersonalizedRecommendation[],
scores: ManuscriptScore,
goals: AuthorGoals
): PrioritizedRecommendation[] {
return recommendations
.map(rec => {
const impactScore = this.calculateImpactScore(rec, scores)
const effortScore = this.calculateEffortScore(rec)
const urgencyScore = this.calculateUrgencyScore(rec, goals)
const priorityScore = (impactScore * 0.4) +
(urgencyScore * 0.3) +
((1 - effortScore) * 0.3)
return {
...rec,
priority: this.getPriorityLevel(priorityScore),
priorityScore,
impactMetrics: {
expectedScoreImprovement: impactScore * 0.1,
affectedDimensions: this.getAffectedDimensions(rec),
cascadeEffects: this.identifyCascadeEffects(rec)
},
sequencing: {
dependencies: this.identifyDependencies(rec, recommendations),
optimalOrder: null // Set later
}
}
})
.sort((a, b) => b.priorityScore - a.priorityScore)
}
}2. Implementation Plan Generator
// packages/manuscript-analysis/src/recommendations/implementation-planner.ts
export class ImplementationPlanner {
async createImplementationPlans(
recommendations: PrioritizedRecommendation[],
metadata: ManuscriptMetadata
): Promise<ImplementationPlan[]> {
// Group related recommendations
const grouped = this.groupRelatedRecommendations(recommendations)
// Create phased implementation plan
const phases = this.createPhases(grouped, metadata)
// Generate detailed plans for each phase
return Promise.all(
phases.map(async (phase, index) => ({
phase: index + 1,
name: phase.name,
duration: phase.estimatedDuration,
recommendations: await this.detailRecommendations(phase.recommendations),
milestones: this.defineMilestones(phase),
successCriteria: this.defineSuccessCriteria(phase),
resources: await this.identifyResources(phase)
}))
)
}
private async detailRecommendations(
recommendations: PrioritizedRecommendation[]
): Promise<DetailedRecommendation[]> {
return Promise.all(
recommendations.map(async rec => ({
...rec,
steps: await this.breakDownIntoSteps(rec),
techniques: await this.suggestTechniques(rec),
examples: await this.findRelevantExamples(rec),
exercises: await this.createPracticeExercises(rec),
checkpoints: this.defineCheckpoints(rec)
}))
)
}
private async breakDownIntoSteps(
recommendation: PrioritizedRecommendation
): Promise<ImplementationStep[]> {
const steps: ImplementationStep[] = []
switch (recommendation.type) {
case 'character':
if (recommendation.title.includes('motivation')) {
steps.push(
{
order: 1,
title: 'Character Backstory Development',
description: 'Create detailed backstory that explains current motivations',
deliverable: 'Character profile document with backstory',
estimatedTime: '2-3 hours',
tools: ['Character worksheet template', 'Backstory generator']
},
{
order: 2,
title: 'Motivation Mapping',
description: 'Map character desires to plot events',
deliverable: 'Motivation-plot alignment chart',
estimatedTime: '1-2 hours',
tools: ['Plot-character matrix template']
},
{
order: 3,
title: 'Scene Revision',
description: 'Revise key scenes to reflect deeper motivations',
deliverable: 'Revised scenes with motivation highlights',
estimatedTime: '4-6 hours',
tools: ['Scene revision checklist']
}
)
}
break
case 'plot':
if (recommendation.title.includes('pacing')) {
steps.push(
{
order: 1,
title: 'Pacing Analysis',
description: 'Chart current pacing across chapters',
deliverable: 'Pacing chart with problem areas marked',
estimatedTime: '1-2 hours',
tools: ['Pacing analysis template']
},
{
order: 2,
title: 'Scene Restructuring',
description: 'Identify scenes to cut, combine, or expand',
deliverable: 'Revised scene outline',
estimatedTime: '2-3 hours',
tools: ['Scene structure worksheet']
}
)
}
break
}
return steps
}
private async createPracticeExercises(
recommendation: PrioritizedRecommendation
): Promise<PracticeExercise[]> {
const exercises: PracticeExercise[] = []
if (recommendation.category === 'character-development') {
exercises.push({
title: 'Character Voice Exercise',
description: 'Write a diary entry from your character\'s perspective',
purpose: 'Develop distinct character voice',
duration: '30 minutes',
evaluation: 'Self-assess for voice consistency and authenticity'
})
}
if (recommendation.category === 'dialogue') {
exercises.push({
title: 'Dialogue Rhythm Practice',
description: 'Rewrite a dialogue scene with varied sentence lengths',
purpose: 'Improve dialogue flow and naturalism',
duration: '45 minutes',
evaluation: 'Read aloud to check rhythm'
})
}
return exercises
}
}3. Adaptive Learning System
// packages/manuscript-analysis/src/recommendations/adaptive-learning.ts
export class AdaptiveLearningSystem {
async learnFromFeedback(params: {
recommendationId: string
userId: string
feedback: RecommendationFeedback
outcomes: RecommendationOutcomes
}): Promise<void> {
const { recommendationId, userId, feedback, outcomes } = params
// Store feedback
await this.storeFeedback(recommendationId, userId, feedback)
// Update recommendation effectiveness scores
await this.updateEffectivenessScores(recommendationId, outcomes)
// Adjust future recommendation weights
await this.adjustRecommendationWeights(
feedback.type,
feedback.wasHelpful,
userId
)
// Update personalization model
await this.updatePersonalizationModel(userId, {
preferredRecommendationTypes: feedback.preferredTypes,
effectiveApproaches: outcomes.successfulApproaches,
timePreferences: feedback.timeSpent
})
}
async trackImplementationProgress(params: {
userId: string
recommendationId: string
progress: ImplementationProgress
}): Promise<ProgressInsights> {
const { userId, recommendationId, progress } = params
// Update progress tracking
await this.updateProgress(recommendationId, progress)
// Analyze implementation patterns
const patterns = await this.analyzeImplementationPatterns(userId)
// Generate insights
const insights = {
completionRate: patterns.avgCompletionRate,
effectiveTimeSlots: patterns.mostProductiveTimes,
preferredPace: patterns.implementationPace,
commonBlockers: patterns.frequentObstacles,
successFactors: patterns.successCorrelations
}
// Adapt future recommendations based on insights
await this.adaptRecommendations(userId, insights)
return insights
}
private async adaptRecommendations(
userId: string,
insights: ProgressInsights
): Promise<void> {
// Adjust recommendation timing
if (insights.effectiveTimeSlots.length > 0) {
await this.updateUserPreference(userId, {
preferredImplementationTimes: insights.effectiveTimeSlots
})
}
// Adjust recommendation complexity
if (insights.completionRate < 0.5) {
await this.updateUserPreference(userId, {
recommendationComplexity: 'simpler',
stepGranularity: 'more-detailed'
})
}
// Adjust recommendation types based on success
const successfulTypes = Object.entries(insights.successFactors)
.filter(([_, rate]) => rate > 0.7)
.map(([type]) => type)
await this.updateUserPreference(userId, {
prioritizeTypes: successfulTypes
})
}
}4. Recommendation Tracking API
// apps/story-analyzer/src/app/api/recommendations/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { RecommendationEngine } from '@mystoryflow/manuscript-analysis'
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, analysisId, userGoals } = await req.json()
// Get analysis and scores
const analysis = await getAnalysis(analysisId)
const scores = await getScores(manuscriptId)
// Generate personalized recommendations
const engine = new RecommendationEngine()
const recommendations = await engine.generateRecommendations({
manuscriptId,
userId: session.user.id,
analysis,
scores,
userGoals
})
// Track recommendation generation
await trackEvent({
userId: session.user.id,
event: 'recommendations_generated',
properties: {
manuscriptId,
recommendationCount: recommendations.recommendations.length,
topPriority: recommendations.recommendations[0]?.type
}
})
// Store recommendations
await storeRecommendations(manuscriptId, recommendations)
return NextResponse.json({
recommendations: recommendations.recommendations,
implementationPlan: recommendations.implementationPlans[0],
estimatedImpact: recommendations.estimatedImpact,
trackingId: recommendations.trackingMetrics.id
})
} catch (error) {
console.error('Recommendation generation failed:', error)
return NextResponse.json(
{ error: 'Failed to generate recommendations' },
{ status: 500 }
)
}
}
// Track implementation progress
export async function PATCH(req: NextRequest) {
try {
const session = await withAuth(req)
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { recommendationId, progress, feedback } = await req.json()
const adaptiveSystem = new AdaptiveLearningSystem()
// Track progress
const insights = await adaptiveSystem.trackImplementationProgress({
userId: session.user.id,
recommendationId,
progress
})
// Process feedback if provided
if (feedback) {
await adaptiveSystem.learnFromFeedback({
recommendationId,
userId: session.user.id,
feedback,
outcomes: progress.outcomes
})
}
return NextResponse.json({
insights,
nextSteps: await generateNextSteps(recommendationId, progress)
})
} catch (error) {
console.error('Progress tracking failed:', error)
return NextResponse.json(
{ error: 'Failed to track progress' },
{ status: 500 }
)
}
}5. Coach Integration for Recommendations
// packages/manuscript-analysis/src/recommendations/coach-integration.ts
export class CoachRecommendationIntegration {
async enhanceWithCoachInsights(
recommendations: PersonalizedRecommendation[],
manuscriptId: string
): Promise<EnhancedRecommendation[]> {
// Find coaches who specialize in the recommendation areas
const relevantCoaches = await this.findRelevantCoaches(recommendations)
return Promise.all(
recommendations.map(async rec => {
const coachInsights = await this.getCoachInsights(
rec.type,
relevantCoaches
)
return {
...rec,
coachSupport: {
available: coachInsights.length > 0,
coaches: coachInsights.map(insight => ({
coachId: insight.coachId,
name: insight.coachName,
expertise: insight.relevantExpertise,
sessionType: this.suggestSessionType(rec, insight),
estimatedSessions: this.estimateSessionCount(rec.effort)
})),
groupSessions: await this.findGroupSessions(rec.type),
resources: await this.getCoachResources(rec.type)
},
hybridApproach: this.createHybridPlan(rec, coachInsights)
}
})
)
}
private createHybridPlan(
recommendation: PersonalizedRecommendation,
coachInsights: CoachInsight[]
): HybridImplementationPlan {
return {
selfGuidedSteps: recommendation.specificActions.filter(
action => this.canBeSelfGuided(action)
),
coachGuidedSteps: recommendation.specificActions.filter(
action => this.benefitsFromCoaching(action)
),
checkpoints: [
{
type: 'self-assessment',
timing: 'after-self-guided',
tools: ['Progress tracker', 'Self-evaluation rubric']
},
{
type: 'coach-review',
timing: 'mid-implementation',
focus: 'Technical execution and refinement'
},
{
type: 'peer-review',
timing: 'before-final',
platform: 'MyStoryFlow community'
}
],
flexibilityOptions: {
canStartWithCoach: recommendation.effort === 'high',
canSkipCoaching: recommendation.impact === 'low',
hybridSessionTypes: ['Q&A', 'Review', 'Workshop']
}
}
}
}Database Schema
-- Personalized recommendations
CREATE TABLE analyzer.recommendations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
manuscript_id UUID REFERENCES analyzer.manuscripts(id),
user_id UUID REFERENCES auth.users(id),
analysis_id UUID REFERENCES analyzer.analysis_results(id),
type VARCHAR(50),
category VARCHAR(50),
title TEXT,
description TEXT,
impact VARCHAR(20),
effort VARCHAR(20),
priority VARCHAR(20),
priority_score DECIMAL(3,2),
specific_actions JSONB,
implementation_plan JSONB,
personalization_data JSONB,
created_at TIMESTAMP DEFAULT NOW(),
INDEX idx_recommendations_manuscript (manuscript_id),
INDEX idx_recommendations_user (user_id),
INDEX idx_recommendations_priority (priority_score DESC)
);
-- Implementation tracking
CREATE TABLE analyzer.recommendation_progress (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
recommendation_id UUID REFERENCES analyzer.recommendations(id),
user_id UUID REFERENCES auth.users(id),
status VARCHAR(50) DEFAULT 'not_started',
progress_percentage INTEGER DEFAULT 0,
started_at TIMESTAMP,
completed_at TIMESTAMP,
time_spent_minutes INTEGER,
steps_completed JSONB,
notes TEXT,
outcome_metrics JSONB,
INDEX idx_progress_recommendation (recommendation_id),
INDEX idx_progress_user (user_id)
);
-- Recommendation feedback
CREATE TABLE analyzer.recommendation_feedback (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
recommendation_id UUID REFERENCES analyzer.recommendations(id),
user_id UUID REFERENCES auth.users(id),
was_helpful BOOLEAN,
clarity_rating INTEGER CHECK (clarity_rating BETWEEN 1 AND 5),
relevance_rating INTEGER CHECK (relevance_rating BETWEEN 1 AND 5),
difficulty_rating INTEGER CHECK (difficulty_rating BETWEEN 1 AND 5),
preferred_types TEXT[],
feedback_text TEXT,
created_at TIMESTAMP DEFAULT NOW(),
INDEX idx_feedback_recommendation (recommendation_id)
);
-- Learning system data
CREATE TABLE analyzer.adaptive_learning (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES auth.users(id),
recommendation_type VARCHAR(50),
success_rate DECIMAL(3,2),
avg_completion_time INTEGER,
preferred_complexity VARCHAR(20),
effective_approaches JSONB,
last_updated TIMESTAMP DEFAULT NOW(),
UNIQUE(user_id, recommendation_type),
INDEX idx_learning_user (user_id)
);MVP Acceptance Criteria
- AI-powered recommendation generation
- Personalization based on user goals
- Priority ranking system
- Detailed implementation plans
- Progress tracking
- Adaptive learning from feedback
- Coach integration options
- Success metrics
- API endpoints
Post-MVP Enhancements
- Community recommendation sharing
- Video tutorial integration
- Real-time recommendation updates
- A/B testing recommendation approaches
- Gamification elements
- Peer accountability features
- Writing exercise generator
- Style mimicry suggestions
Implementation Time
- Recommendation Engine: 1.5 days
- Implementation Planner: 1 day
- Adaptive Learning: 1 day
- API Integration: 0.5 days
- Coach Integration: 0.5 days
- Testing: 1 day
- Total: 5.5 days
Dependencies
- F007 - Manuscript Analysis (for analysis data)
- F009 - Scoring System (for priority calculation)
- F011 - Coach Matching (for coach integration)
Next Feature
After completion, proceed to F011-COACH-MATCHING for AI-powered coach pairing.