AI Case · Home-screen personalisation

Re-scoping an AI brief · Proposal · Not deployed

Personalised Home Screen, Flat LLM Cost

ProjectHome screen of a used-car marketplace in Singapore. A stand-in: the original client work is confidential.
The askAn LLM-driven, hyper-personalised home screen for every user, from launch.
LimitsA model bill that grows with every app open, the cost of ML engineers, and a fixed timeline.
RedesignThree stages, each producing the data the next needs: rules and offline LLM tagging at launch, learned ranking only when traffic and headcount justify it.
ResultLLM cost stays flat at about $11 a month at 1,000 or 20,000 daily users, against up to $15,300 for an LLM on every request (estimates).
REQUEST PATHNo model callSTAGE 1RulesSTAGE 2Per-userweightsSTAGE 3LearnedrankingOFFLINE · ALL STAGESBatch tagging · rule drafts · HITL gate
Read the case
0model calls per request
$11LLM a month, est.
$15,300LLM per request, 20k users, est.
3stages, each feeding the next

The setting is a stand-in. The original is confidential client work. The domain, values and costs here belong to the stand-in.

Summary

An ambitious AI brief, re-scoped to what the budget, the team and the timeline could carry.

The result: LLM cost stays flat at about $11 a month at 1,000 or 20,000 daily users, against $765 and $15,300 for an LLM on every request. The home screen never waits on a model, and every ranking can be replayed exactly.

Spend and hiring follow the stages. Stage 2 gives the largest jump in personalisation for $6 more a month. The ML engineer a learned ranker needs waits until traffic justifies it.

How: the LLM runs only as offline batch jobs, tagging listings and drafting rules behind a human-in-the-loop approval gate. Costs are estimates. The design is not deployed, so there are no live metrics.

Fig. 01

Keep Expensive Intelligence Off the Request Path.

Personalisation architectureThree lanes. Offline, user profile data and historical events go through feature engineering into a feature store in Postgres; optional offline AI enriches features, drafts rules and trains models, and a model and rule registry with an approval gate deploys on a schedule. Live app events and interactions go through an event stream into real-time features and an online store in Redis. At request time, context is normalised, eligibility rules filter first, scoring ranks what is left, and the personalisation API returns home modules. Every decision is logged to S3 and feeds the next nightly run. Expensive intelligence stays off the request path. 01 · Offline and periodic preparation Build and refresh user features, segments and model artefacts — batch, scheduled User profilemembers, attributes, consent Historical eventsapp usage, transactions, logs Feature engineeringETL and aggregationfeature generationcandidate generation Feature storePostgresuser and segmentfeatures, offline Optional · offline AI Feature enrichmentinterest summaries Rule draftscandidate rules, insights Model trainingranking, segmentation Models and rules Model registryversions, validation Rule managementrules, eligibility, weights Approval gatevalidator, replay, people 02 · Real-time signal ingestion App eventsclicks, views, searches Recent interactionssession, in-app behaviour Event streamRedis Streamsarchived to S3 Real-time featuresstream processingsession, increments Online storeRedis, low latencyfeatures, session,candidates 03 · Real-time context and decisioning · no model call External contexttime and day Device and locationdevice type, region Availabilityinventory, campaigns, status Context adapternormalise, mapdefaults Eligibility and rulesfilter first:availability, policy Scoring and rankingrules, thenlearned weights Personalisation APItop-N moduleswith reasons Home modulesranked,personalised UI Deployed model and policies reads deploy scheduled Decision logS3 JSON Lines · inputs, versions, scores, output — replayable nightly · feeds historical events and training Personalisation architectureThree lanes. Offline, user profile data and historical events go through feature engineering into a feature store in Postgres; optional offline AI enriches features, drafts rules and trains models, and a model and rule registry with an approval gate deploys on a schedule. Live app events and interactions go through an event stream into real-time features and an online store in Redis. At request time, context is normalised, eligibility rules filter first, scoring ranks what is left, and the personalisation API returns home modules. Every decision is logged to S3 and feeds the next nightly run. Expensive intelligence stays off the request path. 01 · Offline preparation User profileconsent Historicalevents, logs Feature engineeringETL, aggregation, featurescandidate generation Feature storePostgres · user and segment features Optional · offline AI Feature enrichment summaries Rule drafts candidate rules Model training ranking Models and rules Model registry versions Rule management weights Approval gate validator, people Deploy, scheduled → 03 · Nightly log → 01 02 · Real-time signals App eventsclicks, searches Interactionssession Event streamRedis Streams, archived to S3 Real-time featuressession, incremental aggregates Online storeRedis · features, session, candidates 03 · Request · no model call Time, day Region Availability 01 Context adapter normalise, defaults 02 Eligibility, rules filter first 03 Scoring, ranking reads 02 + weights 04 Personalisation API top-N, with reasons 05 Home modules ranked, personalised Decision logS3 · inputs, versions, scores — replayable

