# The World's First AI Model That Understands Ads Deeply

> Most AI looks at an ad and sees pixels. Ours sees a hook, an offer, a proof structure, and an argument. Here is how we built it and the honest result that killed our best-sounding feature.
- **Author**: Apurv Singh
- **Published**: 2026-09-09
- **Category**: Product
- **URL**: https://marxx.ai/posts/ads-analyzer-model

---

*Most AI looks at an ad and sees pixels. Ours sees a hook, an offer, a proof structure, and an argument. This is how we built it, plus the result that killed the feature we most wanted to ship.*

---

## Ads are the most measured, least understood thing in marketing

Every ad platform will happily tell you what an ad *did*. CTR, CPM, CPP, ROAS, thumbstop rate, hold rate. Meta will slice all of it by placement, age band and device.

None of them will tell you what the ad **was**.

That gap is the whole problem. Performance numbers are downstream of a creative decision (a hook, a claim, a proof, a format) and they never get connected back to it. Which means the single highest leverage question in performance marketing, "what kind of ad should we make next?", still gets answered the way it was answered in 1994: a strategist opens forty competitor ads in forty tabs and takes notes in a spreadsheet.

We wanted a model that could take those notes automatically, on every ad, forever. Not a caption. Not a summary. A structured reading of the argument the ad is making.

## Why frontier vision models don't do this out of the box

Give a general purpose VLM a 12 second Meta ad and ask what it sees. You get something like:

> "A woman in a bathroom holds up a bottle and speaks to the camera. Text appears on screen. The video ends with a logo."

All of that is true. None of it is useful.

The issue isn't capability, it's the objective. General vision models are trained to describe what is depicted. But an ad isn't really a depiction. It's an argument compressed into eight seconds, and the part that matters is the rhetorical structure:

- What claim is being made?
- What proof backs it, and what *kind* of proof is it?
- Which objection is being pre-empted?
- What emotion is loaded, and in which second?
- Which format convention is being borrowed, and from where?

"A woman in a bathroom" is the wrapper. "Founder admits the first formulation failed, then shows the lab report" is the ad.

Getting from the first sentence to the second isn't a prompting trick. You have to decide, up front, what an ad *is* as a data structure. That turned out to be most of the work.

## The core idea: an ad grammar

The centre of the system isn't a model. It's a schema. A fixed grammar that every ad in the corpus gets parsed into.

The design choice that made it work was capturing every persuasive dimension twice: once as free text, once as a closed label.

```
hook                     "Your dermatologist won't tell you this."
hook_type                Taboo Hooks

problem_statement        "Serums stop working after 3 weeks of use."
problem_statement_type   Efficacy Plateau

offer                    "First bottle free, cancel anytime."
offer_type               Free Trial / Demo

social_proof             "Featured in Vogue India and Cosmopolitan."
social_proof_type        Media mentions / press coverage

reliability              "Dermatologically tested, 4-week clinical study, n=112."
reliability_type         Clinical study trials / lab tests

authenticity             "Founder shows the batch that failed QC."
authenticity_type        Admitting limitations

ad_type                  ["Expert Explainer", "Before and After"]
ad_type_reason           "Dermatologist in clinical setting explains mechanism,
                          then split-screen week-1 vs week-4 comparison."
```

The free text is what makes one ad readable. The closed label is what makes ten million ads countable.

Most creative analysis tools pick one and quietly lose the other. Go pure free text and you get lovely summaries you can't aggregate. Go pure classification and you get counts with no evidence behind them, which nobody trusts and nobody should. You need both columns or the layer above doesn't work.

Around these sit the descriptive fields: a chronological walkthrough of the creative, a *separate* narrative for just the first three seconds, dominant emotion, language (including code switched ones like Hinglish, which matters a lot in our market), product name, scene, whether the product is actually demonstrated, background music identification, video duration read off the file rather than guessed, and transcript segments with timings.

The first three seconds get their own field on purpose. The thumbstop is a different decision from the argument. An ad can have a great hook bolted onto a weak body, or the reverse, and if you flatten both into one description you can never tell which one you're looking at.

## The taxonomy is the product

Here's the part that took longest and that nobody writes blog posts about. Choosing the vocabularies.

We hold 202 closed labels across six dimensions:

