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.
Type-safe keys for accessing AgenticScope state with compile-time type checking.
TypedKey enables:
interface TypedKey<T> {
String name();
}interface UserName extends TypedKey<String> {
@Override
default String name() {
return "UserName";
}
}
interface UserAge extends TypedKey<Integer> {
@Override
default String name() {
return "UserAge";
}
}
interface UserProfile extends TypedKey<Profile> {
@Override
default String name() {
return "UserProfile";
}
}class K {
static <T> Class<? extends TypedKey<T>> key(String name);
}Example:
import dev.langchain4j.agentic.declarative.K;
Class<? extends TypedKey<Integer>> countKey = K.key("count");
Class<? extends TypedKey<String>> resultKey = K.key("result");interface TypedAgent {
@Agent(
name = "counter",
typedOutputKey = CountKey.class
)
Integer count(String input);
}
interface CountKey extends TypedKey<Integer> {
@Override
default String name() {
return "count";
}
}interface TypedWorkflow {
@SequenceAgent(
name = "typed-pipeline",
typedOutputKey = ResultKey.class,
subAgents = {Processor1.class, Processor2.class}
)
String process(String input);
}
interface ResultKey extends TypedKey<String> {
@Override
default String name() {
return "final_result";
}
}interface UserDataKey extends TypedKey<UserData> {
@Override
default String name() {
return "user_data";
}
}
// In agent method
@Agent(name = "processor")
String process(AgenticScope scope) {
// Type-safe read
UserData data = scope.readState(UserDataKey.class);
// No casting needed!
String name = data.getName();
int age = data.getAge();
return "Processed: " + name;
}interface ScoreKey extends TypedKey<Double> {
@Override
default String name() {
return "score";
}
}
@Agent(name = "evaluator")
String evaluate(AgenticScope scope) {
// Read with default value
Double score = scope.readState(ScoreKey.class, 0.0);
return score >= 0.8 ? "Pass" : "Fail";
}Example:
interface UserName extends TypedKey<String> {
@Override
default String name() {
return "UserName";
}
}
interface UserAge extends TypedKey<Integer> {
@Override
default String name() {
return "UserAge";
}
}
interface PersonalizedAgent {
// Parameters injected from AgenticScope
@Agent(name = "greeter")
String greet(@K(UserName.class) String name, @K(UserAge.class) int age);
}
// Usage
PersonalizedAgent agent = AgenticServices.createAgenticSystem(
PersonalizedAgent.class,
chatModel
);
// Set state
AgenticScope scope = new AgenticScope();
scope.writeState(new UserName(), "Alice");
scope.writeState(new UserAge(), 30);
// Parameters automatically injected
String greeting = agent.greet("", 0); // Actual values from scopepublic final class StateKeys {
private StateKeys() {}
// User keys
public interface UserId extends TypedKey<String> {
@Override
default String name() { return "user_id"; }
}
public interface UserProfile extends TypedKey<Profile> {
@Override
default String name() { return "user_profile"; }
}
// Processing keys
public interface RawData extends TypedKey<String> {
@Override
default String name() { return "raw_data"; }
}
public interface ProcessedData extends TypedKey<ProcessedResult> {
@Override
default String name() { return "processed_data"; }
}
// Result keys
public interface FinalResult extends TypedKey<String> {
@Override
default String name() { return "final_result"; }
}
public interface Metadata extends TypedKey<Map<String, Object>> {
@Override
default String name() { return "metadata"; }
}
}
// Usage
@Agent(name = "processor", typedOutputKey = StateKeys.ProcessedData.class)
ProcessedResult process(AgenticScope scope) {
String raw = scope.readState(StateKeys.RawData.class);
return processRawData(raw);
}public final class OrderKeys {
public interface OrderId extends TypedKey<String> {
@Override default String name() { return "order_id"; }
}
public interface OrderItems extends TypedKey<List<Item>> {
@Override default String name() { return "order_items"; }
}
public interface OrderTotal extends TypedKey<BigDecimal> {
@Override default String name() { return "order_total"; }
}
}
public final class CustomerKeys {
public interface CustomerId extends TypedKey<String> {
@Override default String name() { return "customer_id"; }
}
public interface CustomerTier extends TypedKey<Tier> {
@Override default String name() { return "customer_tier"; }
}
}interface TaskList extends TypedKey<List<Task>> {
@Override
default String name() {
return "task_list";
}
}
@Agent(name = "task-processor", typedOutputKey = TaskList.class)
List<Task> processTasks(AgenticScope scope) {
List<Task> tasks = scope.readState(TaskList.class, Collections.emptyList());
return tasks.stream()
.filter(Task::isActive)
.collect(Collectors.toList());
}interface ConfigMap extends TypedKey<Map<String, String>> {
@Override
default String name() {
return "config";
}
}
@Agent(name = "configurator")
String configure(AgenticScope scope) {
Map<String, String> config = scope.readState(
ConfigMap.class,
Collections.emptyMap()
);
String apiUrl = config.get("api_url");
String apiKey = config.get("api_key");
return "Configured: " + apiUrl;
}interface AnalysisResult extends TypedKey<Analysis> {
@Override
default String name() {
return "analysis_result";
}
}
class Analysis {
private final String summary;
private final double confidence;
private final List<Finding> findings;
// Constructor, getters, etc.
}
@Agent(name = "analyzer", typedOutputKey = AnalysisResult.class)
Analysis analyze(AgenticScope scope) {
String data = scope.readState("raw_data", String.class);
return new Analysis(
generateSummary(data),
calculateConfidence(data),
extractFindings(data)
);
}
@Agent(name = "reporter")
String generateReport(AgenticScope scope) {
Analysis analysis = scope.readState(AnalysisResult.class);
return String.format(
"Summary: %s\nConfidence: %.2f\nFindings: %d",
analysis.getSummary(),
analysis.getConfidence(),
analysis.getFindings().size()
);
}class LoopCounter {
static int get(AgenticScope scope);
static TypedKey<Integer> key();
}Example:
interface LoopWorkflow {
@LoopAgent(maxIterations = 10, subAgents = {Worker.class})
String work(String input);
@ExitCondition
boolean shouldExit(AgenticScope scope) {
int iteration = LoopCounter.get(scope);
double quality = scope.readState("quality", 0.0);
return quality >= 0.95 || iteration >= 8;
}
}// Good
interface CustomerEmailAddress extends TypedKey<String> {
@Override default String name() { return "customer_email"; }
}
// Avoid
interface Email extends TypedKey<String> {
@Override default String name() { return "e"; }
}public final class PaymentKeys {
public interface Amount extends TypedKey<BigDecimal> {
@Override default String name() { return "payment_amount"; }
}
public interface Method extends TypedKey<PaymentMethod> {
@Override default String name() { return "payment_method"; }
}
public interface Status extends TypedKey<PaymentStatus> {
@Override default String name() { return "payment_status"; }
}
}/**
* Comprehensive user profile including preferences and history
*/
interface UserProfile extends TypedKey<Profile> {
@Override
default String name() {
return "user_profile";
}
}// Consistent pattern for all keys
interface InputData extends TypedKey<String> {
@Override default String name() { return "input_data"; }
}
interface ProcessedData extends TypedKey<ProcessedResult> {
@Override default String name() { return "processed_data"; }
}
interface ValidatedData extends TypedKey<ValidationResult> {
@Override default String name() { return "validated_data"; }
}// Define keys
public final class WorkflowKeys {
public interface InputText extends TypedKey<String> {
@Override default String name() { return "input_text"; }
}
public interface Analysis extends TypedKey<AnalysisResult> {
@Override default String name() { return "analysis"; }
}
public interface Summary extends TypedKey<String> {
@Override default String name() { return "summary"; }
}
public interface Confidence extends TypedKey<Double> {
@Override default String name() { return "confidence"; }
}
}
// Use in workflow
interface TextAnalysisWorkflow {
@SequenceAgent(
name = "analysis-workflow",
typedOutputKey = WorkflowKeys.Summary.class,
subAgents = {Analyzer.class, Summarizer.class}
)
String analyzeText(String input);
}
interface Analyzer {
@Agent(name = "analyzer", typedOutputKey = WorkflowKeys.Analysis.class)
AnalysisResult analyze(
@K(WorkflowKeys.InputText.class) String text
);
}
interface Summarizer {
@Agent(name = "summarizer", typedOutputKey = WorkflowKeys.Summary.class)
String summarize(AgenticScope scope) {
AnalysisResult analysis = scope.readState(WorkflowKeys.Analysis.class);
Double confidence = scope.readState(WorkflowKeys.Confidence.class, 0.0);
return String.format(
"Analysis: %s (Confidence: %.2f)",
analysis.getSummary(),
confidence
);
}
}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