🔬 Fiche Technique · Architecture & Conception

CV Critic Agent

Agent autonome d'audit multi-agent du CV et du positionnement — critique globale, revue CV imprimable, stratĂ©gie priorisĂ©e et rapports versionnĂ©s. MigrĂ© d'une orchestration Node.js manuelle vers un repo Python CrewAI native.

Node.js Python 3.11+ CrewAI Mistral API Claude API BaseLLM adapter FastAPI SSE HMAC-SHA256 Fernet Turnstile Telegram Bot Resend slowapi Markdown Reports
Outil interne
Node.js legacy · CrewAI script · CrewAI native
Mistral par défaut · Anthropic en fallback
Itération de positionnement CV
01 — Objectif

Objectif produit

CV Critic & Strategy Agent est un workflow interne d'audit du CV et du positionnement professionnel. Objectif volontairement opposé à SAIF-IA : challenger le discours au lieu de vendre.

🎯

Ce workflow ne remplace pas la dĂ©cision humaine. Il sert d'outil d'aide Ă  l'itĂ©ration — un Ɠil externe simulĂ© sur un dispositif qu'on ne peut plus voir objectivement.

02 — Architecture

Architecture globale

flowchart TD subgraph Sources["📂 Sources portfolio"] D["data.ts\nchatbot-knowledge.ts\nsystem-prompt.ts\ntools.ts\n/cv page\nfiches techniques"] end Sources --> GC["đŸ‘ïž Global Critic\nregard externe\nsans context.md"] Sources --> PCC["📄 Printable CV Critic\nregard recruteur PDF\nsans context.md"] GC --> GM["reports/run/\nglobal.md"] PCC --> PM["reports/run/\nprintable-cv.md"] GM --> STRAT["🧭 Strategy Agent\nlit critiques + context.md"] PM --> STRAT CTX["⚙ context.md\ndĂ©cisions internes"] --> STRAT STRAT --> SM["reports/run/\nstrategy.md\nsummary.md"] SM --> LATEST["reports/latest/\n*.md"]

Flux 3 agents — critiques indĂ©pendantes + stratĂ©gie contextualisĂ©e · identique Node.js et CrewAI

â„č

L'architecture métier reste stable dans les trois implémentations. Ce qui change : le niveau d'abstraction, la formalisation des rÎles, le passage de contexte et la maintenabilité du runner.

03 — Les 3 agents

Agents & séparation des rÎles

đŸ‘ïž
Global Critic
External Portfolio Critic · regard externe sur l'ensemble du dispositif

Simule un recruteur qui découvre le portfolio complet : CV public, chatbot SAIF-IA, fiches techniques, knowledge serveur. Ne lit pas le contexte interne. Son regard doit rester indépendant et dur. Produit global.md.

📄
Printable CV Critic
Printable CV & ATS Specialist · regard recruteur sur le CV seul

Simule un recruteur qui ouvre uniquement le CV imprimable/téléchargeable. Il juge la lisibilité A4, la clarté en 10 secondes, la cohérence titre/preuves, la compatibilité ATS. Ne lit pas le contexte interne. Produit printable-cv.md.

🧭
Strategy Agent
CV Strategy Synthesizer · transformation critiques → plan d'action

Seul agent à lire context.md (décisions internes validées). Lit les deux critiques et produit un plan d'action priorisé P0/P1/P2. Dans la version CrewAI, reçoit les outputs des deux autres agents via injection automatique (context=[task_global, task_cv]). Produit strategy.md.

Séparation des sources

AgentLit sources portfolioLit context.mdReçoit outputs autres agents
Global Critic✅ Portfolio complet (6 fichiers)❌ Non❌ Non
Printable CV Critic✅ CV seul (3 fichiers)❌ Non❌ Non — intentionnel
Strategy Agent❌ Non (lit les critiques)✅ Oui✅ Oui (global + CV)
â„č

Raison de l'indépendance des critics : chaque critic simule un regard externe naïf. Si le CV Critic lisait la critique globale, son verdict serait biaisé. La séparation est une rÚgle métier, pas une contrainte technique.

04 — SĂ©quences

Séquences d'un run

Trois modes sont conservĂ©s en parallĂšle. MĂȘme contrat de sortie, trois niveaux d'abstraction (SDK direct → script CrewAI → CrewAI native).

Étape 1 — Node.js legacy, orchestration manuelle

