Automatiser ses Candidatures avec OpenClaw : Cas Réel DevOps

Avant : des candidatures dispersées et des doublons risqués. Après : 1 soumission vérifiée par tick. Comment Michał automatise sa recherche emploi avec OpenClaw.

Automatiser ses Candidatures avec OpenClaw : Cas Réel DevOps

Bon dimanche ! ☕

Aujourd'hui, on plonge dans un cas concret : un agent OpenClaw qui ne "cherche pas des idées", mais exécute une campagne de candidature réelle, une offre à la fois, avec navigateur, RAG, règles métier et preuve de soumission avant écriture dans le tracker.

Le cas montre où un agent personnel devient utile : pas dans la magie, mais dans la discipline opérationnelle. Un workflow répétitif, risqué si on le bâcle, et suffisamment contextuel pour qu'un simple script ne suffise pas.


🎯 Le Cas : Candidatures Emploi Automatisées

Auteur : mstaszew-dev (source GitHub)
Contexte : Michał Staszewski mène une campagne de recherche d'emploi logiciel entre Israël, Pologne et Europe remote.
Objectif : faire traiter par OpenClaw une candidature valide par tick, sans dupliquer les candidatures, sans copier l'état de campagne, et sans enregistrer une soumission non confirmée.


📖 L'Histoire

Le problème de départ n'est pas simplement "trouver des offres". C'est le workflow complet d'une campagne sérieuse : ouvrir les bons portails, filtrer les rôles selon des critères stricts, éviter les doublons, adapter les notes de candidature, remplir les formulaires dans un navigateur déjà connecté, vérifier la page de succès, puis seulement mettre à jour le tracker.

Le dépôt openclaw-job-search se présente comme un workspace OpenClaw dédié à cette campagne. Le README explique que l'agent s'exécute "against the live job-search campaign without copying campaign state". Cette phrase est importante : l'agent ne maintient pas une copie parallèle des candidatures. Il travaille sur l'état canonique via des liens vers campaign/ et joblooper/, tout en gardant les secrets dans les mécanismes existants.

La première tentative naïve, dans ce genre de système, serait d'automatiser tout le funnel avec un gros script : scraping, scoring, formulaire, écriture JSON. Le dépôt fait l'inverse. Il garde le shell pour lancer le tick et vérifier les préconditions, puis confie les décisions contextuelles à l'agent. Les règles critiques sont écrites dans AGENTS.md et répétées dans le launcher run-one-job, notamment l'interdiction d'éditer directement tracker.json.

La solution finale ressemble donc à un opérateur assisté par navigateur : OpenClaw utilise un Chrome déjà lancé via CDP, passe par Playwright MCP, consulte un RAG de l'historique de campagne, cherche dans Gmail pour éviter les doublons récents, score l'offre avec le contexte CV, puis ne soumet qu'une seule candidature. Le détail le plus sain : le tracker n'est mis à jour qu'après confirmation explicite dans le navigateur.


🔧 Le Setup Technique

Architecture Agent

Le workspace est organisé autour d'un agent principal persistant, mais le launcher prévoit aussi une rotation de session si le contexte devient trop gros. L'architecture publique documentée ressemble à ceci :

  • Session principale : agent:main:job-search-campaign
  • Navigateur : Chrome existant exposé en CDP sur http://127.0.0.1:9222
  • MCP : Playwright connecté au navigateur connecté de l'utilisateur
  • RAG : recherche sémantique sur l'historique de candidatures et les documents de campagne
  • Etat canonique : campaign/tracker.json, jamais modifié à la main
  • Ecriture autorisée : campaign/update_tracker.py submitted '<json>', uniquement après confirmation
  • Modèle : routeur OpenRouter gratuit, avec credential en SecretRef

Ce n'est pas une automation "headless". C'est volontaire : les sites de recrutement changent souvent, nécessitent parfois une session connectée, et affichent des confirmations qu'un script HTTP ne verrait pas forcément.

Fichiers Clés

SOUL.md

Be resourceful before asking. Read the file, check the context, search for it.
Come back with answers, not questions.

Même si ce SOUL.md reste générique, il donne le ton : l'agent est censé agir avec autonomie, mais pas faire n'importe quoi hors machine.

AGENTS.md

For every explicit `continue` or `run-one-job` request:
1. Read `campaign/AGENT_TICK.md`, `campaign/CONTEXT.md`, and current `campaign/tracker.json`.
2. Select exactly one mid-to-senior Java/Kotlin/Spring, PHP/Laravel/Symfony, or Node/React/NestJS role.
3. Dedupe using RAG (`rag_search_apps`) and Gmail search via Playwright.
4. Use the `playwright` MCP server attached to the existing Chrome CDP endpoint.
7. Submit only the selected application. Verify an explicit success message or thank-you URL.

Le fichier agit comme runbook. Il encode les marchés acceptés, les types de postes, les exclusions, les règles de salaire, et surtout la règle "une seule candidature confirmée par tick".

Launcher

if ! curl -fsS --max-time 2 http://127.0.0.1:9222/json/version >/dev/null; then
  print -u2 "Chrome CDP is not reachable at http://127.0.0.1:9222."
  exit 2
fi

