Aller au contenu
IA 10 min de lecture

IA agentique en Java : construire des agents autonomes avec Spring AI

Agents IA autonomes en Java : architecture, tool use, orchestration multi-agents avec Spring AI et LangChain4j. Exemples concrets pour passer du chatbot à l'agent.

IA agentiqueAgents IAJavaSpring AILangChain4jLLMAnthropicTool use

En 2024, on parlait de LLMs et de chatbots. En 2026, la tendance est aux agents autonomes : des systèmes qui planifient, agissent, observent et s’adaptent sans intervention humaine à chaque étape. Plus d’un quart du code produit chez Google est généré par des agents IA. Cette technologie sort des labos et entre en production.

Voici comment construire un agent IA robuste en Java.

Ce qui distingue un agent d’un simple LLM

Un LLM répond à un prompt. Un agent utilise un LLM comme moteur de raisonnement, mais il peut :

  • Appeler des outils (fonctions Java, APIs, requêtes SQL)
  • Observer les résultats de ces appels
  • Planifier la prochaine action en fonction de ce qu’il a observé
  • Itérer jusqu’à atteindre l’objectif

Le pattern dominant est ReAct (Reasoning + Acting) :

Thought: Je dois trouver le prix du produit X
Action: search_product(name="X")
Observation: {"id": 42, "price": 19.99, "stock": 12}
Thought: J'ai le prix. Je dois maintenant vérifier si le stock est suffisant.
Action: check_availability(product_id=42, quantity=5)
Observation: {"available": true}
Thought: Je peux passer la commande.
Action: create_order(product_id=42, quantity=5)
Final Answer: Commande créée pour 5 unités de X à 19,99 €.

Chaque itération est un appel LLM. L’agent décide seul quand s’arrêter.

Dépendances Maven

<!-- Spring AI avec Claude (Anthropic) -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-anthropic-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

<!-- Ou LangChain4j (plus de features agents) -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-anthropic</artifactId>
    <version>0.32.0</version>
</dependency>
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j</artifactId>
    <version>0.32.0</version>
</dependency>

Construire un agent avec LangChain4j

1. Définir les outils

Les outils sont de simples méthodes Java annotées. Le LLM décide quand les appeler en lisant leurs descriptions.

@Component
public class ProductTools {

    @Autowired
    private ProductRepository productRepository;

    @Tool("Recherche un produit par nom. Retourne son ID, son prix et son stock.")
    public ProductInfo searchProduct(@P("Nom du produit à rechercher") String name) {
        return productRepository.findByName(name)
            .map(p -> new ProductInfo(p.getId(), p.getName(), p.getPrice(), p.getStock()))
            .orElseThrow(() -> new ToolException("Produit '" + name + "' introuvable"));
    }

    @Tool("Vérifie si une quantité donnée est disponible en stock pour un produit.")
    public AvailabilityResult checkAvailability(
            @P("ID du produit") Long productId,
            @P("Quantité souhaitée") int quantity) {
        Product p = productRepository.findById(productId)
            .orElseThrow(() -> new ToolException("Produit ID " + productId + " introuvable"));
        boolean available = p.getStock() >= quantity;
        return new AvailabilityResult(available, p.getStock());
    }

    @Tool("Crée une commande pour un produit. Requiert la confirmation que le stock est disponible.")
    public OrderResult createOrder(
            @P("ID du produit") Long productId,
            @P("Quantité à commander") int quantity,
            @P("Email du client") String customerEmail) {
        // Logique métier : vérification, décrémentation stock, envoi confirmation
        Order order = orderService.create(productId, quantity, customerEmail);
        return new OrderResult(order.getId(), order.getTotalPrice());
    }
}

2. Définir l’interface agent

public interface ShoppingAgent {
    @SystemMessage("""
        Tu es un assistant e-commerce. Tu aides les clients à trouver des produits,
        vérifier leur disponibilité et passer des commandes.
        Utilise toujours les outils disponibles pour obtenir des informations réelles.
        Ne confirme jamais une commande sans avoir vérifié le stock au préalable.
        """)
    String chat(@MemoryId String sessionId, @UserMessage String userMessage);
}

3. Assembler l’agent

@Configuration
public class AgentConfig {

    @Bean
    public ShoppingAgent shoppingAgent(ProductTools tools) {
        return AiServices.builder(ShoppingAgent.class)
            .chatLanguageModel(
                AnthropicChatModel.builder()
                    .apiKey(System.getenv("ANTHROPIC_API_KEY"))
                    .modelName("claude-sonnet-4-6")
                    .maxTokens(4096)
                    .build()
            )
            .tools(tools)
            .chatMemoryProvider(memoryId ->
                MessageWindowChatMemory.withMaxMessages(20)
            )
            .build();
    }
}

4. Exposer via un endpoint REST

@RestController
@RequestMapping("/api/agent")
public class AgentController {

    @Autowired
    private ShoppingAgent agent;

    @PostMapping("/chat")
    public ResponseEntity<ChatResponse> chat(
            @RequestHeader("X-Session-Id") String sessionId,
            @RequestBody ChatRequest request) {
        String response = agent.chat(sessionId, request.message());
        return ResponseEntity.ok(new ChatResponse(response));
    }
}

Test :

curl -X POST http://localhost:8080/api/agent/chat \
  -H "Content-Type: application/json" \
  -H "X-Session-Id: user-123" \
  -d '{"message": "Je veux commander 3 unités du produit Java Handbook"}'

