Quarkus extension that integrates LangChain4j's agentic capabilities, enabling developers to build AI agent-based applications using declarative patterns with support for multiple agent types, agent-to-agent communication, and CDI integration.
This document describes how to provide ChatModels, ChatMemory, Tools, ContentRetrievers, and other resources to agents via static supplier methods.
Provides a ChatModel for the agent to use.
/**
* Supplies a ChatModel for the agent
* Method must be static with no parameters
*
* @return ChatModel instance
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ChatModelSupplier {
}
// Usage
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.temperature(0.7)
.build();
}Usage Example:
public interface MyAgent {
@Agent(description = "Processes requests")
String process(@V("input") String input);
@ChatModelSupplier
static ChatModel chatModel() {
// OpenAI
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.temperature(0.7)
.maxTokens(1000)
.build();
}
}
// Alternative: Using Ollama
public interface OllamaAgent {
@Agent
String process(@V("input") String input);
@ChatModelSupplier
static ChatModel chatModel() {
return OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama2")
.temperature(0.7)
.build();
}
}
// Or inject from CDI (no @ChatModelSupplier needed)
// The extension will automatically use the default ChatModel bean
public interface AutoInjectedAgent {
@Agent
String process(@V("input") String input);
// No @ChatModelSupplier - uses CDI-provided ChatModel
}Provides a ChatMemory instance for conversation history.
/**
* Supplies a ChatMemory instance for the agent
* Method must be static with no parameters
*
* @return ChatMemory instance
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ChatMemorySupplier {
}
// Usage
@ChatMemorySupplier
static ChatMemory chatMemory() {
return MessageWindowChatMemory.withMaxMessages(10);
}Usage Example:
public interface ConversationalAgent {
@Agent(description = "Maintains conversation context")
String chat(@V("message") String message);
@ChatMemorySupplier
static ChatMemory chatMemory() {
// Window-based memory (keeps last N messages)
return MessageWindowChatMemory.withMaxMessages(10);
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}
// Alternative: Token-based memory
public interface TokenBasedMemoryAgent {
@Agent
String chat(@V("message") String message);
@ChatMemorySupplier
static ChatMemory chatMemory() {
return TokenWindowChatMemory.withMaxTokens(1000, new OpenAiTokenizer());
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}Provides a ChatMemory that can be created per-user using a memory ID.
/**
* Supplies a ChatMemoryProvider that creates ChatMemory per memory ID
* Method must be static with one Object parameter
*
* @param memoryId - The memory identifier (typically user ID)
* @return ChatMemory instance for that memory ID
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ChatMemoryProviderSupplier {
}
// Usage
@ChatMemoryProviderSupplier
static ChatMemory chatMemoryProvider(Object memoryId) {
return MessageWindowChatMemory.withMaxMessages(10);
}Usage Example:
public interface MultiUserAgent {
@Agent(description = "Chat agent with per-user memory")
String chat(@MemoryId String userId, @V("message") String message);
@ChatMemoryProviderSupplier
static ChatMemory chatMemoryProvider(Object memoryId) {
// Create isolated memory for each user
return MessageWindowChatMemory.withMaxMessages(10);
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}
// Usage
@Inject
MultiUserAgent agent;
String response1 = agent.chat("user123", "Hello!");
String response2 = agent.chat("user456", "Hello!"); // Separate memory
String response3 = agent.chat("user123", "What did I just say?"); // Remembers "Hello!"Provides tools (functions) that the agent can use.
/**
* Supplies tools for the agent
* Method must be static with no parameters
* Return type must be Object or Object[]
*
* @return Tool object(s)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ToolsSupplier {
}
// Usage - single tool
@ToolsSupplier
static Object tools() {
return new CalculatorTool();
}
// Usage - multiple tools
@ToolsSupplier
static Object[] tools() {
return new Object[] { new CalculatorTool(), new WeatherTool() };
}Usage Example:
@ApplicationScoped
public class MathTools {
@Tool("Adds two numbers")
public int add(int a, int b) {
return a + b;
}
@Tool("Multiplies two numbers")
public int multiply(int a, int b) {
return a * b;
}
@Tool("Calculates factorial")
public long factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
}
@ApplicationScoped
public class WebTools {
@Tool("Fetches content from a URL")
public String fetchUrl(String url) {
// Implementation
return "Content from " + url;
}
@Tool("Searches the web")
public String search(String query) {
// Implementation
return "Search results for " + query;
}
}
public interface AssistantAgent {
@SystemMessage("You are a helpful assistant with access to math and web tools.")
@UserMessage("{{request}}")
@Agent(description = "General purpose assistant", outputKey = "response")
String assist(@V("request") String request);
@ToolsSupplier
static Object[] tools() {
return new Object[] { new MathTools(), new WebTools() };
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}Provides a ToolProvider for dynamic tool resolution.
/**
* Supplies a ToolProvider for the agent
* Method must be static with no parameters
*
* @return ToolProvider instance
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ToolProviderSupplier {
}
// Usage
@ToolProviderSupplier
static ToolProvider toolProvider() {
return new CustomToolProvider();
}Usage Example:
public class DynamicToolProvider implements ToolProvider {
@Override
public Collection<ToolSpecification> provideTools(Object context) {
// Dynamically determine which tools to provide based on context
List<ToolSpecification> tools = new ArrayList<>();
// Add tools based on context
return tools;
}
}
public interface DynamicAgent {
@Agent(description = "Agent with dynamic tools")
String process(@V("input") String input);
@ToolProviderSupplier
static ToolProvider toolProvider() {
return new DynamicToolProvider();
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}Provides a ContentRetriever for RAG (Retrieval Augmented Generation).
/**
* Supplies a ContentRetriever for RAG
* Method must be static with no parameters
*
* @return ContentRetriever instance
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ContentRetrieverSupplier {
}
// Usage
@ContentRetrieverSupplier
static ContentRetriever contentRetriever() {
return new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel);
}Usage Example:
public interface KnowledgeBaseAgent {
@SystemMessage("Answer questions using the provided knowledge base context.")
@UserMessage("Question: {{question}}")
@Agent(description = "Answers questions using knowledge base", outputKey = "answer")
String answer(@V("question") String question);
@ContentRetrieverSupplier
static ContentRetriever contentRetriever() {
// Set up embedding store
EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel();
// Optionally populate the store
// embeddingStore.add(...);
return EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(5)
.minScore(0.7)
.build();
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}Provides a RetrievalAugmentor for advanced RAG scenarios.
/**
* Supplies a RetrievalAugmentor for advanced RAG
* Method must be static with no parameters
*
* @return RetrievalAugmentor instance
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface RetrievalAugmentorSupplier {
}
// Usage
@RetrievalAugmentorSupplier
static RetrievalAugmentor retrievalAugmentor() {
return DefaultRetrievalAugmentor.builder()
.contentRetriever(contentRetriever)
.build();
}Usage Example:
public interface AdvancedRAGAgent {
@Agent(description = "Advanced RAG with custom augmentation")
String answer(@V("question") String question);
@RetrievalAugmentorSupplier
static RetrievalAugmentor retrievalAugmentor() {
ContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(new InMemoryEmbeddingStore<>())
.embeddingModel(new AllMiniLmL6V2EmbeddingModel())
.build();
return DefaultRetrievalAugmentor.builder()
.contentRetriever(retriever)
.contentAggregator(new ReRankingContentAggregator())
.build();
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}Provides an Executor for parallel agent execution.
/**
* Supplies an Executor for parallel agent execution
* Method must be static with no parameters
*
* @return Executor instance
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ParallelExecutor {
}
// Usage
@ParallelExecutor
static Executor executor() {
return Executors.newFixedThreadPool(4);
}Usage Example:
public interface CustomParallelAgent {
@ParallelAgent(
outputKey = "results",
subAgents = { Agent1.class, Agent2.class, Agent3.class, Agent4.class }
)
String processInParallel(@V("input") String input);
@ParallelExecutor
static Executor executor() {
// Custom thread pool for parallel execution
return Executors.newFixedThreadPool(
8,
new ThreadFactoryBuilder()
.setNameFormat("agent-pool-%d")
.setDaemon(true)
.build()
);
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}Provides human response in human-in-the-loop scenarios.
/**
* Supplies human response for human-in-the-loop scenarios
* Method must be static
* Return type must be String
*
* @return Human response string
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface HumanInTheLoopResponseSupplier {
}
// Usage
@HumanInTheLoopResponseSupplier
static String getHumanResponse() {
// Fetch response from user interface, queue, etc.
return "Human approval granted";
}Usage Example:
public interface ApprovalAgent {
@Agent(description = "Process with human approval")
String processWithApproval(@V("request") String request);
@HumanInTheLoop
static void requestApproval(@V("request") String request, @V("proposedAction") String action) {
System.out.println("Approval needed for: " + action);
// Notify user, log request, etc.
}
@HumanInTheLoopResponseSupplier
static String getHumanResponse() {
// In production, this would fetch from a queue, UI, etc.
// For example: return approvalQueue.poll(timeout);
return "Approved";
}
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
}
}Agents can use multiple suppliers to configure different aspects.
Usage Example:
public interface FullyConfiguredAgent {
@SystemMessage("You are a helpful assistant with tools and knowledge base access.")
@UserMessage("{{question}}")
@Agent(description = "Fully configured assistant", outputKey = "answer")
String assist(@V("question") String question);
@ChatModelSupplier
static ChatModel chatModel() {
return new OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.temperature(0.7)
.build();
}
@ChatMemorySupplier
static ChatMemory chatMemory() {
return MessageWindowChatMemory.withMaxMessages(10);
}
@ToolsSupplier
static Object[] tools() {
return new Object[] { new CalculatorTool(), new WebSearchTool() };
}
@ContentRetrieverSupplier
static ContentRetriever contentRetriever() {
EmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();
EmbeddingModel model = new AllMiniLmL6V2EmbeddingModel();
return new EmbeddingStoreContentRetriever(store, model);
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-agentic@1.7.0