MCP vs RAG vs AI Agent: explicação detalhada com exemplos
1. Visão geral
Os três nomes MCP, RAG, AI Agent aparecem por toda parte quando se fala sobre aplicações com LLM, e é fácil confundi-los como “três opções concorrentes”. Na verdade, eles resolvem três problemas diferentes: RAG ajuda o LLM a responder corretamente com base em dados próprios, AI Agent ajuda o LLM a planejar e agir por conta própria, enquanto MCP é um padrão de comunicação para que aplicações LLM se conectem com ferramentas/dados de forma unificada. Após ler este artigo, você diferenciará claramente os três conceitos, saberá quando usar cada um, e terá alguns trechos de código Java/Spring AI funcionando para visualizar a realidade.
2. O que é RAG
RAG (Retrieval-Augmented Generation) é uma técnica que “injeta” dados externos na prompt antes de chamar o LLM, para que o modelo responda com base em dados reais, em vez de apenas usar conhecimento aprendido durante o treinamento.
2.1. Fluxo de trabalho do RAG
- Converter texto de origem (política, documentação, FAQ) em embedding (vetor numérico) e armazenar em vector database.
- Quando o usuário faz uma pergunta, converter a pergunta em embedding, encontrar os trechos de texto mais similares (similarity search).
- Inserir os trechos encontrados na prompt, depois enviar para o LLM gerar a resposta.
Exemplo: OrderService precisa responder “qual é a política de devoluções para compras online?” com base em documentação da empresa, não no conhecimento geral do modelo.
<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. Exemplo de código RAG simples
@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 = """
Com base nos documentos de política abaixo, responda à pergunta do cliente.
Se não houver informação, diga "não tenho certeza".
Documentos:
%s
Pergunta: %s
""".formatted(context, question);
return chatClient.prompt(prompt).call().content();
}
}
vectorStore.similaritySearchretorna os 3 trechos de documentação mais similares em significado à pergunta.- A prompt é “aumentada” (augmented) com contexto obtido do vector store antes de ser enviada ao LLM.
- Se a documentação não contiver informações relevantes, o modelo é orientado a responder “não tenho certeza”, em vez de inventar (hallucination).
Entrada: "Comprei uma camiseta online há 5 dias, posso trocar?"
Saída: "De acordo com a política, você pode trocar em até 7 dias após receber a mercadoria,
desde que o produto ainda tenha a etiqueta. Sua camiseta ainda está dentro do prazo."
Nota: RAG apenas ajuda o modelo a “conhecer mais dados”. Não ajuda o modelo a agir por conta própria (chamar API, atualizar banco de dados). Se você precisa que o modelo faça algo e não apenas responda, você precisa de um Agent.
3. O que é AI Agent
AI Agent é um sistema que usa LLM como “cérebro” para planejar, escolher a ferramenta apropriada, chamar a ferramenta, observar o resultado, e repetir até atingir o objetivo — diferente de RAG que para apenas em responder uma pergunta.
3.1. Loop de raciocínio → ação → observação
O modelo mais comum é ReAct: o modelo pensa (reasoning), escolhe uma ferramenta para chamar (action), recebe o resultado (observation), e pensa novamente baseado no resultado.
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 "Verificar o status do pedido pelo orderId"; }
public String execute(String orderId) {
return orderRepository.findById(orderId)
.map(o -> "Pedido %s está %s".formatted(orderId, o.getStatus()))
.orElse("Pedido não encontrado " + orderId);
}
}
3.2. Agent escolhe e chama ferramenta
@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 mapeia automaticamente o método execute() para function-calling
.call()
.content();
}
}
tools(tools.toArray())informa ao modelo que ele pode chamar essas ferramentas; Spring AI gera automaticamente o schema para function-calling.- O modelo decidirá por si: se a pergunta está relacionada ao status do pedido, ele chama
checkOrderStatuscomorderIdinferido da pergunta, recebe o resultado, e depois reescreve a resposta em linguagem natural.
Usuário: "E aí, como está meu pedido ORD-2024-891?"
Agent: (chama tool checkOrderStatus("ORD-2024-891"))
→ "Seu pedido ORD-2024-891 está sendo entregue, deve chegar em 2 dias."
3.3. Testar comportamento de escolha de ferramenta
@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");
}
Este teste não chama o LLM de verdade, apenas confirma que a lógica da ferramenta funciona corretamente — a parte “model escolhe qual ferramenta” deve ser testada separadamente com um evaluation set, não deve ser unit test rígido porque a saída do LLM não é determinística.
Nota: Agent é poderoso mas risco maior que RAG — se a ferramenta tem permissão de escrita (refund, update), um raciocínio errado do modelo pode causar consequências reais. Sempre adicione uma etapa de confirmação (confirmation) ou restrinja as permissões das ferramentas sensíveis.
4. O que é MCP
MCP (Model Context Protocol) é um padrão aberto que define como uma aplicação LLM (chamada de host) se conecta com fontes de dados e ferramentas externas (chamadas de server) através de um protocolo unificado, em vez de cada integração escrever código separadamente.
4.1. Problema que MCP resolve
Antes do MCP, se você tinha 5 aplicações LLM e queria que cada uma pudesse usar 10 fontes de dados/ferramentas (banco de dados, CRM, sistema de arquivos…), você teria que escrever até 50 integrações separadas — o conhecido problema N×M. MCP o transforma em N+M: cada servidor é escrito uma vez, e qualquer host que siga o padrão MCP consegue chamá-lo.
4.2. Arquitetura do MCP
- Host: aplicação que contém o LLM (chatbot, assistente de IDE…).
- Client: componente dentro do host que mantém conexão 1-1 com um servidor MCP.
- Server: processo que expõe 3 tipos de “capability”:
- Resources: dados legíveis (como endpoint GET).
- Tools: ações que podem ser chamadas (como POST/action).
- Prompts: templates de prompt reutilizáveis.
4.3. Exemplo: escrever um servidor MCP em 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", "Buscar pedido pelo 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(...)cria um servidor que usa transportestdio(o host executa o servidor como subprocess e se comunica via stdin/stdout).tool(orderLookupTool())registra uma ferramentagetOrderByIdcom schema JSON que descreve a entrada, para que qualquer host que siga o padrão MCP consiga entender qual é a ferramenta esperada.- Quando um cliente chama
getOrderById, o servidor retornaCallToolResultcontendo o conteúdo em texto.
4.4. Chamar servidor MCP a partir de cliente 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 inicia automaticamente o servidor MCP (
order-mcp-server.jar) como subprocess, mantendo conexão via stdio. toolCallbackProvider.getToolCallbacks()obtém todas as ferramentas que o servidor expõe (neste casogetOrderById) e as coloca no modelo como function-calling, da mesma forma que você registrariaAgentToolmanualmente na seção 3 — a única diferença é que essa ferramenta vem de um processo independente seguindo o padrão unificado.
Nota: MCP não substitui o function-calling do LLM. É uma camada de transporte/padronização que fica abaixo do function-calling, ajudando as ferramentas a serem escritas uma vez e reutilizadas em muitos hosts (Claude Desktop, IDE, sua aplicação) sem precisar modificar código de integração cada vez.
5. Comparação entre MCP, RAG e AI Agent
| Critério | RAG | AI Agent | MCP |
|---|---|---|---|
| Que problema resolve | Modelo carece de dados próprios/novos | Modelo não age por conta própria, sem planejamento multi-etapa | Código de integração de ferramentas/dados duplicado, sem padronização |
| Unidade de operação | Recuperação + aumento de prompt | Loop de raciocínio-ação-observação | Protocolo de conexão host ↔ server |
| Chama ferramentas/ações | Não (apenas leitura) | Sim | Sim (expõe ferramentas para agent chamar) |
| Nível de risco | Baixo (responde errado apenas se retrieval for ruim) | Alto (pode executar ações incorretas) | Depende das ferramentas abaixo, o próprio protocolo é neutro |
| Relação entre eles | É uma “fonte de dados” que Agent pode usar | Pode usar RAG como uma ferramenta, usar MCP para chamar ferramentas padronizadas | É o “duto” para Agent chamar RAG/ferramentas de forma padronizada |
Resumindo: RAG dá conhecimento ao modelo, Agent dá ao modelo capacidade de agir, MCP é o padrão de comunicação para que a ação/recuperação não precise ser reescrita cada vez que você muda de host ou de ferramenta.
6. Combinando os três em um sistema real
Na prática, projetos grandes não escolhem um dos três, mas os empilham:
Usuário → AI Agent (planeja, loop ReAct)
├─ chama MCP Client → MCP Server "knowledge-base" → retorna resultado RAG similarity search
├─ chama MCP Client → MCP Server "order-service" → ferramenta getOrderById
└─ chama MCP Client → MCP Server "payment-service" → ferramenta refundPayment (precisa confirmação)
Nesta arquitetura:
- RAG fica dentro de um servidor MCP separado (por exemplo, servidor “knowledge-base” expõe resource com resultado de similarity search).
- Agent é a parte que orquestra (orchestrator) executando o loop de reasoning, não se importa como a ferramenta foi escrita, apenas exige que siga MCP.
- MCP é a camada de comunicação unificada entre Agent e todos os servidores, permite adicionar servidor novo (por exemplo, “shipping-service”) sem modificar o código do Agent.
7. Erros comuns
- Usar RAG quando precisa de ação: colocar dados na prompt mas esperar que o modelo atualize o banco de dados sozinho — RAG não faz isso, você precisa de Agent + ferramenta com permissão de escrita.
- Agent sem restringir permissões de ferramentas: deixar agent chamar direto ferramentas como
deleteOrderourefundPaymentsem confirmação humana, levando a danos quando o modelo raciocina errado. - Confundir MCP com um “framework de IA”: MCP é apenas um protocolo de conexão, não pensa por conta própria, não recupera dados. Se seu servidor não expõe ferramentas/resources úteis, MCP por si só não faz o modelo mais inteligente.
- Top-k muito pequeno ou muito grande em RAG: top-k pequeno (1-2) falta contexto, top-k grande (10+) deixa a prompt longa, consome tokens, e dilui o sinal importante. Comece com 3-5 e avalie novamente com seu evaluation set real.
- Não fazer versionamento do schema das ferramentas MCP: quando muda input schema de uma ferramenta sem aumentar a versão, outros hosts usando o servidor antigo vão chamar com parâmetros errados.
8. Conclusão
RAG, AI Agent e MCP não competem uns com os outros — ficam em três camadas diferentes de um sistema LLM: RAG fornece dados, Agent fornece capacidade de decisão e ação, MCP fornece padrão de conexão para que a ação/recuperação não precise ser reescrita cada vez que muda de ferramenta ou host. Ao projetar um sistema de IA real, comece com a pergunta “o modelo precisa saber mais o quê” (RAG), “o modelo precisa fazer o quê” (Agent), depois pense em “como reutilizar essas ferramentas/dados em muitos lugares” (MCP) — essa ordem ajuda a evitar over-engineering já no início.