Créer des applications d'IA agentiques en Java avec LangChain4j et l'IA générative de Google

1. 1. Avant de commencer

Résumé de l'atelier de programmation sous forme de sketchnote

Bienvenue ! Dans cet atelier de programmation, vous allez apprendre à créer des applications d'IA agentique en Java à l'aide du framework LangChain4j et de son nouveau module Google GenAI.

L'IA agentive désigne les systèmes dans lesquels les LLM ne se contentent pas de répondre aux requêtes, mais sont équipés d'outils, de mémoire et de capacités de planification pour atteindre de manière autonome des objectifs complexes en plusieurs étapes.

Vous commencerez par des configurations simples, puis vous apprendrez à créer des agents avec des outils locaux et des sorties structurées. Enfin, vous explorerez des modèles d'orchestration multi-agents avancés, pour aboutir à un système agentique de planification d'actions orientée objectif (GOAP).

Objectifs de l'atelier

  • Configurez le nouveau GoogleGenAiChatModel pour vous connecter à Gemini.
  • Activez la journalisation des requêtes et des réponses pour faciliter le débogage.
  • Donnez à Gemini accès au code Java local à l'aide de Outils.
  • Appliquez le format Structured Output (POJO) de Gemini.
  • Définissez et créez des agents à usage unique avec l'annotation @Agent.
  • Orchestrez des workflows multi-agents séquentiels et parallèles.
  • Créez un système de planification d'actions orientée objectif (GOAP) qui planifie dynamiquement l'exécution des agents.

Prérequis

  • Kit de développement Java (JDK) 17 ou version ultérieure.
  • Maven 3.5 ou version ultérieure installé.
  • Une clé API Gemini provenant de Google AI Studio.

2. 2. Configuration : projet et clé API

Pour commencer, nous devons créer un projet Maven et configurer notre clé API Gemini.

Créer le projet Maven

Créez un répertoire pour votre projet et initialisez-le avec un fichier pom.xml.

Ajoutez les dépendances suivantes à votre pom.xml. Notez que nous utilisons la dernière version 1.16.1-beta26 des modules LangChain4j Google GenAI et Agentic :

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>gemini-agents-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <langchain4j.version>1.16.1-beta26</langchain4j.version>
    </properties>

    <dependencies>
        <!-- LangChain4j Google GenAI Integration -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-google-genai</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

        <!-- LangChain4j Agentic Framework Core -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-agentic</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

        <!-- LangChain4j Agentic Patterns (needed for GOAP) -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-agentic-patterns</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

        <!-- SLF4J Logger for logging requests/responses -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.12</version>
        </dependency>
    </dependencies>
</project>

Obtenir une clé API Gemini

  1. Accédez à Google AI Studio.
  2. Cliquez sur Get API Key (Obtenir une clé API).
  3. Créez une clé ou sélectionnez-en une existante.
  4. Définissez-le comme variable d'environnement dans votre terminal :
export GEMINI_API_KEY="your-api-key-here"

3. 3. Configurer le modèle Gemini Chat

À présent, créons une application "Hello World" simple qui instancie GoogleGenAiChatModel et envoie une invite de test.

Le nouveau GoogleGenAiChatModel utilise le SDK Google GenAI unifié, qui offre un accès unifié aux modèles Gemini.

Créez une classe nommée HelloWorld.java dans src/main/java/com/example/HelloWorld.java :

package com.example;

import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.google.genai.GoogleGenAiChatModel;

public class HelloWorld {
    public static void main(String[] args) {
        // 1. Configure the Gemini model
        ChatModel model = GoogleGenAiChatModel.builder()
                .apiKey(System.getenv("GEMINI_API_KEY"))
                .modelName("gemini-3.5-flash")
                // Enable logging for both requests and responses
                .logRequestsAndResponses(true)
                .build();

        // 2. Invoke the model
        String response = model.chat("Hello Gemini! Explain 'Agentic AI' in one sentence.");

        // 3. Print response
        System.out.println("\n--- Gemini Response ---");
        System.out.println(response);
    }
}

Exécutez cette classe. Comme logRequestsAndResponses(true) est activé, vous verrez des journaux SLF4J détaillés décrivant la charge utile exacte de la requête envoyée à l'API de Google, ainsi que la réponse JSON brute reçue, suivis de la sortie imprimée :

