Build Your First AI Chatbot in Java

Build a Java chatbot with memory, personality, and structured output.

Introduction

30 Second Summary

Every time you chat with an AI assistant, a language model is reading your messages and deciding what to say next. That behavior is built by developers who know how to call, configure, and manage these models.

In this project, you will build a Java chatbot powered by GPT-4o-mini that maintains conversation history and follows a personality you define. LangChain4j provides a free demo key, so you won't need to sign up for anything to get started.

What You'll Build

You'll type a message into your terminal, the AI responds in the voice of a coding mentor (or a pirate, or a patient teacher), and three messages later it still remembers your name.

By the end of this project, you'll have:

  • A working interactive chatbot that responds in a personality you define and remembers everything you said earlier in the conversation.
  • Hands-on proof of how temperature and tokens control LLM behavior. You change a single number and watch the output shift.
  • A structured data extraction pipeline that turns plain English text into typed Java objects your application can use directly.
  • Secret Mission: Add real-time streaming so you see the AI's response appear token-by-token, just like ChatGPT.

Do I need AI or machine learning experience?

You only need basic Java knowledge: classes, interfaces, and methods. This project uses a free demo key from LangChain4j, so there is no sign-up or credit card required.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters for your journey into AI engineering.

Set Up Your Java Project with LangChain4j

Every AI-powered application starts with one thing: a connection to a language model. Before you can send your first message to an AI, you need a project that knows how to talk to one.

In this step, you will create a Java project and add LangChain4j, a library that handles the plumbing of calling LLMs from Java. LangChain4j provides a free demo API key, so you can start making AI calls immediately without signing up for anything.

In this step, get ready to:
  • Create a Maven project in IntelliJ IDEA with the right structure.
  • Add LangChain4j dependencies so your project can call an LLM.
  • Run your first AI call and see the response in your console.
Create a new Maven project

Maven is a build tool that manages your project's dependencies and compiles your code. IntelliJ IDEA has Maven built in, so you do not need to install anything extra.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type IntelliJ IDEA and press Enter to open it.
  • On the Welcome screen, click New Project.
  • If you already have a project open, go to File then New then Project.
  • In the left pane, select Java.
  • In the Name field, enter ai-chatbot.
  • Set the Build system to Maven.
  • From the JDK dropdown, select your JDK 17 installation.
  • Expand Advanced Settings (or click Artifact Coordinates) to reveal the Maven coordinates.
  • Set GroupId to com.example.
  • Set ArtifactId to ai-chatbot.
  • Uncheck Add sample code if it is checked.
  • Click Create.

IntelliJ creates your project with a pom.xml file and the standard src/main/java directory structure. You should see the Project tool window on the left showing your new ai-chatbot project.

Don't see JDK 17 in the dropdown?

Click Download JDK in the JDK dropdown, select version 17 from any vendor (Oracle OpenJDK works fine), and click Download.

If you have JDK 17 installed but it does not appear, click Add JDK and point it to your JDK home directory. Help me find my JDK installation

Add LangChain4j dependencies

Your project needs two LangChain4j libraries: one for the OpenAI integration and one for the core framework. You declare these in pom.xml, and Maven downloads them automatically.

  • In the Project tool window, double-click pom.xml to open it in the editor.
  • Replace the entire contents of pom.xml with the following:
<?xml version="1.0" encoding="UTF-8"?>
<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>ai-chatbot</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <langchain4j.version>1.18.1</langchain4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
    </dependencies>
</project>

What does this file do?

  • The <properties> block pins the Java compiler to version 17 and defines a single version variable for LangChain4j (1.18.1).
  • The langchain4j-open-ai dependency gives you the OpenAiChatModel class for calling GPT models.
  • The langchain4j core dependency provides AI Services, memory, and structured output features you will use in later steps.
  • Save the file with Cmd+S (macOS) or Ctrl+S (Windows).
  • IntelliJ shows a small popup or icon in the editor asking to load Maven changes. Click Load Maven Changes (or the refresh icon that appears).
  • Wait for the progress bar at the bottom to finish.
  • Expand External Libraries in the Project tool window. You should see langchain4j-open-ai and langchain4j listed there.

Dependencies not downloading?

Make sure you are connected to the internet. If the refresh icon did not appear, right-click pom.xml in the Project tool window and select Maven then Reload Project.

Help me resolve Maven dependency issues in IntelliJ

Create HelloLLM.java and make your first AI call