Heavy intelligence is computed offline; personalisation is served fast and reliably in real time. Live behaviour takes two paths: the online store for this visit, and the log, which the nightly run turns into features. The request reads stored data, filters before it scores, and logs every decision so it can be replayed.

What each store holds

DataFormatTransformStore
User profileMember attributes and consent flags; trade-in results as budget band, body type and COE-expiry bucketConsent checked before any signal is used; the plate is dropped once the lookup returnsPostgres profiles
ListingsOne row per car: make, model, variant, registration year, COE category and months left, depreciation, price, statusDerive segment_l1_id (make · model · variant · year band · COE band) and the coarser segment_l2_idPostgres listings
LLM tagsJSON array from a fixed dictionary, e.g. cond:accident_free_claimedSmall model tags free text; a script rejects anything outside the dictionaryPostgres listing_tags, with model version
Behaviour eventsJSON: hashed user key, event type, listing and segment ids, filters, region, daypartSegment ids attached at ingest; search text reduced to filter valuesRedis Streams for hours, then S3 JSON Lines by day, 12 months
SessionLast 50 listing ids per userPushed on every view, trimmed to 50Redis list
Segment signalsPer segment and per user: counts, decayed scores, filter sensitivity (sens_depreciation)Nightly SQL over the day’s logsPostgres feature tables; Stage 3 mirrors them to Redis for serving
CandidatesTop listing ids per segment, with score componentsNightly candidate generation under the approved policyRedis sorted set per segment, fallback per coarser segment
Response snapshotJSON: request id, policy version, fallback_level, scored and shown listings, dropped listings with reasonWritten with every responseRedis Streams, then S3 JSON Lines, 12 months
EmbeddingsVectors for listings and users — Stage 3 onlyWeekly training jobpgvector in the same Postgres
Models and rulesVersioned artefacts with validation metadata; policies as versioned JSONDeployed on a schedule, only after the approval gateS3 for model files, Postgres for versions and status

Engines and schemas are design assumptions, sized to the costed set-up: one small Postgres database and one small Redis store, plus S3 for twelve months of logs. No new service is added at Stage 3.

The First Draft Oversold Itself

An LLM on every request would have fixed the name, not the design.

The brief asked for LLM-driven personalisation. My first draft was a nightly batch re-ranked by one set of hand-written rules — the same for every user, with nothing for new users.

A model call per request would grow cost with every app open, make the home screen wait, and rank the same request differently twice. So the LLM stayed, off the request path.

The Setting

A used-car marketplace in Singapore, where every listing is one car and it sells.

  1. Signals must outlive the listing.Each car sells once, so listing-level collaborative filtering never warms up.
  2. Users come back often.Browsing, shortlists, saved searches and price alerts are the cheapest personalisation input.
  3. Trade-in quotes hint at budget.They also carry identity data that should not be kept.

Singapore adds the COE category, COE months remaining, annual depreciation and deregistration value — features in their own right.

One Home Request

Every signal is stored against the car’s segment, so it survives the sale.

The segment is make, model, variant, registration-year band and COE-remaining band. The trade-in lookup keeps only a budget band, body type and COE-expiry bucket. The plate number is dropped.

Filters run before scoring, so a sold car never takes a slot. Shared weights are scaled by each user’s filter habits — 1.6× on value for the user below.

Every response logs what was shown and why. The later stages learn from that log.

A Worked Request

Stage 2, weekday lunch: a Civic driver who filters by depreciation and price. Illustrative values.

  1. Honda Vezel Hybrid stays first.It arrived through the trade-in path.
  2. Toyota Corolla Altis closes in.Low depreciation matters to this user, so the gap to first narrows from 0.0231 to 0.0145.
  3. Mazda 3 Hatchback drops to third.Shown twice today without a tap, so fatigue pulls it down.

Where the LLM Works

Three jobs, none on the request path.

  1. Per new listing: tagging.A small model tags free text against a fixed dictionary, and a script rejects anything outside it.
  2. Nightly: aggregation.No LLM.
  3. Weekly: rule drafts.A larger model reads aggregates, never raw logs. A schema validator and an offline replay evaluation check each draft. An approval gate releases a policy set, and a multi-armed bandit allocates daily traffic within it.

