Complete AI Course Generation Workflow
This document describes the end-to-end execution flow when generating a complete course using the AI system.
High-Level Overview
Admin UI → API Gateway → Step Functions State Machine
│
┌───────────────┼───────────────┐
▼ ▼ ▼
Validate Generate Content Save & Store
│ │ │
└───────────────┴───────────────┘
▼
Course Ready (DRAFT)
Execution Order
The AI workflow consists of 22 sequential steps orchestrated by AWS Step Functions (EXPRESS type):
| Step | Handler | Status | Progress | Duration |
|---|---|---|---|---|
| 1 | validate-input | VALIDATING | 5% | ~5-30s |
| 2 | Choice: Check validation | - | - | instant |
| 3 | save-course (updateStatus) | GENERATING_OUTLINE | 10% | instant |
| 4 | generate-outline | GENERATING_OUTLINE | 30% | ~30-60s |
| 5 | save-course (updateStatus) | GENERATING_LESSONS | 40% | instant |
| 6 | generate-lesson-prompts | GENERATING_LESSONS | 40-70% | ~5-15s per day |
| 7 | Choice: Video flag | - | - | instant |
| 8 | save-course (updateStatus) | GENERATING_VIDEO | 70% | instant |
| 9 | trigger-video | GENERATING_VIDEO | 70% | ~10-20s |
| 10 | save-course (updateStatus) | SAVING | 90% | instant |
| 11 | save-course (saveCourse) | SAVING | 90% | ~5-15s |
| 12 | generate-thumbnail | SAVING | 90% | ~30-60s |
| 13 | Choice: Thumbnail success | - | - | instant |
| 14 | update-course-thumbnail | SAVING | 92% | instant |
| 15 | Choice: Video polling | - | - | instant |
| 16 | Wait (60s) | - | - | 60s |
| 17 | poll-video-status | SAVING | 95% | ~10-30s |
| 18 | Choice: Video status | - | - | instant |
| 19 | copy-video-to-s3 | SAVING | 98% | ~30-120s |
| 20 | update-course-video | SAVING | 100% | instant |
| 21 | Mark video failed (if error) | - | - | instant |
| 22 | Success | COMPLETED | 100% | - |
Detailed Step-by-Step Workflow
Phase 1: Entry & Validation
Step 1: API Entry Point
File: backend/functions/ai-generation/src/handlers/start-generation.ts
The workflow begins when an admin calls POST /ai-generation/start:
// Request body
{
courseInput: {
title: string;
category_id: string;
duration_days: 7 | 14 | 21;
targetAudience?: string;
difficultyLevel?: 'beginner' | 'intermediate' | 'advanced';
learningObjectives?: string[];
tone?: 'professional' | 'friendly' | 'academic';
},
options?: {
generateVideo?: boolean; // Trigger HeyGen video rendering
avatarId?: string; // HeyGen avatar ID
}
}
Actions:
- Validates request body
- Creates
course_generation_jobsrecord (status: PENDING) - Handles PDF upload if provided (S3 presigned URL or base64)
- Triggers Step Functions state machine
- Returns
jobIdfor status polling
Step 2: Validate Input
File: backend/functions/ai-generation/src/handlers/validate-input.ts
Lambda Config: 30s timeout, 256MB memory
Actions:
- Validates course input fields (title, category, duration, difficulty, tone)
- Estimates total tokens for entire workflow:
- Course outline: ~2000 tokens
- Lesson prompts: ~500 tokens × duration_days
- Video script: ~300 tokens
- Calculates estimated cost (USD)
- Updates job status to
VALIDATING(progress: 5%)
Output:
{
jobId: string;
isValid: boolean;
validationErrors: string[];
estimatedTokens: number;
estimatedCost: number;
}
Phase 2: Content Generation
Step 3: Generate Course Outline
File: backend/functions/ai-generation/src/handlers/generate-outline.ts
Lambda Config: 120s timeout, 512MB memory
Prompt Template: prompts/templates/course-outline.md
Bedrock Config:
- Model:
anthropic.claude-3-sonnet-20240229-v1:0 - Max tokens: 2000
- Temperature: 0.7
Actions:
- Loads prompt template with `` placeholders
- Interpolates with course input data
- If PDF reference provided, uses
invokeClaudeWithPdf() - Calls Bedrock Claude model
- Parses and validates JSON response
- Updates progress to 30%
Output (CourseOutline):
{
"title": "Refined course title",
"description": "300-500 word description",
"whatYouWillLearn": ["4-6 learning outcomes"],
"requirements": ["3-5 prerequisites"],
"instructor": {
"name": "Realistic instructor name",
"title": "Professional title",
"avatar": "JD"
},
"curriculum": [
{
"week": "Week 1: [Theme]",
"lessonsPreview": ["Day 1: ...", "Day 2: ...", "..."]
}
]
}
Step 4: Generate Lesson Prompts
File: backend/functions/ai-generation/src/handlers/generate-lesson-prompts.ts
Lambda Config: 120s timeout, 512MB memory
Prompt Template: prompts/templates/lesson-prompt.md
Actions (loops for each day):
- For each day from 1 to
duration_days:- Calculate week number:
Math.ceil(day / 7) - Calculate day-in-week:
((day - 1) % 7) + 1 -
Determine progress: beginning middle end
- Calculate week number:
- Build lesson prompt with course context
- Call Bedrock Claude for each lesson
- Parse into LessonPrompt structure
- Update progress:
40 + Math.round((day / totalDays) * 30)
Output per Lesson:
{
day: number;
title: string;
duration: number; // minutes (10-30)
generationPrompt: string; // Detailed prompt for content generation
actionItemsHint: string;
keyTakeawaysHint: string;
}
Phase 3: Video Generation
Step 5: Trigger Video Generation
File: backend/functions/ai-generation/src/handlers/trigger-video.ts
Lambda Config: 60s timeout, 256MB memory
Prompt Template: prompts/templates/video-script.md
Actions:
- Always: Generate intro video script via Bedrock
- Script length: ~1300-1500 words
- Duration: ~10-minute narration
- Structure: Hook → Objective → Concept → Deep Explanation → Example → Common Mistakes → Practice Prompt
- If
generateVideo: true: Trigger HeyGen API- Endpoint:
POST /v1/video_generators - Returns
video_idfor status polling
- Endpoint:
Output:
{
status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED' | 'SKIPPED';
script: string | null;
videoId: string | null; // HeyGen video_id
estimatedCompletionSeconds?: number;
}
Phase 4: Persistence
Step 6: Save Course to Database
File: backend/functions/ai-generation/src/handlers/save-course.ts
Lambda Config: 30s timeout, 256MB memory
Actions (within database transaction):
- Calculate quality score (0-1):
- Outline completeness: 60% weight
- Lessons completeness: 40% weight
- Create
coursesrecord:- Status: DRAFT
ai_generated: true- Store CourseOutline in
metadata(JSONB) - Store video script in
intro_video_script
- Create
lessonsrecords (one per day):- Store
generation_promptfor later content generation - Store hints in
metadata
- Store
- Link PDF references to course (if provided)
Database Tables Updated:
courses(1 record)lessons(N records, where N = duration_days)pdf_reference_documents(links to course)course_generation_jobs(status update)
Step 7: Generate Thumbnail
File: backend/functions/ai-generation/src/handlers/generate-thumbnail.ts
Lambda Config: 90s timeout, 512MB memory
Prompt Template: prompts/templates/thumbnail-prompt.md
Bedrock Config:
- Model:
stability.sd3-5-large-v1:0 - Region:
us-west-2(model availability) - Output: 512x512 PNG
Actions:
- Build image prompt from course metadata
- Call Bedrock Stable Diffusion
- Upload to S3:
thumbnails/{courseId}-{timestamp}.png - Generate presigned URL (24-hour expiration)
Phase 5: Video Processing (Optional)
Step 8: Poll Video Status
File: backend/functions/ai-generation/src/handlers/poll-video-status.ts
Lambda Config: 30s timeout, 256MB memory
Only executes if videoId exists from Step 5.
Polling Loop:
- Call HeyGen API:
GET /v1/video_generators/{videoId} - Check status:
processing→ Wait 30s, retry (max 20 attempts)completed→ Proceed to downloadfailed→ Mark as failed, continue workflow
Step 9: Copy Video to S3
File: backend/functions/ai-generation/src/handlers/copy-video-to-s3.ts
Lambda Config: 300s timeout, 1024MB memory
Actions:
- Download video from HeyGen URL
- Upload to S3:
course-videos/{courseId}/{videoId}.mp4 - Set lifecycle policies
Step 10: Update Course with Video
File: backend/functions/ai-generation/src/handlers/update-course-video.ts
Updates courses table:
intro_video_url: S3 URLintro_video_status: COMPLETED
Visual Flow Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│ ADMIN UI │
│ POST /ai-generation/start │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ start-generation.ts │
│ • Validate request • Create job record • Handle PDF • Trigger Step Func │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ STEP FUNCTIONS STATE MACHINE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────────┐ ┌─────────────────────────┐ │
│ │ VALIDATE │───▶│ GENERATE OUTLINE │───▶│ GENERATE LESSON PROMPTS│ │
│ │ INPUT │ │ │ │ (loop: N days) │ │
│ │ 5% │ │ 10-30% │ │ 40-70% │ │
│ └──────────────┘ └──────────────────┘ └─────────────────────────┘ │
│ │ │ │ │
│ │ Bedrock Claude Bedrock Claude │
│ │ │ (N API calls) │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ TRIGGER VIDEO GENERATION │ │
│ │ • Generate video script (Bedrock Claude) │ │
│ │ • Trigger HeyGen (if generateVideo: true) │ │
│ │ 70% │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ SAVE TO DATABASE │ │
│ │ • Create course record • Create lesson records (N) │ │
│ │ • Link PDFs • Calculate quality score │ │
│ │ 90% │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────┴────────────────┐ │
│ ▼ ▼ │
│ ┌──────────────────────────┐ ┌────────────────────────────────────┐ │
│ │ GENERATE THUMBNAIL │ │ VIDEO POLLING (optional) │ │
│ │ Bedrock Stable Diff │ │ • Wait 60s │ │
│ │ Upload to S3 │ │ • Poll HeyGen (max 20 attempts) │ │
│ └──────────────────────────┘ │ • Download & copy to S3 │ │
│ │ └────────────────────────────────────┘ │
│ │ │ │
│ └────────────────┬────────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ GENERATION COMPLETE │ │
│ │ Job status: COMPLETED (100%) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ DATABASE │
├─────────────────────────────────────────────────────────────────────────────┤
│ course_generation_jobs: Job tracking, progress, tokens, cost │
│ courses: Course record (DRAFT), metadata, video script │
│ lessons: N lesson records with generation_prompts │
│ pdf_reference_documents: Linked PDFs │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ S3 │
├─────────────────────────────────────────────────────────────────────────────┤
│ thumbnails/{courseId}-{timestamp}.png │
│ course-videos/{courseId}/{videoId}.mp4 (if video generated) │
└─────────────────────────────────────────────────────────────────────────────┘
What Gets Generated vs. What Needs Manual Completion
Generated Automatically
| Content | Storage | Status |
|---|---|---|
| Course title & description | courses.title, courses.description |
Ready |
| Learning outcomes | courses.metadata.whatYouWillLearn |
Ready |
| Requirements | courses.metadata.requirements |
Ready |
| Instructor info | courses.metadata.instructor |
Ready |
| Curriculum overview | courses.metadata.curriculum |
Ready |
| Lesson titles | lessons.title |
Ready |
| Lesson generation prompts | lessons.generation_prompt |
Ready |
| Intro video script | courses.intro_video_script |
Ready |
| Intro video (optional) | courses.intro_video_url |
Ready (if enabled) |
| Course thumbnail | courses.thumbnail |
Ready |
Requires Separate Generation (Post-Course Creation)
| Content | How to Generate |
|---|---|
| Full lesson content | Use lesson.generation_prompt with lesson-content.md template |
| Lesson video scripts | Generate via separate endpoint/workflow |
| Lesson videos | Trigger HeyGen for each lesson |
| Quiz questions | Use quiz generation prompts |
The initial workflow creates the course structure and generation prompts. Full lesson content is generated on-demand when users access lessons.
Timing Estimates
For a 7-Day Course
| Phase | Duration |
|---|---|
| Validation | ~10s |
| Course Outline | ~45s |
| Lesson Prompts (7×) | ~60s |
| Video Script | ~15s |
| Database Save | ~10s |
| Thumbnail | ~45s |
| Total (no video) | ~3 minutes |
| Video Polling + Copy | ~5-15 minutes |
| Total (with video) | ~8-18 minutes |
For a 21-Day Course
| Phase | Duration |
|---|---|
| Validation | ~15s |
| Course Outline | ~60s |
| Lesson Prompts (21×) | ~3 minutes |
| Video Script | ~15s |
| Database Save | ~15s |
| Thumbnail | ~45s |
| Total (no video) | ~5 minutes |
| Video Polling + Copy | ~5-15 minutes |
| Total (with video) | ~10-20 minutes |
Cost Estimation
Based on Claude 3 Sonnet pricing:
| Course Duration | Estimated Tokens | Estimated Cost |
|---|---|---|
| 7 days | ~5,000-7,000 | ~$0.15-0.25 |
| 14 days | ~8,000-12,000 | ~$0.25-0.40 |
| 21 days | ~12,000-18,000 | ~$0.35-0.55 |
Additional costs:
- Thumbnail (Stable Diffusion): ~$0.04 per image
- HeyGen video: Varies by plan (external service)
Error Handling
All errors are caught by Step Functions and handled by save-course.ts (action: handleError):
- Updates job status to
FAILED - Stores error message in
course_generation_jobs.error_message - Optionally sends SNS notification
- Workflow transitions to
GenerationFailedstate
Non-blocking failures (thumbnail, video) continue the workflow with partial results.
Monitoring
- CloudWatch Logs: Each Lambda has dedicated log group
- X-Ray Tracing: End-to-end request tracking
- Status API:
GET /ai-generation/status/{jobId}returns progress, tokens, cost
Key Files Reference
| File | Purpose |
|---|---|
handlers/start-generation.ts |
API entry, job creation |
handlers/validate-input.ts |
Validation, token estimation |
handlers/generate-outline.ts |
Course structure generation |
handlers/generate-lesson-prompts.ts |
Per-day lesson prompts |
handlers/trigger-video.ts |
Video script + HeyGen trigger |
handlers/save-course.ts |
Database transactions |
handlers/generate-thumbnail.ts |
Image generation |
handlers/poll-video-status.ts |
HeyGen polling |
handlers/copy-video-to-s3.ts |
Video download/upload |
prompts/templates/*.md |
All prompt templates |
prompts/index.ts |
PROMPT_REGISTRY |
services/bedrock-client.ts |
Claude model invocation |
services/heygen-client.ts |
HeyGen API integration |