Now that your project has the LangChain4j library available, you can write your first code that talks to an AI model. You will use the free demo key that LangChain4j provides for learning.

  • In the Project tool window, expand src then main then java.
  • Right-click the java folder and select New then Package.
  • Type chatbot and press Enter.
  • Right-click the new chatbot package and select New then Java Class.
  • Type HelloLLM and press Enter.
  • Replace the generated code with the following:
package chatbot;

import dev.langchain4j.model.openai.OpenAiChatModel;

public class HelloLLM {

    public static void main(String[] args) {
        // Default model with the free demo key
        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .build();

        String answer = model.chat("What is artificial intelligence? Answer in one sentence.");
        System.out.println("AI says: " + answer);
    }
}

What does this code do?

  • OpenAiChatModel.builder() creates a connection to an OpenAI-compatible API. The baseUrl points to LangChain4j's free demo proxy, and the apiKey is simply the string demo.
  • model.chat(...) sends your prompt to gpt-4o-mini and returns the AI's response as a plain Java String.
  • No API key signup is needed. The demo key is rate-limited but perfect for learning.
  • Save the file with Cmd+S (macOS) or Ctrl+S (Windows).

Before you run this, what do you think you will see in the console? A sentence from the AI, an error, or nothing at all?

  • Click the green Run icon in the gutter next to the main method, then select Run 'HelloLLM.main()'.

The Run tool window opens at the bottom of IntelliJ. You should see output starting with AI says: followed by a one-sentence definition of artificial intelligence.

The exact wording varies each time (the AI generates fresh responses), but the structure is always a single sentence.

Seeing a connection error or timeout?

The demo key requires an internet connection. Check that you are online and that your network does not block outbound HTTP requests to langchain4j.dev.

If you see a ClassNotFoundException, your Maven dependencies did not load correctly. Go back to the previous substep and click Load Maven Changes again.

Help me debug my first LangChain4j call

✔️ Awesome, I've got everything!

Great. Double check you have saved both pom.xml and HelloLLM.java.

ⓧ I'd like to double check the full code

Here is the complete pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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>ai-chatbot</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <langchain4j.version>1.18.1</langchain4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
    </dependencies>
</project>

And here is the complete HelloLLM.java:

package chatbot;

import dev.langchain4j.model.openai.OpenAiChatModel;

public class HelloLLM {

    public static void main(String[] args) {
        // Default model with the free demo key
        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .build();

        String answer = model.chat("What is artificial intelligence? Answer in one sentence.");
        System.out.println("AI says: " + answer);
    }
}

You just made your first LLM call from Java in under five minutes. Next up, you will explore how a single number called temperature completely changes the AI's behavior.

Explore Temperature and Randomness

In the last step, you got your first AI response printed in IntelliJ's console. Now you'll discover how to control what the model gives you back.

The same LLM can produce wildly different outputs depending on one number: temperature. Randomness is a dial you control, and understanding it is your first real insight into how these models work.

In this step, get ready to:
  • Control LLM randomness by setting temperature to 0.0 and 1.5.
  • Observe how token limits truncate model output mid-sentence.
Add two temperature-controlled models

Temperature is a number between 0 and 2 that controls how much randomness the model uses when picking its next word. A temperature of 0.0 makes the model deterministic. A temperature of 1.5 makes it highly creative and unpredictable.

  • In HelloLLM.java, find the closing line:
        System.out.println("AI says: " + answer);
  • Add the following code below that line to create a precise model and a creative model, each calling the same prompt twice:
        // Precise model - temperature 0.0 (deterministic)
        OpenAiChatModel preciseModel = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .temperature(0.0)
                .build();

        // Creative model - temperature 1.5 (highly random)
        OpenAiChatModel creativeModel = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .temperature(1.5)
                .build();

        System.out.println("\n--- Precise (temperature=0.0) ---");
        System.out.println("Attempt 1: " + preciseModel.chat("Invent a name for a coffee shop."));
        System.out.println("Attempt 2: " + preciseModel.chat("Invent a name for a coffee shop."));

        System.out.println("\n--- Creative (temperature=1.5) ---");
        System.out.println("Attempt 1: " + creativeModel.chat("Invent a name for a coffee shop."));
        System.out.println("Attempt 2: " + creativeModel.chat("Invent a name for a coffee shop."));

