SeoulMate is an AI-powered Korean drama recommendation system built to understand natural language searches, rank dramas intelligently, and personalize recommendations from user behavior.
The system combines semantic search, lexical search, calibrated ranking indexes, a cross-encoder reranker, and user preference learning to help users discover relevant K-dramas from queries such as romantic comedy, school bullying, contract marriage, hospital setting, or dramas like Crash Landing on You.
Latest stable backend reference:
| Metric | Score |
|---|---|
| Overall Accuracy | 88.50% |
| Precision@3 | 63.58% |
| Recall@10 | 98.46% |
| MRR | 0.963 |
The default backend uses curated ranking priors with calibrated generated fallback support. Generated-only replacement is still experimental and is not the production default.
- Natural language K-drama search
- Query intent detection for genre, actor, theme, mood, keyword, and similar-title searches
- Hybrid retrieval using FAISS semantic search and BM25Plus lexical search
- Fine-tuned SBERT embeddings for K-drama metadata
- Cross-encoder reranking for stronger final ordering
- Calibrated actor, genre, theme, and keyword ranking indexes
- Fuzzy title matching for typo-tolerant search
- User profiles with click, rating, and watchlist-based personalization
- Streamlit frontend for interactive search and profile exploration
- FastAPI backend with analytics and evaluation support
SeoulMate/
+-- backend/
| +-- app.py
| +-- analytics.py
| +-- personalization.py
| +-- query_analyzer.py
| +-- user_profile.py
| +-- ranking/
| | +-- generate_indexes.py
| | +-- config/
| | | +-- curated_priors.json
| | +-- indexes/
| +-- runtime_data/
+-- frontend/
| +-- streamlit_app.py
| +-- requirements.txt
| +-- TEST_FRONTEND.md
+-- training/
| +-- train_pipeline.py
| +-- steps/
| | +-- build_index.py
| | +-- enhanced_index_builder.py
| | +-- generate_training_data.py
| | +-- fine_tune_kdrama_sbert.py
| | +-- fine_tune_cross_encoder.py
| | +-- learning_to_rank.py
| +-- models/
| +-- faiss_index/
| +-- training_data/
+-- data/
| +-- final/
| +-- dramalist_kdramas.xlsx
+-- scrapers/
+-- tests/
| +-- evaluate_accuracy.py
| +-- compare_ranking_modes.py
| +-- validate_generated_indexes.py
| +-- debug_generated_query.py
| +-- reports/
+-- docs/
| +-- archived/
+-- scripts/
| +-- run_backend.ps1
| +-- run_frontend.ps1
| +-- run_accuracy.ps1
+-- CONTRIBUTING.md
+-- CHANGELOG.md
+-- LICENSE
+-- pyproject.toml
+-- README.md
+-- requirements.txt
| Folder | Purpose |
|---|---|
backend/ |
FastAPI recommendation API and runtime logic |
backend/ranking/ |
Curated priors, generated ranking indexes, and index generation |
frontend/ |
Streamlit user interface |
training/ |
Model training, FAISS index building, and training artifacts |
data/final/ |
Final dataset used by training and indexing scripts |
scrapers/ |
Data collection and scraping utilities |
tests/evaluation/ |
Accuracy evaluator and offline generated-index validation |
tests/ranking/ |
Ranking mode comparisons, prior experiments, weak-query reports, and audits |
tests/debug/ |
Single-query tracing and metadata inspection helpers |
tests/smoke/ |
Older API, personalization, filter, and flow checks |
tests/docs/ |
Test reports and historical improvement notes |
docs/ |
Project documentation and archived phase notes |
scripts/ |
Convenience scripts for common project commands |
requirements.txt |
Combined dependency list for full local setup |
pyproject.toml |
Project metadata and Python tooling configuration |
CONTRIBUTING.md |
Development workflow and contribution guidance |
LICENSE |
Project license |
Data scraping
|
v
Final drama dataset
|
v
Training data generation
|
v
SBERT fine-tuning and cross-encoder training
|
v
FAISS index and metadata build
|
v
Generated ranking index calibration
|
v
FastAPI recommendation backend
|
v
Streamlit frontend
|
v
User interactions, ratings, watchlist, and analytics
User query
|
v
Query analyzer
|-- intent detection
|-- genre, actor, theme, and keyword detection
|-- query expansion
|
v
Filtering layer
|-- genre
|-- actor
|-- keyword
|-- rating
|-- similar title
|
v
Hybrid retrieval
|-- FAISS semantic search
|-- BM25Plus lexical search
|
v
Ranking layer
|-- dynamic alpha
|-- curated priors
|-- calibrated generated fallbacks
|-- keyword expansion
|
v
Cross-encoder reranking
|
v
Personalization layer
|-- profile preferences
|-- clicks
|-- ratings
|-- watchlist actions
|
v
Final recommendations
| Area | Tools |
|---|---|
| Backend API | FastAPI, Uvicorn, Pydantic |
| Frontend | Streamlit, Requests, Pandas |
| Semantic Search | Sentence Transformers, FAISS |
| Lexical Search | BM25Plus |
| Reranking | CrossEncoder |
| Matching | RapidFuzz |
| Training | PyTorch, Sentence Transformers |
| Data Processing | Pandas, NumPy, OpenPyXL |
| Storage | Excel, Pickle, FAISS, JSON, JSONL |
Install core dependencies:
pip install -r requirements.txtOr install the main groups manually:
pip install fastapi uvicorn pydantic
pip install sentence-transformers faiss-cpu rank-bm25 rapidfuzz
pip install pandas numpy torch openpyxl
pip install streamlit requestsInstall frontend dependencies:
pip install -r frontend\requirements.txtInstall training dependencies:
pip install -r training\requirements.txtStart the backend:
.\scripts\run_backend.ps1Start the frontend in a second terminal:
.\scripts\run_frontend.ps1Manual backend command:
$env:SEOULMATE_RELOAD="0"
python backend\app.pyManual frontend command:
streamlit run frontend\streamlit_app.pyLocal URLs:
Backend: http://127.0.0.1:8001
Frontend: http://localhost:8501
Use these queries to quickly inspect the system:
romantic comedy
school bullying
contract marriage
hospital setting
time manipulation
thriller
Park Seo Joon
dramas like Crash Landing on You
Health check:
GET /
Analyze a query:
GET /analyze?query=romantic comedy
Get recommendations:
GET /recommend?title=romantic comedy&top_n=5
Common recommendation filters:
genre
director
publisher
rating_value
rating_count
keywords
screenwriters
sort_by
sort_order
similar_to
user_id
session_id
Analytics endpoints:
POST /analytics/interaction
GET /analytics/popular
GET /analytics/trending-searches
GET /analytics/summary
Personalization endpoints:
GET /profile/{user_id}
POST /profile/{user_id}/rate
DELETE /profile/{user_id}
The safest default is the curated system with calibrated generated fallback support.
Default-style configuration:
$env:SEOULMATE_GENRE_PRIOR_SOURCE="curated"
$env:SEOULMATE_ACTOR_PRIOR_SOURCE="curated"
$env:SEOULMATE_THEME_PRIOR_SOURCE="curated"Useful experimental configuration:
$env:SEOULMATE_GENRE_PRIOR_SOURCE="hybrid_calibrated"
$env:SEOULMATE_THEME_PRIOR_SOURCE="fallback_generated"Generated-only modes are useful for analysis, but they are not the recommended default because they currently reduce accuracy compared with the stable curated baseline.
Start the backend first, then run:
.\scripts\run_accuracy.ps1or:
python tests\evaluation\evaluate_accuracy.pyCompare ranking modes:
python tests\ranking\compare_ranking_modes.pyValidate generated indexes offline:
python tests\evaluation\validate_generated_indexes.pyDebug one query:
python tests\debug\debug_generated_query.py "school drama"Report output is organized under:
tests/reports/
+-- audits/
+-- debug/
+-- logs/
Run the full training pipeline:
cd training
python train_pipeline.py --mode fullRun the quick pipeline:
cd training
python train_pipeline.py --mode quickRebuild the FAISS index:
cd training
python training\steps\enhanced_index_builder.py --mode fullRegenerate backend ranking indexes:
python backend\ranking\generate_indexes.pyGenerated ranking indexes are written to:
backend/ranking/indexes/
Curated ranking config lives at:
backend/ranking/config/curated_priors.json
SeoulMate adapts per user through:
- searches
- clicks
- watchlist additions
- ratings
- learned profile preferences
This improves personalized recommendations for that user. The core SBERT model, FAISS index, curated priors, and generated indexes do not retrain automatically from user behavior. Logged behavior can later be used to recalibrate indexes, tune ranking, or train improved rerankers.
- Full generated replacement is not yet strong enough to fully replace curated priors.
- Keyword generated indexes are useful for explicit keyword fallback, but not ready for broad live ranking.
- Some theme queries still need calibration.
time manipulationcan still lean toward literal title matches.- Accuracy scripts require the backend to be running before live evaluation.
The project uses one Git repository at the root:
SeoulMate/.git
Runtime files are ignored through .gitignore, including:
backend/runtime_data/
analytics_data/
backend/analytics_data/
backend/user_profiles/
user_profiles/
__pycache__/
*.pyc
Important dated project history is tracked in:
CHANGELOG.md
The backend is stable and accuracy-tested. The current focus is improving generated ranking replacement quality over time while keeping the curated baseline reliable.
Made with ❤️ for K-drama lovers.