---
title: "Agent IA fiable : pattern intent-first avec MCP"
description: "Pattern intent-first : capter un besoin en langage naturel tout en gardant une exécution métier déterministe, validée et auditable via des actions typées."
url: "https://lonestone.io/blog/agent-ia-fiable-intent-first"
---

[Lonestone](/) ⟩ [Blog](/blog)

# Construire un agent IA fiable : le pattern intent-first, du langage naturel aux actions métier déterministes

24 avril 2026 · 10 min de lecture

Points clés

*   L’entrée en langage naturel est une porte d’accès, pas une délégation totale : l’exécution peut rester strictement déterministe.
*   La fiabilité vient d’un contrat d’actions typées, de validations métier et d’une orchestration en étapes (state machine, gating).

Utiliser une application “traditionnelle” revient souvent à naviguer dans une arborescence : choisir un client, sélectionner un module, remplir un formulaire, valider une action. C’est robuste, mais lent. À l’inverse, exprimer son besoin dans un tchat est immédiat.

Les problèmes apparaîssent dès que le domaine est **strict** (finance, assurance, RH, logistique, santé, industrie). L’intention se formule facilement en langage naturel, mais l’exécution doit rester conforme à une logique métier, à des droits, à des seuils, à des preuves d’audit. Un agent qui improvise “à la conversation” crée rapidement de la dette, des erreurs, ou une perte de confiance.

Ce billet décrit un paradigme de conception qui réconcilie les deux : **un produit piloté par l’intention** (intent-first), où le langage naturel sert d’entrée, et où l’exécution suit un workflow déterministe, cadré et observable.

## Un produit piloté par l’intention, c’est quoi exactement ?

Un produit piloté par l’intention permet à un utilisateur d’exprimer un besoin en langage naturel, puis transforme ce besoin en **actions typées** exécutées par le système.

L’IA intervient là où elle excelle :

*   **traduire une phrase** en action réalisable par le système
*   **désambiguïser** quand l’information manque
*   **résumer** une situation complexe et proposer des options

Ce qui engage l’entreprise reste **déterministe** :

*   validation métier
*   contrôle d’accès
*   calculs
*   appels API
*   écritures en base

L’IA devient une **porte d’accès**, un **routeur**, et non pas un moteur d’exécution libre.

