TV360 Recommendation & Search
Recommendation and search for live TV, films and video on demand in Viettel’s consumer streaming app.
5M+
activated users served by the recommendation blocks
12,000
transactions per second on the real-time serving layer
99.6%
of requests answered in under 100 ms
3–6x
higher engagement than the default baseline in online A/B tests
A streaming app built on recommendations
TV360 is Viettel’s consumer streaming app, covering live TV, films and video on demand. The app surfaces eight personalized recommendation blocks and three search blocks.
I joined the Viettel Big Data Analytics Center as an AI engineer (2021–2024), working on recommendation and search, and moved into a technical lead role. In that role I owned the architecture for both systems and led a team of two to three AI engineers.
Offline meets online
Recommendation is a hybrid of batch and streaming. Spark, Hadoop and Hive jobs run offline to compute candidates and embeddings, and Redis holds the results. Scoring those candidates needs fresh data, so online ranking runs at request time and uses real-time features built from user events.
The split keeps heavy model work out of the request path, while the final ranking still reflects what a user has done most recently. The candidate models are a two-tower deep learning model and content-based embeddings built from title and metadata. Rules and popularity complement the models.
Cold start for new titles and new users
Cold start gets its own path, because new titles and new users have no viewing history. New titles get candidates straight away from content embeddings, so they can appear before anyone has watched them.
New users are handled by real-time session signals. Their first views stream through Kafka and Flink, and their features update from those views, so recommendations start to adapt after their first views.
A Java and Spring serving layer
The serving layer is written in Java on Spring. For each request it reads candidates from Redis, ranks them with real-time features, applies rules and popularity, and returns the blocks to the app. It handles 12,000 transactions per second, and 99.6% of requests finish in under 100 ms.
ETL and data-sync pipelines feed the real-time features, and every model release goes through an MLOps pipeline. The platform underneath processes billions of records per day.
Search with BM25, vectors and an LLM
Search runs on Elasticsearch, which combines BM25 keyword scoring with kNN dense-vector queries. That keeps vector search in the same engine, with no separate vector database. An LLM turns natural-language queries into structured search, and a personalized re-ranker orders the results for each user.
Search serves three blocks: top search, autocomplete and results. I introduced RAG and LLM components into search, and in online A/B tests search click-through rate rose 2–4x over baseline.
Outcome
The personalization stack serves 5M+ activated users. It answers 99.6% of requests in under 100 ms at 12,000 transactions per second. In online A/B tests against the default baseline, recommendations reached 3–6x higher engagement and search reached 2–4x higher click-through.
My role
I designed the overall architecture for recommendation and search, built the real-time serving layer, and led the team of two to three AI engineers that built the models on it, mentoring the junior engineers along the way.
Tech stack
- Java
- Spring
- Spark
- Hadoop
- Hive
- Kafka
- Flink
- Redis
- Elasticsearch
- two-tower model
- content embeddings
- LLM query understanding
- A/B testing