Sur les comptes observés dans les benchmarks Google Ads publics, l'analyse manuelle d'un search query report rate 60 a 80% des negatifs cachés apres 30 minutes d'eyeballing par un PPC manager experimenté. Et le cout de ce miss est mesurable : 14 a 28% du spend mensuel finit en queries non-pertinentes que personne ne verra dans le top 50 des SQR (documentation officielle Google Ads sur les negatifs). C'est typiquement le levier negligé qui plombe le ratio CPA / Quality Score d'un compte mature.
Voici le diagnostic Andrew : un humain qui scanne un SQR fait du pattern matching visuel sur les 200-500 premieres lignes triees par cost. Il rate (1) les queries de la longue traine qui ne pesent que 3 a 8 EUR chacune mais qui cumulees representent 18 a 26% du spend, (2) les variations semantiques regroupables ('avis', 'review', 'opinion', 'temoignage' = meme intent), et (3) les clusters de queries hors-intent commercial (job, formation gratuite, definition, prix bas low-end) que l'algo Smart Bidding n'identifie pas si le tracking conversion est par valeur fixe.
Ce guide donne le pipeline IA reproductible : extraction GAQL du SQR, embeddings (OpenAI ou sentence-transformers), clustering DBSCAN, scoring intent + cost wasted, export CSV pour Google Ads Editor. Tout le code est en Python, fonctionne en local, et le repo public github.com/steerads/google-ads-negatives-ai contient un Dockerfile et un workflow n8n d'exemple. Pour la fondation match types qui conditionne ce pipeline, voir notre guide match types 2026. Notre calculateur de gaspillage budget estime le € brûlé/mois par broad sans négatifs ou bounce LP excessif.
Le probleme : 60 a 80% des negatifs caches en analyse manuelle
L'analyse manuelle d'un search query report (SQR) est l'une des taches les plus chronophages du PPC manager. Sur un compte moyen (5 000 a 15 000 queries / 30 jours), il faut compter 45 minutes a 2 heures pour faire un pass propre. Et meme avec ce temps investi, le taux de detection des negatifs pertinents reste structurellement limité par la fatigue cognitive et le tri par cost descendant.
Trois angles morts systematiques sur l'analyse manuelle :
- La longue traine est invisible. Les 50 premieres queries triees par cost concentrent 35 a 50% du spend. Le PPC manager les voit. Mais les 4 950 queries restantes — chacune a 1 a 8 EUR — concentrent 50 a 65% du spend total et restent inexplorees. C'est dans cette traine que se cachent les patterns recurrents non-pertinents.
- Le pattern matching visuel rate les variations semantiques. Un humain identifie 'avis' comme negatif candidat. Mais 'review', 'opinion', 'temoignage', 'feedback' partagent le meme intent et apparaissent dans des queries differentes. Sans embedding, impossible de regrouper.
- Pas de scoring du cost wasted. Une query a 25 EUR cost / 0 conversion sur 30 jours est plus impactante qu'une query a 4 EUR cost / 0 conversion — meme si la deuxieme apparait 6 fois plus souvent. L'humain trie par alphabet ou cost brut, rarement par cost wasted (cost / conversions).
Sur les comptes observés dans les benchmarks Google Ads publics, le ratio de spend recuperable via clustering IA des negatifs converge vers un seuil tres reproductible : 12 a 22% du spend mensuel apres 60 jours de pipeline actif. Sur un budget de 30 000 EUR/mois, c'est entre 3 600 et 6 600 EUR de spend recuperé tous les mois — non pas vers les concurrents, mais reinjecté dans les queries qui convertissent. Le ROI vs temps PPC manager economisé est superieur a 25x pour la majorite des comptes traites.
Le pipeline IA decrit ici remplace le pass manuel par un workflow reproductible. La premiere execution prend 30 a 45 minutes (setup + run + review). Les executions suivantes prennent 5 a 15 minutes (review du sample uniquement). Pour la base d'audit qui doit precéder cette automatisation, lisez notre checklist d'audit Google Ads.
Pipeline IA en 5 etapes : query report vers liste negatifs
Le pipeline complet enchaine 5 etapes deterministes, chacune testable en isolation. Architecture volontairement simple, pas de dependance ML lourde au-dela de scikit-learn et sentence-transformers / openai. L'output final est un CSV directement importable dans Google Ads Editor.
Pre-processing des search queries : l'etape qu'on saute trop souvent
Avant d'embedder quoi que ce soit, le pre-processing du SQR conditionne 30 a 50% de la qualite finale du clustering. Quatre transformations a appliquer systematiquement. Premiere etape : normalisation Unicode et casse. Les SQR Google Ads contiennent souvent des doublons fantomes : 'Devis Assurance', 'devis assurance', 'devis assurance' (double espace), 'devis-assurance' apparaissent comme 4 queries distinctes alors qu'elles representent la meme chose. Appliquer unicodedata.normalize('NFKC', s).strip().lower() plus une regex \s+ -> ' ' rassemble ces doublons en une ligne unique avec cost et clicks aggregés. Sur un compte FR moyen, cette etape reduit le volume de queries de 8 a 14% sans perte d'information.
Deuxieme etape : deduplication intelligente. Au-dela de la normalisation simple, la deduplication doit traiter les variantes morphologiques (singulier/pluriel, masculin/feminin, accents) selon le besoin. Pour le clustering, garder les variantes brutes est generalement plus utile (l'embedding les regroupera mecaniquement), mais pour l'export negatif final, dedupliquer sur le representative_query du cluster evite de proposer 5 fois le meme negatif. Troisieme etape : filtrage des queries one-shot a faible cost. Une query qui apparait une seule fois sur 30 jours avec 1,20 EUR de cost ne merite pas d'embedding (c'est du bruit pur). Filtrer en amont sur clicks >= 1 AND cost >= 1 evite de polluer le clustering avec 25-40% de bruit irreductible.
Quatrieme etape : detection des PII et termes sensibles. Certaines queries contiennent des numeros de telephone, emails, ou noms propres d'utilisateurs (saisies par erreur dans la barre de recherche au lieu de l'URL). Ces queries n'ont aucun interet d'analyse et leur traitement par OpenAI poserait probleme RGPD. Filtrer en amont via regex et ne JAMAIS envoyer a une API externe. Sur le données Google Ads agrégées 2025-2026, ce type de queries represente 0,3 a 1,2% du SQR — faible en volume mais critique en compliance.
[Google Ads API / CSV export]
|
v Etape 1 — extraction SQR (GAQL ou CSV)
[search_terms.csv : query, clicks, cost, conv]
|
v Etape 2 — embeddings (OpenAI ou ST)
[embeddings.npy : matrice 384/1536 dim]
|
v Etape 3 — clustering DBSCAN ou HDBSCAN
[queries_clustered.csv : query, cluster_id]
|
v Etape 4 — scoring intent + cost wasted
[clusters_scored.csv : cluster, waste_score, intent_score]
|
v Etape 5 — filtrage + match type + export
[negatives_export.csv : pret pour Google Ads Editor]
Voici le squelette Python du runner principal. Le code complet (avec gestion d'erreurs, logs, parametres CLI) est dans le repo public.
# main.py — pipeline complet en 5 etapes
import pandas as pd
import numpy as np
from embeddings import embed_queries
from clustering import cluster_dbscan
from scoring import score_clusters
from export import export_negatives_csv
def main(sqr_path: str, output_path: str, backend: str = "sentence-transformers"):
# Etape 1 — chargement SQR
df = pd.read_csv(sqr_path)
df = df[df["clicks"] >= 1] # filtrer les queries sans clic
print(f"Loaded {len(df)} queries")
# Etape 2 — embeddings
embeddings = embed_queries(df["search_term"].tolist(), backend=backend)
print(f"Embeddings shape: {embeddings.shape}")
# Etape 3 — clustering
df["cluster_id"] = cluster_dbscan(embeddings, eps=0.15, min_samples=5)
n_clusters = df["cluster_id"].nunique() - (1 if -1 in df["cluster_id"].values else 0)
print(f"Found {n_clusters} clusters")
# Etape 4 — scoring
clusters_scored = score_clusters(df, whitelist_path="whitelist.txt")
# Etape 5 — export
export_negatives_csv(clusters_scored, output_path,
min_cluster_size=3, min_waste_score=30)
print(f"Exported negatives to {output_path}")
if __name__ == "__main__":
main(
sqr_path="data/search_terms.csv",
output_path="output/negatives_export.csv",
backend="sentence-transformers", # ou "openai"
)
Chaque etape produit un artefact persisté (CSV ou .npy). Cela facilite le debug : si le clustering produit trop de bruit, on peut iterer sur l'etape 3 sans relancer les embeddings (cher en API call). Cf. notre guide automation Google Ads API Python pour le setup OAuth et l'extraction GAQL detaillee.
Embeddings : OpenAI text-embedding-3 vs sentence-transformers
L'embedding est l'etape critique. Une query vectorisee permet de mesurer la similarite semantique entre 'devis assurance auto' et 'tarif assurance voiture' — meme si zero mot en commun. Sans embedding, le clustering retombe sur du matching de tokens et rate 70% des regroupements pertinents.
Deux backends pertinents en 2026 : OpenAI text-embedding-3-small (hosted, 1536 dim, payant a l'usage) et sentence-transformers all-MiniLM-L6-v2 ou multilingual-e5-base (local, 384 ou 768 dim, gratuit). Le choix depend du volume et de la sensibilite des donnees.
Variante OpenAI — production-ready, batch optimise :
# embeddings.py — backend OpenAI
from openai import OpenAI
import numpy as np
import os
def embed_queries_openai(queries: list[str], batch_size: int = 100) -> np.ndarray:
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
all_embeddings = []
for i in range(0, len(queries), batch_size):
batch = queries[i : i + batch_size]
response = client.embeddings.create(
model="text-embedding-3-small",
input=batch,
encoding_format="float",
)
embeddings = [item.embedding for item in response.data]
all_embeddings.extend(embeddings)
return np.array(all_embeddings, dtype=np.float32)
Variante sentence-transformers — local, zero cout :
# embeddings.py — backend local sentence-transformers
from sentence_transformers import SentenceTransformer
import numpy as np
def embed_queries_st(queries: list[str], model_name: str = "all-MiniLM-L6-v2") -> np.ndarray:
model = SentenceTransformer(model_name)
embeddings = model.encode(
queries,
batch_size=64,
show_progress_bar=True,
normalize_embeddings=True, # important pour cosine
)
return embeddings.astype(np.float32)
def embed_queries(queries: list[str], backend: str = "sentence-transformers") -> np.ndarray:
if backend == "openai":
return embed_queries_openai(queries)
elif backend == "sentence-transformers":
return embed_queries_st(queries, model_name="paraphrase-multilingual-MiniLM-L12-v2")
else:
raise ValueError(f"Unknown backend: {backend}")
Repere terrain : sur un compte FR e-commerce de 8 000 queries, le delta de qualite cluster entre OpenAI et e5-base reste autour de 6 a 9% en precision (mesure : nb negatifs valides / nb propositions). En MiniLM v1 anglo-saxon, le delta monte a 12 a 18% — d'ou le choix paraphrase-multilingual-MiniLM-L12-v2 pour le FR. Pour un compte 100% francophone, c'est le meilleur compromis cout / qualite. Documentation officielle OpenAI embeddings.
text-embedding-3-large vs text-embedding-3-small : quand le large vaut le surcout ? Trois cas concrets ou text-embedding-3-large (3072 dim, 0,13 USD / 1M tokens, soit 6,5x plus cher que small) justifie l'investissement. Cas 1 : intent fin sur secteurs techniques specialisés (medical, juridique, ingenierie) ou la nuance entre 'avocat fiscaliste' et 'avocat fiscal' change le scoring intent. Le large capture 4 a 7% de precision supplementaire mesurable sur la cohorte de validation. Cas 2 : multilingue mixte (FR + EN + ES dans le meme compte) ou la robustesse cross-langue du large evite de splitter le pipeline en trois passes distinctes. Cas 3 : volume tres eleve (50 000+ queries par mois) ou le cout marginal additionnel reste sous 1 EUR par run et sans impact significatif sur le P&L. En dehors de ces trois cas, text-embedding-3-small reste le bon defaut pour un compte FR mid-market.
Analyse de cout API OpenAI sur un cas reel. Pour un compte qui traite 8 000 queries de 4 a 8 mots chaque semaine (en moyenne 32 tokens par query, donc 256 000 tokens par run), le cout du run en small est de 256 000 / 1 000 000 x 0,02 USD = 0,005 USD. Sur 52 runs annuels, soit environ 0,27 USD par compte par an. En large, le cout devient 256 000 / 1 000 000 x 0,13 USD = 0,033 USD par run, soit environ 1,72 USD annuel. Dans les deux cas, le cout API OpenAI est marginal compare au temps PPC manager economisé. Le piege classique : laisser tourner sans batching (1 query par appel au lieu de 100 par batch), ce qui multiplie le cout par 8 a 12 a cause de la repetition des frais fixes par requete. Toujours batcher au minimum a 50-100 queries par appel.
Clustering DBSCAN : pourquoi pas K-means
K-means est l'algorithme de clustering le plus connu, mais c'est le mauvais choix pour les search queries Google Ads. Trois raisons techniques :
- K-means exige de fixer K a l'avance. Combien de clusters de queries non-pertinentes existent dans votre compte ? Personne ne le sait avant l'analyse. K-means force a deviner — et un mauvais K produit soit des clusters trop larges (memes intents melanges) soit trop fins (sur-fragmentation).
- K-means assigne tous les points a un cluster. Or 30 a 50% des queries sont du bruit (queries uniques sans equivalent). DBSCAN les identifie comme bruit (cluster -1) au lieu de les forcer dans un cluster fictif. Resultat : clusters plus nets.
- K-means assume des clusters spheriques de meme densite. Les clusters de queries non-pertinentes ont des densites tres variables (un cluster 'gratuit' tres dense + un cluster 'comparaison concurrent' diffus). DBSCAN gere cette variance, K-means non.
DBSCAN (Density-Based Spatial Clustering of Applications with Noise) prend deux parametres : eps (rayon de voisinage) et min_samples (nb min de points pour former un cluster). Il identifie les zones denses dans l'espace des embeddings et regroupe les points proches.
# clustering.py — DBSCAN
from sklearn.cluster import DBSCAN
import numpy as np
def cluster_dbscan(embeddings: np.ndarray, eps: float = 0.15, min_samples: int = 5) -> np.ndarray:
# Cosine distance pour embeddings normalises
clustering = DBSCAN(
eps=eps,
min_samples=min_samples,
metric="cosine",
n_jobs=-1,
)
labels = clustering.fit_predict(embeddings)
return labels
def cluster_hdbscan(embeddings: np.ndarray, min_cluster_size: int = 5) -> np.ndarray:
import hdbscan
clustering = hdbscan.HDBSCAN(
min_cluster_size=min_cluster_size,
metric="euclidean", # HDBSCAN ne supporte pas cosine direct
cluster_selection_method="eom",
)
labels = clustering.fit_predict(embeddings)
return labels
Calibration du parametre eps : c'est le seuil de similarite cosinus en dessous duquel deux queries sont considerees comme voisines. Plus eps est petit, plus les clusters sont fins. Repere terrain : commencer a eps=0.15. Si plus de 70% des queries sortent en bruit (cluster -1), monter a 0.20. Si les clusters sont trop larges (cluster 'service' qui melange B2B et B2C), descendre a 0.12.
HDBSCAN comme alternative, si DBSCAN produit des clusters mal calibres. HDBSCAN auto-detecte la densite locale et n'a pas besoin de eps. Plus robuste sur datasets heterogenes mais plus lent et plus complexe a debugger.
Avant de passer au scoring, visualisez les clusters obtenus avec UMAP en 2D. umap-learn reduit les embeddings de 384 dim a 2 dim pour un scatter plot lisible. Vous voyez instantanement si le clustering est propre (clusters nets et separes) ou bruite (clusters qui se chevauchent). Si bruite, ajuster eps avant de continuer. 5 minutes investies, evitent 30 minutes de scoring inutile.
Validation des clusters : trois metriques au-dela du visuel UMAP
La visualisation UMAP est utile mais subjective. Pour valider quantitativement la qualite du clustering, trois metriques objectives a calculer systematiquement avant de passer au scoring. Metrique 1 : silhouette score. Implemente dans sklearn.metrics.silhouette_score, cette metrique mesure a quel point chaque point est plus proche de son propre cluster que des autres clusters. Score entre -1 (catastrophique) et +1 (parfait). Un silhouette superieur a 0,3 sur le dataset complet indique un clustering exploitable ; sous 0,15, il faut re-tuner eps. Sur les comptes observés dans les benchmarks Google Ads publics, un compte FR e-commerce mid-size atteint typiquement 0,32 a 0,48 de silhouette en sentence-transformers et 0,38 a 0,55 en OpenAI text-embedding-3-small.
Metrique 2 : pourcentage de bruit. Compter le ratio (labels == -1).mean(). Un bon clustering DBSCAN sur SQR Google Ads devrait avoir entre 25 et 45% de bruit (queries non assignees a un cluster). Sous 25%, eps est trop large et les clusters sont sur-fusionnés. Au-dessus de 60%, eps est trop strict et la majorite des queries sont rejetees. Cette metrique est gratuite a calculer et sert de garde-fou pour eviter les configurations dégeneratives.
Metrique 3 : taille mediane des clusters. Calculer np.median([size for _, size in clusters_size]). Un clustering exploitable produit des clusters de taille mediane entre 4 et 12 queries. Au-dessus de 20, les clusters sont trop larges et melangent des intents differents. Sous 4, ils sont trop fins et le scoring devient instable. Si la taille mediane est hors de cette fenetre, ajuster min_samples (DBSCAN) ou min_cluster_size (HDBSCAN) avant de continuer le pipeline. Sur les iterations de production, surveiller ces trois metriques et logger leur derive permet de detecter les regressions silencieuses du pipeline (un changement d'API embedding, un drift dans les queries source) avant que les negatifs proposes ne soient impactes.
Scoring : pertinence intent + cost wasted
Une fois les queries clusterisees, il faut scorer chaque cluster pour decider lesquels sont des candidats negatifs. Le scoring combine deux dimensions : (1) le cost wasted (combien de spend a ete brule sans conversion) et (2) le intent score (a quel point le cluster est eloigne de votre intent commercial cible).
Cost wasted est trivial a calculer : somme du cost / max(somme conversions, 0.1) par cluster. Plus le ratio est haut, plus le cluster est candidat negatif.
Intent score est plus subtil. On compare la similarite cosinus moyenne du cluster a une whitelist de keywords cibles (vos keywords brand + produits phares + intentions d'achat fortes). Si la similarite moyenne du cluster a la whitelist est superieure a 0.65, c'est probablement un cluster pertinent (a NE PAS exclure). Si inferieure a 0.30, c'est un cluster eloigné de l'intent commercial = candidat negatif fort.
# scoring.py — scoring intent + cost wasted
import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
def score_clusters(df: pd.DataFrame, whitelist_path: str) -> pd.DataFrame:
# Charger whitelist (1 keyword par ligne)
with open(whitelist_path) as f:
whitelist_keywords = [line.strip() for line in f if line.strip()]
# Embedder la whitelist
from embeddings import embed_queries
whitelist_emb = embed_queries(whitelist_keywords, backend="sentence-transformers")
# Aggreger par cluster
cluster_stats = []
for cluster_id, group in df.groupby("cluster_id"):
if cluster_id == -1:
continue # ignorer le bruit
cost = group["cost"].sum()
conv = group["conversions"].sum()
clicks = group["clicks"].sum()
size = len(group)
# waste_score : cout par conversion (0.1 floor pour eviter division par 0)
waste_score = cost / max(conv, 0.1)
# intent_score : similarite cosinus moyenne avec whitelist
cluster_emb = np.stack(group["embedding"].values)
sim_matrix = cosine_similarity(cluster_emb, whitelist_emb)
intent_score = float(sim_matrix.max(axis=1).mean())
# exemple representatif (query la plus centrale du cluster)
centroid = cluster_emb.mean(axis=0)
distances = np.linalg.norm(cluster_emb - centroid, axis=1)
representative_idx = distances.argmin()
representative_query = group.iloc[representative_idx]["search_term"]
cluster_stats.append({
"cluster_id": cluster_id,
"size": size,
"cost": round(cost, 2),
"conversions": round(conv, 2),
"clicks": clicks,
"waste_score": round(waste_score, 2),
"intent_score": round(intent_score, 3),
"representative_query": representative_query,
"negative_candidate": waste_score > 30 and intent_score < 0.30,
})
result = pd.DataFrame(cluster_stats)
return result.sort_values("waste_score", ascending=False)
Lecture du tableau de sortie : on trie par waste_score decroissant, puis on filtre sur intent_score < 0.30 ET size >= 3. Ce trio identifie les clusters qui (1) brulent du spend, (2) sont eloignes de l'intent commercial, et (3) ont assez de queries pour valoir un negatif (pas un one-shot).
Repere des seuils Andrew :
waste_score >= 30(au moins 30 EUR de cost sans conversion) — correspond au seuil de pertinence sur la majorite des comptes.intent_score < 0.30(similarite cosinus avec whitelist sous 30%) — au-dessus, risque de faux negatif.size >= 3(au moins 3 queries dans le cluster) — sous 3, c'est du bruit ou de la longue traine, traiter au cas par cas.
Ces seuils sont des points de depart. Ajustez selon vos retours sample manuel apres 2-3 iterations.
Export CSV pret pour Google Ads Editor
L'output final doit etre directement importable dans Google Ads Editor — sinon vous perdez 30 minutes a remettre en forme manuellement. Format attendu : CSV UTF-8, colonnes specifiques, match type clair, action explicite ("Add" pour ajout).
# export.py — generation CSV Google Ads Editor
import pandas as pd
def export_negatives_csv(
clusters_scored: pd.DataFrame,
output_path: str,
min_cluster_size: int = 3,
min_waste_score: float = 30,
max_intent_score: float = 0.30,
) -> None:
# Filtrer les clusters candidats
candidates = clusters_scored[
(clusters_scored["size"] >= min_cluster_size)
& (clusters_scored["waste_score"] >= min_waste_score)
& (clusters_scored["intent_score"] < max_intent_score)
]
rows = []
for _, cluster in candidates.iterrows():
# Determine match type selon la dispersion du cluster
# (a parametrer plus finement en prod : ici simplification)
match_type = "Phrase match" if cluster["size"] <= 8 else "Broad match"
rows.append({
"Action": "Add",
"Campaign": "ALL", # ou cluster["campaign"] si disponible
"Ad group": "", # vide = niveau campagne
"Keyword": cluster["representative_query"],
"Match type": match_type,
"Status": "Enabled",
"Comment": f"AI cluster #{cluster['cluster_id']} | "
f"size={cluster['size']} | "
f"waste={cluster['waste_score']} EUR | "
f"intent={cluster['intent_score']}",
})
out_df = pd.DataFrame(rows)
out_df.to_csv(output_path, index=False, encoding="utf-8")
print(f"Wrote {len(out_df)} negative candidates to {output_path}")
Procedure d'import dans Google Ads Editor : Google Ads Editor > File > Import > From CSV. Le tool detecte les colonnes, valide le format match type, et propose un diff visuel avant push. Reviewer le diff (sample 30-50 lignes), valider, puis Post Changes. Pour la documentation officielle Google Ads Editor, voir support.google.com/google-ads/answer/2475106.
Match type — heuristique terrain :
- Phrase match pour les clusters tres homogenes (size 3-8 queries, expressions courtes et similaires). Match precis, faible risque de blocking accidentel d'une query pertinente.
- Broad match pour les concepts larges (size superieur a 15, queries variees autour d'un meme intent). Plus large, mais ne pas oublier de monitorer apres push pour detecter les blocages excessifs.
- Exact match rarement pertinent en negatif IA — on traite plutot des concepts que des queries exactes.
Ne JAMAIS uploader le CSV directement sans review humain au premier run. Sample 30-50 lignes aleatoires, verifier qu'aucun terme brand / produit phare / keyword cible apparait, valider le match type. Si plus de 10% du sample est faux positif, ajuster les seuils (min_waste_score plus haut, max_intent_score plus bas) et re-runner. Une fois pipe valide sur 2-3 iterations (taux d'erreur sous 5%), automatisable en mode auto.
ROI typique : combien on recupere de spend
Question pratique : combien ce pipeline IA rapporte vs l'analyse manuelle ? Reponse en chiffres terrains, mesure sur les comptes observés dans les benchmarks Google Ads publics.
Lecture : le pipeline IA n'est pas magique sur la precision (un humain attentif fait aussi bien). Son avantage est sur le recall — il identifie 75 a 88% des negatifs pertinents vs 20 a 35% pour un humain en 30 minutes. Et le temps PPC manager passe de 30 min a 5-15 min par execution apres setup initial. Le vrai gain est cumulatif : sur 12 mois, ce sont 4 a 6 heures de PPC manager economisees ET 12 a 22% de spend mieux utilise.
Repere chiffre Andrew : sur un compte de 30 000 EUR/mois, recuperer 15% du spend = 4 500 EUR/mois redirigés vers les queries qui convertissent. A iso-CPA, c'est 4 500 / CPA conversions supplementaires en bottom-funnel chaque mois. Sur 12 mois, c'est typiquement entre 230 et 380 conversions additionnelles, sans hausse de budget. Le ROI vs investissement initial (45 min de setup + cle API embeddings) est superieur a 50x.
Le ratio cle Andrew : recall divisé par temps investi. Pour l'analyse manuelle 2h, le ratio est environ 0,25 (50% de recall / 2h). Pour le pipeline IA, le ratio est environ 6 (80% de recall / 0,2h apres setup). C'est 24x plus efficient sur la metrique qui compte vraiment — combien de spend wasted on identifie par heure de PPC manager investie. Et ce ratio s'ameliore encore au fil des executions : a la 5e iteration, le sample manuel se reduit a 15-25 negatifs a reviewer (vs 60-80 au premier run), parce que les patterns recurrents sont absorbes par les clusters precedents.
Mesure pratique sur 60 jours : les comptes qui activent le pipeline en mode hebdomadaire convergent vers un steady-state ou la majorite des nouveaux negatifs viennent de patterns nouveaux (concurrents qui montent, requetes saisonnieres, evolutions semantiques de l'audience). Le pipeline detecte ces nouveautes en moins de 7 jours apres apparition, vs 30-60 jours en monitoring manuel — soit une fenetre de 23-53 jours economisee de spend wasted sur chaque pattern emergent. C'est le vrai moat de l'automatisation embedding-based : la vitesse de reaction, pas juste le recall ponctuel.
Notre moteur d'auto-optimisation embarque ce pipeline en mode managé : extraction GAQL automatique, embeddings hosted, clustering DBSCAN, scoring + filtrage, et propositions hebdomadaires validables en 1 clic. Pour les comptes qui veulent industrialiser sans coder. Voir aussi notre guide 10 scripts Google Ads pour l'automatisation cote scripts natifs, et notre guide n8n + Google Ads pour scheduler le pipeline en workflow self-hosted. Les complementarites entre ces trois angles — pipeline IA, scripts natifs, n8n self-hosted — sont detaillees dans notre collection de prompts ChatGPT Google Ads.
Erreurs courantes a eviter dans l'industrialisation
Cinq erreurs reviennent recurrentes en mise en production de pipeline IA negatifs Google Ads. Chacune cree soit du faux negatif (negatifs mal cibles qui bloquent du trafic pertinent), soit du faux positif (negatifs ratés). Diagnostic et correction directe.
1. Lancer le pipeline sans whitelist brand et produits cibles. Diagnostic : le pipeline propose des negatifs qui contiennent vos termes brand ou les noms de vos produits phares, parce que ceux-ci sont mal differencies semantiquement de queries non-pertinentes. Correction : maintenir une whitelist whitelist.txt avec au minimum les variantes brand (mots simples + composés), 10 a 20 noms de produits cibles, et les termes commerciaux forts. Cette whitelist sert au scoring intent (intent_score) et bloque l'export de tout cluster dont le representative_query matche partiellement la whitelist.
2. Confondre exact match et phrase match dans l'export negatif. Diagnostic : exporter un cluster en exact match sur le representative_query bloque uniquement la query exacte (10-15% du cluster), laissant 85-90% du cost wasted continuer. Correction : utiliser phrase match comme defaut pour les clusters cohérents, et broad match pour les concepts larges. Tester avant push en interrogant le Keyword Planner sur le negatif propose : si le volume estime est superieur a 5x le volume du cluster d'origine, le match type est trop large.
3. Lancer le pipeline trop frequemment et generer du bruit. Diagnostic : un pipeline daily produit des dizaines de propositions par jour, dont 60-70% sont des reformulations des propositions precedentes. La fatigue de validation pousse l'equipe a tout valider sans review. Correction : lancer hebdomadaire (lundi matin), pas daily. La fenetre de 7 jours stabilise les patterns et evite les decisions premature sur des queries one-shot. Pour les comptes a tres haute frequence d'evolution (saisonnalité forte), passer a bi-hebdomadaire au maximum, jamais quotidien.
4. Ignorer les clusters de petite taille mais haut waste. Diagnostic : le filtre min_cluster_size: 3 exclut systematiquement les clusters de 1-2 queries qui peuvent porter un waste eleve (une query a 80 EUR sans conversion, par exemple). Ces patterns isoles passent sous le radar du pipeline. Correction : ajouter une seconde passe qui propose les queries individuelles au-dessus de waste_score = 60 independamment de leur appartenance a un cluster. Ces propositions individuelles sont plus risquees a auto-valider, donc reviewer manuellement avant export.
5. Ne pas mesurer la performance post-application. Diagnostic : les negatifs sont uploades, le pipeline tourne en autopilot, mais personne ne verifie si le CPA et le ROAS s'ameliorent reellement. Correction : tracker un indicateur de sante du pipeline : delta_cpa_30j_post_negatifs (variation CPA dans les 30 jours apres push de N negatifs). Si la variation est nulle ou negative, c'est que les negatifs proposes etaient marginaux. Ajuster les seuils min_waste_score et max_intent_score pour cibler des clusters a impact plus net. Sur les comptes observés dans les benchmarks Google Ads publics, l'amelioration mesurable apparait typiquement dans les 3 a 5 premieres iterations puis se stabilise.
Pour les comptes qui veulent industrialiser sans runner le pipeline eux-memes, notre module Auto-optimisation tourne le pipeline embeddings + clustering chaque semaine sur votre compte, propose les negatifs candidats valides via UI, et applique apres review. Connexion OAuth en 2 minutes, premiere analyse en 5 minutes. Repo public github.com/steerads/google-ads-negatives-ai disponible pour ceux qui preferent self-hoster, avec Dockerfile et workflow n8n d'exemple.
Sources
Sources officielles consultées pour ce guide :
FAQ
Faut-il forcement utiliser OpenAI ou peut-on rester 100% open-source ?
Vous pouvez rester 100% open-source. sentence-transformers (modele all-MiniLM-L6-v2 ou multilingual-e5-base) tourne en local sur CPU, sans cle API ni cout, avec une qualite suffisante pour clusteriser des search queries. La difference observable vs OpenAI text-embedding-3-small : -8 a -14% de precision sur les clusters fins (separer 'devis' commercial vs 'devis' juridique), mais +zero euro de cout d'API. Pour des comptes traitant moins de 5 000 queries/mois, sentence-transformers est suffisant. Au-dela, ou si vous traitez du multilingue avec accents, text-embedding-3-small a un meilleur rapport qualite/effort. Le pipeline supporte les deux backends via une variable d'environnement.
Quel volume minimum de search queries pour que le clustering soit fiable ?
Seuil pratique : 500 queries minimum sur la fenetre d'analyse (typiquement 30 jours). En dessous, DBSCAN n'a pas assez de densite pour identifier des clusters stables et la majorite des queries sortent comme bruit. Sweet spot : 2 000 a 10 000 queries sur 30 jours. Au-dela de 10 000, decoupez par campagne pour eviter de melanger des intents trop differents (B2B + B2C dans le meme clustering = bruit). Si votre compte fait moins de 500 queries/30j, etendez la fenetre a 60 ou 90 jours.
DBSCAN ou HDBSCAN : lequel choisir en pratique ?
HDBSCAN est generalement superieur pour les search queries Google Ads : il gere les clusters de densite variable, ce qui correspond a la realite (un cluster 'gratuit' tres dense + un cluster 'concurrent' diffus). DBSCAN exige de fixer un parametre eps unique pour tout le dataset, ce qui force des compromis. Cependant, DBSCAN reste plus simple a parametrer et plus rapide sur petits volumes (moins de 5 000 queries). Recommandation : commencer en DBSCAN avec eps autour de 0,15 et min_samples de 5, puis passer a HDBSCAN si les clusters paraissent mal calibres. Le pipeline supporte les deux via une variable de config.
Comment evaluer la qualite des negatifs proposes par le pipeline avant de les uploader ?
Trois checks obligatoires avant tout upload Google Ads Editor. (1) Sample manuel : reviewer 30 a 50 negatifs aleatoires, viser un taux de validation superieur a 90%. (2) Whitelist : verifier qu'aucun terme brand, produit phare ou keyword cible apparait dans la liste (le script doit avoir une whitelist en input). (3) Match type : verifier que le match type exporte correspond bien a l'intention (phrase match pour les expressions exactes, broad match pour les concepts). Si moins de 90% de validation au sample manuel, ajuster les seuils du scoring (cost_min, cluster_size_min) plutot que d'uploader tel quel. Iterer 2 a 3 fois avant de viser le mode auto.
Peut-on automatiser ce pipeline en run hebdomadaire sur n8n ou Cloud Run ?
Oui, et c'est l'objectif final pour industrialiser. Le pipeline Python tourne en moins de 3 minutes sur 5 000 queries (CPU local) ou en moins de 60 secondes via OpenAI batch embeddings + DBSCAN GPU. Vous pouvez le scheduler en cron sur Cloud Run, Lambda, ou en workflow n8n. Pattern recommande : run hebdomadaire (lundi matin), output dans un Google Sheet partage, validation humaine du sample, upload via Google Ads Editor (ou via Google Ads API directement si confiance pipeline elevee). Le repo github.com/steerads/google-ads-negatives-ai inclut un Dockerfile et un exemple n8n workflow.