Tutoriel · Salesforce · Agentique
IA agentique Salesforce : concevoir des actions Apex pour Agentforce
Sujets, instructions et actions : écrire des méthodes @InvocableMethod qu’un agent sait choisir et appeler, en gardant les droits et les garde-fous de la plateforme.
Un agent, des sujets, des actions
Avec Agentforce, Salesforce passe de l’assistant qui répond à l’agent qui agit : il comprend une demande, choisit les opérations à lancer, les exécute dans l’organisation et formule la réponse. On retrouve la boucle de tout harness d’agent, appliquée aux données et aux processus Salesforce.
Un agent se compose de :
- sujets (topics) : les domaines qu’il sait traiter, chacun avec une description de classification, un périmètre et des instructions ;
- actions : ce qu’il peut faire dans chaque sujet. Une action est un flow, une méthode Apex invocable, un modèle de prompt ou une action standard ;
- un moteur de raisonnement qui, à chaque message, choisit le sujet, puis les actions et leurs paramètres.
Exemple fil rouge : un agent de service pour une compagnie fictive, Acme Assurances, qui renseigne les clients sur leur contrat.
Bien découper les sujets
Le moteur choisit d’abord un sujet à partir de sa description de classification. Des sujets qui se recoupent provoquent des erreurs d’aiguillage ; un sujet trop large dilue ses instructions.
Sujet : Suivi de contrat
Description de classification : questions d'un client sur l'état, l'échéance ou la
validité de son contrat d'assurance.
Périmètre : consulter un contrat existant. Ne jamais modifier, résilier ou créer un contrat.
Instructions :
- Demander le numéro de contrat s'il n'a pas été donné ; ne jamais le deviner.
- Utiliser l'action « Consulter le statut d'un contrat » avant toute réponse sur un contrat.
- Si le contrat est introuvable, ne pas confirmer son existence : proposer de vérifier le numéro.
- Pour une résiliation ou un litige, transférer à un conseiller. Les instructions disent quoi faire, mais aussi quoi ne pas faire et quand passer la main à un humain. Elles restent courtes et concrètes, comme pour un nouveau collègue.
Une action Apex invocable
Quand un flow ne suffit pas (logique complexe, appel d’API, volume), on écrit une méthode @InvocableMethod. C’est le même mécanisme que pour un flow : une liste de demandes en entrée, une liste de résultats en sortie.
public with sharing class ContratStatutAction {
public class Demande {
@InvocableVariable(
required=true
label='Numéro de contrat'
description='Numéro du contrat donné par le client, au format AC- suivi de 6 chiffres (ex. : AC-104233).'
)
public String numeroContrat;
}
public class Resultat {
@InvocableVariable(label='Trouvé' description='Vrai si un contrat visible par l\'utilisateur correspond au numéro.')
public Boolean trouve;
@InvocableVariable(label='Statut' description='Statut du contrat : Actif, Suspendu ou Résilié.')
public String statut;
@InvocableVariable(label='Date d\'échéance' description='Prochaine date d\'échéance du contrat.')
public Date echeance;
@InvocableVariable(label='Message' description='Explication à reformuler pour le client si le contrat est introuvable.')
public String message;
}
@InvocableMethod(
label='Consulter le statut d\'un contrat'
description='Renvoie le statut et la prochaine échéance d\'un contrat d\'assurance à partir de son numéro. N\'effectue aucune modification.'
category='Contrats'
)
public static List<Resultat> consulter(List<Demande> demandes) {
// Méthode invocable : une liste en entrée, une liste en sortie, dans le même ordre.
Set<String> numeros = new Set<String>();
for (Demande d : demandes) {
numeros.add(d.numeroContrat?.trim().toUpperCase());
}
Map<String, Contrat__c> parNumero = new Map<String, Contrat__c>();
for (Contrat__c c : [
SELECT Numero__c, Statut__c, Date_Echeance__c
FROM Contrat__c
WHERE Numero__c IN :numeros
WITH USER_MODE
]) {
parNumero.put(c.Numero__c, c);
}
List<Resultat> resultats = new List<Resultat>();
for (Demande d : demandes) {
Contrat__c c = parNumero.get(d.numeroContrat?.trim().toUpperCase());
Resultat r = new Resultat();
r.trouve = c != null;
if (c != null) {
r.statut = c.Statut__c;
r.echeance = c.Date_Echeance__c;
} else {
r.message = 'Aucun contrat accessible ne correspond à ce numéro. Vérifier le numéro avec le client.';
}
resultats.add(r);
}
return resultats;
}
} L’action est en lecture seule et le dit dans sa description. Elle normalise le numéro reçu (espaces, minuscules), car le modèle recopie ce que le client a tapé.
Les descriptions guident le modèle
Pour un flow appelé par un humain, le label et la description sont de la documentation. Pour un agent, ce sont des instructions : le modèle ne voit pas le code, il choisit l’action et remplit ses paramètres uniquement d’après ces textes.
- Décrire ce que fait l’action et ce qu’elle ne fait pas.
- Donner le format attendu de chaque paramètre, avec un exemple.
- Décrire chaque champ du résultat, pour que le modèle sache quoi en dire.
- Prévoir un résultat explicite pour l’échec (« introuvable ») plutôt qu’une exception : le modèle peut alors répondre proprement.
Droits et garde-fous
- Mode utilisateur :
with sharingetWITH USER_MODEappliquent le partage et les droits de champ de l’utilisateur sous lequel l’agent s’exécute. Un agent de service tourne avec un utilisateur dédié : lui donner le strict nécessaire. - Pas de fuite par l’absence : « aucun contrat accessible » plutôt que « ce contrat appartient à un autre client ».
- Actions sensibles : séparer lecture et écriture en actions distinctes, et réserver les écritures à des sujets précis, avec confirmation du client.
- Trust Layer : la couche de confiance de Salesforce masque certaines données sensibles avant l’envoi au modèle, détecte les contenus toxiques et journalise les échanges. Elle complète les droits, elle ne les remplace pas.
Tester l’agent et l’action
Deux niveaux de tests :
- L’action, en Apex classique : elle doit fonctionner en lot et respecter l’ordre des demandes.
- L’agent, par des conversations de référence : pour chaque phrase type, le bon sujet est-il choisi, la bonne action appelée avec les bons paramètres ? Salesforce fournit un outil de test d’agents pour les jouer en série, sur le modèle d’une évaluation.
@IsTest
private class ContratStatutActionTest {
@IsTest
static void renvoieLeStatutDansLOrdreDesDemandes() {
insert new Contrat__c(Numero__c = 'AC-000001', Statut__c = 'Actif', Date_Echeance__c = Date.today().addMonths(3));
ContratStatutAction.Demande connue = new ContratStatutAction.Demande();
connue.numeroContrat = ' ac-000001 ';
ContratStatutAction.Demande inconnue = new ContratStatutAction.Demande();
inconnue.numeroContrat = 'AC-999999';
Test.startTest();
List<ContratStatutAction.Resultat> resultats =
ContratStatutAction.consulter(new List<ContratStatutAction.Demande>{ connue, inconnue });
Test.stopTest();
Assert.isTrue(resultats[0].trouve);
Assert.areEqual('Actif', resultats[0].statut);
Assert.isFalse(resultats[1].trouve);
Assert.isNotNull(resultats[1].message);
}
}