/Users/mst/bin/openclaw agent \
  --agent main \
  --model free \
  --local \
  --timeout 600 \
  --thinking high \
  --session-key "agent:main:job-search-campaign" \
  --message "$msg"

La précondition Chrome est un bon choix : si le navigateur connecté n'est pas disponible, l'agent ne démarre pas. Cela évite les demi-runs qui terminent sur une page de login ou un état fantôme.

Étapes de Mise en Place

  1. Créer un workspace OpenClaw dédié à la campagne.
mkdir openclaw-job-search
cd openclaw-job-search
  1. Relier l'état de campagne réel plutôt que le dupliquer.
ln -s /Users/mst/Downloads/job-search/job-apply campaign
ln -s /Users/mst/ZCodeProject/joblooper joblooper
  1. Lancer Chrome avec CDP sur le port attendu, depuis le profil déjà connecté.
open -a "Google Chrome" --args --remote-debugging-port=9222
  1. Vérifier les briques OpenClaw avant un tick.
openclaw gateway status
openclaw models status
openclaw mcp probe playwright
  1. Lancer une candidature unique.
job-search-agent
  1. Ajouter une consigne ponctuelle si nécessaire.
job-search-agent "Prefer an Israeli remote Java/Spring role this tick."

📊 Les Résultats

Gains mesurés :

  • 1 candidature confirmée maximum par tick, ce qui limite le risque d'emballement.
  • 0 écriture directe autorisée dans le tracker : toutes les soumissions passent par update_tracker.py.
  • 60 jours de recherche Gmail prévus dans le gate de déduplication, en plus du RAG.

Résultats non publiés :

Le dépôt ne publie pas de compteur final de candidatures, de taux de réponse, ni de temps gagné hebdomadaire. C'est une limite importante pour l'analyse : on peut vérifier l'architecture et les garde-fous, pas affirmer que le système a obtenu plus d'entretiens.

Limites rencontrées :

  • Le workflow dépend fortement d'un Chrome existant et connecté.
  • Les règles de marché sont très personnalisées : Israël, Pologne, Europe remote, B2B, stacks spécifiques.
  • Le système reste fragile face aux portails qui changent leurs formulaires ou ajoutent des captchas.

🧐 Analyse Critique

✅ Ce qui est bien fait

  • Le tracker est protégé. Le launcher répète que tracker.json ne doit jamais être édité directement. C'est exactement le genre de règle qui évite une corruption silencieuse dans une automation longue.
  • Le système exige une preuve navigateur. L'agent doit voir une confirmation ou une URL de remerciement avant d'enregistrer une candidature. Cette séparation entre action externe et écriture interne est saine.
  • La déduplication est multi-signal. Le RAG capte les similarités de rôle et d'entreprise ; Gmail couvre l'historique réel des échanges. Pour une campagne d'emploi, c'est plus robuste qu'un simple match exact sur le nom de l'entreprise.
  • Le périmètre par tick est volontairement petit. Une seule candidature par run, puis stop. C'est moins spectaculaire qu'un bot qui spamme 200 formulaires, mais beaucoup plus contrôlable.

⚠️ Points d'attention

  • Le navigateur connecté est une surface sensible. Playwright sur Chrome existant donne accès à des sessions réelles. Il faut limiter le workspace, surveiller les actions et éviter les prompts externes non filtrés.
  • Le scoring reste subjectif. Le dépôt interdit les scripts automatiques de scoring et demande à l'agent de raisonner lui-même. C'est flexible, mais cela rend les décisions plus difficiles à auditer si les traces ne sont pas bien conservées.
  • Les règles métier sont longues. Plus un runbook contient d'exceptions, plus il faut tester les cas limites : salaire manquant, rôle hybride ambigu, entreprise déjà contactée, portail qui masque le titre exact.
  • L'autonomie externe mérite un consentement explicite. Ici, la soumission est autorisée par une commande de campagne. Pour d'autres lecteurs, c'est une frontière à ne pas banaliser : candidater, envoyer un email ou contacter un recruteur engage une personne réelle.

💡 Améliorations possibles

  • Ajouter un rapport hebdomadaire public ou privé : candidatures envoyées, doublons évités, portails fiables, taux de confirmation.
  • Enregistrer un court audit JSON par tick : candidat sélectionné, raisons du score, preuves de déduplication, URL de confirmation.
  • Ajouter un mode dry-run qui prépare la candidature sans cliquer sur "submit", utile pour tester de nouveaux portails.
  • Documenter la configuration RAG minimale pour qu'un lecteur puisse reproduire la déduplication sans connaître tout l'historique de campagne.

🎓 Ce qu'on en retient

Leçons clés :

  1. Un bon agent OpenClaw n'est pas seulement un prompt : c'est un workspace, un runbook, des outils, des préconditions et des garde-fous.
  2. Les automatisations externes doivent écrire l'état après preuve, pas avant intention.
  3. Le RAG est particulièrement utile pour les workflows où les doublons ne se ressemblent jamais parfaitement.
  4. Limiter l'agent à une action confirmée par run donne une meilleure observabilité qu'un gros batch opaque.

Pour aller plus loin :


À dimanche prochain pour un nouveau cas ! 🦞