Agentische KI-Anwendungen in Java mit LangChain4j und Google GenAI erstellen

1. 1. Hinweis

Sketchnote-Zusammenfassung des Codelabs

Willkommen! In diesem Codelab erfahren Sie, wie Sie mit dem beliebten LangChain4j-Framework und dem neuen Google GenAI-Modul agentenbasierte KI-Anwendungen in Java erstellen.

Agentenbasierte KI bezieht sich auf Systeme, in denen LLMs nicht nur auf Prompts reagieren, sondern mit Tools, Speicher und Planungsfunktionen ausgestattet sind, um komplexe, mehrstufige Ziele autonom zu erreichen.

Sie beginnen mit einfachen Konfigurationen, gehen dann zum Erstellen von Agenten mit lokalen Tools und strukturierten Ausgaben über und erkunden schließlich erweiterte Orchestrierungsmuster für mehrere Agenten, die in einem zielorientierten Aktionsplanungssystem (Goal-Oriented Action Planning, GOAP) für Agenten gipfeln.

Aufgaben

  • Konfigurieren Sie das neue GoogleGenAiChatModel, um eine Verbindung zu Gemini herzustellen.
  • Aktivieren Sie das Logging von Anfragen und Antworten, um die Fehlerbehebung zu erleichtern.
  • Gewähren Sie Gemini mit Tools Zugriff auf lokalen Java-Code.
  • Erzwingen Sie das Format Strukturierte Ausgabe (POJOs) von Gemini.
  • Definieren und erstellen Sie Agenten für einen bestimmten Zweck mit der Annotation @Agent.
  • Orchestrieren Sie sequenzielle und parallele Multi-Agenten-Workflows.
  • Erstellen Sie ein zielorientiertes Aktionsplanungssystem (Goal-Oriented Action Planning, GOAP), das die Ausführung von Agenten dynamisch plant.

Voraussetzungen

  • Java Development Kit (JDK) 17 oder höher.
  • Maven 3.5 oder höher ist installiert.
  • Ein Gemini API-Schlüssel von Google AI Studio.

2. 2. Einrichtung: Projekt und API-Schlüssel

Zuerst müssen wir ein neues Maven-Projekt erstellen und unseren Gemini API-Schlüssel konfigurieren.

Maven-Projekt erstellen

Erstellen Sie ein neues Verzeichnis für Ihr Projekt und initialisieren Sie es mit einer pom.xml-Datei.

Fügen Sie der Datei pom.xml die folgenden Abhängigkeiten hinzu. Wir verwenden die neueste Version 1.16.1-beta26 der LangChain4j-Module Google GenAI und 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>

Gemini API-Schlüssel abrufen

  1. Rufen Sie Google AI Studio auf.
  2. Klicken Sie auf API-Schlüssel abrufen.
  3. Erstellen Sie einen neuen Schlüssel oder wählen Sie einen vorhandenen aus.
  4. Legen Sie ihn als Umgebungsvariable in Ihrem Terminal fest:
export GEMINI_API_KEY="your-api-key-here"

3. 3. Gemini-Chatmodell konfigurieren

Erstellen wir nun eine einfache „Hallo Welt“-Anwendung, die das GoogleGenAiChatModel instanziiert und einen Test-Prompt sendet.

Das neue GoogleGenAiChatModel verwendet das einheitliche Google GenAI SDK, das einheitlichen Zugriff auf Gemini-Modelle bietet.

Erstellen Sie eine Klasse namens HelloWorld.java in 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);
    }
}

Führen Sie diese Klasse aus. Da logRequestsAndResponses(true) aktiviert ist, sehen Sie ausführliche SLF4J-Logs mit der genauen Anfrage-Nutzlast, die an die Google API gesendet wurde, und der empfangenen JSON-Rohantwort, gefolgt von der gedruckten Ausgabe:

--- 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. Lokale Java-Tools definieren

LLMs sind leistungsstark, aber durch ihre Trainingsdaten begrenzt. Wir können ihre Fähigkeiten erweitern, indem wir ihnen Tools zur Verfügung stellen (auch als Funktionsaufrufe bezeichnet).

In LangChain4j sind Tools einfache Java-Klassen, deren Methoden mit @Tool annotiert sind.