--- Gemini Response ---
**Agentic AI** refers to autonomous artificial intelligence systems that can proactively plan, make decisions, use tools, and execute multi-step tasks to achieve specific goals with minimal human supervision.

4. 4. Définir les outils Java locaux

Les LLM sont puissants, mais ils sont limités par leurs données d'entraînement. Nous pouvons étendre leurs capacités en leur fournissant des outils (également appelés "appels de fonctions").

Dans LangChain4j, les outils sont de simples classes Java dont les méthodes sont annotées avec @Tool.

Écrivons un outil simple qui calcule le nombre de jours entre deux dates. Créez DateTools.java dans src/main/java/com/example/DateTools.java :

package com.example;

import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class DateTools {

    @Tool("Calculates the number of days between two dates")
    public long daysBetween(
            @P("The start date in ISO format (YYYY-MM-DD)") String startDate,
            @P("The end date in ISO format (YYYY-MM-DD)") String endDate) {
        LocalDate start = LocalDate.parse(startDate);
        LocalDate end = LocalDate.parse(endDate);
        return ChronoUnit.DAYS.between(start, end);
    }
}

Pour utiliser cet outil, nous allons définir un service d'IA. Dans le framework agentique LangChain4j, nous pouvons le créer à l'aide de AgenticServices.agentBuilder().

Créez ToolDemo.java dans src/main/java/com/example/ToolDemo.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.google.genai.GoogleGenAiChatModel;
import dev.langchain4j.service.V;

public class ToolDemo {

    public interface DateAssistant {
        @Agent
        String ask(@V("prompt") String prompt);
    }

    public static void main(String[] args) {
        ChatModel model = GoogleGenAiChatModel.builder()
                .apiKey(System.getenv("GEMINI_API_KEY"))
                .modelName("gemini-3.5-flash")
                .build();

        // Build the assistant and register our DateTools
        DateAssistant assistant = AiServices.builder(DateAssistant.class)
                .chatModel(model)
                .tools(new DateTools())
                .build();

        String response = assistant.ask("How many days are there between 2026-01-01 and 2026-06-30?");
        System.out.println("Response: " + response);
    }
}

Lorsque vous exécutez ce code, Gemini analyse l'invite, se rend compte qu'il doit appeler daysBetween, génère une demande d'appel d'outil, LangChain4j exécute votre méthode Java locale, renvoie le résultat à Gemini, et Gemini met en forme la réponse finale en langage naturel.

Le résultat doit ressembler à ceci :

Response: There are 180 days between 2026-01-01 and 2026-06-30.

5. 5. Définir une sortie structurée

Souvent, vous souhaitez que le LLM renvoie des données dans un format structuré spécifique (par exemple, un format JSON correspondant à un schéma) plutôt que du texte brut.

LangChain4j gère cela automatiquement lorsque vous spécifiez un POJO ou un record Java comme type renvoyé de votre méthode d'agent ou de service.

Définissons un record Java représentant une extraction de livre :

Créez Book.java dans src/main/java/com/example/Book.java :

package com.example;

public record Book(String title, String author, int publicationYear) {}

Créez à présent une interface d'agent qui renvoie Book. Créez StructuredOutputDemo.java dans src/main/java/com/example/StructuredOutputDemo.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.google.genai.GoogleGenAiChatModel;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public class StructuredOutputDemo {

    public interface BookExtractor {
        @UserMessage("Extract book details from: {{text}}")
        Book extract(@V("text") String text);
    }

    public static void main(String[] args) {
        ChatModel model = GoogleGenAiChatModel.builder()
                .apiKey(System.getenv("GEMINI_API_KEY"))
                .modelName("gemini-3.5-flash")
                .build();

        BookExtractor extractor = AiServices.builder(BookExtractor.class)
                .chatModel(model)
                .build();

        String text = "I just finished reading 'Project Hail Mary' by Andy Weir, published in 2021. It was amazing!";
        Book book = extractor.extract(text);

        System.out.println("Extracted POJO: " + book);
    }
}

Gemini 3.5 Flash est compatible avec les sorties strictes de schéma JSON. LangChain4j génère le schéma JSON à partir de votre enregistrement Book, demande à Gemini de le remplir et désérialise la réponse JSON en enregistrement Book.

Le résultat doit ressembler à ceci :

Extracted POJO: Book[title=Project Hail Mary, author=Andy Weir, publicationYear=2021]

