Build an AI agent that catches creative fatigue before ROAS falls
By the time your ROAS dashboard turns red, the ad has already been dying for two weeks. Creative fatigue shows up in the leading metrics first — frequency climbing, CTR sliding, CPM creeping up as the algorithm fights to find fresh eyes. If you're only watching ROAS, you're reacting to a lagging indicator. An AI agent that watches the leading signals can flag fatigue while there's still budget left to save.
Roas is the last thing to move
ROAS is a downstream number. It's the product of CTR, conversion rate, and average order value, all filtered through whatever attribution model you're using. When creative fatigues, the first thing that breaks is engagement — people have seen the ad enough times that they stop clicking. That drags CTR down, which pushes CPM up because the platform's auction rewards engagement. Conversion rate usually holds steady longer because the people who do click are still the same intent-qualified audience. So you get a stretch of days where CPM rises and CTR falls but ROAS looks fine, because volume drops before efficiency craters.
By the time ROAS actually dips, you've usually burned a week or two of declining efficiency without noticing, because the metric that's supposed to warn you is the one still holding steady. An agent built to catch fatigue early has to skip ROAS as the primary trigger and go straight to the signals that move first.
The signals that predict fatigue
You don't need exotic data science here. Fatigue has a well-documented signature, and most of it is sitting in your ad platform's API already:
- Frequency — once average frequency crosses roughly 3-4 within a 7-day window for cold audiences, engagement typically starts decaying. The exact threshold varies by vertical and audience size, so the agent should learn your account's own baseline rather than using a fixed number from a blog post.
- CTR trend — not the absolute number, the slope. A 3-day rolling CTR that's down 15-20% from its 14-day average is a stronger signal than a single bad day.
- CPM trend relative to account average — rising CPM on a specific ad while account-wide CPM stays flat means the auction is penalizing that specific creative, not the market.
- First-time impression ratio — if the platform exposes it, a shrinking share of impressions going to new users versus repeat exposure is one of the cleanest early signals available.
- Video hook rate / thumb-stop ratio — for video and reels, a drop in 3-second view rate or hook rate often precedes CTR decline by several days.
None of these alone is definitive. Frequency can climb because you tightened your audience, not because the ad is stale. CPM can rise because of seasonality. The agent's job is to combine signals and require confirmation across two or three of them before it flags anything — that's what separates a useful alert from noise you'll start ignoring within a week.
The data pipeline underneath it
An agent is only as good as the data feeding it, and this is where most "AI for ads" tools fall apart — they run on whatever the ad platform's dashboard shows you, which is aggregated, delayed, and missing the granularity you need. You want ad-level, day-level data flowing into your own warehouse, not platform-native reporting.
- Pull raw data daily from the Meta Marketing API, Google Ads API, and TikTok Ads API at the ad level — impressions, clicks, spend, frequency, video metrics, not just campaign rollups.
- Land it in a warehouse (BigQuery, Snowflake, whatever you're already on) with a stable schema so you can join creative metadata — format, hook type, offer, launch date — against performance.
- Build rolling window tables — 3-day, 7-day, and 14-day rolling averages for CTR, CPM, frequency, and hook rate per ad. This is the layer the agent actually queries.
- Tag creative launch dates so the agent knows an ad's age. Fatigue thresholds should scale with age — a 3-day-old ad with a CTR dip is noise, a 21-day-old ad with the same dip is a pattern.
This pipeline doesn't need to be complicated. A daily scheduled job (Airflow, dbt, or even a cron script calling the ad APIs) that lands data and refreshes the rolling tables is enough. The complexity should live in the logic layer, not the plumbing.
Building the agent's decision logic
"AI agent" doesn't have to mean a large language model making judgment calls — for this use case, a rules engine wrapped in an LLM for summarization is often the more reliable design. The detection logic can be deterministic:
- Score each active ad daily against its own historical baseline, not a universal threshold. An ad that normally runs at 2.1% CTR getting flagged at 1.8% means something different than an ad that normally runs at 0.9%.
- Require multi-signal confirmation — for example, flag only when frequency is above the account's 75th percentile and 3-day CTR is down 15%+ from the 14-day average and the ad has been live more than 5 days.
- Use an LLM layer for the explanation, not the detection — once the rules engine flags an ad, have the LLM pull the creative's metadata, format, and recent metrics, and write a plain-English summary: "This UGC video ad has been live 18 days, frequency hit 4.2, CTR dropped 22% over the last 3 days versus its 14-day average, and hook rate fell from 28% to 19%. This pattern matches prior fatigued ads in this account." That's the part that actually gets read and acted on by a media buyer at 8am.
- Route the alert into Slack or email with the ad ID, spend at risk, and a suggested action — pause, refresh creative, or expand audience to lower frequency.
Keeping detection rule-based and explanation LLM-based gives you the best of both: consistent, auditable triggers plus a natural-language layer that makes the alert actually useful to a human who has ten other things going on that day.
What happens after the flag
A fatigue alert is only valuable if it triggers a decision within a day or two, not a shrug. Build the workflow, not just the alert:
- Auto-pause thresholds for extreme cases — if frequency is above 6 and CTR has dropped more than 35%, the agent can pause spend automatically rather than waiting for a human to see the Slack message.
- Creative refresh queue — tie the flag into your creative production pipeline so a fatigued ad automatically triggers a request for a new variant using the same offer and hook style, so you're not starting from zero.
- Track flag accuracy — log every flag and whether the ad's performance actually declined further in the following week. This is how you tune thresholds over time instead of guessing. If half your flags turn out to be false alarms, tighten the multi-signal requirement.
The measurable payoff here isn't just cleaner dashboards — it's spend efficiency. Catching fatigue three to five days earlier than a human scanning reports manually means less budget wasted on rising CPMs before someone notices and pauses the ad.
Creative fatigue is predictable, not random — it has a signature in frequency, CTR slope, and CPM drift well before ROAS ever moves. Build the pipeline to capture that signature at the ad level, let a rules engine do the detection, and use an LLM to translate the pattern into something a media buyer can act on before lunch. The teams that catch fatigue early aren't smarter — they're just watching the metrics that move first.