Erstellen wir ein einfaches Tool, das die Anzahl der Tage zwischen zwei Datumsangaben berechnet. Erstellen Sie DateTools.java in 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);
    }
}

Um dieses Tool zu verwenden, definieren wir einen KI-Dienst. Im LangChain4j-Agentic-Framework können wir ihn mit AgenticServices.agentBuilder() erstellen.

Erstellen Sie ToolDemo.java in 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);
    }
}

Wenn Sie diesen Code ausführen, analysiert Gemini den Prompt, erkennt, dass daysBetween aufgerufen werden muss, generiert eine Tool-Aufrufanfrage, LangChain4j führt Ihre lokale Java-Methode aus, sendet das Ergebnis zurück an Gemini und Gemini formatiert die endgültige Antwort in natürlicher Sprache.

Die Ausgabe sollte in etwa so aussehen:

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

5. 5. Strukturierte Ausgabe definieren

Oft soll das LLM Daten in einem bestimmten strukturierten Format (z.B. JSON, das einem Schema entspricht) und nicht als Nur-Text zurückgeben.

LangChain4j verarbeitet dies automatisch, wenn Sie ein POJO oder einen Java-record als Rückgabetyp Ihrer Agenten- oder Dienstmethode angeben.

Definieren wir einen Java-record, der eine Buch-Extraktion darstellt:

Erstellen Sie Book.java in src/main/java/com/example/Book.java:

package com.example;

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

Erstellen Sie nun eine Agentenschnittstelle, die Book zurückgibt. Erstellen Sie StructuredOutputDemo.java in 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 unterstützt die Ausgabe im strikten JSON-Schema. LangChain4j generiert das JSON-Schema aus Ihrem Book-Record, weist Gemini an, es zu füllen, und deserialisiert die JSON-Antwort zurück in einen Book-Record.

Die Ausgabe sollte in etwa so aussehen:

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

6. 6. Ersten Agenten erstellen

Im langchain4j-agentic-Framework wird ein Agent mit der Annotation @Agent für die Methode der Schnittstelle definiert. Diese Annotation enthält einen Namen und eine Beschreibung, die von Orchestrierungssystemen (z. B. Planern) verwendet werden können.

Erstellen wir einen einfachen Agenten, der ein Thema aufnimmt und eine sehr kurze Geschichte dazu entwirft.

Erstellen Sie CreativeWriter.java in 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);
}

Testen wir diesen Agenten einzeln. Erstellen Sie AgentDemo.java in 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);
    }
}

Die Ausgabe sollte in etwa so aussehen:

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.

Durch Hinzufügen der Annotation @Agent ist diese Klasse nun für komplexere Workflows wie die sequenzielle Verkettung vorbereitet.

7. 7. Multi-Agenten-Workflows orchestrieren: Sequenziell

Ein gängiges agentenbasiertes Muster ist der sequenzielle Workflow, bei dem Agenten in einer vordefinierten Reihenfolge ausgeführt werden. Die Ausgabe eines Agenten wird als Eingabe für den nächsten verwendet.

Definieren wir einen zweiten Agenten, StyleEditor, der die von CreativeWriter geschriebene Geschichte an einen bestimmten Stil anpasst (z.B. komisch, dramatisch).

Erstellen Sie StyleEditor.java in 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);
}

Verknüpfen wir nun CreativeWriter und StyleEditor sequenziell mit AgenticServices.sequenceBuilder().

Wir verwenden einen UntypedAgent, der die Sequenz darstellt, sodass wir Eingaben über eine gemeinsame Map übergeben und Ausgaben erfassen können.

Erstellen Sie SequentialWorkflowDemo.java in 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);
    }
}

Die Ausgabe sollte in etwa so aussehen:

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.

AgenticScope

Während der Ausführung verwaltet LangChain4j einen AgenticScope.

  1. Die Eingabe-Map {"topic": "...", "style": "..."} wird in den Bereich geschrieben.
  2. CreativeWriter wird ausgeführt. Er benötigt topic, das er aus dem Bereich liest. Er gibt story aus, die wieder im Bereich gespeichert wird.
  3. StyleEditor wird ausgeführt. Er benötigt story (vom Autor erstellt) und style (aus den ursprünglichen Eingaben). Er gibt edited_story in den Bereich aus.
  4. Der Workflow wird abgeschlossen und der Wert von edited_story wird zurückgegeben.