What does this code do?

  • preciseModel uses temperature(0.0). This tells the model to always pick the most likely next word. The output is nearly identical every time.
  • creativeModel uses temperature(1.5). This cranks up randomness so the model explores less likely words. You get different answers each run.
  • Both models send the same prompt ("Invent a name for a coffee shop.") twice so you can compare whether the responses repeat or diverge.

Before you run this, what do you expect to see? Will both models give you the same coffee shop name twice, or will one of them vary?

  • Save HelloLLM.java.
  • Click the green play button next to your main method (or press Ctrl+Shift+F10 on Windows / Ctrl+Shift+R on macOS) to run the program.

In the console output, look at the "Precise" section. Both attempts produce the same name (or extremely similar ones). Now look at the "Creative" section. The two attempts produce different names each time.

Why does temperature 0.0 repeat itself?

LLMs predict the next token by assigning probabilities to every possible continuation. Temperature 0.0 always picks the highest-probability token. Since the probabilities are fixed for the same input, you get the same output.

Higher temperatures flatten the probability curve, letting lower-ranked tokens get picked. That is where the creativity (and occasional nonsense) comes from.

Both models giving identical outputs?

  • Make sure you set temperature(1.5) on creativeModel, not on preciseModel.
  • Confirm you saved the file before running. IntelliJ shows unsaved changes with an asterisk in the file tab.

Help me debug why both temperature models return the same output.

Add a token limit to see truncation

Tokens are not the same as words. A single English word might be one token, or it might be split into two or three. When you set a maximum token limit, the model stops generating once it hits that ceiling, even mid-sentence.

  • Add the following code at the bottom of your main method, below the creative model print statements:
        // Token limit demonstration
        OpenAiChatModel limitedModel = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .maxTokens(20)
                .build();

        System.out.println("\n--- Limited to 20 tokens ---");
        System.out.println(limitedModel.chat("Explain the history of the Java programming language in detail."));

What does this code do?

  • maxTokens(20) caps the model's response at 20 tokens. You are asking a question that needs a long answer, so the response gets cut off abruptly.
  • This proves that tokens are the unit LLMs think in. You control costs and response length by setting this limit.

What do you think 20 tokens of a detailed Java history explanation will look like?

  • Save HelloLLM.java and run the program again.

In the "Limited to 20 tokens" section, you'll see a response that starts normally but stops abruptly mid-sentence. That is the token limit in action.

Response doesn't look truncated?

  • Check that you used maxTokens(20) (not a larger number). With 20 tokens the answer is clearly cut short.
  • If you see a full sentence, you may have the .maxTokens(20) line on the wrong builder. Confirm it is on limitedModel.

Help me understand why my token-limited model isn't truncating.

✔️ Awesome, I've got everything!

Great. Double check you've saved HelloLLM.java before moving on.

ⓧ I'd like to double check the full code

Your HelloLLM.java should contain these sections in order. Scroll up through the step and confirm each one is present:

  • The original default model builder and its model.chat() call (from Step 1).
  • preciseModel with temperature(0.0) and two print calls using "Invent a name for a coffee shop."
  • creativeModel with temperature(1.5) and two print calls using the same prompt.
  • limitedModel with maxTokens(20) and a print call asking about Java history.

You now know how to tune randomness and cap output length. Next up, you'll learn how to give the AI a personality so it responds in character every time.

Control AI Behavior with System Prompts

You now understand how to control the randomness of an LLM's output. But the model is still completely generic. Ask it a question and you get a Wikipedia-style answer with no personality or constraints.

System prompts are how you turn a generic model into a specialist. In this step, you will use AI Services, a LangChain4j pattern that lets you define an AI's personality with a single annotation on a Java interface.

In this step, get ready to:
  • Define two AI Service interfaces with different system prompts.
  • Ask both personalities the same question and compare their answers.
Create the AI Service interfaces

An AI Service in LangChain4j is a Java interface that represents a specific AI personality or behavior. You annotate the method with @SystemMessage and LangChain4j handles the rest.

  • In IntelliJ, right-click the chatbot package (under src/main/java), then select New > Java Class.
  • Name it PersonalityBot and press Enter.
  • Replace the entire file contents with the following code:
package chatbot;

import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;

public class PersonalityBot {

    // AI Service interface with a pirate personality
    interface PirateAssistant {
        @SystemMessage("You are a pirate. Respond to everything in pirate speak. Use pirate slang and say 'Arrr' often.")
        String chat(String userMessage);
    }