6. 6. Créer votre premier agent

Dans le framework langchain4j-agentic, un agent est défini à l'aide de l'annotation @Agent sur la méthode de l'interface. Cette annotation fournit un nom/une description qui peuvent être utilisés par les systèmes d'orchestration (comme les planificateurs).

Créons un agent simple qui prend un sujet et rédige une très courte histoire à son sujet.

Créez CreativeWriter.java dans src/main/java/com/example/CreativeWriter.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface CreativeWriter {

    @SystemMessage("You are a creative writer. Generate a draft of a story no more than 3 sentences long.")
    @UserMessage("Write a story about {{topic}}.")
    @Agent(description = "Generates a story based on the given topic", outputKey = "story")
    String writeStory(@V("topic") String topic);
}

Testons cet agent individuellement. Créez AgentDemo.java dans src/main/java/com/example/AgentDemo.java :

package com.example;

import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.google.genai.GoogleGenAiChatModel;

public class AgentDemo {
    public static void main(String[] args) {
        ChatModel model = GoogleGenAiChatModel.builder()
                .apiKey(System.getenv("GEMINI_API_KEY"))
                .modelName("gemini-3.5-flash")
                .build();

        CreativeWriter writer = AgenticServices.agentBuilder(CreativeWriter.class)
                .chatModel(model)
                .build();

        String story = writer.writeStory("a lonely robot on Mars");
        System.out.println("Generated Story:\n" + story);
    }
}

Un résultat semblable à celui-ci s'affiche :

Generated Story:
For eighty years, the rusty little rover trundled across the crimson dunes, diligently collecting soil samples for a home world that had long since gone dark. Each evening, it pointed its high-gain antenna toward the fading blue speck of Earth and beamed its lonely coordinates into the silent cosmos. Receiving only static in return, the robot tucked its camera arm close against the freezing Martian wind and sang a quiet lullaby to the empty stars.

En ajoutant l'annotation @Agent, cette classe est désormais prête à participer à des workflows plus complexes, tels que l'enchaînement séquentiel.

7. 7. Orchestrer des workflows multi-agents : séquentiel

Un modèle agentique courant est le workflow séquentiel, dans lequel les agents s'exécutent dans un ordre prédéfini. La sortie d'un agent est transmise en tant qu'entrée de l'agent suivant.

Définissons un deuxième agent, StyleEditor, qui modifiera l'histoire écrite par CreativeWriter pour l'adapter à un style spécifique (comique ou dramatique, par exemple).

Créez StyleEditor.java dans src/main/java/com/example/StyleEditor.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface StyleEditor {

    @SystemMessage("You are a professional editor. Analyze and rewrite the story to align with the style requested.")
    @UserMessage("Rewrite this story: '{{story}}' into a {{style}} style.")
    @Agent(description = "Edits a story to fit a specific style", outputKey = "edited_story")
    String editStory(@V("story") String story, @V("style") String style);
}

Connectons maintenant CreativeWriter et StyleEditor de manière séquentielle à l'aide de AgenticServices.sequenceBuilder().

Nous utiliserons un UntypedAgent représentant la séquence, ce qui nous permettra de transmettre des entrées et de collecter des sorties via une carte partagée.

Créez SequentialWorkflowDemo.java dans src/main/java/com/example/SequentialWorkflowDemo.java :

package com.example;

import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.agentic.UntypedAgent;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.google.genai.GoogleGenAiChatModel;
import java.util.Map;

public class SequentialWorkflowDemo {
    public static void main(String[] args) {
        ChatModel model = GoogleGenAiChatModel.builder()
                .apiKey(System.getenv("GEMINI_API_KEY"))
                .modelName("gemini-3.5-flash")
                .build();

        // 1. Build the sub-agents
        CreativeWriter writer = AgenticServices.agentBuilder(CreativeWriter.class)
                .chatModel(model)
                .outputKey("story") // Output goes into AgenticScope as "story"
                .build();

        StyleEditor editor = AgenticServices.agentBuilder(StyleEditor.class)
                .chatModel(model)
                .outputKey("edited_story") // Output goes into AgenticScope as "edited_story"
                .build();

        // 2. Build the sequence
        UntypedAgent workflow = AgenticServices.sequenceBuilder()
                .subAgents(writer, editor)
                .outputKey("edited_story") // Final output of workflow
                .build();

        // 3. Run the workflow with inputs
        Map<String, Object> inputs = Map.of(
                "topic", "a cat learning to fly",
                "style", "Shakespearean"
        );

        String result = (String) workflow.invoke(inputs);
        System.out.println("Final Edited Story:\n" + result);
    }
}

