Quand l'IA confond Naruto et Kill Bill : matcher des posters avec TMDB et 3 passes d'IA

Imaginez. Vous avez un outil de partage de fichiers avec une jolie grille de posters façon Netflix. Vous partagez votre collection de Looney Tunes avec un ami. Il ouvre la page et voit... le poster de Kill Bill sur le Volume 1. Les Gardiens de la Galaxie sur le Volume 2. Un anime japonais obscur sur le Volume 5. Classe.

C'est exactement ce qui m'est arrivé avec ShareBox, mon side project de partage de fichiers. L'idée de base était simple : prendre le nom du dossier, chercher sur TMDB (la base de données de films/séries), afficher le poster. Sauf que les noms de dossiers ressemblent à ça :

Naruto.INTEGRALE.MULTI.VFF.1080p.BluRay.x264-AMB3R
Moi.Moche.Et.Mechant.COLLECTION.(2010-2024).MULTI.VFF.2160.4KLight.HDR10
Vol 1
S01

Le regex de nettoyage que j'avais écrit (extract_title_year()) faisait le boulot dans 80% des cas. Mais les 20% restants, c'était le chaos. "Vol 1" matchait Kill Bill. "S01" matchait une série chinoise au hasard. "Moi.Moche.Et.Mechant.COLLECTION" matchait le 4e film au lieu de la saga. Et "Naruto" retournait systématiquement Naruto Shippuden — parce que Shippuden est plus populaire sur TMDB.

L'idée : empiler des couches d'IA comme des filets de sécurité

Plutôt que d'écrire un regex surhumain, j'ai décidé de traiter le problème en couches. Le principe : le truc gratuit fait le gros du travail, l'IA ne corrige que ce qui passe entre les mailles. Trois passes, du moins cher au plus cher :

  1. Regex + TMDB : nettoyage basique du nom, recherche TMDB, premier résultat. Gratuit, instantané.
  2. Pass 1 — "C'est quoi ce titre ?" : quand le regex rate, envoyer le nom brut à Claude Haiku pour qu'il devine le vrai titre.
  3. Pass 2 — "C'est le bon match ?" : envoyer toutes les paires {nom du dossier, titre TMDB trouvé} à l'IA pour qu'elle détecte les erreurs évidentes.
  4. Pass 3 — "Lequel tu choisis ?" : quand l'IA dit "c'est faux", chercher 15 candidats sur TMDB et demander à l'IA de choisir le bon.

Un cron tourne toutes les 30 minutes pour faire les passes 1 et 2 sur les nouvelles entrées. La passe 3 ne se déclenche que quand la passe 2 trouve un faux positif. En pratique, sur 290 dossiers, la passe 3 n'a tourné que 9 fois.

Les fails mémorables : un bestiaire du faux positif

Avant de parler d'optimisation, quelques perles. Voici ce que TMDB retournait quand on lui envoyait les noms tels quels :

Nom du dossierCe que TMDB a matchéRéaction
Vol 1Kill Bill : Volume 1...
Vol 2Les Gardiens de la Galaxie Vol. 2Ah.
CoversAlien : CovenantPardon ?
CIACIA (le film)Techniquement correct.
PLAYLISTZoey et son incroyable playlistNon.
MovieScary MovieJ'aurais dû m'en douter.
STREAMStream (le film)Il y a un film qui s'appelle Stream ???
WiiWii_ja!Je ne veux même pas savoir.

La première leçon : TMDB trouve toujours quelque chose. Même pour "CLIPINF" (un dossier technique de Blu-ray), il retournait un résultat avec un poster. Il faut donc aussi savoir ne pas chercher.

Étape zéro : la liste noire des mots génériques

Avant même l'IA, j'ai dû écrire un skipPattern — un regex qui filtre les noms qui ne seront jamais des titres de films :

vol\s*\d+, movie, films?, série, covers?, wii, nds, switch, 3ds, xbox,
bdmv, clipinf, playlist, stream$, meta, backup, samples?, nfo...

30+ patterns. Le piège : "stream" doit matcher "STREAM" (le dossier Blu-ray) mais pas "Streaming Wars" (un film South Park). D'où le stream$ ancré en fin de chaîne. Même chose pour "movie" vs "The Movie Database". Les regex, c'est jamais fini.

Pass 1 : le prompt qui skip trop

Le prompt initial était raisonnable : "extrais le titre propre du film ou de la série à partir de ce nom de fichier". Testé sur 290 noms réels, résultat : 72 faux skips.

L'IA voyait "Naruto.INTEGRALE" et se disait : "INTEGRALE, c'est pas un titre, c'est un descripteur. Skip." Pareil pour "Pokemon La Series" — "La Series" n'est pas un titre. Et les courts-métrages Pikachu ? "Les Vacances De Pikachu", skip, c'est un bonus.

