MCP vs RAG vs AI Agent : explication détaillée

Cet article a une courte vidéo :YouTubeTikTok

1. Aperçu

Les trois noms MCP, RAG et AI Agent apparaissent partout quand on parle d’applications LLM, et il est facile de les confondre comme « trois options concurrentes ». En réalité, ils résolvent trois problèmes différents : RAG aide le LLM à répondre correctement en fonction des données propriétaires, AI Agent aide le LLM à planifier et agir lui-même, tandis que MCP est un standard de communication pour que l’application LLM se connecte aux outils/données de manière unifiée. Après avoir lu cet article, vous distinguerez clairement les trois concepts, saurez quand utiliser lequel, et aurez quelques extraits de code Java/Spring AI fonctionnels pour visualiser la réalité.

2. Qu’est-ce que RAG

RAG (Retrieval-Augmented Generation) est une technique d’« intégration » de données externes dans le prompt avant d’appeler le LLM, pour que le modèle réponde en se basant sur des données réelles plutôt que seulement sur les connaissances apprises lors de l’entraînement.

2.1. Pipeline de RAG

  1. Convertir le texte source (politique, documents, FAQ) en embedding (vecteur numérique) et le stocker dans une base de données vectorielle.
  2. Quand l’utilisateur pose une question, convertir la question en embedding et trouver les passages de texte les plus proches (similarity search).
  3. Intégrer les passages trouvés dans le prompt, puis envoyer le tout au LLM pour générer la réponse.

Par exemple, OrderService doit répondre à la question « Quelle est la politique d’échange et de retour pour les commandes en ligne ? » en se basant sur les documents de politique de l’entreprise, pas sur les connaissances générales du modèle.

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
</dependency>

2.2. Exemple de code RAG simple

@Service
public class ReturnPolicyAssistant {

    private final VectorStore vectorStore;
    private final ChatClient chatClient;

    public ReturnPolicyAssistant(VectorStore vectorStore, ChatClient.Builder builder) {
        this.vectorStore = vectorStore;
        this.chatClient = builder.build();
    }

    public String answer(String question) {
        List<Document> relevant = vectorStore.similaritySearch(
                SearchRequest.query(question).withTopK(3));

        String context = relevant.stream()
                .map(Document::getContent)
                .collect(Collectors.joining("\n---\n"));

        String prompt = """
                En vous basant sur le document de politique ci-dessous, répondez à la question du client.
                S'il n'y a pas d'information, dites "je ne suis pas certain".

                Document :
                %s

                Question : %s
                """.formatted(context, question);

        return chatClient.prompt(prompt).call().content();
    }
}
  • vectorStore.similaritySearch retourne les 3 passages de document les plus proches sémantiquement de la question.
  • Le prompt est « augmenté » (augmented) avec le contexte extrait du vector store avant d’être envoyé au LLM.
  • Si le document ne contient pas d’information pertinente, le modèle est guidé pour répondre « je ne suis pas certain » au lieu d’inventer (hallucination).
Input:  « J'ai acheté un t-shirt en ligne il y a 5 jours, puis-je l'échanger ? »
Output: « Selon la politique, vous pouvez échanger ou retourner dans les 7 jours 
         suivant la réception, à condition que le produit ait son étiquette. 
         Votre t-shirt est encore dans le délai. »

Note : RAG aide seulement le modèle à « connaître plus de données ». Cela ne l’aide pas à agir par lui-même (appeler une API, mettre à jour une base de données). Si vous avez besoin que le modèle fasse quelque chose plutôt que juste répondre, vous avez besoin d’un Agent.

3. Qu’est-ce qu’un AI Agent

AI Agent est un système qui utilise un LLM comme « cerveau » pour planifier, sélectionner un outil approprié, appeler cet outil, observer le résultat, puis répéter jusqu’à atteindre l’objectif — différent de RAG qui s’arrête à répondre à une seule question.

3.1. La boucle reason → act → observe

Le modèle le plus courant est ReAct : le modèle réfléchit (reasoning), sélectionne un outil à appeler (action), reçoit le résultat (observation), puis réfléchit à nouveau en fonction de ce résultat.

public interface AgentTool {
    String name();
    String description();
    String execute(String input);
}

@Component
public class CheckOrderStatusTool implements AgentTool {
    private final OrderRepository orderRepository;

    public CheckOrderStatusTool(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public String name() { return "checkOrderStatus"; }
    public String description() { return "Vérifier le statut de la commande par orderId"; }

    public String execute(String orderId) {
        return orderRepository.findById(orderId)
                .map(o -> "La commande %s est %s".formatted(orderId, o.getStatus()))
                .orElse("Commande non trouvée " + orderId);
    }
}

3.2. L’Agent sélectionne et appelle les outils

@Service
public class SupportAgent {

    private final ChatClient chatClient;
    private final List<AgentTool> tools;

    public SupportAgent(ChatClient.Builder builder, List<AgentTool> tools) {
        this.chatClient = builder.build();
        this.tools = tools;
    }