Un résultat semblable à celui-ci s'affiche :

Final Edited Story:
For years had Barnaby with envy watched
The soaring sparrows with a green-eyed spite,
Convinced that heavy gravity was but
An idle law he deigned to tolerate.
But lo! Upon the rare and azure moon,
He scaled the summit of the ancient oak,
Closed fast his eyes, and leapt with blind belief.
While the dread squirrel shrieked in sheer dismay,
No downward plunge befell the daring beast;
For summer's zephyr bore his belly up,
And through the vault of night's celestial sphere,
He rowed his velvet paws among the stars.

Présentation d'AgenticScope

Pendant l'exécution, LangChain4j gère un AgenticScope.

  1. La carte d'entrée {"topic": "...", "style": "..."} est écrite dans le champ d'application.
  2. CreativeWriter s'exécute. Il nécessite topic, qu'il lit à partir du champ d'application. Il génère story, qui est réenregistré dans le champ d'application.
  3. StyleEditor s'exécute. Il nécessite story (produit par le rédacteur) et style (à partir des entrées initiales). Il génère edited_story dans le champ d'application.
  4. Le workflow se termine et renvoie la valeur de edited_story.

8. 8. Orchestrer des workflows multi-agents : parallèle

Dans certains cas, les sous-agents peuvent travailler indépendamment sur la même entrée, et leurs tâches peuvent être exécutées en parallèle.

Par exemple, créons un conseiller de voyage qui demande des recommandations à un expert en cinéma et à un expert en restauration pour une ville, puis les combine.

Créez MovieExpert.java dans src/main/java/com/example/MovieExpert.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import java.util.List;

public interface MovieExpert {
    @UserMessage("Suggest 2 movies filmed in {{city}}.")
    @Agent(description = "Suggests movies filmed in a city")
    List<String> findMovies(@V("city") String city);
}

Créez DiningExpert.java dans src/main/java/com/example/DiningExpert.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import java.util.List;

public interface DiningExpert {
    @UserMessage("Suggest 2 local dishes to try in {{city}}.")
    @Agent(description = "Suggests local dishes to try in a city")
    List<String> findDishes(@V("city") String city);
}

À présent, créons le système agentique parallèle. Nous allons définir une interface pour notre coordinateur de premier niveau : TravelAdvisorAgent.

Créez TravelAdvisorAgent.java dans src/main/java/com/example/TravelAdvisorAgent.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.V;
import java.util.Map;

public interface TravelAdvisorAgent {
    @Agent
    Map<String, Object> planTrip(@V("city") String city);
}

Créez ParallelWorkflowDemo.java dans src/main/java/com/example/ParallelWorkflowDemo.java pour l'associer :

package com.example;

import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.google.genai.GoogleGenAiChatModel;
import java.util.List;
import java.util.Map;

public class ParallelWorkflowDemo {
    public static void main(String[] args) {
        ChatModel model = GoogleGenAiChatModel.builder()
                .apiKey(System.getenv("GEMINI_API_KEY"))
                .modelName("gemini-3.5-flash")
                .build();

        // 1. Build independent agents
        MovieExpert movieExpert = AgenticServices.agentBuilder(MovieExpert.class)
                .chatModel(model)
                .outputKey("movies")
                .build();

        DiningExpert diningExpert = AgenticServices.agentBuilder(DiningExpert.class)
                .chatModel(model)
                .outputKey("dishes")
                .build();

        // 2. Build the Parallel agent
        TravelAdvisorAgent travelAdvisor = AgenticServices.parallelBuilder(TravelAdvisorAgent.class)
                .subAgents(movieExpert, diningExpert)
                .output(scope -> {
                    // Combine the independent outputs in the AgenticScope
                    List<String> movies = scope.readState("movies", List.of());
                    List<String> dishes = scope.readState("dishes", List.of());
                    return Map.of("movies", movies, "dishes", dishes);
                })
                .build();

        // 3. Invoke
        Map<String, Object> plan = travelAdvisor.planTrip("Tokyo");
        System.out.println("Tokyo Trip Plan:\n" + plan);
    }
}