npm run critique-cv
  │
  ├─ charge .env.local (ANTHROPIC_API_KEY)
  ├─ prĂ©pare reports/<timestamp>/
  │
  ├─ [1] Global Critic — Anthropic SDK direct
  │    lit : data.ts · chatbot-knowledge · system-prompt · tools · /cv · fiches
  │    Ă©crit : global.md via writeFileSync
  │
  ├─ [2] Printable CV Critic — indĂ©pendant, sans contexte du [1]
  │    lit : data.ts · /cv · fiches techniques
  │    Ă©crit : printable-cv.md
  │
  ├─ [3] Strategy Agent
  │    lit : global.md + printable-cv.md + context.md (manuellement)
  │    Ă©crit : strategy.md
  │
  └─ summary.md + copie dans reports/latest/

Cette version contrĂŽle explicitement chaque Ă©tape : prompts, sĂ©quence, passage de contexte, Ă©criture des rapports — orchestration custom sans framework.

Étape 2 — Script Python CrewAI de migration

npm run critique-cv-crew
  │
  ├─ charge .env.local via python-dotenv
  ├─ prĂ©pare reports/<timestamp>/
  │
  ├─ Crew.kickoff() — Process.sequential
  │
  ├─ Task 1 : global_critic (External Portfolio Critic)
  │    role/goal/backstory formalisĂ©s · LLM via LiteLLM anthropic/claude-haiku-*
  │    context=[] — intentionnellement vide
  │    output_file → global.md (Ă©crit automatiquement par CrewAI)
  │
  ├─ Task 2 : cv_critic (Printable CV & ATS Specialist)
  │    indĂ©pendant — context=[] intentionnellement vide
  │    output_file → printable-cv.md
  │
  ├─ Task 3 : strategy_agent (CV Strategy Synthesizer)
  │    context=[task_global, task_cv] → injection automatique des outputs
  │    context.md injectĂ© dans la description de la tĂąche
  │    output_file → strategy.md
  │
  └─ write_report() + write_summary() → latest/

Étape 3 — CrewAI native autonome

PYTHONPATH=src python -m cv_critic_agent.main
  │
  ├─ charge .env.local / .env
  ├─ sĂ©lectionne le provider LLM
  │    par dĂ©faut : CV_CRITIC_PROVIDER=mistral
  │
  ├─ CVCriticCrew
  │    ├─ Global Critic
  │    ├─ Printable CV Critic
  │    └─ Strategy Agent
  │
  └─ reports/<timestamp>/ + reports/latest/

La version cible vit dans un repo séparé cv-critic-agent avec un package Python, une CLI, une configuration CrewAI native, des tests et un provider Mistral par défaut.

Durée typique : 60 à 120 secondes (3 appels LLM séquentiels, contexte large).

05 — Migration CrewAI

Migration Node.js → CrewAI

Trois implémentations maintenues cÎte à cÎte. La migration explicite les responsabilités : agents, tùches, prompts, sources, providers LLM, rapports et tests.

Avant — Node.js / Anthropic SDK

Orchestration custom

  • 3 appels SDK Anthropic directs
  • Agents nommĂ©s "CV Critic Agent" (pas de rĂŽle distinct)
  • Passage de contexte manuel (lire fichier → injecter dans prompt)
  • Écriture des fichiers via writeFileSync
  • Retry/erreurs gĂ©rĂ©s manuellement
  • ~330 lignes de plumbing orchestration
Aprùs — Python / CrewAI native

Repo agentique autonome

  • Agent/Task/Crew API dĂ©clarative
  • RĂŽles, goals et backstories formalisĂ©s par agent
  • Passage de contexte automatique via context=[task]
  • Helpers partagĂ©s pour prompts, sources et rapports
  • Provider Mistral par dĂ©faut, Anthropic en fallback
  • Tests mock sans appel API rĂ©el

Pourquoi CrewAI

Les 3 agents (Global Critic → Printable CV Critic → Strategy) correspondent au modĂšle Agent/Task/Crew : rĂŽles distincts, sĂ©quence fixe, un seul agent lit les outputs des autres. L'orchestration manuelle Node.js a Ă©tĂ© conservĂ©e en miroir pour permettre la comparaison directe entre les deux niveaux d'abstraction.

Ce qui ne change pas

Provider LLM paramétrable

CV_CRITIC_PROVIDER=mistral
CV_CRITIC_MODEL=mistral-medium-latest
MISTRAL_API_KEY=...

# fallback de comparaison
CV_CRITIC_PROVIDER=anthropic
CV_CRITIC_MODEL=claude-haiku-4-5-20251001
ANTHROPIC_API_KEY=...

Le provider n'est plus figé dans le code. Le mode mock reste le standard pour les tests, Mistral devient le choix par défaut pour les runs réels à coût réduit.

06 — Refactoring

Étapes techniques de refactoring