    // AI Service interface with a teacher personality
    interface TeacherAssistant {
        @SystemMessage("You are a patient teacher explaining concepts to a 10-year-old child. Use simple language, short sentences, and fun analogies.")
        String chat(String userMessage);
    }
}

What does this code do?

Each interface defines a different AI personality using the @SystemMessage annotation. The annotation text is the system prompt that tells the LLM how to behave.

  • PirateAssistant instructs the model to respond in pirate speak.
  • TeacherAssistant instructs the model to explain things simply, as if to a 10-year-old.
  • The chat(String userMessage) method signature tells LangChain4j to send the user's message and return the AI's response as a String.

Why AI Services instead of raw model calls?

You could pass a system message manually on every call, but that gets messy fast. AI Services let you define the personality once on the interface and forget about it. Every call through that interface automatically includes the system prompt.

This is the pattern you will use for every LLM interaction going forward. Define an interface, annotate it, and let LangChain4j handle the wiring.

  • Confirm there are no red underlines in the editor. IntelliJ should resolve all three imports without errors.

Seeing red underlines on the imports?

  • Make sure your pom.xml still has both langchain4j-open-ai and langchain4j dependencies. The AiServices class lives in the core langchain4j artifact.
  • Try clicking the Maven reload button (the circular arrow icon in IntelliJ's Maven tool window) to re-download dependencies.

Help me fix import errors in my LangChain4j PersonalityBot.java file.

Add the main method and run your personality bot

The interfaces define personalities. Now you need a main method that creates instances of each and sends them the same question so you can compare the results.

  • In PersonalityBot.java, place your cursor just before the closing } of the class (the last line of the file).
  • Add the following main method inside the class, between the TeacherAssistant interface and the closing brace:
    public static void main(String[] args) {
        // Create the model using the free demo key
        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .build();

        // Create AI Service instances from each interface
        PirateAssistant pirate = AiServices.create(PirateAssistant.class, model);
        TeacherAssistant teacher = AiServices.create(TeacherAssistant.class, model);

        // Ask the same question to both personalities
        String question = "What is Java?";

        System.out.println("--- Pirate Assistant ---");
        System.out.println(pirate.chat(question));

        System.out.println("\n--- Teacher Assistant ---");
        System.out.println(teacher.chat(question));
    }

What does this code do?

  • AiServices.create(PirateAssistant.class, model) generates a runtime implementation of your interface. Every call to .chat() on that instance automatically includes the @SystemMessage as context.
  • Both assistants share the same model and the same question. The only difference is the system prompt annotation on each interface.
  • The printed output will show how one annotation completely changes the model's tone and vocabulary.
  • Save PersonalityBot.java.

Before you run this, think about what you expect. Both assistants receive the exact same question. Will the answers be similar or completely different?

  • Right-click anywhere in PersonalityBot.java and select Run 'PersonalityBot.main()'.

In IntelliJ's console, you will see the pirate assistant answer in pirate slang (think "Arrr, matey!") and the teacher assistant explain Java in simple, child-friendly language. The same question, two completely different responses.

Seeing the same generic answer from both?

  • Check that each interface has its own @SystemMessage annotation with different text. If both share the same annotation text, you will get the same style of response.
  • Make sure you are calling pirate.chat(question) and teacher.chat(question) separately, not the same variable twice.
  • If the demo key quota is exhausted, you may get an error instead of a response. Wait a few minutes and try again.

Help me debug my PersonalityBot AI Service interfaces.

✔️ Awesome, I've got everything!

Great. Double check that you have saved PersonalityBot.java and that both responses printed in the console with visibly different personalities.

ⓧ I'd like to double check the full code

Scroll up and confirm your PersonalityBot.java has both of these chunks in order:

  • The package declaration, three imports, the class declaration, and both interface definitions (PirateAssistant and TeacherAssistant).
  • The main method that builds the model, creates both assistants with AiServices.create(), and prints both responses.

You now have full control over your AI's personality. One annotation swaps it from a pirate to a teacher. Next up, you will tackle a bigger problem: the AI forgets everything you just said the moment you send a new message.

Add Conversation Memory

Your PersonalityBot can answer in pirate speak or child-friendly language. But try asking it a follow-up question and it completely forgets what you just said.

Each call to the LLM is stateless by default. The model has no idea what you said 10 seconds ago. This is the core limitation you need to solve to build any real conversational application.

