[{"data":1,"prerenderedAt":384},["ShallowReactive",2],{"project-\u002Fprojects\u002Fscanauto-fr":3,"related-\u002Fprojects\u002Fscanauto-fr":362,"legal-link-fr":382},{"id":4,"title":5,"body":6,"description":340,"extension":341,"git":342,"git_soon":345,"meta":346,"navigation":347,"order":334,"path":348,"seo":349,"stack":350,"stem":355,"type":356,"updated":357,"web":358,"__hash__":361},"projects_fr\u002Fprojects\u002Fscanauto.md","Scan Auto",{"type":7,"value":8,"toc":323},"minimark",[9,17,22,25,29,109,113,191,195,198,201,205,210,216,218,222,228,230,234,240,242,246,252,254,258,261,275,281,287,293,302,305,311,313,317],[10,11,12,16],"p",{},[13,14,15],"strong",{},"Savoir en moins de 60 secondes si une annonce de voiture d'occasion est honnête ou piégée"," — un outil né de ma propre galère d'acheteur, qui transforme une URL leboncoin en rapport d'analyse complet.",[18,19,21],"h2",{"id":20},"quel-est-le-contexte-de-scan-auto","Quel est le contexte de Scan Auto ?",[10,23,24],{},"ScanAuto est une application SaaS full-stack qui génère un rapport d'analyse complet sur une annonce de véhicule d'occasion (voiture ou moto). L'utilisateur colle une URL leboncoin ou un texte brut et reçoit en moins de 60 secondes : un score global, une analyse de fiabilité moteur, une projection de coûts sur 5 ans et une stratégie de négociation argumentée. Le projet est né d'un besoin personnel avant tout : en cherchant ma propre voiture d'occasion, j'ai réalisé que les acheteurs particuliers n'ont aucun outil pour évaluer rapidement si une annonce est honnête ou piégée.",[18,26,28],{"id":27},"quelle-stack-technique","Quelle stack technique ?",[30,31,32,44,61,71,81,91,97,103],"ul",{},[33,34,35,38,39,43],"li",{},[13,36,37],{},"React 18 + Vite"," — SPA avec routing hash-based (sans serveur), lazy loading par page, ",[40,41,42],"code",{},"useReducer"," pour la gestion d'état de l'analyse. Vite a été choisi pour la rapidité du dev server et la simplicité de configuration comparé à CRA.",[33,45,46,49,50,53,54,53,57,60],{},[13,47,48],{},"Tailwind CSS 3 + CSS variables"," — Système de couleurs monochrome défini en variables CSS (",[40,51,52],{},"--bg",", ",[40,55,56],{},"--surface",[40,58,59],{},"--accent","…), switchable dark\u002Flight sans JavaScript. Typographie Space Grotesk (display) + Inter (corps).",[33,62,63,66,67,70],{},[13,64,65],{},"Django 4.2 + DRF"," — Backend stateless : aucune base de données locale (",[40,68,69],{},"DATABASES = {}","), pas de migrations. Choix assumé pour un backend purement orienté orchestration d'API.",[33,72,73,76,77,80],{},[13,74,75],{},"Supabase"," — Auth JWT + PostgreSQL avec RLS (Row-Level Security). Chaque table filtre sur ",[40,78,79],{},"user_id = auth.uid()"," côté base : isolation des données sans logique applicative supplémentaire.",[33,82,83,86,87,90],{},[13,84,85],{},"Anthropic Claude API (anthropic==0.40.0)"," — Haiku 4.5 pour les étapes simples\u002Frapides (extraction, négociation, infos pratiques), Sonnet 4.6 pour les étapes analytiques (scoring, coûts de maintenance). Prompt caching ",[40,88,89],{},"ephemeral"," sur tous les system prompts.",[33,92,93,96],{},[13,94,95],{},"Firecrawl"," — Extraction Markdown depuis les URLs d'annonces. Gère les protections anti-bot sans parsing HTML manuel ; le Markdown produit est directement injectable dans Claude.",[33,98,99,102],{},[13,100,101],{},"Gunicorn"," — 2 workers, 4 threads, timeout 300s pour les streams SSE longs.",[33,104,105,108],{},[13,106,107],{},"Chrome Extension MV3"," — Content script sur leboncoin.fr, side panel, service worker. Aucun build step — JavaScript natif.",[18,110,112],{"id":111},"points-techniques-notables","Points techniques notables",[30,114,115,129,145,155,161,167,185],{},[33,116,117,120,121,124,125,128],{},[13,118,119],{},"Pipeline 5 étapes avec parallélisme Python"," : R1 (extraction) → R2+R5 en parallèle via ",[40,122,123],{},"threading.Thread"," + ",[40,126,127],{},"Queue"," (scoring + infos pratiques) → R3 (coûts maintenance) → R4 (négociation). Le GIL Python ne bloque pas les I\u002FO réseau : le gain est réel (~30% de temps total).",[33,130,131,134,135,53,138,53,141,144],{},[13,132,133],{},"Calculs côté serveur avant tout appel LLM"," : ",[40,136,137],{},"km_par_an",[40,139,140],{},"age_vehicule",[40,142,143],{},"positionnement_pourcentage",", projections kilométriques à 2 et 5 ans — tous calculés en Python avant d'être injectés comme faits immuables dans les prompts. Évite les erreurs de calcul des LLMs sur des valeurs critiques.",[33,146,147,150,151,154],{},[13,148,149],{},"Streaming SSE progressif"," : chaque étape complète émet un ",[40,152,153],{},"event: r1|r2|…|complete"," en SSE. Le frontend dispatche chaque événement dans un reducer et révèle les sections du rapport au fur et à mesure. L'utilisateur voit le rapport se construire en temps réel sans attendre la fin du pipeline.",[33,156,157,160],{},[13,158,159],{},"Context slimming pour R3 et R4"," : ces étapes reçoivent uniquement les champs pertinents de R1\u002FR2 (pas le JSON complet), pour éviter le token bloat sur Sonnet. L'économie est d'environ 30-40% de tokens d'input sur ces deux appels.",[33,162,163,166],{},[13,164,165],{},"BYOK chiffré (Bring Your Own Key)"," : les clés Anthropic des utilisateurs sont chiffrées server-side avec Fernet (cryptography lib), déchiffrées en mémoire uniquement au moment de l'appel. Le backend ne stocke jamais la clé en clair ; les utilisateurs gardent le contrôle de leurs quotas.",[33,168,169,172,173,176,177,180,181,184],{},[13,170,171],{},"Extension Chrome MV3 — extraction JSON-LD"," : le content script parse le ",[40,174,175],{},"@type:Vehicle"," JSON-LD de leboncoin en priorité (donnée structurée fiable), avec 4 stratégies de fallback sur le DOM pour les champs absents du JSON-LD (critères, photos, description). Les données transitent via ",[40,178,179],{},"chrome.storage.session"," vers le side panel, puis vers l'app via URL prefill (",[40,182,183],{},"#\u002F?prefill=\u003CJSON>",").",[33,186,187,190],{},[13,188,189],{},"Dégradation gracieuse"," : R3 et R4 peuvent échouer (timeout, erreur API) sans bloquer le rapport. Le frontend affiche un état d'erreur localisé sur la section concernée ; les sections précédentes restent affichées.",[18,192,194],{"id":193},"ce-que-jai-appris-apporté","Ce que j'ai appris \u002F apporté",[10,196,197],{},"Le challenge principal a été la conception du pipeline d'orchestration : gérer la dépendance entre étapes, le parallélisme contrôlé, les timeouts et la propagation d'erreurs partielles, tout en maintenant un streaming fluide vers le client. Cela m'a forcé à penser chaque prompt comme une interface contractuelle — inputs stricts, outputs JSON schématisés, règles métier explicites pour éviter les hallucinations sur des données financières.",[199,200],"hr",{},[18,202,204],{"id":203},"schémas","Schémas",[206,207,209],"h3",{"id":208},"architecture-globale","Architecture globale",[10,211,212],{},[213,214],"img",{"alt":209,"src":215},"\u002Fdiagrams\u002Fscanauto\u002F01-architecture-globale.svg",[199,217],{},[206,219,221],{"id":220},"pipeline-danalyse-séquence","Pipeline d'analyse (séquence)",[10,223,224],{},[213,225],{"alt":226,"src":227},"Pipeline d'analyse","\u002Fdiagrams\u002Fscanauto\u002F02-pipeline-sequence.svg",[199,229],{},[206,231,233],{"id":232},"flux-extension-chrome-app","Flux extension Chrome → app",[10,235,236],{},[213,237],{"alt":238,"src":239},"Flux extension Chrome","\u002Fdiagrams\u002Fscanauto\u002F03-extension-chrome-flow.svg",[199,241],{},[206,243,245],{"id":244},"sélection-du-modèle-par-étape","Sélection du modèle par étape",[10,247,248],{},[213,249],{"alt":250,"src":251},"Sélection du modèle","\u002Fdiagrams\u002Fscanauto\u002F04-model-selection.svg",[199,253],{},[206,255,257],{"id":256},"dépendances-et-pipeline-ia","Dépendances et pipeline IA",[10,259,260],{},"L'analyse repose sur 5 appels Claude enchaînés et partiellement parallélisés. Les dépendances entre étapes sont strictes : chaque appel ne reçoit que les données des étapes dont il a réellement besoin.",[10,262,263,264,53,267,270,271,274],{},"Avant tout appel LLM, le serveur pré-calcule ",[40,265,266],{},"km\u002Fan",[40,268,269],{},"âge du véhicule"," et ",[40,272,273],{},"âge de l'annonce"," — injectés comme faits immuables pour éviter les erreurs de calcul côté modèle.",[10,276,277,280],{},[13,278,279],{},"R1 — Extraction (Haiku 4.5)"," — Premier appel, aucune dépendance. L'annonce brute est envoyée telle quelle. Claude extrait les données structurées : identification, kilométrage, options, signaux vendeur, informations manquantes.",[10,282,283,286],{},[13,284,285],{},"R2 + R5 — Scoring & Infos pratiques (Sonnet 4.6 + Haiku 4.5, en parallèle)"," — Dépendent tous les deux de R1 uniquement. Dès que R1 termine, les deux appels partent simultanément via des threads Python indépendants. R2 calcule le score global, positionne le prix marché et analyse la fiabilité moteur. R5 traite en parallèle les infos réglementaires : Crit'Air, carte grise, assurance, consommation.",[10,288,289,292],{},[13,290,291],{},"R3 — Coûts & Vigilance (Sonnet 4.6)"," — Dépend de R1 + R2. Reçoit un contexte allégé (champs pertinents uniquement, pas les JSONs complets) pour limiter les tokens sur Sonnet. Produit les travaux imminents, la projection de coûts sur 5 ans et une checklist d'inspection spécifique au modèle.",[10,294,295,298,299,301],{},[13,296,297],{},"R4 — Négociation (Haiku 4.5)"," — Dernier appel, contexte minimal : identité du véhicule (R1), prix et signaux vendeur (R2), résumé des risques (R3), et le ",[40,300,143],{}," pré-calculé. Génère un prix cible argumenté, un message vendeur neutre et une projection de décote à 2 et 5 ans.",[10,303,304],{},"Chaque résultat est streamé en SSE dès qu'il est prêt — le rapport se construit progressivement côté frontend. Si R3 ou R4 échouent, le rapport s'affiche quand même avec un état d'erreur localisé sur la section concernée.",[10,306,307],{},[213,308],{"alt":309,"src":310},"Pipeline IA","\u002Fdiagrams\u002Fscanauto\u002F05-pipeline-ia.svg",[199,312],{},[206,314,316],{"id":315},"architecture-de-sécurité-byok","Architecture de sécurité BYOK",[10,318,319],{},[213,320],{"alt":321,"src":322},"Architecture BYOK","\u002Fdiagrams\u002Fscanauto\u002F06-byok-security.svg",{"title":324,"searchDepth":325,"depth":325,"links":326},"",2,[327,328,329,330,331],{"id":20,"depth":325,"text":21},{"id":27,"depth":325,"text":28},{"id":111,"depth":325,"text":112},{"id":193,"depth":325,"text":194},{"id":203,"depth":325,"text":204,"children":332},[333,335,336,337,338,339],{"id":208,"depth":334,"text":209},3,{"id":220,"depth":334,"text":221},{"id":232,"depth":334,"text":233},{"id":244,"depth":334,"text":245},{"id":256,"depth":334,"text":257},{"id":315,"depth":334,"text":316},"SaaS d'analyse de véhicules d'occasion par IA — pipeline Claude multi-étapes avec streaming SSE, auth Supabase, BYOK et extension Chrome.","md",[343,344],"Répertoire Git","https:\u002F\u002Fgithub.com\u002FOwenLB\u002Fscanauto",false,{},true,"\u002Fprojects\u002Fscanauto",{"title":5,"description":340},[351,352,353,354,75,95,107],"React","Tailwind CSS","Django","Claude API","projects\u002Fscanauto","App web","2026-07-17",[359,360],"Voir l'app","https:\u002F\u002Fscanauto.netlify.app","amrL82VYgOtxCzRsHrLLBJNh4A8vZEb9SoWSBi1onH8",[363,368,372,377],{"title":364,"type":365,"path":366,"order":367},"Finixa","App web & mobile","\u002Fprojects\u002Ffinixa",1,{"title":369,"type":370,"path":371,"order":325},"Peliculle","App iOS","\u002Fprojects\u002Fpeliculle",{"title":373,"type":374,"path":375,"order":376},"Portfolio Développeur","Site","\u002Fprojects\u002Fportfolio-dev",4,{"title":378,"type":379,"path":380,"order":381},"Portfolio Photo","Site full-stack","\u002Fprojects\u002Fportfolio-photo",5,{"title":383},"Mentions légales",1784470460528]