    public String handle(String userMessage) {
        return chatClient.prompt()
                .user(userMessage)
                .tools(tools.toArray()) // Spring AI mappe automatiquement la méthode execute() en function-call
                .call()
                .content();
    }
}
  • tools(tools.toArray()) indique au modèle quels outils il peut appeler ; Spring AI génère automatiquement le schéma pour le function-calling.
  • Le modèle décide automatiquement : si la question concerne le statut de la commande, il appelle checkOrderStatus avec le orderId déduit de la question, reçoit le résultat, puis reformule en langage naturel.
User: « Où en est ma commande ORD-2024-891 ? »
Agent: (appelle l'outil checkOrderStatus(« ORD-2024-891 »))
       → « La commande ORD-2024-891 est en cours de livraison, elle devrait arriver dans 2 jours. »

3.3. Tester le comportement de sélection d’outil

@Test
void shouldCallCheckOrderStatusWhenAskingAboutOrder() {
    AgentTool tool = new CheckOrderStatusTool(orderRepository);
    when(orderRepository.findById("ORD-1")).thenReturn(Optional.of(shippedOrder));

    String result = tool.execute("ORD-1");

    assertThat(result)
        .contains("ORD-1")
        .contains("SHIPPED");
}

Ce test n’appelle pas le LLM réel, seulement pour confirmer que la logique de l’outil fonctionne correctement — la partie « quel outil le modèle sélectionne » devrait être testée séparément avec un eval set, pas avec des unit tests rigides car la sortie du LLM n’est pas déterministe.

Note : Les Agents sont puissants mais plus risqués que RAG — si un outil a des droits d’écriture (remboursement, mise à jour), un mauvais raisonnement du modèle peut avoir des conséquences réelles. Ajoutez toujours une étape de confirmation ou limitez les droits pour les outils sensibles.

4. Qu’est-ce que MCP

MCP (Model Context Protocol) est un standard ouvert qui définit comment une application LLM (appelée host) se connecte aux sources de données et outils externes (appelés server) via un protocole unifié, au lieu que chaque intégration écrive son propre code.

4.1. Le problème que MCP résout

Avant MCP, si vous aviez 5 applications LLM et que chaque app devait accéder à 10 sources de données/outils (base de données, CRM, système de fichiers…), vous deviez écrire jusqu’à 50 intégrations distinctes — le problème classique N×M. MCP le transforme en N+M : chaque serveur n’est écrit qu’une fois, tout hôte conforme au standard MCP peut l’appeler.

4.2. Architecture MCP

  • Host : application contenant le LLM (chatbot, assistant IDE…).
  • Client : composant du host, maintient une connexion 1-1 avec un serveur MCP.
  • Server : processus exposant 3 types de « capabilities » :
    • Resources : données lisibles (ressemble à un endpoint GET).
    • Tools : actions appelables (ressemble à POST/action).
    • Prompts : modèles de prompt réutilisables.

4.3. Exemple : écrire un serveur MCP en Java

<dependency>
    <groupId>io.modelcontextprotocol.sdk</groupId>
    <artifactId>mcp</artifactId>
    <version>0.8.0</version>
</dependency>
public class OrderMcpServer {

    public static void main(String[] args) {
        McpSyncServer server = McpServer.sync(StdioServerTransport.builder().build())
                .serverInfo("order-service", "1.0.0")
                .tool(orderLookupTool())
                .build();

        server.start();
    }

    private static McpServerFeatures.SyncToolSpecification orderLookupTool() {
        var schema = """
                {
                  "type": "object",
                  "properties": { "orderId": { "type": "string" } },
                  "required": ["orderId"]
                }
                """;

        return new McpServerFeatures.SyncToolSpecification(
                new Tool("getOrderById", "Chercher la commande par orderId", schema),
                (exchange, args) -> {
                    String orderId = (String) args.get("orderId");
                    String status = OrderLookup.findStatus(orderId);
                    return new CallToolResult(List.of(new TextContent(status)), false);
                });
    }
}
  • McpServer.sync(...) construit un serveur utilisant le transport stdio (l’hôte lance le serveur comme un sous-processus et communique via stdin/stdout).
  • tool(orderLookupTool()) enregistre un outil getOrderById avec un schéma JSON décrivant l’entrée, pour que tout hôte conforme à MCP puisse lire « quels champs cet outil nécessite ».
  • Quand le client appelle getOrderById, le serveur retourne un CallToolResult contenant le contenu textuel.

4.4. Appeler le serveur MCP depuis le client Spring AI

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
spring:
  ai:
    mcp:
      client:
        stdio:
          connections:
            order-service:
              command: java
              args: ["-jar", "order-mcp-server.jar"]
@Service
public class McpAgentBridge {

    private final SyncMcpToolCallbackProvider toolCallbackProvider;
    private final ChatClient chatClient;

    public McpAgentBridge(SyncMcpToolCallbackProvider toolCallbackProvider,
                           ChatClient.Builder builder) {
        this.toolCallbackProvider = toolCallbackProvider;
        this.chatClient = builder.build();
    }

    public String ask(String question) {
        return chatClient.prompt()
                .user(question)
                .tools(toolCallbackProvider.getToolCallbacks())
                .call()
                .content();
    }
}
  • Spring AI démarre automatiquement le serveur MCP (order-mcp-server.jar) comme sous-processus, en maintenant la connexion via stdio.
  • toolCallbackProvider.getToolCallbacks() récupère tous les outils que le serveur expose (ici getOrderById) et les intègre au modèle comme function-calling, exactement comme vous avez enregistré manuellement AgentTool dans la section 3 — la différence est que cet outil est fourni par un processus indépendant conformément au standard.

Note : MCP ne remplace pas le function-calling du LLM. C’est une couche de transport/normalisation sous le function-calling, permettant d’écrire un outil une fois et de le réutiliser sur plusieurs hôtes (Claude Desktop, IDE, votre application) sans modifier le code d’intégration à chaque fois.

5. Comparaison MCP, RAG et AI Agent

Critère RAG AI Agent MCP
Résout le problème de Le modèle manque de données propriétaires/nouvelles Le modèle n’agit pas lui-même, ne peut pas planifier sur plusieurs étapes Code d’intégration d’outils/données dupliqué, pas standardisé
Unité d’opération Récupération + augmentation du prompt Boucle reason-act-observe Protocole de connexion host ↔ server
Appelle-t-il des outils/actions ? Non (lecture seule) Oui Oui
Niveau de risque Faible (seules les mauvaises réponses si la récupération est faible) Élevé (peut exécuter des actions incorrectes) Dépend de l’outil sous-jacent, le protocole lui-même est neutre
Relations entre eux Est une « source de données » que l’Agent peut utiliser Peut utiliser RAG comme outil, utiliser MCP pour appeler les outils de manière standardisée Est le « pipeline » permettant à l’Agent d’appeler RAG/outils de manière uniforme

En résumé : RAG donne au modèle des connaissances, Agent lui donne la capacité d’agir, MCP est le standard de communication pour que cette action/récupération ne soit pas réécrite à chaque changement d’hôte ou d’outil.

6. Combiner les trois dans un système réel

En pratique, les grands projets ne choisissent pas l’un des trois, mais les empilent :

User → AI Agent (planification, boucle ReAct)
          ├─ appelle MCP Client → MCP Server « knowledge-base » → retourne les résultats de RAG similarity search
          ├─ appelle MCP Client → MCP Server « order-service »  → outil getOrderById
          └─ appelle MCP Client → MCP Server « payment-service » → outil refundPayment (nécessite confirmation)

Dans cette architecture :

  • RAG se trouve dans un serveur MCP distinct (par exemple, le serveur « knowledge-base » expose une ressource contenant les résultats de similarity search).
  • Agent est l’orchestrateur exécutant la boucle de raisonnement, sans se soucier du langage dans lequel l’outil est écrit, tant qu’il respecte MCP.
  • MCP est la couche de communication unifiée entre l’Agent et tous les serveurs, vous permettant d’ajouter des serveurs (par exemple « shipping-service ») sans modifier le code de l’Agent.

7. Erreurs courantes

  • Utiliser RAG quand on a besoin d’action : intégrer des données dans le prompt mais s’attendre à ce que le modèle mette à jour la base de données lui-même — RAG ne peut pas le faire, vous avez besoin d’un Agent + d’outils avec droits d’écriture.
  • Agent sans contrôle des droits d’outil : autoriser l’Agent à appeler directement des outils comme deleteOrder ou refundPayment sans confirmation humaine, risquant des dégâts si le raisonnement du modèle échoue.
  • Confondre MCP avec un « framework d’IA » : MCP n’est qu’un protocole de connexion, il ne raisonne pas, n’effectue pas de récupération lui-même. Si votre serveur n’expose aucun outil/ressource utile, avoir MCP ne rendra pas le modèle plus intelligent.
  • Top-k trop petit ou trop grand dans RAG : top-k petit (1-2) risque de manquer de contexte, top-k grand (10+) rend le prompt long, consomme des tokens et dilue les signaux importants. Commencez par 3-5 et évaluez avec un vrai eval set.
  • Ne pas versionner le schéma des outils MCP : quand vous modifiez le schéma d’entrée d’un outil sans augmenter la version, les autres hôtes utilisant l’ancien serveur l’appelleront avec des paramètres incorrects.

8. Conclusion

RAG, AI Agent et MCP ne se concurrencent pas — ils se situent à trois couches différentes d’un système LLM : RAG fournit les données, Agent fournit la capacité de décision et d’action, MCP fournit le standard de connexion pour que ce dernier ne soit pas réécrit à chaque changement d’outil ou d’hôte. Lors de la conception d’un système IA réel, vous devez commencer par la question « que le modèle doit-il savoir de plus ? » (RAG), « que le modèle doit-il faire ? » (Agent), puis « comment rendre cet outil/ces données réutilisables à plusieurs endroits ? » (MCP) — cet ordre vous aide à éviter sur-ingénierie d’emblée.

mcpragai-agentspring-aillm