In this step, get ready to:
  • Experience the stateless conversation problem firsthand.
  • Fix the problem by wiring up conversation memory.
  • Compare the forgetful and remembering assistants side by side.
Create the forgetful assistant

You already know how to build AI Services from the previous step. Now you will create a plain assistant with no special configuration and watch what happens when you try to have a conversation.

  • Right-click the chatbot package in IntelliJ's Project panel and select New, then Java Class.
  • Name it MemoryBot and press Enter.
  • Replace the file contents with the following code:
package chatbot;

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;

public class MemoryBot {

    interface Assistant {
        String chat(String userMessage);
    }

    public static void main(String[] args) {
        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .build();

        // WITHOUT memory - each call is independent
        Assistant forgetful = AiServices.create(Assistant.class, model);

        System.out.println("--- Without Memory ---");
        System.out.println("User: My name is Alex");
        System.out.println("AI: " + forgetful.chat("My name is Alex. Nice to meet you!"));
        System.out.println("\nUser: What is my name?");
        System.out.println("AI: " + forgetful.chat("What is my name?"));
    }
}

What does this code do?

The Assistant interface is the simplest possible AI Service. It has a single chat method with no @SystemMessage annotation.

You create the forgetful assistant using AiServices.create(Assistant.class, model) with no memory configuration. Then you send two messages: introduce yourself, then ask the AI to recall your name.

Before you run this, what do you think the AI will answer when you ask "What is my name?" in that second, separate call?

  • Save MemoryBot.java.
  • Click the green play button next to the main method and select Run 'MemoryBot.main()'.

Under the "Without Memory" heading, the AI responds to your second question by saying it does not know your name. It has zero awareness of the previous message you sent.

Why does the AI forget?

Each call to forgetful.chat() sends only that single message to the model. The model never sees the first message when processing the second one. Without memory, every call is completely independent.

This is the core problem. Any chatbot, customer service agent, or coding assistant that cannot recall what you said 10 seconds ago is useless. Let's fix it.

Wire up conversation memory

LangChain4j provides MessageWindowChatMemory, which stores recent messages and replays them to the model on every new call. The model sees the full conversation history, not just the latest message.

  • In MemoryBot.java, find the closing brace of the main method. Place your cursor just before it and add the following code:
        // WITH memory - conversation state is maintained
        Assistant remembering = AiServices.builder(Assistant.class)
                .chatModel(model)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(20))
                .build();

        System.out.println("\n--- With Memory ---");
        System.out.println("User: My name is Alex");
        System.out.println("AI: " + remembering.chat("My name is Alex. Nice to meet you!"));
        System.out.println("\nUser: What is my name?");
        System.out.println("AI: " + remembering.chat("What is my name?"));

What does this code do?

  • AiServices.builder(Assistant.class) uses the builder pattern instead of the simple create() call. This lets you attach extra components like memory.
  • MessageWindowChatMemory.withMaxMessages(20) keeps the 20 most recent messages in a window. Older messages get evicted so the context does not grow forever.
  • When you call remembering.chat(), LangChain4j automatically prepends all stored messages to the new request. The model now sees the full conversation.
Run and compare both assistants

Now you have both assistants in the same file. One forgets, one remembers. Before you run it, predict: will the memory-enabled assistant correctly say "Alex" on the follow-up question?

  • Save MemoryBot.java.
  • Click the green play button next to main and select Run 'MemoryBot.main()'.

You should see two sections in the console output. Under "Without Memory", the AI cannot recall your name. Under "With Memory", the AI greets you by name on the follow-up question.

What is a context window?

LLMs do not truly "remember" anything between calls. Memory works by replaying the full conversation history as part of every new request. The model reads everything each time, like re-reading a chat log from the top.

The withMaxMessages(20) limit exists because models have a maximum context window. If you replay thousands of messages, you would exceed that limit and the call would fail.

AI still says it does not know your name in the With Memory section?

  • Make sure you used AiServices.builder(Assistant.class) (not AiServices.create()) for the remembering assistant.
  • Confirm that .chatMemory(MessageWindowChatMemory.withMaxMessages(20)) is chained before .build().
  • Check that you are calling remembering.chat() (not forgetful.chat()) in the "With Memory" section.

Help me debug my MemoryBot conversation memory

✔️ Awesome, I've got everything!

Great. Double check that you have saved MemoryBot.java and the console shows both sections with the memory contrast.

ⓧ I'd like to double check the full code

