Your First LLM Agent

This page sets up a project that uses astra-langchain4j, makes a model call and shows how to run agents without a provider while testing.

Set up a project

Start with a project from the Quick Start, then add this dependency to the POM’s <dependencies> section:

<dependency>
    <groupId>com.astralanguage</groupId>
    <artifactId>astra-langchain4j</artifactId>
    <version>2.6.1</version>
</dependency>

Use ASTRA 2.6.1 for the whole project: the parent POM and every ASTRA dependency should have the same version. You need Java 17 or later, an API key and access to a model from the provider you choose. Keep API keys out of version control.

Make an LLM request

Save this file as src/main/astra/LLMDemo.astra:

Download LLMDemo.astra

import astra.langchain4j.*;

agent LLMDemo {
    module LLMModel llm;
    module Templater tpl;
    module Console C;
    module System S;

    plan +!main(list args) {
        try {
            llm.loadApiKeyFromEnv("OPENAI_API_KEY");
            llm.initialize("gpt-4o-mini",
                "astra.langchain4j.configuration.OpenAiConfiguration", []);

            PromptTemplate prompt = tpl.createPromptTemplate(
                "Explain ${topic} in one sentence.");
            tpl.addBinding(prompt, "topic", "beliefs in an agent program");
            llm.chat(prompt, string Reply);
            C.println(Reply);
        } recover {
            C.println("The LLM request could not be completed.");
        }
        S.exit();
    }
}

Set OPENAI_API_KEY in the environment of the terminal that will run Maven, then run:

mvn -Dastra.main=LLMDemo

The example prints the model’s answer, which varies between requests. It makes one remote model call and can incur provider charges. Replace the model name with one available to your account.

loadApiKeyFromEnv(...) reads the named environment variable. Alternatively, loadApiKeyFromFile(...) reads the first line of a key file, and setApiKey(...) takes the key itself. File paths are relative to the running process: astra:run starts it in the project directory, so api.key refers to a file next to pom.xml. Add such a file to .gitignore.

initialize(...) selects a model, provider configuration and optional parameter list. chat(...) binds its second argument to the returned text. The try/recover block handles action failures, including a missing environment variable, so the agent can report the problem and finish.

Choose a provider

The library provides these configuration classes:

Provider

Configuration class

OpenAI

astra.langchain4j.configuration.OpenAiConfiguration

Google Gemini

astra.langchain4j.configuration.GeminiConfiguration

Anthropic

astra.langchain4j.configuration.AnthropicConfiguration

Pass the chosen provider’s model name and API key to LLMModel. The configuration classes accept temperature(...) and topP(...) entries, for example [temperature(0.2)]. Supported settings also depend on the chosen model. An empty list uses provider defaults.

The LLMAgent class

Instead of declaring an LLMModel module, an agent can extend astra.langchain4j.LLMAgent. It declares the module as model and provides an !initialize(...) goal that loads the key from a file and configures the model:

import astra.langchain4j.*;

agent Explainer extends LLMAgent {
    module Console console;
    module System S;

    rule +!main(list args) {
        !initialize("gpt-4o-mini", "api.key",
            "astra.langchain4j.configuration.OpenAiConfiguration");
        model.chat("Explain intentions in one sentence.", string reply);
        console.println(reply);
        S.exit();
    }
}

OpenAIAgent, GeminiAgent and ClaudeAgent extend LLMAgent with a default model for their provider, so !initialize("api.key") is enough. An agent that extends LLMAgent can also call model.loadApiKeyFromEnv(...) and model.initialize(...) directly, as the examples in the rest of this section do.

All the reasoning agents extend LLMAgent, so model is available in every agent that uses them.

Test without a provider

A configuration class is an ordinary Java class that implements astra.langchain4j.Configuration and returns a LangChain4j ChatModel. initialize(...) loads it by name, so a project can supply its own. A scripted model that returns fixed replies lets you run and test agents without an API key, network access or charges:

package llm;

import astra.langchain4j.Configuration;
import astra.term.ListTerm;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.response.ChatResponse;

public class ScriptedConfiguration implements Configuration {
    public ChatModel createChatModel(String modelName, String apiKey, ListTerm params) {
        return new ChatModel() {
            public ChatResponse doChat(ChatRequest request) {
                return ChatResponse.builder()
                    .aiMessage(AiMessage.from("<answer>42</answer>"))
                    .build();
            }
        };
    }
}

Save it as src/main/java/llm/ScriptedConfiguration.java and pass "llm.ScriptedConfiguration" to initialize(...). A more useful script inspects the prompt in request.messages() and chooses a reply, so that each step of a multi-call strategy can be exercised. The A2A example project uses this technique to run its reasoning agents deterministically in its tests.

Where next?