Le résultat doit ressembler à ceci :

Tokyo Trip Plan:
{movies=[Lost in Translation, Tokyo Story], dishes=[Monjayaki, Edomae-zushi]}
In this parallel workflow, `movieExpert` and `diningExpert` execute concurrently. The `output(...)` configuration defines how to gather their respective results from the `AgenticScope` and merge them into the final result.

## 9. Build Goal-Oriented Agents (GOAP)
Duration: 07:00

Sequential and Parallel workflows are structured and predictable, but rigid. What if we want the system to figure out the path to the goal autonomously, but in a **deterministic, algorithmic way** rather than trusting an LLM to loop (like ReAct)?

This is where **Goal-Oriented Action Planning (GOAP)** shines. 

By looking at the declared inputs and outputs of each agent, the `GoalOrientedPlanner` builds a dependency graph. When you invoke the system with a goal, it calculates the shortest path from the current state (available variables) to that goal.

Let's build a Horoscope & News combined writer. It requires 4 agents:
1. `PersonExtractor`: extracts person details from prompt.
2. `SignExtractor`: extracts zodiac sign from prompt.
3. `HoroscopeGenerator`: generates horoscope given person and sign.
4. `AmusingWriter`: writes a funny story combining a horoscope and a news story.

We will also define a `StoryFinder` that uses a mock search tool.