| Dimension | Labels | Examples |
|---|---:|---|
| `ad_type` | **107** | ASMR - Greenscreen - Notes App - News Broadcast - Native Search - Comment Response - Cinematic B-Roll - Us Vs Them - Before and After - Advent Calendar |
| `hook_type` | **38** | Myth-Buster - Discomfort - CCTV/Raw-Angle - Macro-Texture - Oddly Satisfying - Screen-Record - Trend-Reaction - Hand-in-Frame - Command - Handwriting |
| `offer_type` | **28** | Buy One Get One - Zero-Interest Financing - Early Bird - Limited Stock / Scarcity - Money-Back Guarantee - Loyalty Rewards |
| `reliability_type` | **11** | Clinical study trials / lab tests - On-time delivery statistics - Case numbers - Service-level metrics - Transparent data |
| `social_proof_type` | **11** | Star ratings - Trusted by... claims - Media mentions - Awards & certifications - Industry partnerships |
| `authenticity_type` | **7** | Admitting limitations - Behind-the-scenes - Everyday relatable settings - Real people instead of actors/models |

Each of the 107 ad formats carries a definition, a list of **visual cues**, and where relevant a list of **auditory cues**:

```json
{
  "Adtype": "ASMR",
  "Meaning": "Focuses on high-quality sensory sounds to create a physical response.",
  "Visual_Cues": ["Extreme close-ups", "Sensitive mics visible", "Slow hand movements"],
  "Auditory_Cues": ["Whispering", "Crisp tapping", "Crinkling", "No background music"],
  "Example": "The crisp sound of a soda can opening followed by the fizzing noise."
}
```

Those cue lists are the actual mechanism. They take a fuzzy craft category that a good strategist recognises on sight and turn it into something a model can ground in observable evidence.

We also make the model show its work. Every `ad_type` comes back with an `ad_type_reason` citing the specific cue it saw. If you can't audit a classification, you can't trust it at scale, and at some point you will need to defend a number to a client who disagrees with it.

The closed vocabularies come with a hard rule attached: use the list, nothing else. Where nothing fits, the answer is `Others`, not an invented label. That constraint is the entire difference between a taxonomy and a word cloud.

### The convergence loop

Some dimensions genuinely can't be closed. Audience is the obvious one. "Busy mothers of toddlers with gut health concerns" is a real, useful category that no fixed list was ever going to anticipate. Same story for problem categories and product categories.

Left alone, an open vocabulary is a garbage generator. Run it over a hundred thousand ads and you end up with four thousand near synonyms for the same audience. Busy moms. Time poor mothers. Mothers with no time. Overwhelmed parents. Your aggregation layer is dead before you write it.

So the extraction pass isn't stateless. Before classifying, the model gets the categories that already exist in the corpus and is told to match an existing one before minting a new one. Guardrails handle the rest. Categories have to be two to four words, have to be semantic rather than structural, and a blocklist rejects the schema shaped labels that models love to emit: `category`, `general`, `other`, `miscellaneous`, `problem_category`, `value proposition`.

One rule in the prompt cost us more iterations than the rest combined, so it's worth quoting:

> When you are giving output for the audience category make sure it identifies a specific persona and not a generic one. Refrain from giving output like "Men, age", instead give "Non-chalant basketball players" or "Parents looking for baby products".

Vocabulary convergence is deeply unglamorous work. It's also the thing that decides whether anything you build on top actually functions.

## The retrieval layer

Structured tags answer "how many ads in this category use a scarcity offer?" They don't answer "find me ads that feel like this one."

For that, every creative gets embedded with `gemini-embedding-2` at 3072 dimensions. One multimodal space covering text, image and video, stored as `halfvec` in Postgres behind a pgvector HNSW index. Because it's a single space, a text query and a video creative are directly comparable. You can search for "a founder talking to camera about a supply chain problem" and get matches across formats, languages and countries, with no tags involved at all.

One implementation note, offered here because it cost us an embarrassing amount of time and might cost you less.

**HNSW silently caps your recall, and your `LIMIT` will lie to you.** We had a search that refused to return more than 40 results no matter what we asked it for. Not the query. Not the filters. Not the data. It was `hnsw.ef_search`, the index's own candidate list size, sitting at its default of 40. The index stops exploring after `ef_search` candidates and hands back whatever it found, so your `LIMIT 200` never gets to be the binding constraint. No error, no warning, no log line. If you're building vector search on pgvector, set `ef_search` explicitly per query and find out what your real ceiling is.

## The result that killed the feature we most wanted

This is the part most companies in this space leave out of their blog posts.

Once you have creative embeddings and performance data sitting in the same database, the obvious product writes itself. Score a new creative against historical winners, predict whether it'll perform. "AI predicts your winning ads." It demos beautifully. Every prospect asks for it.

We ran the backtest before building it.