AvantRefactoringRésultat
Script Node.js monolithiqueExtraction sources, prompts, rapportsUnit tests possibles sans LLM
Prompts couplés au runnerprompts.py + sources.pyContrat lisible, partagé par 3 modes
Écriture fichiers dupliquĂ©ereports.pyreports/<timestamp> et latest/ centralisĂ©s
Provider Anthropic figéllm.py provider-agnosticMistral par défaut, Anthropic en fallback
Mock LLM heuristique fragileMockLLM(responses=, fallback=) injectableTests qui ne dépendent plus des prompts internes
Agent caché dans le portfolioRepo cv-critic-agent public sur GitHubProjet autonome, présentable et déployable
Indices magiques REPORT_SPECS[0/1]spec_by_slug("global") / spec_by_slug("printable-cv")Ajout/réorganisation de specs sans casser crew.py
Logique Mistral mĂȘlĂ©e Ă  llm.pyModule mistral_utils.py (clean messages + extraction)SĂ©paration des responsabilitĂ©s, importable
Sources copiées à la mainscripts/sync_sources.py --checkSync reviewable + détection drift CI
Pas de CI.github/workflows/ci.yml : Ruff + pytestLint et 16 tests mock verts Ă  chaque push
CV_CRITIC_PROVIDER mal validéRaise explicite sur valeur inconnueErreur claire au lieu de comportement silencieux
Bug : context.md ignoré en mode CrewAI nativebuild_strategy_prompt_header() + test régressionContrat éditorial préservé dans les 3 implémentations
YAML CrewAI scaffold (config/agents.yaml, tasks.yaml) en double avec prompts.pyDéfinition code-first des Agent/Task en PythonSource unique de vérité, prompts dynamiques (injection context.md runtime), YAML statique impossible
Pas de visibilité temps réel des runsEventBus (sync pub/sub) + FastAPI/SSE + Living Agent Graph React FlowRun mock en CLI rich, run en API streamé en SSE, run dans UI Vercel animé
Token statique X-API-Token partagĂ© en clair pour les runs rĂ©elsPhase 5 — gate HMAC + Fernet PII + Turnstile + Telegram approval + budget cap + UI Next.js (S1→S7)Real runs gardĂ©s derriĂšre approbation humaine, PII chiffrĂ©es at rest, dĂ©gradation silencieuse au cap journalier, 288 tests verts

Tests automatisés (288 tests, 0 appel API)

07 — Contexte interne

Gestion du contexte interne

Le fichier context.md contient les décisions internes déjà validées que le Strategy Agent doit respecter.

Exemples de décisions dans context.md

🎯

Le contexte sert Ă  ne pas recommander des actions dĂ©jĂ  Ă©cartĂ©es avec raison. Il Ă©vite les allers-retours sur des dĂ©cisions stabilisĂ©es. Les critics ne le lisent pas — leur regard doit rester externe.

08 — Sorties versionnĂ©es

Sorties & versioning

Chaque run gĂ©nĂšre un dossier horodatĂ© et une copie latest pour accĂšs rapide. Les trois implĂ©mentations produisent la mĂȘme structure.

reports/
  2026-06-04T19-16-50Z/       # run CrewAI native
    global.md                 # critique globale (252 lignes)
    printable-cv.md           # critique CV imprimable (252 lignes)
    strategy.md               # plan d'action P0/P1/P2 (180 lignes)
    summary.md                # résumé exécutif

  2026-05-14T21-52-40Z/       # run Node.js
    global.md
    printable-cv.md
    strategy.md
    summary.md

  latest/                     # dernier run (mis Ă  jour automatiquement)
    global.md
    printable-cv.md
    strategy.md
    summary.md
💡

Comparer deux runs : diff reports/<run1>/strategy.md reports/<run2>/strategy.md — permet de mesurer si les recommandations ont Ă©voluĂ© aprĂšs des mises Ă  jour du portfolio.

09 — DĂ©ploiement

Plan de déploiement CrewAI native

Phase 1 — CLI locale

Phase 2 — GitHub Actions CI

Phase 3 — API + UI locale

Phase 4 — DĂ©ploiement (livrĂ©)

Choix Render plutÎt que Vercel pour l'API : le backend est statefull (RunManager en RAM, threads, écriture fichiers reports/<timestamp>/, SSE long-lived 60-90s). Le modÚle serverless de Vercel Functions imposerait une réécriture vers Redis + queue + Blob storage. Render free tier accepte le design tel quel (1 container always-on, disk attaché, cold start 30s acceptable pour une démo).

Phase 5 — Access gate avec validation humaine

Ouvre les real runs au public derriĂšre un flow d'approbation human-in-the-loop. Plan complet et threat model : docs/PHASE5_ACCESS_GATE.md. 7 sprints, S1–S7 livrĂ©s au 9 juin 2026 (288 tests verts + UI 8 routes), reste sync portfolio + smoke prod.