![Schéma conceptuel : une intention en langage naturel traverse un routeur qui dispatche vers des agents d'exécution spécialisés](/.netlify/images?url=_astro%2Fillustration-routage.gneIU9hV.jpg&w=1376&h=768&dpl=69ef5b374d70a50008b43b29)

## Étude de cas : analyse commerciale d’après facturation

Prenons le cas d’une plateforme de facturation. Nous souhaitons produire une analyse de la relation client basée sur les factures d’un client sur l’année passée, avec par exemple les taux de marge de chaque produit, les délais de paiement, des recommendations commerciales.

Cette opération peut être effectuée via l’UI mais elle se prête très bien à une demande en langage naturel :

```
Analyse ma relation client avec John Doe sur l'année passée.
```

Il y a cependant des freins à utiliser uniquement l’IA: le domaine métier est complexe, de nombreux calculs doivent être effectués de façon précise, et la plus value de la fonctionnalité viens de **notre expertise interne**, on ne peut pas juste donner les factures et laisser l’IA prendre l’intégralité des décisions.

L’intérêt du pattern intent-first, c’est de proposer une **porte d’entrée** vers une succession d’étapes **déterministes**.

### Le LLM comprend la demande et route vers l’agent approprié

Dans un premier temps, un agent routeur interprète la demande et sélectionne un agent dédié à cette tâche, dit “d’exécution”.

C’est le premier filtre vers un traitement déterministe : chaque agent d’exécution a accès à un sous-ensemble d’outils, et suit un pipeline précis.

stateDiagram-v2

    state "Routage via LLM" as Routage
    state fork\_state <<fork>>
    state "Agent clientelle" as Clientelle {
      state "..." as cli\_internal
      \[\*\] --> cli\_internal
      cli\_internal --> \[\*\]
    }
    state "Agent facturation" as Facturation {
      state "..." as fact\_internal
      \[\*\] --> fact\_internal
      fact\_internal --> \[\*\]
    }
    state "Agent analyse commerciale" as AnalyseCommerciale {
      state "..." as analysis\_internal
      \[\*\] --> analysis\_internal
      analysis\_internal --> \[\*\]
    }

    \[\*\] --> Routage: "Analyse ma relation client avec John Doe sur l'année passée."
    Routage --> fork\_state: { "agent": "..." }
    fork\_state --> Clientelle
    fork\_state --> Facturation
    fork\_state --> AnalyseCommerciale

### Boucle d’exécution

L’agent d’exécution utilise une boucle itérative (contrôlée par le SDK Vercel) qui peut se résumer comme ceci :

stateDiagram-v2

    state "Préparation: collecte d'outils et de contexte (prepareStep)" as Preparation
    state "Génération LLM" as Generation
    state "Appel de l'outil" as Appel
    state "Ajout du résultat de l'outil au contexte" as Ajout
    state "Résultat final" as Resultat
    
    \[\*\] --> Preparation
    Preparation --> Generation
    Generation --> Appel: Si la génération a produit un appel d'outil
    Generation --> Resultat
    Appel --> Ajout
    Ajout --> Preparation
    Resultat --> \[\*\]

_Note: En pratique d’autres étapes comme la gestion d’erreurs avant et après l’appel d’outil sont automatiquement appliquées par le sdk Vercel._

Nous utilisons le hook `prepareStep` pour diriger la génération à chaque étape en indiquant au LLM les outils qu’il a à disposition.

```
onPrepareStep: (options: { steps: StepResult<MyToolSet>[] }): {
  // en sortie nous voulons une liste d'outils à disposition du LLM
  activeTools: (keyof MyToolSet)[]
  // en fonction de nos contraintes on veut pouvoir imposer au LLM l'execution d'un outil
  // ou au contraire interdire d'utiliser un outil ce qui aura pour effet de sortir de la boucle
  toolChoice: 'auto' | 'required' | 'none'
} => {

  // steps est la liste des itérations précédentes passée par vercel
  const { steps = [] } = options ?? {}
```

Chaque étape de notre workflow défini un ensemble d’outils. On détermine le passage d’une étape à l’autre par la présence d’un résultat d’outil valide.

### Etape 1: récupérer les factures du client

```
  // Etape 1: appeler GET_BILLS_FOR_CUSTOMER
  if (
    !hasToolWithResult(
      steps,
      'GET_BILLS_FOR_CUSTOMER',
      result => result.output != null
    )) {
    return {
      activeTools: [
        'GET_BILLS_FOR_CUSTOMER',
        'SEARCH_CUSTOMER',
      ],
      toolChoice: 'required' as const
    }
  }
```

Pour la première étape de notre workflow, le LLM a besoin d’une liste de factures depuis la base de données. Il lui serait impossible de répondre sans ces données, donc on ne le laisse pas avancer sans.

`hasToolWithResult` est un helper qui nous permet de détecter un appel d’outil dans une itération précédente et de valider son résultat. Ici, tant que le LLM n’a pas appelé l’outil `GET_BILLS_FOR_CUSTOMER`, on le force à l’appeler.

On note la présence d’un deuxième outil: `SEARCH_CUSTOMER`. L’outil `GET_BILLS_FOR_CUSTOMER` nécessite un `customerId`. Cet identifiant se trouve potentiellement dans le contexte (si la demande intervient au cours d’une discussion plus large sur John Doe), mais s’il n’y est pas, le LLM doit avoir les moyens de le retrouver. Le LLM peut choisir de commencer par un `SEARCH_CUSTOMER`, repoussant l’appel de `GET_BILLS_FOR_CUSTOMER` à l’itération suivante.

Enfin, `toolChoice` est passé à `required`, car à cette étape on sait que le LLM n’a aucune raison de sortir de la boucle. On lui demande donc d’itérer avec les outils fournis.

### Etape 2: appliquer notre logique métier

```
  // Etape 2: appliquer une méthode de calcul métier
  // pour produire des données exploitables
  if (
    !hasToolWithResult(
      steps,
      'COLLECT_ANALYSIS_DATA',
      result => result.output != null
  )) {

    // escape hatch: si aucune facture on s'arrête !
    const bills = getToolResults(
      steps,
      'GET_BILLS_FOR_CUSTOMER'
    ).flatMap(
      result => result.output.items
    )
    if (bills.length === 0) {
      return { activeTools: [], toolChoice: 'none' as const }
    }

    return {
      activeTools: ['COLLECT_ANALYSIS_DATA'],
      toolChoice: 'auto' as const
    }
  }
```

Pour la deuxième étape nous allons envoyer les factures trouvées vers notre algorithme afin de produire des données exploitables. C’est dans cet outil que réside l’expertise de la plateforme.

Comme pour la première étape, on utilise `hasToolWithResult` pour forcer l’itération tant que le résultat attendu n’est pas obtenu.

Avant tout, dans le cas où la période ne contient aucune ligne facturable, on doit s’arrêter. Si on laissait le choix au LLM, il y a de fortes chances qu’il essaie de créer une fausse analyse en inventant des factures qui n’existent pas (hallucination).

Par ailleurs le LLM doit faire le lien entre la demande “sur l’année passée” et les dates de factures qu’il a récupéré, il est possible qu’aucune ne corresponde. En passant toolChoice à ‘auto’, on laisse la possibilité au LLM de ne pas appeler l’outil et donc de sortir lui-même de la boucle dans ce cas.

### Implémentation des outils

Les outils sont défini avec le protocol MCP et via des schéma écrits avec Zod. L’utilisation de `.describe()` sur les schémas permet d’ajouter des instructions à destination du LLM pour le guider dans son usage.

```
export const collectAnalysisDataToolDefinition = defineTool(
  'COLLECT_ANALYSIS_DATA',
  'Get analytics data from a set of bills.',
  z.object({
    clientId: z.uuid().describe('The ID of the client.'),
    billIds: z.array(z.number())
      .describe(`The IDs of the bills to analyze.\
        Send empty array if all bills of the client must be included.`
      ),
  }),
  z.object({
    averageSpent: z.number(),
    maxSpent: z.number(),
    mostOrderedProducts: z.array(z.object({
      productId: z.number(),
      productName: z.string(),
      quantity: z.number(),
    })),
    averagePaymentDelay: z.number(),
    customerConfidencePercentage: z.number()
  }),
)
```

Côté implémentation, il est très important de retourner des erreurs compréhensibles et informatives pour le LLM. Celà lui permet de prendre en compte ses erreurs et d’itérer, là où des erreurs génériques conduisent à un abandon rapide, ou pire, une corruption du contexte, ce qui produira des hallucinations.

```
export const collectAnalysisData = implementTool(
  collectAnalysisDataToolDefinition,
  async (args, extra) => {
  
    const { customerId, billIds } = args

    // Guard: vérifier l'existence du client
    const customer = await DatabaseService.getCustomer(customerId);
    if (customer == null) {
      throw new ToolArgumentError(['customerId'],
        `Customer not found: ${customerId}.\
        You may need to use SEARCH_CUSTOMER to find the correct id first.`
      )
    }

    // Guard: vérifier la période et les lignes facturables
    const items = await DatabaseService.getBills(customerId);
    const invalidItemIds = itemIds.filter(
      iid => !items.some(i => iid === i.id)
    )
    if (invalidItemIds.length > 0) {
      throw new ToolArgumentError(['billIds'],
        `Invalid bill ids: ${invalidItemIds.join(', ')}.\
        Possible bill ids are: ${items?.map(i => i.id).join(', ')}.`
      )
    }

    return AnalysisService.analyzeBills(customerId, billIds);

  // ...
```

Attention tout de même, donner trop d’information peut produire des résultats inattendus. Par exemple ici si le LLM essai d’executer une analyse avec un identifiant inconnu, il recevra une liste complète d’identifiants valides, il peut essayer de “se débrouiller” avec une facture sans rapport, simplement parce qu’on lui a suggéré que c’était une possibilité. Ce genre de problème se corrige en partie avec un prompt système.

Une autre approche consiste à demander une validation explicite à l’utilisateur grâce à l’élicitation (article à venir).

### Rendu via un template prédéterminé

Une fois les outils appelés nous avons des données concrètes provenant de calculs déterministes. On pourrait laisser le LLM finaliser sa boucle et rédiger un message, mais il aurait la possibilité de modifier nos données si durement validées !

À la place nous allons lui demander de générer une phrase d’introduction et de conclusion, nous mettrons nous-même les données en forme dans un template. Ainsi les données sont sûres, et le format est répétable.

Pour les parties générées de notre message nous utilisons un outil “vide” qui, uniquement par le format de ses arguments, va inciter le LLM à produire les textes dont nous avons besoin.

```
export const writeAnalysisSummaryToolDefinition = defineTool(
  'WRITE_ANALYSIS_SUMMARY',
  'Write a short introduction and conclusion for a customer analysis.',
  z.object({
    introductionMessage: z.string()
      .describe('A short sentence explaining what the analysis covers (period, scope).'),
    conclusionMessage: z.string()
      .describe('A summary of the analysis and proposed next steps.'),
  }),
  z.object({
    introductionMessage: z.string(),
    conclusionMessage: z.string()
  }),
)

export const writeAnalysisSummary = implementTool(
  writeAnalysisSummaryToolDefinition,
  async (args, extra) => {
    const { introductionMessage, conclusionMessage } = args

    return {
      introductionMessage,
      conclusionMessage
    }
  }
)
```

Cet appel a pour seul but de produire dans le contexte des valeurs qui seront inclues dans le template de réponse.

On laisse ensuite l’itération se terminer. Le message produit n’a pas d’importance et sera remplacé par l’execution du template suivant:

```
{{ introductionMessage }}

## Analytics

| Dépense moyenne | Dépense maximale | Délai de paiement moyen | Indice de confiance |
| --------------- | ---------------- | ----------------------- | ------------------- |
| {{ averageSpent }} | {{ maxSpent }} | {{ averagePaymentDelay }} | {{ customerConfidencePercentage }} |

## Produits les plus commandés

|     Produit   |   Quantité    |
| ------------- | ------------- |
| {{ mostOrderedProducts[0].productName }} | {{ mostOrderedProducts[0].quantity }} |
| {{ mostOrderedProducts[1].productName }} | {{ mostOrderedProducts[1].quantity }} |
| {{ mostOrderedProducts[2].productName }} | {{ mostOrderedProducts[2].quantity }} |

## Conclusion
{{ conclusionMessage }}
```

## Conclusion

Le produit piloté par l’intention apporte une expérience utilisateur très directe, surtout quand l’action “à demander” est plus simple à exprimer qu’à chercher dans une UI. La fiabilité vient d’une idée simple : **laisser le langage naturel ouvrir la porte**, puis faire avancer l’utilisateur dans un workflow strict, explicite, vérifiable et observable.

Ce paradigme se prête bien aux [applications métier](/blog/application-metier) et se combine naturellement avec un cadrage solide des règles et des responsabilités (voir aussi [cadrage projet](/blog/cadrage-projet)).

FAQ

## Questions fréquentes

### En quoi ce pattern est-il différent d’un chatbot “classique” ?

Un chatbot “classique” vise surtout à répondre en texte, avec une confiance totale faite au LLM sur la logique métier. Un produit piloté par l’intention transforme la demande en **actions typées** exécutées par le système, avec des validations métier et une traçabilité. L’IA sert d’interface et de routeur, l’exécution reste cadrée.

### Quand une interface en langage naturel vaut-elle mieux qu’une UI traditionnelle ?

Une interface intent-first est pertinente quand l’utilisateur connaît le résultat souhaité (“créer une facture”, “créer un client”, “générer un rapport”) et que le chemin UI serait long ou fragmenté. Quand le besoin est exploratoire ou visuel (comparaison fine, navigation, édition), une UI dédiée reste souvent plus efficace.

### Comment garder une logique métier stricte avec un LLM ?

La logique métier reste dans une couche déterministe : schémas d’actions, validations, contrôles d’accès, state machine, et confirmation au point d’engagement. Les sorties structurées aident à limiter l’ambiguïté (voir [structured outputs Mistral](https://docs.mistral.ai/capabilities/structured-output/custom_structured_output/)).

### Comment éviter qu’un agent appelle le mauvais outil au mauvais moment ?

Une approche efficace consiste à limiter les tools disponibles à chaque step. Par exemple, au début seuls des outils de recherche et de lecture sont autorisés. Les outils d’écriture ne deviennent disponibles qu’après validation et confirmation.

### Où placer la confirmation utilisateur ?

La confirmation se place au point d’engagement : écriture en base, transaction, envoi, publication. MCP propose un mécanisme standard pour cela via l’elicitation ([elicitation MCP](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation)).

### Ce pattern est-il compatible avec des modèles plus petits et moins coûteux ?

Oui, parce qu’il réduit la part “ouverte” du problème. Le système guide le modèle via un ensemble d’actions typées, des étapes explicites et des validations déterministes. Cela permet d’obtenir des résultats stables sans dépendre uniquement de la capacité de raisonnement du modèle.

### Comment tester un produit piloté par l’intention ?

Le test se fait au niveau des workflows : jeux de prompts réalistes, cas limites (données manquantes, ambiguïtés), erreurs outils, et mesures de taux de succès par step. Une approche progressive de type [POC](/blog/poc-definition) permet de cadrer les risques avant une mise en production.

![Agent IA fiable : pattern intent-first avec MCP](/.netlify/images?url=_astro%2Fthumbnail.CgH7Jf4I.jpg&w=1424&h=752&dpl=69ef5b374d70a50008b43b29)

Sommaire

[1 / Un produit piloté par l’intention, c’est quoi exactement ?](#un-produit-piloté-par-lintention-cest-quoi-exactement-) [2 / Étude de cas : analyse commerciale d’après facturation](#étude-de-cas--analyse-commerciale-daprès-facturation) [3 / Conclusion](#conclusion)

Sommaire 1 / Un produit piloté par l’intention, c’est quoi exactement ? 2 / Étude de cas : analyse commerciale d’après facturation 3 / Conclusion

Lonestone est une agence qui conçoit et développe des produits web et mobile innovants intégrant de l'IA.

Nos experts partagent leurs expériences sur le blog. Contactez-nous pour discuter de vos projets !

[En savoir plus sur Lonestone](/)

[Parler à un expert](/contact)

![](/_astro/use-case-corner.3qL77H9h.png)

Besoin d'accompagnement ?

Nos experts sont disponibles pour discuter de votre projet.

[Parler à un expert](/contact)

## On continue la lecture ?

[Tous les articles](/blog)

[![Application métier : Définition, rôle et exemples concrets](/.netlify/images?url=_astro%2Fthumbnail.OHfpBOW5.webp&w=1200&h=973&dpl=69ef5b374d70a50008b43b29)

Application métier : Définition, rôle et exemples concrets

Découvrez le guide des applications métier : définition, avantages et exemples concrets pour optimiser vos processus d'entreprise.



](/blog/application-metier)[![Comment réussir le cadrage de vos projets de produits numériques ? ](/.netlify/images?url=_astro%2Fthumbnail.Cv6lu4MM.jpg&w=1200&h=973&dpl=69ef5b374d70a50008b43b29)

Comment réussir le cadrage de vos projets de produits numériques ? 

Découvrez comment un cadrage projet efficace optimise le développement de produits numériques : flexibilité, itération, alignement des objectifs stratégiques.



](/blog/cadrage-projet)[![POC (Proof of Concept) : Définition & étapes clés](/.netlify/images?url=_astro%2Fthumbnail.mxH_DB9N.jpg&w=1200&h=973&dpl=69ef5b374d70a50008b43b29)

POC (Proof of Concept) : Définition & étapes clés

Un POC permet de vérifier la faisabilité et la pertinence d'une idée à moindre coût. Réalisé tôt dans le projet, il limite la prise de risques notamment financiers.



](/blog/poc-definition)

[Tous les articles](/blog)

## Nos guides

### Guide de l'IA générative

[

01\. IA, Machine Learning & Deep Learning



](/ai/deep-learning)[

02\. LLM (Large Language Model) : définition et fonctionnement



](/ai/llm)[

03\. RAG + MCP : Architecture pour agents IA



](/ai/rag-mcp)[

04\. Agents IA : définition et cas d'usage



](/ai/agents-ia)[

05\. Boostez vos logiciels avec l'intégration de l'IA



](/ai/integration-ia)[

06\. Meilleurs outils IA pour les entreprises



](/ai/outils-ia)

### Guide pour créer un SaaS IA

[

01\. Construire un SaaS IA rentable : la méthode qui fonctionne



](/creer-saas-ia/roadmap)[

02\. Comparatif LLM 2026 : quel modèle choisir pour votre SaaS ?



](/creer-saas-ia/comparatif-llm-saas)[

03\. Héberger un SaaS IA en France : performances, coûts et souveraineté



](/creer-saas-ia/heberger-ia)[

04\. Framework d’évaluation IA : pourquoi et comment le mettre en place



](/creer-saas-ia/evaluation)[

05\. MCP (Model Context Protocol) : le standard pour connecter IA et logiciels



](/creer-saas-ia/mcp)[

06\. Bien choisir entre RAG et ses alternatives : ce que personne ne vous explique vraiment



](/creer-saas-ia/rag)[

07\. Comment concevoir un copilote IA qui crée de la vraie valeur dans un SaaS



](/creer-saas-ia/copilote-ia)[

08\. POC IA : comment valider votre idée avant d’investir dans un SaaS IA



](/creer-saas-ia/poc)

### Blog de Lonestone

Retrouvez nos derniers articles sur le développement web, l'IA, le product design et les retours d'expérience de nos projets.

[Accéder au blog](/blog)

Lonestone apporte son expertise product à 200+ grands comptes, PME et startups depuis 11 ans.

Avec notre équipe senior et nos méthodes rodées, vous pouvez comptez sur une livraison rapide d'un produit robuste vraiment utile.

## Nos solutions

[

01\. Intégration IA pour éditeurs logiciels



](/solutions/integration-ia-editeurs)[

02\. Automatisation de process avec IA



](/solutions/automatisation-process-ia)[

03\. Création de SaaS pour startup



](/solutions/creation-saas)[

04\. Développement d'applications métier



](/solutions/developpement-outil-metier)[

05\. Création d'app mobile



](/solutions/creation-app-mobile)[

06\. Création de site web



](/solutions/creation-site-web)[

07\. Création de CRM sur mesure



](/solutions/crm-sur-mesure)[

08\. Création de marketplace sur mesure



](/solutions/creation-marketplace)[

09\. Refonte de site web



](/solutions/refonte-site-web)

## On discute de votre projet ?

Échange gratuit et sans engagement, directement avec un expert du sujet. Devis sous 48h.

[Demander un devis](/contact) Prendre rdv

Contacter l'équipe  
de Lonestone