Pooled across the whole corpus, creative embeddings separated high performers from low performers at **AUC 0.94**. We nearly shipped on the spot.

Then we controlled for base rates, demeaning performance within account and within week so the model had to beat the brand and the budget instead of riding them. Same embeddings, same ads, same labels. AUC fell to **0.36**. Worse than a coin flip.

```
              0.0        0.5 (chance)        1.0
               |            |                 |
pooled         |============================| 0.94   <- brand + budget
within-        |==========|                    0.36   <- the creative itself
account/week
```

The 0.94 was never about creative. It was measuring which brands run ads and how much they spend. Large established advertisers post better numbers than small ones, and their creatives are trivially separable in embedding space because they look like a large advertiser's creatives. Better lighting, better talent, better edit. The model had learned to recognise budget and we'd nearly sold it as insight.

So we killed the win probability pitch outright. Embeddings encode similarity, not causality. They'll tell you reliably that two creatives are making the same move. They won't tell you which move wins in your account this month, because the honest answer is that the same creative performs differently across accounts, and no amount of pixel level understanding changes that.

If a vendor offers you a creative win probability score, ask them one question. What's your AUC after demeaning within account and week? Either they haven't run it or they have.

## What deep understanding is actually good for

Losing prediction stung for about a week. It turned out not to matter, because the four things that survive are the four things a strategist actually does all day.

**Retrieval.** "Show me every ad in home fragrance that opens on a myth buster hook, uses clinical proof, and runs under 15 seconds." That used to be a two day manual review. Now it's a filtered query.

**Gap analysis.** Parse your ads into the grammar. Parse your category's ads into the same grammar. Subtract. What comes out is a concrete list of arguments your competitors are making that you aren't, and more usefully, arguments nobody in the category is making yet. This is the actual deliverable, and it needs no prediction whatsoever.

**Trend detection.** Once every ad is a structured object, counting becomes strategy. There is deliberately no model anywhere in this loop. It's pure statistics over the tag corpus:

- Wilson lower bound on share, so a tactic seen in 3 ads can't outrank one seen in 300
- Poisson z-score on week over week counts, to separate real acceleration from noise
- Sector lift against the category baseline, so "uses star ratings" doesn't top the chart in every single vertical
- Survival, meaning how long ads using a tactic stay live. Without visibility into competitor spend, continued runtime is the cleanest signal available that somebody is choosing to keep paying for something.

A trend engine that hallucinates is worse than no trend engine, so this one can't.

**Hypothesis generation.** Not "this will win", but "your category has saturated the discount led hook and nobody is running a mechanism explainer." That's a testable brief. It's also, if you're honest about it, what you wanted from prediction in the first place.

## What it still gets wrong

Carousels are the weakest surface by some distance. Multi card creatives with per card assets don't reduce cleanly to one argument, and our per asset linkage is still incomplete.

The `Others` bucket has a real cost. Forcing a closed vocabulary means genuinely novel formats land in `Others` until a human notices the cluster and promotes it. New formats emerge faster than we add labels, and they always will.

Taxonomy drift is ongoing. The convergence loop keeps open categories tight, but it's a heuristic, not a guarantee, and it needs periodic human pruning.

Text heavy static ads in non-Latin scripts still lose detail on OCR.

And the big one, restated: the system reads ads, it doesn't forecast them. Every output is descriptive or comparative. None of it is causal.

## What "first" means here

We're not claiming to be first to point a vision model at an advertisement. Plenty of people have done that.

The claim is narrower:

1. A fixed, craft grounded ad grammar. 202 closed labels across six persuasive dimensions, each with observable visual and auditory cues and a required evidence citation, applied uniformly across millions of creatives rather than assembled ad hoc per query.
2. A convergence loop on the open dimensions, so the vocabulary stabilises instead of fragmenting as the corpus grows.
3. A shared multimodal space where text queries and video creatives are directly comparable, sitting alongside the structured layer rather than replacing it.
4. A published negative result on the prediction question, and a product that doesn't sell what the data won't support.

The fourth one is what we'd defend hardest. Understanding ads deeply and predicting ad performance are different problems, and the industry has spent a few years now selling the second while having only half solved the first. We chose to solve the first properly and say out loud that the second doesn't work the way everyone hopes it does.

That's the model. It reads ads the way a good strategist reads them, structurally and sceptically, at a scale no strategist can match.

---

*If you're building on Meta creative intelligence, we'd genuinely like to compare taxonomies. The model was never the hard part.*

---
- [More Product articles](https://marxx.ai/posts/category/product)
- [All articles](https://marxx.ai/posts)