Build LLM-powered applications in Java with support for chatbots, agents, RAG, tools, and much more
Legacy chain API for sequential processing. Chains are deprecated in favor of AiServices, which provides a more flexible and powerful API.
CRITICAL DEPRECATION NOTICE: This API is deprecated and should NOT be used for new development. All chain functionality has been superseded by AiServices. This documentation is maintained only for legacy code migration purposes.
Functional interface representing a chain step.
package dev.langchain4j.chain;
/**
* Functional interface representing a chain step that takes an input and produces an output
* Deprecated in favor of AiServices
* @deprecated Use AiServices instead
*/
@Deprecated
public interface Chain<Input, Output> {
/**
* Execute the chain step
* @param input Input to process
* @return Output from processing
*/
Output execute(Input input);
}Thread Safety:
Common Pitfalls:
Exception Handling:
Performance Notes:
Related APIs:
AiServices - Modern replacement with better featuresConversationalChain - Concrete chain implementationConversationalRetrievalChain - RAG-enabled chain implementationChain for conversing with a ChatModel while maintaining memory.
package dev.langchain4j.chain;
/**
* A chain for conversing with a ChatModel while maintaining a memory of the conversation
* Includes a default ChatMemory (message window with max 10 messages)
* Deprecated in favor of AiServices
* @deprecated Use AiServices with chatMemory configuration instead
*/
@Deprecated
public class ConversationalChain implements Chain<String, String> {
/**
* Create builder for configuring chain
* @return Builder instance
*/
public static ConversationalChainBuilder builder();
/**
* Execute chain with user message
* @param userMessage User message to process
* @return Response from chat model
*/
public String execute(String userMessage);
}Thread Safety:
Common Pitfalls:
Edge Cases:
Exception Handling:
Performance Notes:
Replacement with AiServices:
// OLD (Deprecated):
ConversationalChain chain = ConversationalChain.builder()
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
// NEW (Recommended):
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();Related APIs:
AiServices - Modern replacementChatMemory - Memory managementMessageWindowChatMemory - Default memory implementationChatModel - Underlying model interface/**
* Builder for ConversationalChain
*/
public class ConversationalChainBuilder {
/**
* Set the chat model
* @param chatModel Chat model to use
* @return Builder instance
*/
public ConversationalChainBuilder chatModel(ChatModel chatModel);
/**
* Set the chat memory
* @param chatMemory Chat memory instance
* @return Builder instance
*/
public ConversationalChainBuilder chatMemory(ChatMemory chatMemory);
/**
* Build the chain
* @return ConversationalChain instance
*/
public ConversationalChain build();
}Thread Safety:
Common Pitfalls:
Exception Handling:
Related APIs:
ConversationalChain - Built chain instanceChatModel - Required model parameterChatMemory - Optional memory parameterChain for conversing with a ChatModel based on retrieved information.
package dev.langchain4j.chain;
/**
* A chain for conversing with a ChatModel based on information retrieved by a ContentRetriever
* Includes default ChatMemory (message window with max 10 messages)
* Supports RAG with RetrievalAugmentor
* Deprecated in favor of AiServices with contentRetriever/retrievalAugmentor
* @deprecated Use AiServices with contentRetriever or retrievalAugmentor instead
*/
@Deprecated
public class ConversationalRetrievalChain implements Chain<String, String> {
/**
* Create builder for configuring chain
* @return Builder instance
*/
public static Builder builder();
/**
* Execute chain with query
* @param query Query to process
* @return Response from chat model with retrieved context
*/
public String execute(String query);
}Thread Safety:
Common Pitfalls:
Edge Cases:
Exception Handling:
Performance Notes:
Retrieval Process:
Replacement with AiServices:
// OLD (Deprecated):
ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder()
.chatModel(chatModel)
.contentRetriever(contentRetriever)
.build();
// NEW (Recommended):
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.contentRetriever(contentRetriever)
.build();Related APIs:
AiServices - Modern replacement with better RAG supportContentRetriever - Basic retrieval interfaceRetrievalAugmentor - Advanced retrieval with re-rankingChatMemory - Conversation history managementEmbeddingStore - Vector storage for retrieval/**
* Builder for ConversationalRetrievalChain
*/
public class Builder {
/**
* Set the chat model
* @param chatModel Chat model to use
* @return Builder instance
*/
public Builder chatModel(ChatModel chatModel);
/**
* Set the chat memory
* @param chatMemory Chat memory instance
* @return Builder instance
*/
public Builder chatMemory(ChatMemory chatMemory);
/**
* Set content retriever for RAG
* @param contentRetriever Content retriever instance
* @return Builder instance
*/
public Builder contentRetriever(ContentRetriever contentRetriever);
/**
* Set retrieval augmentor for RAG
* @param retrievalAugmentor Retrieval augmentor instance
* @return Builder instance
*/
public Builder retrievalAugmentor(RetrievalAugmentor retrievalAugmentor);
/**
* Build the chain
* @return ConversationalRetrievalChain instance
*/
public ConversationalRetrievalChain build();
}Thread Safety:
Common Pitfalls:
Exception Handling:
Configuration Priority:
Related APIs:
ConversationalRetrievalChain - Built chain instanceContentRetriever - Simple retrieval optionRetrievalAugmentor - Advanced retrieval option with re-rankingimport dev.langchain4j.chain.ConversationalChain;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
// Deprecated approach
ConversationalChain chain = ConversationalChain.builder()
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String response1 = chain.execute("Hello, my name is Alice");
String response2 = chain.execute("What is my name?");
// Response: "Your name is Alice"Recommended alternative using AiServices:
import dev.langchain4j.service.AiServices;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String response1 = assistant.chat("Hello, my name is Alice");
String response2 = assistant.chat("What is my name?");
// Response: "Your name is Alice"Why AiServices is Better:
import dev.langchain4j.chain.ConversationalRetrievalChain;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
// Deprecated approach
ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder()
.chatModel(chatModel)
.contentRetriever(contentRetriever)
.build();
String response = chain.execute("What does the documentation say about configuration?");Recommended alternative using AiServices:
import dev.langchain4j.service.AiServices;
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.contentRetriever(contentRetriever)
.build();
String response = assistant.chat("What does the documentation say about configuration?");Additional AiServices Benefits for RAG:
import dev.langchain4j.chain.Chain;
// Deprecated approach - custom chain
class SummarizationChain implements Chain<String, String> {
private final ChatModel chatModel;
public SummarizationChain(ChatModel chatModel) {
this.chatModel = chatModel;
}
@Override
public String execute(String longText) {
String prompt = "Summarize the following text:\n\n" + longText;
return chatModel.generate(prompt);
}
}
Chain<String, String> chain = new SummarizationChain(chatModel);
String summary = chain.execute(longText);Recommended alternative using AiServices:
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;
interface Summarizer {
@UserMessage("Summarize the following text:\n\n{{text}}")
String summarize(@V("text") String text);
}
Summarizer summarizer = AiServices.create(Summarizer.class, chatModel);
String summary = summarizer.summarize(longText);Why This is Better:
import dev.langchain4j.chain.Chain;
// Deprecated approach - manual chaining
Chain<String, String> extractChain = input -> {
// Extract key information
return chatModel.generate("Extract key facts from: " + input);
};
Chain<String, String> summarizeChain = facts -> {
// Summarize facts
return chatModel.generate("Summarize these facts: " + facts);
};
String input = "Long document text...";
String facts = extractChain.execute(input);
String summary = summarizeChain.execute(facts);Recommended alternative using AiServices:
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;
interface DocumentProcessor {
@UserMessage("Extract key facts from the following text:\n\n{{text}}")
String extractFacts(@V("text") String text);
@UserMessage("Summarize these facts:\n\n{{facts}}")
String summarize(@V("facts") String facts);
}
DocumentProcessor processor = AiServices.create(DocumentProcessor.class, chatModel);
String input = "Long document text...";
String facts = processor.extractFacts(input);
String summary = processor.summarize(facts);Benefits of Method-Based Approach:
Chain, ConversationalChain, ConversationalRetrievalChainBefore (Deprecated):
ConversationalChain chain = ConversationalChain.builder()
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String response = chain.execute("Hello");After (Recommended):
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String response = assistant.chat("Hello");Migration Checklist:
Before (Deprecated):
ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder()
.chatModel(chatModel)
.contentRetriever(contentRetriever)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String response = chain.execute("Query about documentation");After (Recommended):
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.contentRetriever(contentRetriever)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String response = assistant.chat("Query about documentation");Migration Checklist:
Enhanced RAG with Result Type:
interface Assistant {
Result<String> chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.contentRetriever(contentRetriever)
.build();
Result<String> result = assistant.chat("Query");
String response = result.content();
List<Content> sources = result.sources(); // Access retrieved content!
TokenUsage tokenUsage = result.tokenUsage();Before (Deprecated):
class MyCustomChain implements Chain<InputType, OutputType> {
private final ChatModel chatModel;
private final SomeService service;
@Override
public OutputType execute(InputType input) {
// Custom logic
String prompt = buildPrompt(input);
String response = chatModel.generate(prompt);
return parseResponse(response);
}
}After (Recommended):
interface MyService {
@UserMessage("{{customPrompt}}")
@SystemMessage("You are a helpful assistant specialized in...")
OutputType process(@V("customPrompt") String prompt);
}
// Use OutputParser for custom type conversion
MyService service = AiServices.builder(MyService.class)
.chatModel(chatModel)
.build();
// Custom logic in application code
OutputType result = service.process(buildPrompt(input));For Complex Parsing:
interface MyService {
@UserMessage("Process this input: {{input}}")
String processRaw(@V("input") InputType input);
}
// Custom parser implementation
class MyOutputParser implements OutputParser<OutputType> {
@Override
public OutputType parse(String text) {
// Custom parsing logic
return parseResponse(text);
}
@Override
public FormatInstructions formatInstructions() {
return new FormatInstructions("Return data in format...");
}
}
MyService service = AiServices.builder(MyService.class)
.chatModel(chatModel)
.outputParser(new MyOutputParser())
.build();Type Safety: Define methods with specific return types (POJOs, primitives, collections)
Annotations: Use @SystemMessage, @UserMessage, @V for templates
Tools: Easily integrate function calling
Streaming: Support for streaming responses via TokenStream
Moderation: Built-in content moderation with @Moderate
Memory Management: Per-user memory with @MemoryId
Result Metadata: Access token usage, sources, tool executions via Result<T>
Flexibility: Create any interface you need without implementing Chain
Better Testing: Easier to mock interfaces for testing
Modern API: More intuitive and aligned with current best practices
Before:
Chain<String, String> step1 = input -> chatModel.generate("Step 1: " + input);
Chain<String, String> step2 = input -> chatModel.generate("Step 2: " + input);
Chain<String, String> step3 = input -> chatModel.generate("Step 3: " + input);
String result = step3.execute(step2.execute(step1.execute(originalInput)));After:
interface Pipeline {
@UserMessage("Step 1: {{input}}")
String step1(@V("input") String input);
@UserMessage("Step 2: {{input}}")
String step2(@V("input") String input);
@UserMessage("Step 3: {{input}}")
String step3(@V("input") String input);
}
Pipeline pipeline = AiServices.create(Pipeline.class, chatModel);
String result = pipeline.step3(pipeline.step2(pipeline.step1(originalInput)));Before:
class StatefulChain implements Chain<String, String> {
private final List<String> history = new ArrayList<>();
private final ChatModel chatModel;
@Override
public String execute(String input) {
history.add(input);
String context = String.join("\n", history);
return chatModel.generate("Context: " + context + "\nQuery: " + input);
}
}After:
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(100))
.build();
// Memory handled automatically!
String response = assistant.chat(message);Chains were the original API for building LLM-powered applications in LangChain4j. However, AiServices provides a superior approach:
@Test
void testConversationalChain() {
// Use test double for ChatModel
ChatModel mockModel = mock(ChatModel.class);
when(mockModel.generate(anyString())).thenReturn("Mocked response");
ConversationalChain chain = ConversationalChain.builder()
.chatModel(mockModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String response = chain.execute("Hello");
assertEquals("Mocked response", response);
verify(mockModel, times(1)).generate(anyString());
}@Test
void testAiService() {
ChatModel mockModel = mock(ChatModel.class);
when(mockModel.generate(any())).thenReturn(Response.from(
AiMessage.from("Mocked response")
));
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(mockModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
String response = assistant.chat("Hello");
assertEquals("Mocked response", response);
verify(mockModel, times(1)).generate(any());
}Testing Benefits of AiServices:
// Each execute() call:
// 1. String concatenation for prompt
// 2. Memory lookup (if ConversationalChain)
// 3. Retrieval (if ConversationalRetrievalChain)
// 4. ChatModel API call
// 5. String return
ConversationalChain chain = ConversationalChain.builder()
.chatModel(chatModel)
.build();
long start = System.currentTimeMillis();
String response = chain.execute("Query");
long duration = System.currentTimeMillis() - start;
// Duration depends primarily on ChatModel latency// Similar performance to chains but with benefits:
// - Type parsing adds minimal overhead
// - Streaming can improve perceived performance
// - Caching opportunities for prompts
// - Better connection pooling in some implementations
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.build();
long start = System.currentTimeMillis();
String response = assistant.chat("Query");
long duration = System.currentTimeMillis() - start;
// Similar duration to chain, but with more featuresPerformance Verdict:
try {
ConversationalChain chain = ConversationalChain.builder()
.chatModel(chatModel)
.build();
String response = chain.execute("Query");
} catch (RuntimeException e) {
// Must catch generic RuntimeException
// No way to distinguish error types
// No access to partial results
// Memory state may be corrupted
}try {
interface Assistant {
String chat(String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.build();
String response = assistant.chat("Query");
} catch (Exception e) {
// Can catch specific exceptions:
// - ToolExecutionException
// - ModerationException
// - etc.
// Better error context available
}AiServices - Primary replacement for all chain functionalityChatMemory - Conversation state management (compatible with both)ChatModel - Underlying model interface (compatible with both)ContentRetriever - RAG retrieval (compatible with both)RetrievalAugmentor - Advanced RAG (compatible with both)@SystemMessage - System prompt annotation@UserMessage - User message template annotation@Tool - Function calling integrationTokenStream - Streaming response supportResult<T> - Rich result with metadata@MemoryId - Per-user memory management@Moderate - Content moderationIf you need help migrating from Chains to AiServices:
For all new development, use AiServices instead of Chains. The migration effort is minimal and the benefits are substantial.
Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j@1.11.0