Flow utilisateur cible

  1. Demandeur remplit un formulaire (nom, société, email, motif) protégé par Cloudflare Turnstile (siteverify server-side, score < 0.5 rejeté).
  2. Owner reçoit la demande sur Telegram (inline buttons Approve / Reject) et par email (liens HMAC signés, accept encodé dans le token pour bloquer le tampering URL).
  3. Owner approuve → session token HMAC envoyĂ© par email au demandeur, liĂ© Ă  son email, liĂ© Ă  son IP Ă  la 1Ăšre utilisation, valide 24 h, quota 3 runs.
  4. Budget cap quotidien (MAX_TOKENS_PER_DAY=200000) avec alertes Telegram à 80 % et 100 %, dégradation vers mock au-delà.

Threat model

6 adversaires (bot crawler, curieux, concurrent hostile, attaquant ciblĂ©, canal owner compromis, insider futur) × 6 surfaces (formulaire public, dĂ©cision owner, session requester, OAuth optionnel V2, stockage PII, exĂ©cution run). DĂ©cisions principales : Cloudflare Turnstile, magic-link admin (pas de token en URL), Fernet pour PII at rest, IP-binding au lieu de cookies cross-origin, rĂ©tention 7 j pending / 90 j dĂ©cidĂ©es avec endpoint RGPD DELETE.

Sprints livrés

SprintLivréModules
S1 — Security foundations2026-06-05security/crypto.py (HMAC sign/verify timing-safe, expiry encodĂ©), security/pii.py (Fernet), security/logging_filter.py (mask emails), security/security_middleware.py (Turnstile + slowapi + CSP/HSTS headers)
S2 — AccessRequest storage2026-06-06access_requests/models.py (state machine PENDING/APPROVED/REJECTED/CONSUMED/EXPIRED/REVOKED + IP binding + quotas), access_requests/store.py (JSON one-file-per-request, fcntl advisory locking, PII Fernet at rest, lazy TTL purge)
S3 — Notifier layer2026-06-06notifier/telegram.py (owner pending + budget alert + is_owner_callback), notifier/email.py (Resend approved + rejected + admin magic link, HTML + plain bundles). Fail-soft, http_client_factory injectable.
S4 — Decision endpoints2026-06-08POST /api/access-requests (honeypot + Turnstile + 3/h rate limit), GET .../{id}/status, GET .../{id}/decide?token= (HMAC idempotent, accept dans la signature), POST /api/telegram/webhook (owner chat_id + callback_query + /cmd), admin magic-link login → session 24 h, GET /api/admin/requests, PATCH /api/admin/requests/{id}
S5 — Real run gate2026-06-08POST /api/runs accepte X-Session-Token (HMAC sub=session+req), verify_session (sig + exp + approved + IP-bind + quota), Store.atomic_consume_run (locked read+validate+bind+consume+write), budget/tracker.py (fichier JSON par jour UTC, alertes 80/100 % idempotentes), dĂ©gradation silencieuse real→mock si cap atteint sans consommer de slot. +62 tests.
S6 — UI Next.js 162026-06-088 routes : /access-request (form + Turnstile widget + honeypot) → /access-request/[id]/status (poll 5s, 6 statuts) → /access-granted/[token] (POST run avec X-Session-Token, gĂšre degraded:true). Admin : /admin/login (magic link) → /admin/session/[token] (redeem → localStorage) → /admin/requests (panel filtrĂ©, actions approve/reject/revoke). Home : deux CTAs Mock/Real.
S7.1 — Wiring budget runtime2026-06-09budget/wiring.py record_real_run (fail-soft, factory injectable) + hook depuis run_manager._execute() via budget_callback (closure asyncio.run) + helper _build_budget_callback cĂŽtĂ© api.py lisant app.state. Estimation pessimiste 15 000 tokens/run (3 agents × 5 000 max_tokens). +9 tests.
S7.2 — Cron cleanup TTL2026-06-09POST /api/cron/cleanup (header X-Cleanup-Secret, hmac.compare_digest) → AccessRequestStore.cleanup_expired + BudgetTracker.cleanup_old_days(retain_days=90). 503 si secret non configurĂ©, 401 si header faux, 200 + counts sinon. Fix bonus : race buffer Python sur add_tokens corrigĂ©e par fh.flush() avant LOCK_UN. +12 tests.
S7.3 — Docs + sync portfolio2026-06-09README cv-critic-agent : section Phase 5 (flow, token taxonomy, budget cap, where modules live), arborescence Ă  jour, env de dĂ©ploiement complĂšte. Sync cv-portfolio : highlight Phase 5 + cette fiche technique.

Conventions code (Phase 5)

10 — Limites & suite

Limites & suite

Limites actuelles

Suite directe