refactor: simplify AI payload construction and update system prompt for professional ECF synthesis
This commit is contained in:
+70
-68
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Bilans de compétences</title>
|
<title>Bilans de compétences</title>
|
||||||
<script type="module" crossorigin src="/assets/index-CCT8mUTJ.js"></script>
|
<script type="module" crossorigin src="/assets/index-CPsAkw2K.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-hUIr4MzD.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-hUIr4MzD.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+41
-51
@@ -546,44 +546,22 @@ const Bilan = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const buildPayloadForAI = useCallback(() => {
|
const buildPayloadForAI = useCallback(() => {
|
||||||
const selected = items.filter((item) => item.checked);
|
const validated = items.filter(
|
||||||
const byBucket = (array, bucket) =>
|
(item) => item.checked && (item.level === "ac" || item.level === "ma")
|
||||||
array.filter((item) => item.bucket === bucket).map((item) => item.label);
|
);
|
||||||
|
const byBucket = (bucket) =>
|
||||||
const acquis = selected.filter((item) => item.level === "ac");
|
validated.filter((item) => item.bucket === bucket).map((item) => item.label);
|
||||||
const maitrise = selected.filter((item) => item.level === "ma");
|
|
||||||
const inProgress = selected.filter((item) => item.level === "ec");
|
|
||||||
const notAcquired = selected.filter((item) => item.level === "na");
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
prenom: firstName.trim(),
|
cp: currentCp,
|
||||||
nom: lastName.trim(),
|
cpTitle: titles[currentCp]?.h2 || "",
|
||||||
tone: aiTone,
|
apprenant: `${firstName.trim()} ${lastName.trim()}`.trim(),
|
||||||
counts: {
|
validatedCount: validated.length,
|
||||||
total: items.length,
|
performance: byBucket("Performance"),
|
||||||
selected: selected.length,
|
competence: byBucket("Competence"),
|
||||||
acquis: acquis.length,
|
connaissance: byBucket("Connaissance"),
|
||||||
maitrise: maitrise.length,
|
|
||||||
inProgress: inProgress.length,
|
|
||||||
notAcquired: notAcquired.length,
|
|
||||||
},
|
|
||||||
performance: {
|
|
||||||
acquis: byBucket(acquis, "Performance"),
|
|
||||||
maitrise: byBucket(maitrise, "Performance"),
|
|
||||||
improve: byBucket([...notAcquired, ...inProgress], "Performance"),
|
|
||||||
},
|
|
||||||
competence: {
|
|
||||||
acquis: byBucket(acquis, "Competence"),
|
|
||||||
maitrise: byBucket(maitrise, "Competence"),
|
|
||||||
improve: byBucket([...notAcquired, ...inProgress], "Competence"),
|
|
||||||
},
|
|
||||||
connaissance: {
|
|
||||||
acquis: byBucket(acquis, "Connaissance"),
|
|
||||||
maitrise: byBucket(maitrise, "Connaissance"),
|
|
||||||
improve: byBucket([...notAcquired, ...inProgress], "Connaissance"),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}, [aiTone, firstName, items, lastName]);
|
}, [currentCp, firstName, items, lastName]);
|
||||||
|
|
||||||
const generateAISummaryMistral = useCallback(async () => {
|
const generateAISummaryMistral = useCallback(async () => {
|
||||||
if (!MISTRAL_API_KEY) {
|
if (!MISTRAL_API_KEY) {
|
||||||
@@ -592,16 +570,31 @@ const Bilan = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const payload = buildPayloadForAI();
|
const payload = buildPayloadForAI();
|
||||||
if (!payload.counts.selected) {
|
if (!payload.validatedCount) {
|
||||||
showStatus("Sélectionne quelques critères avant d'utiliser l'IA.", "error");
|
showStatus("Aucune compétence validée (Acquis/Maîtrisé) à synthétiser.", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const observationTexte = observation.trim();
|
|
||||||
const notesFormateur = aiNotes.trim();
|
const notesFormateur = aiNotes.trim();
|
||||||
|
|
||||||
setIsAiLoading(true);
|
setIsAiLoading(true);
|
||||||
showStatus("Génération du résumé via Mistral…", "info", true);
|
showStatus("Génération de la synthèse ECF via Mistral…", "info", true);
|
||||||
|
|
||||||
|
const formatBucket = (label, items) =>
|
||||||
|
items.length ? `${label} :\n${items.map((c) => `- ${c}`).join("\n")}` : "";
|
||||||
|
|
||||||
|
const userMessage = [
|
||||||
|
`CP évalué : ${payload.cp} — ${payload.cpTitle}`,
|
||||||
|
payload.apprenant ? `Apprenant : ${payload.apprenant}` : "",
|
||||||
|
"",
|
||||||
|
"Compétences validées (Acquis / Maîtrisé) :",
|
||||||
|
formatBucket("Performance", payload.performance),
|
||||||
|
formatBucket("Compétence", payload.competence),
|
||||||
|
formatBucket("Connaissance", payload.connaissance),
|
||||||
|
notesFormateur ? `\nNotes du formateur : ${notesFormateur}` : "",
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
const callMistral = async (model) => {
|
const callMistral = async (model) => {
|
||||||
const response = await fetch("https://api.mistral.ai/v1/chat/completions", {
|
const response = await fetch("https://api.mistral.ai/v1/chat/completions", {
|
||||||
@@ -612,23 +605,21 @@ const Bilan = () => {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model,
|
model,
|
||||||
temperature: 0.6,
|
temperature: 0.4,
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
content: `Tu es ingénieur pédagogique spécialisé dans la formation CDUI (Titre Professionnel Concepteur Développeur d'Interfaces Utilisateur, niveau 6). Tu maîtrises parfaitement les référentiels de compétences du titre, les CP1 à CP9, et les exigences de France Compétences.
|
content: `Tu es ingénieur pédagogique expert du Titre Professionnel CDUI (Concepteur Développeur d'Interfaces Utilisateur, niveau 6). Tu rédiges des synthèses pour les bilans ECF (Évaluation en Cours de Formation), documents officiels du Ministère du Travail.
|
||||||
À partir des données de l'évaluation (critères et niveaux d'acquisition), du bilan automatique et des notes du formateur, rédige une synthèse pédagogique finale de 8 à 10 phrases.
|
|
||||||
Contraintes : valoriser les réussites de l'apprenant, évoquer les axes de progression avec bienveillance, reformuler et enrichir les notes du formateur, varier le vocabulaire et les connecteurs, adopter un ton professionnel et encourageant, en lien avec les attendus du titre CDUI.`,
|
À partir des compétences validées (Acquis et Maîtrisé) organisées par Performance, Compétence et Connaissance, rédige une synthèse ECF en 4 à 5 lignes maximum.
|
||||||
|
- Réponds aux attendus officiels du CP évalué.
|
||||||
|
- Formule un jugement pédagogique global : ne liste pas les critères un à un.
|
||||||
|
- Sois synthétique et précis, sans fioritures ni répétitions.
|
||||||
|
- Adopte un registre professionnel et factuel, adapté à un document officiel.`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
content: `Données JSON : ${JSON.stringify(payload)}
|
content: userMessage,
|
||||||
|
|
||||||
Observation affichée :
|
|
||||||
${observationTexte}
|
|
||||||
|
|
||||||
Notes formateur :
|
|
||||||
${notesFormateur}`,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@@ -687,7 +678,6 @@ ${notesFormateur}`,
|
|||||||
dismissProgressToasts,
|
dismissProgressToasts,
|
||||||
insertAISummaryIntoObservation,
|
insertAISummaryIntoObservation,
|
||||||
mistralModel,
|
mistralModel,
|
||||||
observation,
|
|
||||||
showStatus,
|
showStatus,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user