Le module Page-Skill permet Γ l'agent Circuy d'apprendre la structure fonctionnelle d'une page Γ partir du DOM rendu (Accessibility Tree) pour gΓ©nΓ©rer instantanΓ©ment des visites guidΓ©es (Shepherd.js) en rΓ©ponse aux questions en langage naturel des utilisateurs.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PAGE WEB CLIENT (DOM Rendu) β
β β
β [ Input Client ] [ Select Pays ] [ Btn Valider ] β
ββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β 1. Scan DOM / A11y Tree Extraction (SDK JS)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CIRCUY OVERLAY / CONTENT SCRIPT β
β β
β - Masquage PII / Anonymisation β
β - Simplification : Balises interactives + aria-* + data-* β
ββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β 2. Payload AnonymisΓ© (JSON A11y)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CIRCUY BACKEND (Hono / Node.js + LLM Engine) β
β β
β ββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ β
β β A. Mode Recording β β B. Mode Runtime Question β β
β β Generates Page-Skill JSON β OR β Maps Intent -> Shepherd.js JSON β β
β ββββββββββββββββ¬ββββββββββββββββ ββββββββββββββββββ¬ββββββββββββββββββ β
βββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββ
β β
βΌ βΌ
ββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββ
β DB / Vector Store (Scaleway/OVH) β β SDK Client (ExΓ©cute le Tour) β
β Table: `page_skills` β β `shepherd.addSteps().start()` β
ββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββ
Le SDK extrait un sous-ensemble minimal du DOM pour ne pas surcharger la fenΓͺtre de context du LLM (token-efficiency) et protΓ©ger la vie privΓ©e (RGPD).
Le script parcourt le DOM et filtre uniquement les Γ©lΓ©ments possΓ©dant une sΓ©mantique d'interaction :
Avant tout envoi au serveur, le SDK applique un masque d'expressions régulières (Regex) sur le texte et les valeurs des inputs :
// types/ai.ts
export interface A11yNode {
id: string;
tag: string;
type?: string;
label?: string;
placeholder?: string;
role?: string;
selector: string; // SΓ©lecteur rΓ©silient gΓ©nΓ©rΓ© par le SDK
isInteractive: boolean;
}
export interface PageScanPayload {
urlPattern: string; // Ex: /finance/invoices/*
pageTitle: string;
nodes: A11yNode[];
}
Lorsqu'un crΓ©ateur/admin enregistre une page, le backend passe le PageScanPayload au LLM pour gΓ©nΓ©rer un Page-Skill JSON structurΓ©.
CREATE TABLE page_skills (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id),
name VARCHAR(255) NOT NULL, -- ex: "CrΓ©ation de Facture"
route_pattern VARCHAR(512) NOT NULL, -- ex: "/app/invoices/new"
summary TEXT NOT NULL, -- Description globale par le LLM
actions_json JSONB NOT NULL, -- Liste des intents et sΓ©lecteurs
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_page_skills_org_route ON page_skills(organization_id, route_pattern);
{
"summary": "Formulaire de crΓ©ation de facture client avec sΓ©lection d'articles et tva.",
"intents": [
{
"intent_id": "select_client",
"user_goal": "Choisir ou changer de client",
"selector": "#client-select-dropdown",
"fallback_selector": "select[name='clientId']",
"action_type": "click"
},
{
"intent_id": "add_discount",
"user_goal": "Appliquer une remise ou un pourcentage de rΓ©duction",
"selector": "[data-testid='invoice-discount-input']",
"fallback_selector": "input[name='discount']",
"action_type": "input"
}
]
}
Quand l'utilisateur pose une question dans le widget flottant Circuy ( "Comment ajouter une remise ?" ) :
Le backend rΓ©cupΓ¨re le Page-Skill associΓ© Γ l'URL courante et envoie la requΓͺte suivante au LLM (ex: Claude 3.5 Sonnet ou GPT-4o) avec Structured Outputs (JSON Schema) :
Tu es l'agent d'exΓ©cution UI de Circuy.
A partir de la question de l'utilisateur et du "Page-Skill" fourni, génère les étapes d'un tour Shepherd.js.
RΓGLES STRICTES :
1. N'utilise QUE les sΓ©lecteurs CSS fournis dans le Page-Skill.
2. Chaque Γ©tape doit comporter un titre court et un texte explicatif concis.
3. Retourne un tableau d'Γ©tapes au format JSON strict.
[PAGE SKILL]
{actions_json}
[QUESTION UTILISATEUR]
"{user_query}"
{
"tour_id": "dynamic_ai_tour",
"steps": [
{
"attachTo": {
"element": "[data-testid='invoice-discount-input']",
"on": "bottom"
},
"title": "Saisir la remise",
"text": "Entrez ici le pourcentage de rΓ©duction Γ appliquer Γ la facture.",
"buttons": [
{
"text": "TerminΓ©",
"action": "next"
}
]
}
]
}
Le SDK rΓ©agit Γ la rΓ©ponse de l'API et dΓ©marre le guide interactif dans la page hΓ΄te.
// sdk/src/ai-agent.ts
import Shepherd from 'shepherd.js';
export async function handleUserQuestion(question: string) {
const currentPath = window.location.pathname;
// 1. Appel API au backend Circuy
const response = await fetch('https://api.circuy.com/v1/ai/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question, route: currentPath })
});
const { steps } = await response.json();
if (!steps || steps.length === 0) {
showFallbackMessage("DΓ©solΓ©, je n'ai pas trouvΓ© l'Γ©lΓ©ment correspondant sur cette page.");
return;
}
// 2. Instanciation dynamique du tour Shepherd.js
const tour = new Shepherd.Tour({
defaultStepOptions: {
cancelIcon: { enabled: true },
classes: 'circuy-shepherd-theme',
scrollTo: { behavior: 'smooth', block: 'center' }
}
});
// 3. Injection des Γ©tapes gΓ©nΓ©rΓ©es par l'IA
steps.forEach((step: any) => {
tour.addStep({
id: step.attachTo.element,
text: step.text,
title: step.title,
attachTo: step.attachTo,
buttons: [
{
text: 'Compris',
action: tour.next
}
]
});
});
// 4. Lancement du parcours
tour.start();
}
Afin de garantir la qualité et la pertinence des guides sur les environnements de production, tout parcours généré dynamiquement par le LLM est soumis à un statut de modération strict. Lorsqu'une question d'un utilisateur déclenche la création d'un nouveau guide, celui-ci est exécuté uniquement pour l'utilisateur demandeur en mode éphémère et simultanément enregistré dans la base de données avec le statut pending_review. Une notification est envoyée dans l'espace d'administration (ou sur Slack/Teams) de l'équipe Produit/Admin. Tant que le parcours n'a pas été relu, éventuellement ajusté et explicitement approuvé par un administrateur (status: 'published'), il reste invisible pour l'ensemble des autres utilisateurs de la plateforme. Cela permet de bénéficier de la réactivité de l'IA tout en maintenant un contrôle qualité total sur la documentation et l'UX globale.