Retour aux projets
IA | Projet personnel · 2026 · En cours

Radar 🎯

TypeScript
LangGraph.js
Mistral API
pgvector
PostgreSQL
Supabase
Express
Zod
Next.js
Chart.js

Contexte

Je cherche un poste en IA et automatisation pour septembre 2026. En relisant mes projets, trois briques que le marché attend sur une offre « AI Engineer » manquaient : la recherche vectorielle (mes RAG précédents utilisaient du full-text PostgreSQL, jamais d'embeddings), l'orchestration d'agents avec un framework nommé, et l'évaluation de LLM — aucun de mes projets ne mesurait la qualité des réponses d'un modèle.

Radar couvre les trois d'un coup, sur un cas d'usage que j'utilise vraiment : trier les offres auxquelles je postule.

Ce que fait Radar

  • Ingestion : je colle le texte d'une offre, elle est parsée en métadonnées structurées (stack, séniorité, localisation, type de contrat) par function calling, puis embeddée et stockée.
  • Recherche vectorielle : retrouver les offres proches d'une requête libre (« stage IA à Paris avec du RAG »), par similarité cosinus.
  • Agent de matching : un graphe à trois étapes qui extrait les exigences, calcule un score justifié critère par critère, puis rédige une accroche de candidature.
  • Évaluation : un jeu de 18 offres annotées à la main mesure si le score tombe dans la fourchette attendue, et un second appel LLM juge les accroches sur leur fidélité factuelle.
  • Observabilité : coût, tokens et latence de chaque appel LLM, restitués par étape d'agent.

Approche technique

Recherche vectorielle. Les offres sont embeddées avec mistral-embed (1024 dimensions) et stockées dans PostgreSQL avec pgvector, index HNSW en vector_cosine_ops. J'embed le texte brut plutôt qu'un résumé : à l'échelle d'une offre d'emploi, ça tient largement dans la fenêtre du modèle sans chunking, et ça garde les nuances du texte pour la recherche.

Agent multi-étapes. Le graphe LangGraph est une machine à états explicite, pas un enchaînement de prompts : chaque nœud a une entrée/sortie typée, est testable isolément, et l'aiguillage après le scoring est une décision de code — si le score est sous le seuil, on ne dépense pas un appel LLM à rédiger une accroche pour une offre déjà écartée. L'extraction gère un retry localisé puis un fallback : un JSON malformé par le modèle ne doit pas faire tomber tout le graphe.

Score justifié. Le scoring refuse volontairement une note opaque. Il retourne un détail par critère (stack, séniorité, localisation) plus un raisonnement court, pour pouvoir dire pourquoi un score est bas. La pondération est assumée et documentée : la stack compte double, sauf quand l'offre n'en mentionne aucune (un poste de pilotage) — auquel cas la pondérer écraserait des critères pourtant pertinents.

Anti-hallucination. Le prompt de rédaction interdit explicitement de mentionner un fait absent du profil. Le profil lui-même (profile.json) est recopié à la main depuis mon CV réel, jamais enrichi pour améliorer un score : l'intérêt du projet est de rester honnête.

Évaluation versionnée. Le dataset compare à une fourchette et non à une valeur exacte — un LLM n'est pas déterministe, exiger un score au point près produirait un test qui échoue au hasard. Chaque run est horodaté pour comparer avant/après un changement de prompt.

Coût et latence. Chaque appel LLM est enregistré avec ses tokens et son coût estimé au moment de l'appel — pas recalculé à la lecture, sinon un changement de tarif réécrirait silencieusement l'historique. Le coût des runs d'éval est isolé de celui de la recherche d'emploi.

Ce que ça a appris

Le résultat le plus instructif est venu de l'éval elle-même. Avec 6 offres, la précision était à 83 % ; en passant à 18 offres plus variées, elle est descendue à 72 %. Le petit échantillon masquait une vraie faiblesse : sur les offres de pilotage de projet, sans stack technique à comparer, l'agent sous-évalue une expérience métier pourtant pertinente. C'est exactement ce à quoi sert un eval set — remplacer une impression par une mesure, y compris quand la mesure est moins flatteuse.

Limites connues

  • Ingestion manuelle : pas de scraping automatique (fragile, souvent contraire aux CGU, et sans rapport avec les compétences visées).
  • Le LLM-juge partage les biais du modèle qu'il évalue : le dataset annoté à la main reste la référence.
  • La calibration sur les offres de pilotage reste le principal chantier ouvert.
  • Les prix Mistral sont figés dans le code : le coût affiché est un estimé, la facturation réelle fait foi.

Statut

En cours. Le backend, l'éval et le dashboard sont fonctionnels et validés en conditions réelles. Le prochain chantier est la calibration du scoring sur les offres non techniques, et le remplacement progressif des offres du dataset par de vraies annonces reçues au fil de la recherche.

Afouanee.dev

© 2026 Afouane MOUHAMAD