Aller au contenu

Générer des posts LinkedIn avec l'IA pour des commerciaux multi-secteurs

Nos commerciaux couvrent chacun plusieurs secteurs, avec pour chaque secteur une cible différente (DSI, marketing, direction générale) et un discours qui devrait varier en conséquence. Personne n'a le temps de tenir ce rythme de personnalisation en plus de la prospection : voici comment un pipeline n8n génère chaque semaine une base de posts LinkedIn prêts à retoucher, classés par secteur et par cible ; et pourquoi la vraie difficulté n'était pas de brancher un LLM sur un prompt.


Le problème

Sur LinkedIn, un post pertinent pour un DSI de mutuelle n'a rien à voir avec un post pour une direction marketing d'éditeur logiciel. En pratique, personne n'a le temps de tenir ce rythme de personnalisation sectorielle en plus de la prospection, alors les posts LinkedIn passent à la trappe ou se limitent à des partages génériques.

L'idée de départ était simple : partir de ce qu'on sait déjà (le secteur et la cible de chaque commercial, la documentation produit) et laisser un agent IA produire chaque semaine une base de posts prêts à retoucher, classés par secteur et par cible. Pas un post final à publier tel quel : une matière première qualifiée, pour ne plus partir d'une page blanche.

Point terrain : la vraie difficulté n'était pas de brancher un LLM sur un prompt. C'était de construire les bonnes données en amont (qui cible quoi, avec quels arguments), et d'éviter que le résultat ressemble à un mailing de prospection déguisé plutôt qu'à un vrai post de réseau social.


Architecture & stack technique

Le tout tourne dans n8n, avec les briques suivantes :

  • n8n Data Tables pour stocker deux jeux de données : le mapping commercial × secteur × cible (avec les références clients et arguments produit associés), et une base de faits produit condensée depuis la documentation interne.
  • Un agent IA (nœud LangChain Agent de n8n) branché sur OpenRouter, avec sortie structurée forcée (Structured Output Parser) pour récupérer directement du JSON exploitable plutôt qu'un bloc de texte à parser à la main.
  • Un nœud Code pour regrouper les posts générés par commercial et construire un fichier .html de synthèse.
  • SharePoint pour héberger le fichier hebdomadaire.
  • Microsoft Teams pour prévenir chaque commercial en message privé, avec le lien vers son fichier.
flowchart TD
    A["Trigger hebdomadaire\nlundi 7h"] --> B["Data Table\nfaits produit"]
    B --> C["Data Table\ncommercial x secteur x cible"]
    C --> D{"Boucle\n1 ligne a la fois"}
    D -->|pour chaque ligne| E["Agent IA\nOpenRouter + sortie structuree"]
    E --> F["Fusion ligne + posts generes"]
    F --> D
    D -->|boucle terminee| G["Regroupement par commercial"]
    G --> H["Generation du fichier .html"]
    H --> I["Upload SharePoint"]
    I --> J{"Switch : quel commercial ?"}
    J --> K1["Teams 1:1 Commercial A"]
    J --> K2["Teams 1:1 Commercial B"]
    J --> K3["Teams 1:1 Commercial C"]
    J --> K4["Teams 1:1 Commercial D"]

Le choix du modèle IA a évolué en cours de route : parti sur un modèle "flagship" pour la qualité, retour à un modèle beaucoup plus léger après un premier test : le rapport qualité/prix ne justifiait pas l'écart pour une tâche de copywriting court (détail plus bas).


Construction étape par étape

1. Cartographier les données existantes

