Rapport de synthèse d'audit généré par IA dans n8n : de Supabase à l'e-mail
Publié le 18 juillet 2026 · 8 min de lecture
Un questionnaire d'audit bien mené produit des dizaines de réponses horodatées dans une base de données — mais une base de données ne se lit pas en comité de direction. Quelqu'un doit relire chaque réponse, repérer les non-conformités, les hiérarchiser et rédiger une synthèse présentable. C'est le dernier kilomètre de l'audit, et c'est presque toujours celui qu'on bâcle faute de temps. Ce tutoriel construit le workflow n8n qui fait ce travail à la demande : lecture des réponses dans Supabase, synthèse par un modèle de langage contraint à un format précis, envoi d'un rapport HTML lisible.
Ce workflow complète deux articles précédents : la collecte des réponses avec horodatage dans notre guide sur la piste d'audit RGPD avec n8n et Supabase, et la relance des dossiers incomplets détaillée dans automatiser les relances de dossiers incomplets. Le rapport de synthèse est la troisième brique : celle qui transforme des lignes en décision.
Pourquoi ne pas laisser un LLM rédiger le rapport en roue libre
La tentation est grande de brancher un AI Agent avec juste une instruction du type « rédige un rapport d'audit » et de le laisser interroger la base tout seul. Deux problèmes concrets s'ensuivent : le modèle peut résumer de mémoire au lieu de citer les réponses réelles (une non-conformité inventée dans un rapport d'audit n'est pas un détail), et le format de sortie varie d'une exécution à l'autre — impossible à archiver ou à comparer d'un dossier au suivant.
La bonne architecture ici n'est pas un agent autonome mais une Basic LLM Chain (chainLlm) : un prompt entre, une réponse structurée sort, rien de plus. Le contexte — les réponses réelles du dossier — est injecté explicitement dans le prompt par le workflow, pas récupéré par le modèle lui-même. C'est la même logique que nous détaillons dans notre guide pour débuter avec les nodes IA de n8n : quand la tâche est déterministe et se décrit en une phrase, la chaîne simple bat l'agent, en fiabilité comme en coût.
Architecture en quatre briques
- Déclenchement — un Execute Workflow Trigger (appelé depuis un formulaire interne ou un webhook « Générer le rapport ») ou, plus simplement pour commencer, un Manual Trigger avec un champ
dossier_iden entrée. - Récupération des données — un node Supabase qui lit toutes les lignes de
audit_responsespour ce dossier, triées par horodatage. - Synthèse — une Basic LLM Chain avec un Structured Output Parser, qui reçoit les réponses mises en forme et rend un JSON structuré (synthèse, points forts, non-conformités, plan d'action).
- Diffusion — un node Send Email qui transforme le JSON en HTML lisible et l'envoie au responsable du dossier.
Étape 1 — Rassembler les réponses d'un dossier
Le node Supabase, opération Get Many Rows, table audit_responses, filtre dossier_id = {{ $json.dossier_id }}, tri par created_at ascendant. C'est exactement la table décrite dans notre guide de piste d'audit : chaque ligne porte question, reponse et un horodatage serveur — la garantie que le rapport s'appuie sur des faits enregistrés, pas sur une reconstitution a posteriori.
Étape 2 — Mettre le contexte en forme avant de l'envoyer au modèle
Un node Code (JavaScript) transforme le tableau de lignes en un bloc de texte numéroté, plus facile à raisonner pour le modèle qu'un JSON brut :
const lignes = items.map((item, i) => {
const { question, reponse } = item.json;
return `${i + 1}. Q: ${question}\n R: ${reponse}`;
});
return [{ json: { contexte: lignes.join("\n\n"), dossier_id: items[0].json.dossier_id } }];
Cette étape a un effet secondaire important : elle fixe le nombre exact de questions traitées. Si le dossier compte 8 réponses, le prompt en verra 8 — pas de risque que le modèle en oublie une ou en invente une neuvième en résumant « de tête ».
Étape 3 — Le prompt et le schéma de sortie structuré
Le message système est l'endroit où se joue toute la fiabilité du rapport :
Tu es auditeur qualité. Voici les réponses d'un dossier d'audit, dans l'ordre :
{{ $json.contexte }}
Rédige une synthèse factuelle. Règles impératives :
- Chaque non-conformité doit citer le numéro de la question concernée.
- N'invente aucune réponse : si une question n'a pas de réponse, note-le comme "non renseigné".
- Le plan d'action contient entre 1 et 5 actions, classées par priorité décroissante.
- Réponds uniquement en français, ton neutre et professionnel.
Le Structured Output Parser impose ensuite le schéma JSON, exactement comme décrit dans notre guide d'extraction de données de factures — le même mécanisme, appliqué ici à un rapport plutôt qu'à un champ de facture :
{
"type": "object",
"properties": {
"synthese": { "type": "string", "description": "Résumé en 3 à 5 phrases" },
"points_forts": { "type": "array", "items": { "type": "string" } },
"non_conformites": {
"type": "array",
"items": {
"type": "object",
"properties": {
"question_num": { "type": "integer" },
"description": { "type": "string" },
"gravite": { "type": "string", "enum": ["mineure", "majeure", "critique"] }
},
"required": ["question_num", "description", "gravite"]
}
},
"plan_action": { "type": "array", "items": { "type": "string" } }
},
"required": ["synthese", "points_forts", "non_conformites", "plan_action"]
}
Contraindre la gravité à une énumération plutôt qu'à du texte libre change tout en aval : le node suivant peut trier ou colorer les non-conformités par gravité sans repasser par un prompt.
Étape 4 — Transformer le JSON en e-mail HTML lisible
Un node Send Email (SMTP) construit le corps du message par expressions, en itérant sur les tableaux avec une petite fonction inline plutôt qu'un gabarit externe :
<h2>Rapport d'audit — dossier {{ $json.dossier_id }}</h2>
<p>{{ $json.synthese }}</p>
<h3>Points forts</h3>
<ul>{{ $json.points_forts.map(p => `<li>${p}</li>`).join('') }}</ul>
<h3>Non-conformités</h3>
<ul>{{ $json.non_conformites.map(n => `<li><b>[${n.gravite}]</b> Q${n.question_num} — ${n.description}</li>`).join('') }}</ul>
<h3>Plan d'action</h3>
<ol>{{ $json.plan_action.map(a => `<li>${a}</li>`).join('') }}</ol>
Un e-mail HTML se lit sur n'importe quel client sans dépendance externe — c'est le choix retenu par le workflow de rapport du Pack Conformité & Audit. Si un PDF archivable est réellement nécessaire (signature, dépôt réglementaire), n8n ne propose pas de node natif de conversion : un node HTTP Request vers un service comme ConvertAPI ou PDF4me ajoute cette étape en fin de chaîne, sans changer le reste du workflow.
Le workflow complet, prêt à importer
{
"name": "Rapport de synthèse d'audit",
"nodes": [
{ "id": "1", "name": "Déclenchement manuel", "type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1, "position": [0, 0], "parameters": {} },
{ "id": "2", "name": "Réponses du dossier", "type": "n8n-nodes-base.supabase",
"typeVersion": 1, "position": [220, 0],
"parameters": { "operation": "getAll", "tableId": "audit_responses",
"filters": { "conditions": [{ "keyName": "dossier_id", "condition": "eq", "keyValue": "={{ $json.dossier_id }}" }] },
"sort": { "rules": [{ "field": "created_at", "direction": "ASC" }] } } },
{ "id": "3", "name": "Mise en forme du contexte", "type": "n8n-nodes-base.code",
"typeVersion": 2, "position": [440, 0],
"parameters": { "jsCode": "const lignes = items.map((item, i) => { const { question, reponse } = item.json; return `${i + 1}. Q: ${question}\\n R: ${reponse}`; }); return [{ json: { contexte: lignes.join('\\n\\n'), dossier_id: items[0].json.dossier_id } }];" } },
{ "id": "4", "name": "Synthèse IA", "type": "@n8n/n8n-nodes-langchain.chainLlm",
"typeVersion": 1.5, "position": [660, 0],
"parameters": { "promptType": "define",
"text": "=Voici les réponses d'un dossier d'audit, dans l'ordre :\n\n{{ $json.contexte }}\n\nRédige une synthèse factuelle. Chaque non-conformité doit citer le numéro de question concernée. N'invente aucune réponse. Le plan d'action contient entre 1 et 5 actions priorisées. Réponds en français." },
"hasOutputParser": true } },
{ "id": "5", "name": "Modèle de chat", "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1, "position": [620, 220], "parameters": { "model": "gpt-4o-mini" } },
{ "id": "6", "name": "Format de sortie", "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"typeVersion": 1.2, "position": [740, 220],
"parameters": { "schemaType": "manual", "inputSchema": "// schéma JSON détaillé à l'étape 3 : synthese, points_forts[], non_conformites[], plan_action[]" } },
{ "id": "7", "name": "Envoyer le rapport", "type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1, "position": [880, 0],
"parameters": { "subject": "=Rapport d'audit — dossier {{ $json.dossier_id }}",
"html": "=<!-- gabarit HTML détaillé à l'étape 4 --><h2>Rapport d'audit — dossier {{ $json.dossier_id }}</h2><p>{{ $json.synthese }}</p>" } }
],
"connections": {
"Déclenchement manuel": { "main": [[{ "node": "Réponses du dossier", "type": "main", "index": 0 }]] },
"Réponses du dossier": { "main": [[{ "node": "Mise en forme du contexte", "type": "main", "index": 0 }]] },
"Mise en forme du contexte": { "main": [[{ "node": "Synthèse IA", "type": "main", "index": 0 }]] },
"Synthèse IA": { "main": [[{ "node": "Envoyer le rapport", "type": "main", "index": 0 }]] },
"Modèle de chat": { "ai_languageModel": [[{ "node": "Synthèse IA", "type": "ai_languageModel", "index": 0 }]] },
"Format de sortie": { "ai_outputParser": [[{ "node": "Synthèse IA", "type": "ai_outputParser", "index": 0 }]] }
}
}
Importez ce squelette (Workflows → Import from File), branchez la credential Supabase et le modèle de chat, et testez avec un dossier_id réel : le rapport arrive en quelques secondes, structuré, sourcé, prêt à être relu.
Trois pièges à connaître
- Un dossier trop volumineux : au-delà d'une cinquantaine de réponses, le contexte injecté dans le prompt devient long et coûteux en tokens. Découpez le rapport en deux passes — une chaîne qui résume chaque section du questionnaire, puis une seconde qui synthétise les résumés — plutôt que de tout envoyer d'un coup.
- Le silence n'est pas une conformité : une question sans réponse (
reponsevide ou nulle) doit apparaître explicitement comme « non renseigné » dans le prompt, sinon le modèle a tendance à l'ignorer plutôt qu'à la signaler comme un manque — exactement le type de dossier incomplet que couvre notre article sur les relances automatiques. - Le rapport n'est pas la piste d'audit : le rapport de synthèse est une lecture humaine, régénérable à tout moment ; la piste d'audit (les lignes
audit_responseshorodatées) est la preuve. Ne remplacez jamais la seconde par le premier — si un contrôleur demande la preuve d'une réponse, c'est la ligne en base qui fait foi, pas le résumé qu'en a fait un LLM.
Aller plus loin
Construire ce pipeline de zéro — filtre Supabase, mise en forme du contexte, schéma de sortie structuré, gabarit HTML — prend une bonne matinée la première fois, surtout pour calibrer un prompt qui ne survole pas les non-conformités. Le Pack Conformité & Audit (149 €) livre ce workflow de rapport prêt à importer, aux côtés du bot de questionnaire guidé, de l'enregistrement d'audit trail dans Supabase et des relances automatiques des dossiers incomplets — les quatre briques qui couvrent la collecte et le suivi d'audit de bout en bout. Pour une vue d'ensemble de tout ce que couvre FlowKit, y compris le tri d'e-mails et l'assistant documentaire RAG, le Bundle FlowKit Complet (269 €) regroupe les trois packs.
FAQ
Questions fréquentes
Le modèle peut-il inventer des non-conformités qui n'existent pas ?
Seulement si vous le laissez raisonner sans contexte. Le prompt décrit dans cet article contraint explicitement le modèle à ne citer que des non-conformités appuyées sur une réponse précise du dossier, avec référence à la question concernée. Sans réponse source, pas de non-conformité : c'est une règle à écrire noir sur blanc dans le message système, pas un comportement par défaut.
Faut-il envoyer le rapport en PDF ?
n8n n'a pas de node natif de conversion HTML vers PDF : le plus simple et le plus robuste reste un e-mail HTML, lisible partout sans dépendance externe. Si un PDF est vraiment nécessaire (archivage, signature), un node HTTP Request vers un service comme ConvertAPI ou PDF4me fait la conversion en une étape supplémentaire.
Le rapport fonctionne-t-il pour un seul dossier ou pour plusieurs à la fois ?
Le workflow décrit ici prend un identifiant de dossier en entrée et produit un rapport par dossier — le cas d'usage le plus courant (audit RGPD, onboarding, questionnaire fournisseur). Pour un rapport consolidé sur plusieurs dossiers, il suffit d'agréger les réponses de tous les dossiers concernés avant l'étape de synthèse ; le prompt doit alors demander une vision globale plutôt que dossier par dossier.
Bundle FlowKit Complet
269 €