The model proposes. Scripts check. People decide.

Fig. 02

Where the Model Sits.

Where the model sits The model works offline: it tags new listings, aggregates nightly and drafts rules weekly. Every rule change passes a human-in-the-loop approval gate. The home request reads only what is already stored and makes no model call. Offline · model here Tags new listings Aggregates nightly Drafts rules weekly Approval gate Home request reads only what is already stored 01 Pick candidates 02 Filter, then score 03 Show and log No model call Where the model sits The model runs offline behind a human approval gate. The home request reads only stored data and makes no model call. Offline · model here Tags new listings Aggregates nightly Drafts rules weekly Approval gate Home request reads only what is stored 01 Pick candidates 02 Filter, then score 03 Show and log No model call

The model runs as an offline batch job behind a human-in-the-loop approval gate. The home request reads only what is already stored.

Three Stages

Each stage produces the data the next one needs.

Stage 1Cold StartL1 · about $111 a monthLogs fairly from day one.
Stage 2Rule DeepeningL2 · about $117 a monthPer-user weights, approved weekly. The biggest jump, for $6 more.
Stage 3Learned RankingL3 · about $180 a monthLearned weights, with rules as the fallback. Only if the traffic comes.
Offline · every stageBatch LLM tagging and rule drafting, gated by human-in-the-loop approval.
Cloud cost at 1,000 daily users, estimated.

The Real Cost Is People

Moving to the next stage is a staffing decision.

Staff cost more than the cloud. Stage 1 needs 20–30% of one person’s time, Stage 2 adds 30–50% of an analyst, and Stage 3 adds half to one full-time ML engineer. All assumed. The stage costs above are cloud bills at 1,000 daily users, estimated.

What It Saves

The argument is the slope: cost follows new listings, not traffic.

Money Is the Smaller Saving

The bigger gains are not money. The home screen never waits on a model, every ranking can be replayed exactly, and the logs are shaped for training from day one.

Fig. 03

Cost Follows Listings, Not App Opens.

Monthly LLM cost by daily users Estimated monthly LLM cost. The offline design stays at 11 dollars at 1,000 and 20,000 daily users. An LLM on every request costs 765 and 15,300 dollars. 1,000 daily users $11offline design $765LLM on every request 20,000 daily users $11offline design $15,300LLM on every request Monthly LLM cost by daily users Offline design: 11 dollars at both traffic levels. LLM on every request: 765 and 15,300 dollars. Offline Every request 1,000 daily users $11 $765 20,000 daily users $11 $15,300

Monthly LLM cost, estimated from public list prices, September 2026. Tagging 8,000 new listings a month is $9 of the $11.

What Would Prove This Wrong

Three things would break it.

  1. Segments too fine.If most segments see a handful of events a week, co-interaction is noise. A coarser segment is the fallback.
  2. Tags too weak.If LLM tags disagree with a human reading of a sample, new-listing cold start fails first.
  3. Too little traffic.At 1,000 daily users, telling policies apart may take over a week. A minimum traffic share keeps each option alive.

Personalisation, Measured

A share, not a label: home requests served from personal candidates rather than the fallback. Every snapshot records which, so the number comes for free.

The levels are my own scale: L1 is context and segment, L2 individual inputs on shared weights, L3 weights learned from data.

Assumptions and Sources

Costs are estimates. AWS Asia Pacific (Singapore) on-demand and Claude API list prices, September 2026, with the batch discount for offline jobs. 8,000 new listings a month with 20% re-tagged, 5 home requests per daily user and twelve weekly draft calls. Load balancer, data transfer, monitoring, backups, failover, non-production environments and staff time are not included.

Sources. COE and bidding (LTA), PARF and deregistration value (LTA OneMotoring), loan-to-value limits (MAS). Unique-inventory cold start draws on published eBay, FINN.no, OLX and Airbnb work.

Everything else — weights, bands, thresholds, token counts — is an assumption, not a measurement.

That is one system, designed and costed before a line of code. The rest of my AI work is on the AI page.

Notice — Any screen shown here as a recreation was rebuilt from scratch for this portfolio using fictional data. No client-owned screens, source files or confidential material were used. Publicly published material is credited on the card that shows it. Recreations produced 19 Aug 2026; working files retained. This case is a design worked on paper. It began in confidential client work, so it is set in an unrelated stand-in domain. Values are illustrative and costs are estimates from public list prices.

© 2026 Jiwon Nam · South Korea