Movie Finder Agent
July 11, 2025 ยท View on GitHub
ย ย ย ย
ย ย ย ย
Movie Finder Agent
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
API keys you'll need
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.
Domain Model
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
)
How It Works
- 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
Running
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.
Key Patterns
Spring Configuration
@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>
Advanced Workflow Control
@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