Here is the complete MemoryBot.java file:

package chatbot;

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;

public class MemoryBot {

    interface Assistant {
        String chat(String userMessage);
    }

    public static void main(String[] args) {
        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .build();
        // WITHOUT memory - each call is independent
        Assistant forgetful = AiServices.create(Assistant.class, model);

        System.out.println("--- Without Memory ---");
        System.out.println("User: My name is Alex");
        System.out.println("AI: " + forgetful.chat("My name is Alex. Nice to meet you!"));
        System.out.println("\nUser: What is my name?");
        System.out.println("AI: " + forgetful.chat("What is my name?"));

        // WITH memory - conversation state is maintained
        Assistant remembering = AiServices.builder(Assistant.class)
                .chatModel(model)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(20))
                .build();
        System.out.println("\n--- With Memory ---");
        System.out.println("User: My name is Alex");
        System.out.println("AI: " + remembering.chat("My name is Alex. Nice to meet you!"));
        System.out.println("\nUser: What is my name?");
        System.out.println("AI: " + remembering.chat("What is my name?"));
    }
}

You have solved the stateless conversation problem. Your AI now carries context across multiple turns. Next up, you will combine everything into a real interactive chatbot you can talk to in your terminal.

Build the Interactive Chatbot

You now have all the building blocks: model calls, custom personalities via system prompts, and conversation memory.

In this step, you assemble everything into a real running application. A terminal chatbot that has a personality, remembers your full conversation, and responds in real time. This is your first complete AI-powered program.

In this step, get ready to:
  • Create a CodingMentor AI Service with a custom personality and memory.
  • Add an interactive input loop using Scanner.
  • Run a multi-turn conversation and confirm the chatbot remembers context.
Create the CodingMentor AI Service

This file combines everything you learned in the previous steps into a single cohesive chatbot. The AI Service interface defines the personality, and the builder wires in conversation memory.

  • In the src/main/java/chatbot folder, create a new file called Chatbot.java.
  • Paste the following code into the file:
package chatbot;

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
import java.util.Scanner;

public class Chatbot {

    // AI Service interface - the @SystemMessage defines the chatbot's personality
    interface CodingMentor {
        @SystemMessage("You are a friendly and encouraging coding mentor. You help developers learn new concepts by giving clear explanations with examples. Keep responses concise (2-3 paragraphs max). If someone asks a non-coding question, you can still answer but gently steer back to tech topics.")
        String chat(String userMessage);
    }

    public static void main(String[] args) {
        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1")
                .apiKey("demo")
                .modelName("gpt-4o-mini")
                .build();

        // Wire the AI Service with conversation memory (20 messages max)
        CodingMentor mentor = AiServices.builder(CodingMentor.class)
                .chatModel(model)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(20))
                .build();
    }
}

What does this code do?

The CodingMentor interface is your AI Service. The @SystemMessage annotation tells the LLM how to behave for every message sent through this interface.

The AiServices.builder call wires together the model and a MessageWindowChatMemory that keeps the last 20 messages. This is exactly what you tested in MemoryBot.java, now paired with a real personality.

Add the interactive conversation loop

The AI Service is ready, but there is no way for a user to talk to it yet. You need a loop that reads input from the terminal, sends it to the mentor, and prints the response.

  • In Chatbot.java, find this line inside the main method:
        CodingMentor mentor = AiServices.builder(CodingMentor.class)
                .chatModel(model)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(20))
                .build();
  • Add the following code directly after the .build(); line, before the closing } of the main method:
        // Set up the interactive console loop
        Scanner scanner = new Scanner(System.in);

        System.out.println("=== AI Coding Mentor ===");
        System.out.println("Type your message and press Enter. Type 'exit' to quit.\n");

        // Keep reading input until the user types "exit"
        while (true) {
            System.out.print("You: ");
            String input = scanner.nextLine();

            if ("exit".equalsIgnoreCase(input.trim())) {
                System.out.println("Goodbye! Happy coding!");
                break;
            }

            // Send the input to the AI and print the response
            String response = mentor.chat(input);
            System.out.println("Mentor: " + response + "\n");
        }

        scanner.close();

What does this loop do?

  • Scanner reads one line of text from the terminal each time the user presses Enter.
  • The while (true) loop keeps the conversation going until the user types exit.
  • Each message is sent to mentor.chat(input), which calls the LLM with your full conversation history attached automatically.
  • Save Chatbot.java.