Le fix tenait en trois règles ajoutées au prompt :

  • "En cas de doute, skip=false" — le filet de sécurité. Un faux positif sera corrigé par le pass 2. Un faux skip est perdu pour toujours.
  • "GARDE les collections et intégrales" — INTEGRALE, COLLECTION, COMPLETE sont des indicateurs de série, pas des raisons de skipper.
  • "Traduis les titres connus" — "Despicable Me" doit devenir "Moi, moche et méchant" pour que TMDB trouve le résultat français.

Résultat v2 : 72 → 41 skips. Naruto, Pokemon, Batman, les Looney Tunes, les courts-métrages Pikachu — tous récupérés. Et surtout : zéro régression. Aucun dossier qui marchait avant ne s'est cassé.

Pass 2 : quand l'IA ne connaît pas le contexte

La passe 2 envoyait des paires comme {"S01", "Saison 1"} et demandait : "est-ce que c'est le bon match ?" Sur 247 paires, l'IA a signalé 55 erreurs. Problème : 46 d'entre elles étaient fausses.

Pourquoi ? L'IA ne savait pas que mon système avait un mécanisme dédié pour les saisons. Quand elle voyait "S01" matché à "Saison 1", elle se disait : "S01 tout seul, ça ne veut rien dire, c'est sûrement un mauvais match." Alors qu'en réalité, c'est le poster de la saison 1 récupéré via l'API TMDB /tv/{id}/season/1 à partir de la série parente. C'est correct.

34 saisons des Simpsons. 11 saisons de Walking Dead. 4 saisons de Batman. Toutes signalées comme "faux positif" alors qu'elles étaient parfaitement correctes.

Le fix : expliquer le contexte à l'IA. Une section "cas spéciaux" dans le prompt qui dit explicitement : les saisons matchées à leur numéro, c'est normal. Les traductions, c'est normal. Les collections, c'est normal.

Résultat : 55 → 9. Les 9 restants ? Tous de vrais problèmes. Kill Bill, Gundam ZZ, Demon Slayer — des matchs objectivement foireux.

Pass 3 : TMDB et le piège de la popularité

La passe 2 a détecté que "Naruto" était mal matché et a suggéré de chercher "Naruto" sur TMDB. Super. Sauf que TMDB retourne les résultats par popularité. Et Naruto Shippuden est plus populaire que le Naruto original. Prendre le premier résultat, c'est refaire exactement la même erreur.

La solution : ne pas choisir automatiquement. Récupérer les 15 premiers candidats (séries, films, tous mélangés), et demander à l'IA de choisir le bon en lui donnant le contexte du nom de fichier. "INTEGRALE" dans le nom → c'est la série complète, pas un film dérivé. "S01" → c'est une série TV, pas un film.

L'IA reçoit la liste et répond juste {"idx": 1} — l'index du bon candidat. Pour Naruto, elle choisit correctement la série originale de 2002 plutôt que Shippuden ou le film. Le mot "INTEGRALE" dans le nom du fichier fait toute la différence.

Petit piège technique : parfois l'IA ajoute une explication après le JSON. {"idx": 1} parce que INTEGRALE indique la série complète... Ce qui casse le parsing JSON classique. Le fix : extraire le {"idx": N} par regex plutôt que de parser tout le texte.

Les chiffres

PasseAvant optimisationAprèsGain
Extraction (deviner le titre)72 faux skips41-43%
Vérification (détecter les erreurs)55 faux négatifs9 vrais problèmes-84%
Choix du candidat4 échecs de parsing0-100%

Le tout testé sur 290 entrées réelles. Pas des exemples choisis — toute la base, y compris les cas tordus genre "N° 057 - 2000 Walt Disney - Les Aventures de Tigrou et de Winnie l'Ourson.avi".

Ce que j'en retiens

Le 80/20 s'applique aux prompts. 46 faux négatifs sur 55 venaient d'un seul pattern : les dossiers de saison. Une ligne dans le prompt a éliminé 84% des erreurs. Pas besoin de réécrire le prompt entier — identifier le pattern dominant et le traiter.

"En cas de doute, ne fais rien" est souvent le meilleur choix. Pour la passe 1 : "en cas de doute, skip=false". Pour la passe 2 : "en cas de doute, correct=true". L'idée : les couches suivantes rattraperont. Mieux vaut un faux positif qu'on corrigera qu'un faux négatif qu'on perd.

L'IA ne connaît pas votre architecture. Quand le prompt de vérification voyait "S01 → Saison 1", il ne savait pas que c'était un match légitime via un mécanisme de saisons dédié. Il faut expliquer le contexte métier à l'IA, pas juste lui demander de vérifier.

Le parsing compte autant que le prompt. Le prompt peut être parfait — si l'IA ajoute un paragraphe d'explication après le JSON et que votre json_decode() plante, c'est pareil. Parsez de manière défensive.

Comme dans l'article sur la boucle d'itération : le prompt qui marche le mieux n'est pas celui qui donne le plus d'instructions. C'est celui qui décrit les cas limites avec précision. "Les saisons sont CORRECTES" vaut plus que 20 lignes de règles génériques.

Commentaires (0)