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.
Custom logic-based orchestration where you define the planning algorithm that coordinates agent execution, plus pre-built planning patterns (P2P and GOAP).
Planning-based agents enable:
static PlannerBasedService<UntypedAgent> plannerBuilder();
static <T> PlannerBasedService<T> plannerBuilder(Class<T> agentServiceClass);Quick Start:
UntypedAgent agent = AgenticServices.plannerBuilder()
.planner(() -> new MyCustomPlanner())
.subAgents(agent1, agent2, agent3)
.build();interface Planner {
default void init(InitPlanningContext context) {
// Optional initialization
}
default Action firstAction(PlanningContext context) {
return nextAction(context);
}
Action nextAction(PlanningContext context);
default AgenticSystemTopology topology() {
return AgenticSystemTopology.SEQUENCE;
}
default boolean terminated() {
return false;
}
// Helper methods
default Action noOp() { return new NoOpAction(); }
default Action call(AgentInstance... agents) { return new AgentCallAction(List.of(agents)); }
default Action done() { return new DoneAction(); }
default Action done(Object result) { return new DoneWithResultAction(result); }
}class AgentCallAction extends Action {
public List<AgentInstance> agents();
}
class DoneAction extends Action {
// Signals completion
}
class DoneWithResultAction extends Action {
public Object result();
}
class NoOpAction extends Action {
// No operation
}interface PlanningContext {
AgenticScope agenticScope();
List<AgentInstance> agents();
Action lastAction();
List<Object> lastActionResults();
}class CustomPlanner implements Planner {
private int stepCount = 0;
@Override
public Action nextAction(PlanningContext context) {
stepCount++;
AgenticScope scope = context.agenticScope();
if (stepCount == 1) {
// First step: fetch data
AgentInstance fetcher = findAgent(context.agents(), "fetcher");
return call(fetcher);
} else if (stepCount == 2) {
// Second step: validate
Object data = scope.readState("fetched_data");
if (isValid(data)) {
AgentInstance processor = findAgent(context.agents(), "processor");
return call(processor);
} else {
return done("Invalid data");
}
} else if (stepCount == 3) {
return done(scope.readState("processed_data"));
}
return done();
}
@Override
public boolean terminated() {
return stepCount >= 3;
}
private AgentInstance findAgent(List<AgentInstance> agents, String name) {
return agents.stream()
.filter(agent -> agent.name().equals(name))
.findFirst()
.orElseThrow();
}
}Automatic planning that extracts required variables from prompts and activates agents dynamically.
class P2PPlanner implements Planner {
public P2PPlanner();
public P2PPlanner(int maxAgentsInvocations);
public P2PPlanner(Predicate<AgenticScope> exitCondition);
public P2PPlanner(int maxInvocations, Predicate<AgenticScope> exitCondition);
public P2PPlanner(int maxInvocations, BiPredicate<AgenticScope, Integer> exitCondition);
public P2PPlanner(ChatModel chatModel, int maxInvocations, Predicate<AgenticScope> exitCondition);
}Usage:
import dev.langchain4j.agentic.patterns.p2p.P2PPlanner;
// Simple P2P planner
UntypedAgent agent = AgenticServices.plannerBuilder()
.planner(P2PPlanner::new)
.subAgents(dataFetcher, dataProcessor, summaryGenerator)
.build();
// With max invocations
UntypedAgent limited = AgenticServices.plannerBuilder()
.planner(() -> new P2PPlanner(15))
.subAgents(agent1, agent2, agent3)
.build();
// With exit condition
UntypedAgent conditional = AgenticServices.plannerBuilder()
.planner(() -> new P2PPlanner(
20,
scope -> scope.hasState("completed") || scope.hasState("error")
))
.subAgents(agent1, agent2, agent3)
.build();How P2P Works:
Best for:
Dependency graph-based planning that automatically determines optimal execution path.
class GoalOrientedPlanner implements Planner {
public GoalOrientedPlanner();
}Usage:
import dev.langchain4j.agentic.patterns.goap.GoalOrientedPlanner;
UntypedAgent agent = AgenticServices.plannerBuilder()
.planner(GoalOrientedPlanner::new)
.subAgents(
dataFetcher, // Produces: raw_data
dataValidator, // Requires: raw_data, Produces: validated_data
dataTransformer, // Requires: validated_data, Produces: transformed_data
reportGenerator // Requires: transformed_data, Produces: report
)
.build();How GOAP Works:
Best for:
| Feature | P2P Planner | GOAP Planner | Custom Planner |
|---|---|---|---|
| Planning Approach | LLM-based | Graph analysis | Your logic |
| Configuration | ChatModel optional | None needed | Full control |
| Exit Conditions | Customizable | Goal-based | You define |
| Best For | Natural language | Data pipelines | Business logic |
class MultiPhasePlanner implements Planner {
private enum Phase { GATHER, ANALYZE, DECIDE, EXECUTE }
private Phase currentPhase = Phase.GATHER;
private int retryCount = 0;
@Override
public Action nextAction(PlanningContext context) {
AgenticScope scope = context.agenticScope();
List<AgentInstance> agents = context.agents();
switch (currentPhase) {
case GATHER:
if (!scope.hasState("data_gathered")) {
return call(findAgent(agents, "gatherer"));
}
currentPhase = Phase.ANALYZE;
return noOp();
case ANALYZE:
if (!scope.hasState("analysis_complete")) {
return call(findAgent(agents, "analyzer"));
}
currentPhase = Phase.DECIDE;
return noOp();
case DECIDE:
Boolean shouldProceed = scope.readState("should_proceed", Boolean.class);
if (shouldProceed == null) {
return call(findAgent(agents, "decider"));
} else if (shouldProceed) {
currentPhase = Phase.EXECUTE;
return noOp();
} else {
return done("Decision was to not proceed");
}
case EXECUTE:
if (!scope.hasState("execution_result")) {
return call(findAgent(agents, "executor"));
} else if (scope.hasState("execution_error") && retryCount < 3) {
retryCount++;
scope.writeState("execution_result", null);
return call(findAgent(agents, "executor"));
}
return done(scope.readState("execution_result"));
default:
return done();
}
}
@Override
public boolean terminated() {
return currentPhase == Phase.EXECUTE && retryCount >= 3;
}
private AgentInstance findAgent(List<AgentInstance> agents, String name) {
return agents.stream()
.filter(a -> a.name().equals(name))
.findFirst()
.orElseThrow();
}
}Example:
interface DataProcessingSystem {
@PlannerAgent(
name = "data-orchestrator",
subAgents = {Fetcher.class, Validator.class, Processor.class, Reporter.class}
)
String processData(String source);
@PlannerSupplier
default Planner planner() {
return new CustomDataPlanner();
}
}
DataProcessingSystem system = AgenticServices.createAgenticSystem(
DataProcessingSystem.class,
chatModel
);
String result = system.processData("database://prod");Access agent metadata in your planner:
interface AgentInstance {
Class<?> type();
String name();
String agentId();
String description();
Class<?> outputType();
String outputKey();
boolean async();
List<AgentArgument> arguments();
AgentInstance parent();
List<AgentInstance> subagents();
boolean leaf();
AgenticSystemTopology topology();
}enum AgenticSystemTopology {
SEQUENCE, // Sequential execution
PARALLEL, // Parallel execution
LOOP, // Loop execution
ROUTER, // Conditional routing
STAR, // Supervisor topology
AI_AGENT, // AI-powered agent
NON_AI_AGENT, // Non-AI agent
HUMAN_IN_THE_LOOP // Human interaction
}Control execution topology in your planner:
class ParallelPlanner implements Planner {
@Override
public Action nextAction(PlanningContext context) {
// Call multiple agents
return call(agents.get(0), agents.get(1), agents.get(2));
}
@Override
public AgenticSystemTopology topology() {
return AgenticSystemTopology.PARALLEL;
}
}Use P2P Planner when:
Use GOAP Planner when:
Use Custom Planner when:
Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-agenticdocs
declarative
A2AClientAgent
ActivationCondition
Agent
ConditionalAgent
ErrorHandler
ExitCondition
HumanInTheLoop
HumanInTheLoopResponseSupplier
LoopAgent
LoopCounter
Output
ParallelAgent
ParallelExecutor
PlannerAgent
SequenceAgent
SupervisorAgent
SupervisorRequest
quick-start
workflows