Avant d'écrire la moindre ligne de workflow, il a fallu regarder ce qu'on avait déjà : un fichier de profils commerciaux (secteur ciblé, poste ciblé, taille d'entreprise, références clients, arguments produit) construit précédemment pour le sourcing de prospects. Ce fichier existait déjà à un niveau de granularité fin : un même secteur pouvait apparaître plusieurs fois selon que la cible soit IT ou marketing. Un réflexe naturel aurait été de "dédupliquer" ces lignes pour simplifier ; en réalité elles représentent des discours réellement différents, donc elles ont toutes été conservées comme autant d'entrées distinctes.

2. Condenser la documentation produit en faits vérifiables

Pour éviter que le LLM invente des fonctionnalités ou des chiffres, une passe de lecture de la documentation produit a permis d'en extraire une cinquantaine de faits courts et vérifiables (une fonctionnalité, une caractéristique technique, un chiffre précis), classés par thème. Cette base sert de garde-fou factuel injecté dans le prompt système, plutôt que de laisser le modèle "broder" à partir de sa connaissance générale du produit.

Exemple de fait extrait (anonymisé) :

{
  "theme": "scenarios",
  "fait": "Les scenarios peuvent etre declenches par un formulaire (a la validation) ou un ciblage (segment recalcule quotidiennement pour integrer les nouveaux contacts)."
}

3. Construire les Data Tables

n8n propose depuis peu des Data Tables natives (pas besoin de base de données externe pour ce volume). Deux tables ont suffi :

Table Colonnes Volume
commercial_secteurs commercial, secteur, cible, taille_entreprise, zone_geo, references_client, avantages_campaign ~110 lignes
produit_faits theme, fait ~50 lignes

4. Le pipeline de génération

Le cœur du workflow boucle sur chaque ligne de commercial_secteurs (une boucle "Split In Batches", taille de lot 1) et appelle un agent IA avec :

  • un prompt système figé, qui porte toutes les contraintes de style (voir plus bas),
  • un prompt utilisateur dynamique, rempli avec les champs de la ligne en cours,
  • une sortie structurée forcée, pour récupérer directement { "posts": [...] } avec un angle et un texte par proposition.

Extrait du prompt système (traduit des contraintes réelles, sans donnée interne) :

Ecris en francais, a la premiere personne du singulier, du point de vue du commercial,
comme un vrai post LinkedIn public (retour d'experience, opinion, observation de
terrain) - jamais comme un message de prospection ou un email adresse a un
interlocuteur unique.

Ne vouvoie jamais un interlocuteur precis. Bannis les formules du type "je souhaite
attirer votre attention sur...", "vous voulez que je vous montre...".

Genere exactement 3 propositions, avec des angles differents : preuve sociale
(reference client), insight sectoriel (douleur metier), actualite produit
(fonctionnalite).

5. Regroupement et mise en forme

Une fois la boucle terminée sur toutes les lignes, un nœud de code JavaScript regroupe les résultats par commercial (un item en entrée par ligne secteur, un item en sortie par commercial, avec la liste de ses propositions) :

const groups = {};
for (const item of items) {
  const c = item.json.commercial;
  if (!groups[c]) groups[c] = [];
  groups[c].push({ secteur: item.json.secteur, posts: item.json.posts /* ... */ });
}
return Object.keys(groups).map((commercial) => ({
  json: { commercial, entries: groups[commercial] }
}));

Un second nœud de code construit ensuite une page .html autonome par commercial (une section par secteur, les 3 propositions de post dedans), convertie en fichier binaire pour l'upload.

6. Distribution : SharePoint + Teams par branche

Plutôt qu'un message Teams géant listant tous les secteurs (illisible pour les commerciaux qui en couvrent une trentaine), le fichier .html est déposé sur un espace SharePoint partagé, et seul le lien est envoyé sur Teams. Pour l'envoi, un Switch route chaque item vers un nœud Teams dédié par commercial (chat 1:1 déjà existant), plutôt qu'un seul nœud générique, ce qui reprend un pattern déjà utilisé sur un autre workflow interne pour des rappels de tâches quotidiens.


Ce qui a foiré (et ce que ça apprend)

Le champ qui disparaît après un appel API

Un Switch qui ne route plus rien

Premier bug du premier test réel : le Switch routait zéro item vers les 4 branches, alors qu'il recevait bien les bons items en entrée. La cause : le nœud d'upload SharePoint remplace le contenu de chaque item par la réponse de l'API (identifiant de fichier, lien, métadonnées) ; et le champ commercial qu'utilisait le Switch pour router n'existait donc plus à cet endroit du pipeline.

La leçon générale, valable sur n'importe quel outil de ce type : dès qu'un nœud appelle une API externe, il faut supposer qu'il remplace les données de l'item plutôt qu'il les enrichit. Le correctif consiste à référencer explicitement le nœud précédent qui portait encore la bonne valeur, plutôt que de lire "l'item courant" en aveugle.

Le ton "message de prospection déguisé"

Deuxième surprise, plus intéressante : les premiers posts générés se terminaient tous par des formules du type "vous voulez que je vous montre un exemple adapté à votre catalogue ?". Techniquement corrects, ils sonnaient comme un message privé adressé à un prospect précis, pas comme un post public adressé à un réseau. En creusant, la cause était dans le prompt lui-même : la consigne demandait une "invitation à échanger en message privé" en fin de post, ce qui pousse mécaniquement le modèle vers un ton de démarchage 1:1.

Correctif : bannir explicitement les formulations de prospection dans le prompt système, et remplacer la consigne de fin par soit une question ouverte adressée à l'ensemble du réseau, soit l'absence de question : un post qui se termine sur une observation forte fonctionne tout aussi bien.

Le coût du modèle

Premier test sur un modèle "flagship" : environ 0,037 $ par appel. Sur l'échelle complète du run hebdomadaire (une centaine de lignes), ça représentait une facture mensuelle non négligeable pour une tâche de copywriting qui n'a pas besoin d'un raisonnement poussé. Passage à un modèle nettement plus économique du même fournisseur : qualité perçue équivalente sur ce cas d'usage, coût divisé par plusieurs.


Limites actuelles et pistes d'amélioration

  • Pas de boucle de feedback : les posts générés ne sont pas notés ni réutilisés pour affiner le prompt au fil du temps. Une prochaine itération pourrait stocker un simple pouce haut/bas par post pour ajuster les instructions.
  • Pas de rotation de contenu : le pipeline régénère tout chaque semaine sans mémoire de ce qui a déjà été proposé, avec un risque de répétition sur les secteurs récurrents.
  • Validation humaine obligatoire : c'est un choix assumé (la mise en production réelle des messages reste manuelle), mais ça veut dire que le gain de temps dépend entièrement de l'adoption par les commerciaux.
  • Robustesse du parsing HTML dans Teams : le rendu HTML dans les messages Teams reste limité à un sous-ensemble de balises ; le choix de renvoyer un lien plutôt que le contenu complet inline règle le problème pour l'instant, mais un vrai visualiseur dédié serait plus confortable à terme.

FAQ

Pourquoi ne pas avoir tout mis dans un seul message Teams ? Parce que certains commerciaux couvrent plus de 30 combinaisons secteur/cible : un message qui listerait tout deviendrait illisible dans une bulle de chat. Un fichier consultable à part, avec juste le lien envoyé, reste bien plus confortable.

Pourquoi une sortie structurée plutôt que du texte libre parsé après coup ? Parce que parser du texte libre pour en extraire 3 posts distincts est fragile (formats variables, oublis de séparateurs). Forcer un schéma JSON dès la génération élimine cette classe entière de bugs.

Le contenu généré est-il publiable tel quel ? Non, et ce n'est pas l'objectif. C'est une base de travail qualifiée par secteur et par cible, pas un post final : la relecture et la personnalisation humaine restent nécessaires.

Conclusion

Le projet confirme une intuition déjà éprouvée sur d'autres automatisations : la partie IA (le prompt, le modèle) n'est presque jamais la partie qui prend le plus de temps. Ce qui prend du temps, c'est structurer les données en amont pour qu'elles soient exploitables, et itérer sur le prompt une fois les premiers résultats en main pour corriger des biais qu'on n'avait pas anticipés : ici, un ton de prospection qui s'est glissé dans du contenu censé être public. Le pipeline tourne maintenant sur deux commerciaux en test avant un déploiement complet.


Sur le même terrain (agent IA + sortie structurée + n8n), voir aussi Automatiser une variable grammaticale avec une IA, où le même principe de sortie forcée sert cette fois de garde-fou de qualité plutôt que de format.