LangChain4j Agentic Framework provides a comprehensive Java library for building multi-agent AI systems with support for workflow orchestration, supervisor agents, planning-based execution, declarative configuration, agent-to-agent communication, and human-in-the-loop workflows.
Execute agents one after another in a defined sequence, with output from each agent available to subsequent agents via AgenticScope.
→ Declarative @SequenceAgent Reference
import dev.langchain4j.agentic.AgenticServices;
UntypedAgent pipeline = AgenticServices.sequenceBuilder()
.subAgents(fetchAgent, processAgent, validateAgent)
.name("data-pipeline")
.build();
Object result = pipeline.invoke("Process this data");Sequential workflows are ideal for:
/**
* Create untyped sequential agent builder
* @return SequentialAgentService for building sequential workflows
*/
static SequentialAgentService<UntypedAgent> sequenceBuilder();
/**
* Create typed sequential agent builder
* @param agentServiceClass Agent interface class
* @return SequentialAgentService for building typed sequential workflows
*/
static <T> SequentialAgentService<T> sequenceBuilder(Class<T> agentServiceClass);Usage Examples:
import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.agentic.UntypedAgent;
// Create untyped sequential workflow
UntypedAgent pipeline = AgenticServices.sequenceBuilder()
.subAgents(fetchAgent, processAgent, validateAgent)
.name("data-pipeline")
.description("Sequential data processing pipeline")
.build();
Object result = pipeline.invoke("Process this data");
// Create typed sequential workflow
interface DataPipeline {
String process(String input);
}
DataPipeline typedPipeline = AgenticServices.sequenceBuilder(DataPipeline.class)
.subAgents(fetchAgent, processAgent, validateAgent)
.build();
String result = typedPipeline.process("Process this");Complete configuration interface for sequential workflows.
/**
* Service for sequential agent workflows
* Extends AgenticService with all base configuration methods
*/
interface SequentialAgentService<T> extends AgenticService<SequentialAgentService<T>, T> {
// Inherits methods from AgenticService:
/**
* Build and return the configured workflow
*/
T build();
/**
* Set sub-agents to execute sequentially
*/
SequentialAgentService<T> subAgents(Object... agents);
/**
* Set sub-agents as executor list
*/
SequentialAgentService<T> subAgents(List<AgentExecutor> agentExecutors);
/**
* Set pre-invocation callback
*/
SequentialAgentService<T> beforeCall(Consumer<AgenticScope> beforeCall);
/**
* Set workflow name
*/
SequentialAgentService<T> name(String name);
/**
* Set workflow description
*/
SequentialAgentService<T> description(String description);
/**
* Set output key for AgenticScope
*/
SequentialAgentService<T> outputKey(String outputKey);
/**
* Set custom output function
*/
SequentialAgentService<T> output(Function<AgenticScope, Object> output);
/**
* Set error handler
*/
SequentialAgentService<T> errorHandler(Function<ErrorContext, ErrorRecoveryResult> errorHandler);
/**
* Add agent listener
*/
SequentialAgentService<T> listener(AgentListener listener);
}Usage Examples:
UntypedAgent sequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.name("multi-step-processor")
.description("Processes data through three sequential steps")
.outputKey("final_result")
.beforeCall(scope -> {
scope.writeState("timestamp", System.currentTimeMillis());
scope.writeState("workflow_id", UUID.randomUUID().toString());
})
.output(scope -> {
// Aggregate results from all stages
return Map.of(
"stage1", scope.readState("agent1_output"),
"stage2", scope.readState("agent2_output"),
"stage3", scope.readState("agent3_output"),
"workflow_id", scope.readState("workflow_id")
);
})
.errorHandler(errorContext -> {
System.err.println("Error: " + errorContext.error().getMessage());
return new ErrorRecoveryResult("fallback", false);
})
.listener(myListener)
.build();Understanding the sequential workflow execution model:
// Agent definitions
UntypedAgent fetchAgent = AgenticServices.agentBuilder()
.chatModel(chatModel)
.name("fetcher")
.description("Fetches raw data from source")
.outputKey("raw_data")
.build();
UntypedAgent processAgent = AgenticServices.agentBuilder()
.chatModel(chatModel)
.name("processor")
.description("Processes and transforms data")
.outputKey("processed_data")
.context(scope -> {
String rawData = (String) scope.readState("raw_data");
return "Process this data: " + rawData;
})
.build();
UntypedAgent validateAgent = AgenticServices.agentBuilder()
.chatModel(chatModel)
.name("validator")
.description("Validates processed data")
.context(scope -> {
String processedData = (String) scope.readState("processed_data");
String rawData = (String) scope.readState("raw_data");
return "Validate this processed data: " + processedData +
" (original: " + rawData + ")";
})
.build();
// Sequential workflow
UntypedAgent pipeline = AgenticServices.sequenceBuilder()
.subAgents(fetchAgent, processAgent, validateAgent)
.name("etl-pipeline")
.build();
// Execute - data flows through each stage
Object result = pipeline.invoke("Fetch data from source X");Define sequential workflows using annotations.
Usage Examples:
interface DataProcessingSystem {
// Sequential workflow declaration
@SequenceAgent(
name = "etl-pipeline",
description = "Extract, transform, and load data pipeline",
outputKey = "processed_data",
subAgents = {Extractor.class, Transformer.class, Loader.class}
)
String runETL(String source);
}
interface Extractor {
@Agent(name = "extractor", outputKey = "raw_data")
String extract(String source);
}
interface Transformer {
@Agent(name = "transformer", outputKey = "transformed_data")
String transform(AgenticScope scope) {
String rawData = (String) scope.readState("raw_data");
// Transform logic
return transformData(rawData);
}
}
interface Loader {
@Agent(name = "loader", outputKey = "load_status")
String load(AgenticScope scope) {
String data = (String) scope.readState("transformed_data");
// Load logic
return storeData(data);
}
}
DataProcessingSystem system = AgenticServices.createAgenticSystem(
DataProcessingSystem.class,
chatModel
);
String result = system.runETL("database://source");Execute logic before the sequential workflow starts.
/**
* Set pre-invocation callback
* @param beforeCall Consumer receiving AgenticScope
* @return Builder for chaining
*/
SequentialAgentService<T> beforeCall(Consumer<AgenticScope> beforeCall);Usage Examples:
UntypedAgent sequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.beforeCall(scope -> {
// Initialize state
scope.writeState("start_time", System.currentTimeMillis());
scope.writeState("workflow_id", UUID.randomUUID().toString());
scope.writeState("environment", System.getenv("ENV"));
// Log workflow start
System.out.println("Starting sequential workflow: " +
scope.readState("workflow_id"));
// Load configuration
Config config = loadConfiguration();
scope.writeState("config", config);
})
.build();Customize the final output based on AgenticScope state.
/**
* Set custom output function
* @param output Function receiving AgenticScope and returning final output
* @return Builder for chaining
*/
SequentialAgentService<T> output(Function<AgenticScope, Object> output);Usage Examples:
UntypedAgent sequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.output(scope -> {
// Aggregate results from all agents
Object result1 = scope.readState("agent1_output");
Object result2 = scope.readState("agent2_output");
Object result3 = scope.readState("agent3_output");
long startTime = (Long) scope.readState("start_time");
long duration = System.currentTimeMillis() - startTime;
return Map.of(
"results", List.of(result1, result2, result3),
"duration_ms", duration,
"workflow_id", scope.readState("workflow_id"),
"timestamp", System.currentTimeMillis()
);
})
.build();
// Return detailed report
UntypedAgent reportingSequence = AgenticServices.sequenceBuilder()
.subAgents(dataAgent, analysisAgent, summaryAgent)
.output(scope -> {
return new Report(
scope.readState("data", String.class),
scope.readState("analysis", Analysis.class),
scope.readState("summary", String.class),
scope.agentInvocations()
);
})
.build();Handle errors during sequential execution.
/**
* Set error handler for sequential workflow
* @param errorHandler Function receiving ErrorContext and returning ErrorRecoveryResult
* @return Builder for chaining
*/
SequentialAgentService<T> errorHandler(Function<ErrorContext, ErrorRecoveryResult> errorHandler);Usage Examples:
UntypedAgent sequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.errorHandler(errorContext -> {
Throwable error = errorContext.error();
AgenticScope scope = errorContext.agenticScope();
// Log error with context
System.err.println("Sequential workflow error: " + error.getMessage());
System.err.println("Failed at stage: " +
scope.agentInvocations().size());
// Decide whether to continue or stop
if (error instanceof RecoverableException) {
// Store error and continue
scope.writeState("error_occurred", true);
scope.writeState("error_message", error.getMessage());
return new ErrorRecoveryResult("recovered", true);
} else if (error instanceof ValidationException) {
// Stop execution, return error result
return new ErrorRecoveryResult(
Map.of("error", error.getMessage(), "valid", false),
false
);
} else {
// Fatal error, stop execution
return new ErrorRecoveryResult(null, false);
}
})
.build();
// Error handling with retryThe error handling with retry
UntypedAgent resilientSequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.beforeCall(scope -> {
scope.writeState("retry_count", 0);
})
.errorHandler(errorContext -> {
AgenticScope scope = errorContext.agenticScope();
int retryCount = scope.readState("retry_count", 0);
if (retryCount < 3) {
scope.writeState("retry_count", retryCount + 1);
System.err.println("Retrying (attempt " + (retryCount + 1) + ")");
return new ErrorRecoveryResult("retry", true);
} else {
return new ErrorRecoveryResult("max_retries_exceeded", false);
}
})
.build();Extract, Transform, Load data processing.
UntypedAgent etlPipeline = AgenticServices.sequenceBuilder()
.subAgents(extractor, transformer, validator, loader)
.name("etl-pipeline")
.beforeCall(scope -> {
scope.writeState("pipeline_id", UUID.randomUUID().toString());
scope.writeState("start_time", System.currentTimeMillis());
})
.output(scope -> {
long duration = System.currentTimeMillis() -
(Long) scope.readState("start_time");
return Map.of(
"status", "completed",
"records_processed", scope.readState("record_count"),
"duration_ms", duration,
"pipeline_id", scope.readState("pipeline_id")
);
})
.listener(pipelineMonitor)
.build();Validate data through multiple validation stages.
UntypedAgent validationPipeline = AgenticServices.sequenceBuilder()
.subAgents(
schemaValidator,
businessRuleValidator,
securityValidator,
complianceValidator
)
.name("multi-stage-validator")
.output(scope -> {
boolean allValid = true;
List<String> errors = new ArrayList<>();
for (String key : List.of("schema", "business", "security", "compliance")) {
Boolean valid = (Boolean) scope.readState(key + "_valid");
if (valid == null || !valid) {
allValid = false;
String error = (String) scope.readState(key + "_error");
if (error != null) errors.add(error);
}
}
return new ValidationResult(allValid, errors);
})
.build();Generate, refine, and review content.
UntypedAgent contentPipeline = AgenticServices.sequenceBuilder()
.subAgents(
draftGenerator,
contentRefiner,
factChecker,
styleEditor,
finalReviewer
)
.name("content-pipeline")
.beforeCall(scope -> {
scope.writeState("content_version", 1);
scope.writeState("revision_history", new ArrayList<String>());
})
.output(scope -> {
String finalContent = (String) scope.readState("final_content");
List<String> history = (List<String>) scope.readState("revision_history");
return new ContentResult(
finalContent,
history,
scope.agentInvocations().size()
);
})
.build();Research, analyze, synthesize findings.
UntypedAgent researchWorkflow = AgenticServices.sequenceBuilder()
.subAgents(
topicResearcher,
dataCollector,
statisticalAnalyzer,
insightGenerator,
reportWriter
)
.name("research-workflow")
.output(scope -> {
return new ResearchReport(
scope.readState("research_data", List.class),
scope.readState("analysis", Analysis.class),
scope.readState("insights", List.class),
scope.readState("report", String.class)
);
})
.listener(researchTracker)
.build();Multi-level approval workflow.
UntypedAgent approvalChain = AgenticServices.sequenceBuilder()
.subAgents(
managerApproval,
directorApproval,
vpApproval,
ceoApproval
)
.name("approval-chain")
.errorHandler(errorContext -> {
AgenticScope scope = errorContext.agenticScope();
// If any approval fails, stop the chain
if (errorContext.error() instanceof ApprovalDeniedException) {
scope.writeState("approval_status", "DENIED");
scope.writeState("denied_by", errorContext.error().getMessage());
return new ErrorRecoveryResult("denied", false);
}
return new ErrorRecoveryResult(null, false);
})
.output(scope -> {
String status = (String) scope.readState("approval_status");
if (status == null) status = "APPROVED";
return new ApprovalResult(
status,
scope.agentInvocations().stream()
.map(inv -> inv.agentName() + ": " + inv.output())
.collect(Collectors.toList())
);
})
.build();Always specify outputKey for agents that produce data needed by subsequent agents.
UntypedAgent agent1 = AgenticServices.agentBuilder()
.chatModel(chatModel)
.name("fetcher")
.outputKey("fetched_data") // Make data available to next agents
.build();
UntypedAgent agent2 = AgenticServices.agentBuilder()
.chatModel(chatModel)
.name("processor")
.outputKey("processed_data")
.context(scope -> {
String data = (String) scope.readState("fetched_data");
return "Process this: " + data;
})
.build();Use beforeCall to initialize shared state.
UntypedAgent sequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.beforeCall(scope -> {
// Initialize configuration
scope.writeState("config", loadConfig());
scope.writeState("start_time", System.currentTimeMillis());
scope.writeState("environment", System.getenv("ENV"));
})
.build();Provide error handlers for robust workflows.
UntypedAgent sequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.errorHandler(errorContext -> {
// Log error
logError(errorContext.error());
// Decide recovery strategy
if (isRecoverable(errorContext.error())) {
return new ErrorRecoveryResult("fallback_value", true);
} else {
return new ErrorRecoveryResult(null, false);
}
})
.build();Add listeners to track execution.
AgentListener monitor = new AgentListener() {
@Override
public void afterAgentInvocation(AgentResponse response) {
System.out.println(response.agentName() + " completed in " +
response.duration().toMillis() + "ms");
}
};
UntypedAgent sequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.listener(monitor)
.build();Provide custom output functions for clear results.
UntypedAgent sequence = AgenticServices.sequenceBuilder()
.subAgents(agent1, agent2, agent3)
.output(scope -> {
return Map.of(
"final_result", scope.readState("agent3_output"),
"intermediate_results", List.of(
scope.readState("agent1_output"),
scope.readState("agent2_output")
),
"execution_summary", Map.of(
"total_agents", 3,
"invocations", scope.agentInvocations().size()
)
);
})
.build();Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-agentic@1.11.0docs
declarative
A2AClientAgent
ActivationCondition
Agent
ConditionalAgent
ErrorHandler
ExitCondition
HumanInTheLoop
HumanInTheLoopResponseSupplier
LoopAgent
LoopCounter
Output
ParallelAgent
ParallelExecutor
PlannerAgent
SequenceAgent
SupervisorAgent
SupervisorRequest
quick-start
workflows