8. 8. Multi-Agenten-Workflows orchestrieren: Parallel

In einigen Szenarien können Unteragenten unabhängig voneinander an derselben Eingabe arbeiten und ihre Aufgaben parallel ausgeführt werden.

Erstellen wir beispielsweise einen Reiseberater, der einen Filmexperten und einen Gastronomieexperten um Empfehlungen für eine Stadt bittet und diese dann kombiniert.

Erstellen Sie MovieExpert.java in 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);
}

Erstellen Sie DiningExpert.java in 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);
}

Erstellen wir nun das parallele agentenbasierte System. Wir definieren eine Schnittstelle für unseren Koordinator der obersten Ebene: TravelAdvisorAgent.

Erstellen Sie TravelAdvisorAgent.java in 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);
}

Erstellen Sie ParallelWorkflowDemo.java in src/main/java/com/example/ParallelWorkflowDemo.java, um alles zusammenzufügen:

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);
    }
}

Die Ausgabe sollte in etwa so aussehen:

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; }
}

Erstellen Sie Sign.java in src/main/java/com/example/Sign.java:

package com.example;

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

2. Unteragenten definieren

Erstellen Sie HoroscopeGenerator.java in 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);
}

Erstellen Sie PersonExtractor.java in 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);
}

Erstellen Sie SignExtractor.java in 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);
}

Erstellen Sie AmusingWriter.java in 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. Tool zum Finden von Geschichten definieren

Wir erstellen eine einfache Suchtoolklasse, die eine Mock-Geschichte zurückgibt. Erstellen Sie MockSearchTool.java in 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!";
    }
}

Erstellen Sie StoryFinder.java in src/main/java/com/example/StoryFinder.java Agent, der dieses Tool verwendet:

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. GOAP-Agentensystem erstellen und ausführen

Erstellen Sie GoapDemo.java in 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);
    }
}

So hat GOAP das Problem gelöst:

Bei Aufruf:

  1. Das System erkennt, dass der Ausgangszustand nur prompt enthält.
  2. Das gewünschte Ziel ist writeup.
  3. Es erstellt ein Abhängigkeitsdiagramm und berechnet den Pfad:
    • prompt -> PersonExtractor -> person
    • prompt -> SignExtractor -> sign
    • person + sign -> HoroscopeGenerator -> horoscope
    • sign -> StoryFinder -> story
    • person + horoscope + story -> AmusingWriter -> writeup
  4. Die berechnete Ausführungsreihenfolge ist: [PersonExtractor, SignExtractor, HoroscopeGenerator, StoryFinder, AmusingWriter].
  5. Jeder Sub-Agent wird der Reihe nach ausgeführt und das Endergebnis wird zurückgegeben.

Die Ausgabe sollte den Ausführungspfad und den endgültigen Text enthalten, ähnlich wie hier:

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. Bereinigen

So bereinigen Sie Ressourcen:

  • Löschen Sie alle API-Schlüssel aus Google AI Studio, die Sie nicht mehr verwenden.
  • Heben Sie die Festlegung der Umgebungsvariable in Ihrem Terminal auf:
unset GEMINI_API_KEY

10. 11. Glückwunsch

Glückwunsch! Sie haben eine Reihe von agentischen KI-Anwendungen mit LangChain4j und dem neuen Google GenAI-Modul erstellt.

Wenn Sie Ihre Fähigkeiten weiter ausbauen möchten, empfehlen wir Ihnen, sich die offizielle Dokumentation zum LangChain4j-Modul Google GenAI anzusehen und mehr über das Erstellen von Agenten mit LangChain4j zu erfahren.

Das haben Sie gelernt

  • Konfigurieren von GoogleGenAiChatModel.
  • Verwenden von lokalen Java-Tools und Logging von Anfragen und Antworten.
  • Ausgeben von strukturierten POJO-Daten.
  • Erstellen von Agenten für einen bestimmten Zweck mit @Agent.
  • Orchestrieren von sequenziellen, parallelen und zielorientierten Aktionsplanungs-Workflows (Goal-Oriented Action Planning, GOAP) für Agenten.

Weitere Informationen