Skip to main content

TV360 Recommendation & Search

Recommendation and search for live TV, films and video on demand in Viettel’s consumer streaming app.

  • Technical Lead
  • Recommender Systems
  • Real-time Serving
  • 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.

TV360: offline and online pipelines to serving, recommendation and searchOffline · batchOnline · streamingBatch jobsSpark · Hadoop · HiveCandidates & embeddingstwo-tower modelcontent-based embeddingsNew titlescold start: candidatesfrom content embeddingsUser eventsviews and sessionsKafka · Flinkstream processingReal-time featuresnew users: session signalsfeature store in RedisRediscandidates · real-time features · serving cacheRecommendationsSearchJava / Spring serving12,000 TPS · app requests99.6% under 100 msOnline rankingcandidates from Redisranked on real-time featuresRules & popularitycomplement the ranking8 recommendation blocks5M+ activated usersLLM query understandingto structured searchElasticsearchBM25 full-textkNN vector searchPersonalized re-rank3 search blockstop search, autocomplete,resultsOnline A/B testingengagement 3–6x (recommendations) · CTR 2–4x (search)versus the default baseline
Architecture. Offline batch and online streaming both feed Redis, which the Java and Spring serving layer reads. Search runs separately on Elasticsearch.

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