✔️ Awesome, I've got everything!

Great. Double check you have saved the file before running it.

ⓧ I'd like to double check the full code

Your Chatbot.java should contain two sections in order:

  • The CodingMentor interface with @SystemMessage, followed by the model builder and the AiServices.builder call with MessageWindowChatMemory.withMaxMessages(20).
  • The Scanner setup, the welcome messages, the while (true) loop with the exit check, and scanner.close() at the end.

Scroll up and confirm both chunks are present inside your main method, in that order.

Run the chatbot and test conversation memory

Before you run this, think about what you expect to happen. Will the chatbot remember your name if you tell it, then ask about it a few messages later?

  • Right-click Chatbot.java in IntelliJ and select Run 'Chatbot.main()'.
  • You should see this banner appear in the console:
=== AI Coding Mentor ===
Type your message and press Enter. Type 'exit' to quit.

You:
  • Type a greeting that includes your name, like Hi, my name is Alex. What can you help me with? and press Enter.
  • Ask a coding question, like What's the difference between an interface and an abstract class?.
  • Now ask What's my name? to test memory.

The chatbot responds in a friendly mentor tone and correctly recalls your name from earlier in the conversation. The memory window keeps all your messages available to the AI across turns.

  • Type exit and press Enter to stop the chatbot.

Try a different personality

Change the text inside @SystemMessage to anything you like. Try a pirate, a sarcastic code reviewer, or a Shakespearean poet. Re-run and see how the chatbot's tone completely changes with a single annotation swap.

Chatbot not responding or throwing an error?

  • Make sure IntelliJ shows no red underlines in Chatbot.java. A missing import or typo in the @SystemMessage string will prevent compilation.
  • If you see a timeout or connection error, the demo key has a rate limit. Wait 30 seconds and try again.
  • If the console shows You: but nothing happens after you press Enter, check that the cursor is in the IntelliJ Run tab (not the Terminal tab).

Why is my chatbot not responding to input?

You just built your first complete AI application. A chatbot with personality and memory, running entirely from your terminal. In the Secret Mission, you will push further and make the AI return structured Java objects instead of raw text.

Secret mission

Secret Mission: Structured Output and Streaming

Your chatbot can talk, but can it think in structured data? In production AI applications, you rarely want raw text back from a model. You want typed Java objects you can store in a database or pass to another service. In this mission, you'll make the LLM return a proper Java record, then add real-time streaming so responses appear token-by-token, just like ChatGPT.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally using the free LangChain4j demo key, so there are no ongoing costs.

Resources you used:

  • The ai-chatbot Maven project folder (contains all Java source files and dependencies).

Keep everything running

No action needed. Choose this if you are still actively building or plan to continue with the next project in this series (building a RAG pipeline).

  • Your ai-chatbot project stays on your machine, ready to open in IntelliJ any time.
  • The LangChain4j demo key is free with no billing, so leaving the project on disk costs nothing.
  • To extend, swap the demo key for a real OpenAI or Google Gemini API key to unlock faster responses and more models.

Pause - I'll come back to this later

There is nothing to pause. The project has no running processes or cloud resources that consume resources while idle.

  • Close IntelliJ IDEA to free up memory.
  • Your project folder stays on disk and you can reopen it any time by opening the ai-chatbot folder in IntelliJ.

Delete - I don't want to use this again

Remove the project folder entirely to start fresh.

  • Close the project in IntelliJ IDEA.
  • Delete the ai-chatbot folder from your Desktop.

On macOS or Linux, run this in your terminal:

rm -rf ~/Desktop/ai-chatbot

On Windows, run this in PowerShell:

Remove-Item -Recurse -Force ~\Desktop\ai-chatbot

That removes all Java source files, the Maven configuration, and any downloaded dependencies. No cloud resources or accounts need to be deleted.

Nice Work!

Nice Work!

Nice work! You just built a fully interactive AI chatbot in Java, powered by LangChain4j and the free demo key.

You've learned how to:

  • Called an LLM from Java code and controlled its output using temperature and token limits.
  • Used AI Services with @SystemMessage annotations to give an AI a custom personality that responds in character.
  • Solved the stateless conversation problem with MessageWindowChatMemory and built a real-time terminal chatbot that remembers your entire conversation.
  • Secret Mission: Extracted structured Java objects from unstructured text and added real-time token streaming to see the AI respond word-by-word.

Ready to quiz yourself?