L’agent va automatiquement :

  1. Chercher le produit search_product("Java Handbook")
  2. Vérifier le stock check_availability(id, 3)
  3. Créer la commande si disponible create_order(id, 3, email)
  4. Répondre à l’utilisateur avec le récapitulatif

Gestion des erreurs et des cas limites

Un agent en production doit être robuste. Les LLMs peuvent tenter d’appeler des outils avec des paramètres incorrects, ou boucler.

@Tool("Envoie un email de confirmation au client.")
public EmailResult sendConfirmation(@P("Adresse email") String email,
                                    @P("Contenu du message") String message) {
    // Validation : jamais faire confiance à ce que le LLM passe
    if (!email.matches("^[^@]+@[^@]+\\.[^@]+$")) {
        throw new ToolException("Email invalide : " + email);
    }
    if (message.length() > 2000) {
        throw new ToolException("Message trop long (max 2000 caractères)");
    }
    // Envoi réel
    emailService.send(email, message);
    return new EmailResult(true, "Email envoyé à " + email);
}

Timeout sur le cycle agent :

@Bean
public ShoppingAgent shoppingAgent(ProductTools tools) {
    return AiServices.builder(ShoppingAgent.class)
        .chatLanguageModel(
            AnthropicChatModel.builder()
                .apiKey(System.getenv("ANTHROPIC_API_KEY"))
                .modelName("claude-sonnet-4-6")
                .maxTokens(4096)
                .timeout(Duration.ofSeconds(60)) // timeout global
                .build()
        )
        .tools(tools)
        .maxSequentialToolCalls(10) // max 10 appels d'outils par tour
        .build();
}

Agents multi-étapes avec Spring AI

Spring AI (depuis 1.0) propose une API ChatClient fluente qui permet de chaîner des appels avec des outils déclarés comme des @Bean :

@Service
public class AnalysisAgentService {

    private final ChatClient chatClient;

    public AnalysisAgentService(ChatClient.Builder builder,
                                 DataAnalysisTools tools) {
        this.chatClient = builder
            .defaultSystem("Tu es un analyste de données. Utilise les outils pour répondre.")
            .defaultFunctions("fetchSalesData", "computeStats", "generateReport")
            .build();
    }

    public String analyze(String question) {
        return chatClient.prompt()
            .user(question)
            .call()
            .content();
    }
}

@Component
public class DataAnalysisTools {

    @Bean
    @Description("Récupère les données de ventes pour une période donnée")
    public Function<SalesRequest, SalesData> fetchSalesData() {
        return req -> salesRepository.findByPeriod(req.from(), req.to());
    }

    @Bean
    @Description("Calcule des statistiques (moyenne, max, tendance) sur un jeu de données")
    public Function<SalesData, Statistics> computeStats() {
        return data -> statisticsService.compute(data);
    }

    @Bean
    @Description("Génère un rapport structuré à partir des statistiques")
    public Function<Statistics, Report> generateReport() {
        return stats -> reportGenerator.generate(stats);
    }
}

Orchestration multi-agents

Pour les tâches complexes, plusieurs agents spécialisés se coordonnent. Un agent orchestrateur délègue aux agents spécialistes.

@Service
public class OrchestratorAgent {

    private final ShoppingAgent shoppingAgent;
    private final SupportAgent supportAgent;
    private final AnalysisAgent analysisAgent;

    public String route(String sessionId, String userMessage) {
        // L'orchestrateur classe l'intention et délègue
        String intent = classifyIntent(userMessage);

        return switch (intent) {
            case "ORDER"    -> shoppingAgent.chat(sessionId, userMessage);
            case "SUPPORT"  -> supportAgent.chat(sessionId, userMessage);
            case "ANALYSIS" -> analysisAgent.analyze(userMessage);
            default         -> "Je ne comprends pas votre demande.";
        };
    }

    private String classifyIntent(String message) {
        // Appel LLM léger (Haiku) pour la classification
        return classifierModel.classify(message, List.of("ORDER", "SUPPORT", "ANALYSIS", "OTHER"));
    }
}

Points de vigilance en production

Actions irréversibles : gardez un humain dans la boucle pour les actions critiques (suppression de données, virements, envois massifs). Implémentez un step de confirmation explicite.

Logging complet : chaque appel d’outil, chaque réponse LLM, chaque itération doit être tracé. Sans logs, déboguer un agent est presque impossible.

Coûts : un agent avec 10 itérations consomme 10× les tokens d’un appel simple. Surveillez votre usage via l’API Anthropic et mettez des alertes.

Tests : testez chaque outil indépendamment avec des mocks, puis testez le flux agent avec un LLM réel sur les scénarios critiques.


Les agents IA en Java sont maintenant accessibles et productifs. Spring AI et LangChain4j fournissent les briques pour passer d’un PoC à la production en quelques semaines. La clé : commencer par des outils simples, bien délimités, avec des validations strictes des entrées/sorties.

Sur le même sujet

Service lié

IA & automatisation métier

Découvrir le service
Photo d'Amine MEGDICHE, auteur de l'article

Amine MEGDICHE

Développeur AEM & Java Full Stack — Freelance depuis 2013

Vous avez un projet sur ces sujets ?

Envoyez-moi un message pour qualifier votre besoin, vos contraintes techniques et le bon format d'intervention.

Cadrer mon besoin