### 1. Define Model Classes
Create `Person.java` in `src/main/java/com/example/Person.java`:
```java
package com.example;

public record Person(String name) {
    @Override public String toString() { return name; }
}

Créez Sign.java dans src/main/java/com/example/Sign.java :

package com.example;

public record Sign(String signName) {
    @Override public String toString() { return signName; }
}

2. Définir les sous-agents

Créez HoroscopeGenerator.java dans src/main/java/com/example/HoroscopeGenerator.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface HoroscopeGenerator {
    @UserMessage("Generate a funny horoscope for {{person}} who is a {{sign}}.")
    @Agent(description = "Generates horoscopes based on name and zodiac sign")
    String horoscope(@V("person") Person person, @V("sign") Sign sign);
}

Créez PersonExtractor.java dans src/main/java/com/example/PersonExtractor.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface PersonExtractor {
    @UserMessage("Extract the person name from: {{prompt}}")
    @Agent(description = "Extracts a person from user's prompt")
    Person extractPerson(@V("prompt") String prompt);
}

Créez SignExtractor.java dans src/main/java/com/example/SignExtractor.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface SignExtractor {
    @UserMessage("Extract the zodiac sign from: {{prompt}}")
    @Agent(description = "Extracts a zodiac sign from user's prompt")
    Sign extractSign(@V("prompt") String prompt);
}

Créez AmusingWriter.java dans src/main/java/com/example/AmusingWriter.java :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface AmusingWriter {
    @UserMessage("Create an amusing writeup for {{person}} based on their horoscope: {{horoscope}} and current news story: {{story}}.")
    @Agent(description = "Create an amusing writeup combining horoscope and news")
    String write(@V("person") Person person, @V("horoscope") String horoscope, @V("story") String story);
}

3. Définir l'outil de recherche d'histoires

Nous allons créer une classe d'outil de recherche simple qui renvoie une histoire fictive. Créez MockSearchTool.java dans src/main/java/com/example/MockSearchTool.java :

package com.example;

import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;

public class MockSearchTool {
    @Tool("Searches the web for news stories related to a zodiac sign")
    public String searchNews(@P("The zodiac sign") String sign) {
        return "Breaking news: A massive cheese festival was announced for " + sign + " natives today!";
    }
}

Créez un agent StoryFinder.java dans src/main/java/com/example/StoryFinder.java qui utilise cet outil :

package com.example;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface StoryFinder {
    @UserMessage("Find a funny story for zodiac sign {{sign}} using search tool.")
    @Agent(description = "Finds a news story on the internet about a zodiac sign")
    String findStory(@V("sign") Sign sign);
}

4. Créer et exécuter le système agentique GOAP

Créez GoapDemo.java dans src/main/java/com/example/GoapDemo.java :

package com.example;

import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.agentic.UntypedAgent;
import dev.langchain4j.agentic.patterns.goap.GoalOrientedPlanner;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.google.genai.GoogleGenAiChatModel;
import java.util.Map;

public class GoapDemo {
    public static void main(String[] args) {
        ChatModel model = GoogleGenAiChatModel.builder()
                .apiKey(System.getenv("GEMINI_API_KEY"))
                .modelName("gemini-3.5-flash")
                .build();

        // Instantiate sub-agents with output keys matching the inputs of other agents
        HoroscopeGenerator horoscopeGen = AgenticServices.agentBuilder(HoroscopeGenerator.class)
                .chatModel(model).outputKey("horoscope").build();

        PersonExtractor personExt = AgenticServices.agentBuilder(PersonExtractor.class)
                .chatModel(model).outputKey("person").build();

        SignExtractor signExt = AgenticServices.agentBuilder(SignExtractor.class)
                .chatModel(model).outputKey("sign").build();

        StoryFinder storyFinder = AgenticServices.agentBuilder(StoryFinder.class)
                .chatModel(model).tools(new MockSearchTool()).outputKey("story").build();

        AmusingWriter writer = AgenticServices.agentBuilder(AmusingWriter.class)
                .chatModel(model).outputKey("writeup").build();

        // Build the GOAP Planner agentic system
        UntypedAgent horoscopeNewsAgent = AgenticServices.plannerBuilder()
                .subAgents(horoscopeGen, personExt, signExt, storyFinder, writer)
                .outputKey("writeup") // The Goal we want to achieve
                .planner(GoalOrientedPlanner::new) // Register the GOAP Planner
                .build();

        // Input provides only "prompt"
        Map<String, Object> inputs = Map.of(
                "prompt", "My name is Alice and my zodiac sign is Leo"
        );

        System.out.println("Invoking GOAP Agentic System...");
        String result = (String) horoscopeNewsAgent.invoke(inputs);

        System.out.println("\n--- Final Writeup ---");
        System.out.println(result);
    }
}

Comment GOAP a résolu le problème :

En cas d'appel :

  1. Le système détecte que l'état initial ne contient que prompt.
  2. L'objectif souhaité est de writeup.
  3. Il crée un graphique des dépendances et calcule le chemin :
    • prompt -> PersonExtractor -> person
    • prompt -> SignExtractor -> sign
    • person + sign -> HoroscopeGenerator -> horoscope
    • sign -> StoryFinder -> story
    • person + horoscope + story -> AmusingWriter -> writeup
  4. L'ordre d'exécution calculé est le suivant : [PersonExtractor, SignExtractor, HoroscopeGenerator, StoryFinder, AmusingWriter].
  5. Il exécute chaque sous-agent dans l'ordre et renvoie le résultat final.

Un résultat indiquant le chemin d'exécution et la rédaction finale doit s'afficher, comme suit :

Invoking GOAP Agentic System...
[com.example.GoapDemo.main()] INFO dev.langchain4j.agentic.patterns.goap.GoalOrientedSearchGraph - Agents path sequence: [extractPerson, extractSign, findStory, horoscope, write]

--- Final Writeup ---
**The Cosmic Registry's Weekly Forecast & Special Event Guide for: Alice, the Leo Lioness**

Alice, the universe looked at your astrological chart this week and frankly, it's asking for your autograph. Your main-character energy is currently so potent that secondary characters are practically fading into the background.

But the biggest cosmic news of the week? The universe has finally recognized your royal status with the announcement of a **Massive Cheese Festival**...

9. 10. Effectuer un nettoyage

Pour nettoyer les ressources :

  • Veillez à supprimer toutes les clés API de Google AI Studio si vous ne les utilisez plus.
  • Annulez la définition de la variable d'environnement dans votre terminal :
unset GEMINI_API_KEY

10. 11. Félicitations

Félicitations ! Vous avez créé une série d'applications d'IA agentique à l'aide de LangChain4j et du nouveau module Google GenAI.

Pour aller plus loin, nous vous encourageons à consulter la documentation officielle du module LangChain4j Google GenAI et à en savoir plus sur la création d'agents avec LangChain4j.

Ce que vous avez appris

  • Configurer GoogleGenAiChatModel
  • Utiliser des outils Java locaux et la journalisation des requêtes/réponses
  • Comment générer des données POJO structurées.
  • Découvrez comment créer des agents à usage unique avec @Agent.
  • Orchestrer des workflows agentiques séquentiels, parallèles et de planification d'actions orientée objectif (GOAP)

En savoir plus