An intelligent movie recommendation agent that analyzes taste profiles and suggests streaming-available movies.
What It Teaches:
- 🏗️ Domain-Driven Design with rich domain models
- 🔄 Complex workflows with conditions and retries
- 📊 Spring Data integration with repositories
- 🎭 Persona-based prompting for creative content
- 🛠️ Multiple API integration (OMDB, streaming services)
- 📈 Progress tracking and event publishing
- 🤝 Human-in-the-loop confirmations
Besides your LLM keys such as as OPENAI_API_KEY
, you will need to set the following environment variables:
export OMDB_API_KEY=<your_omdb_key>
export X_RAPIDAPI_KEY=<your_rapidapi_key>
See Open Movie Database and Rapid API Hub for information about required integrations, and to obtain API keys.
data class MovieBuff(
override val name: String,
val movieRatings: List<MovieRating>,
val countryCode: String,
val streamingServices: List<String>
) : Person
data class DecoratedMovieBuff(
val movieBuff: MovieBuff,
val tasteProfile: String // AI-generated analysis
)
- Find MovieBuff from repository (with confirmation)
- Analyze their taste profile using AI
- Research current news for inspiration
- Generate movie suggestions (excluding seen movies)
- Filter by streaming availability
- Create Roger Ebert-style writeup
First, start the PostgresSQL database using Docker with:
docker compose up
Start the application under your IDE or by navigating to the scripts
directory and typing ./run.sh
.
You will find the app at http://localhost:2001. Thanks to Stanley Kubrick.
The logging will channel Severance.
@ConfigurationProperties(prefix = "embabel.examples.moviefinder")
data class MovieFinderConfig(
val suggestionCount: Int = 5,
val suggesterPersona: Persona = Roger,
val model: String = OpenAiModels.GPT_41_MINI
)
interface MovieBuffRepository : CrudRepository<MovieBuff, String>
@Action(
post = [HAVE_ENOUGH_MOVIES], // Condition check
canRerun = true // Retry if needed
)
fun suggestMovies(/* params */): StreamableMovies
@Condition(name = HAVE_ENOUGH_MOVIES)
fun haveEnoughMovies(context: OperationContext): Boolean