Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
LangGraph4j
https://github.com/langgraph4j/langgraph4j
Admin
LangGraph4j is a Java library for building stateful, multi-agent applications with LLMs, inspired by
...
Tokens:
195,282
Snippets:
610
Trust Score:
7
Update:
6 months ago
Context
Skills
Chat
Benchmark
72.7
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# LangGraph4j Context Documentation LangGraph4j is a Java library for building stateful, multi-agent applications with Large Language Models (LLMs). Inspired by the Python LangGraph library, it enables developers to create complex agentic workflows using cyclical graphs where nodes represent computational steps and edges define control flow. Unlike traditional directed acyclic graphs (DAGs), LangGraph4j supports cycles, making it ideal for agent-based architectures where control flow can loop back for retries, clarifications, or iterative refinement. The library integrates seamlessly with popular Java LLM frameworks including LangChain4j and Spring AI, providing built-in agent executors (ReACT agents), checkpoint persistence for state recovery, streaming support for real-time responses, and a visual Studio interface for debugging and development. LangGraph4j manages stateful execution through a channel-based state management system, supports parallel execution of graph branches, enables subgraph composition for modularity, and offers multiple serialization strategies for persistence. With features like interruption handling, time-travel debugging, and graph visualization (PlantUML/Mermaid), it provides a complete toolkit for building production-ready AI agent applications in Java. ## Core APIs and Functions ### Building a Basic State Graph Define a state schema with channels, add nodes with actions, connect them with edges, and compile into an executable graph. ```java import org.bsc.langgraph4j.StateGraph; import org.bsc.langgraph4j.state.AgentState; import org.bsc.langgraph4j.state.Channel; import org.bsc.langgraph4j.state.Channels; import java.util.*; // Define custom state class class MyState extends AgentState { public static final String MESSAGES = "messages"; public static final Map<String, Channel<?>> SCHEMA = Map.of( MESSAGES, Channels.appender(ArrayList::new) ); public MyState(Map<String, Object> initData) { super(initData); } public List<String> messages() { return this.<List<String>>value(MESSAGES).orElse(List.of()); } } // Build and execute graph var graph = new StateGraph<>(MyState.SCHEMA, MyState::new) .addNode("process", state -> { System.out.println("Processing: " + state.messages()); return Map.of(MyState.MESSAGES, "Processed!"); }) .addEdge(StateGraph.START, "process") .addEdge("process", StateGraph.END) .compile(); // Execute synchronously Optional<MyState> result = graph.invoke( Map.of(MyState.MESSAGES, "Hello") ); result.ifPresent(s -> System.out.println("Final: " + s.messages())); // Or stream results for (var output : graph.stream(Map.of(MyState.MESSAGES, "Hello"))) { System.out.println("Step: " + output.node() + " -> " + output.state().messages()); } ``` ### Conditional Edges and Routing Route execution dynamically based on state using conditional edges with edge mappings. ```java import org.bsc.langgraph4j.action.AsyncEdgeAction; import java.util.concurrent.CompletableFuture; class RouterState extends AgentState { public static final String DECISION = "decision"; public static final String DATA = "data"; public static final Map<String, Channel<?>> SCHEMA = Map.of( DECISION, Channel.of(), DATA, Channels.appender(ArrayList::new) ); public RouterState(Map<String, Object> init) { super(init); } public String decision() { return this.<String>value(DECISION).orElse("unknown"); } } var workflow = new StateGraph<>(RouterState.SCHEMA, RouterState::new) .addNode("analyzer", state -> { boolean hasError = state.value("input", "").contains("error"); return Map.of( RouterState.DECISION, hasError ? "handle_error" : "continue", RouterState.DATA, "Analyzed" ); }) .addNode("handle_error", state -> Map.of(RouterState.DATA, "Error handled") ) .addNode("continue", state -> Map.of(RouterState.DATA, "Processing normally") ) .addEdge(StateGraph.START, "analyzer") .addConditionalEdges("analyzer", (AsyncEdgeAction<RouterState>) (state, config) -> CompletableFuture.completedFuture(state.decision()), Map.of( "handle_error", "handle_error", "continue", "continue" ) ) .addEdge("handle_error", StateGraph.END) .addEdge("continue", StateGraph.END) .compile(); var result = workflow.invoke(Map.of("input", "error occurred")); ``` ### LangChain4j AgentExecutor (ReACT Agent) Build a tool-calling agent that can reason and act using LangChain4j models and tools. ```java import org.bsc.langgraph4j.agentexecutor.AgentExecutor; import dev.langchain4j.model.ollama.OllamaChatModel; import dev.langchain4j.agent.tool.Tool; import dev.langchain4j.agent.tool.P; import static java.lang.String.format; // Define tools class SearchTools { @Tool("Search the web for information") String webSearch(@P("search query") String query) { return format("Results for '%s': Found 3 relevant articles...", query); } @Tool("Get current weather for a location") String getWeather(@P("city name") String city) { return format("Weather in %s: Sunny, 72°F", city); } } // Build and run agent var model = OllamaChatModel.builder() .modelName("qwen2.5:7b") .baseUrl("http://localhost:11434") .temperature(0.0) .build(); var agent = AgentExecutor.builder() .chatModel(model) .toolsFromObject(new SearchTools()) .build() .compile(); // Execute agent with streaming for (var step : agent.stream(Map.of( "messages", "What's the weather like in San Francisco?" ))) { System.out.println("Agent step: " + step.node()); step.state().messages().forEach(msg -> System.out.println(" " + msg.text()) ); } // Or invoke for final result var finalState = agent.invoke(Map.of( "messages", "Search for LangGraph4j documentation" )); finalState.ifPresent(state -> System.out.println("Final response: " + state.finalResponse().orElse("No response")) ); ``` ### Spring AI AgentExecutor Build tool-calling agents using Spring AI framework integration. ```java import org.bsc.langgraph4j.agentexecutor.springai.AgentExecutor; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.api.OllamaApi; import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; // Tool definitions class CalculatorTools { @Tool(description = "Add two numbers") int add(@ToolParam(description = "first number") int a, @ToolParam(description = "second number") int b) { return a + b; } @Tool(description = "Multiply two numbers") int multiply(@ToolParam(description = "first number") int a, @ToolParam(description = "second number") int b) { return a * b; } } // Spring configuration @Configuration class AgentConfig { @Bean OllamaChatModel chatModel() { return OllamaChatModel.builder() .ollamaApi(OllamaApi.builder() .baseUrl("http://localhost:11434") .build()) .defaultOptions(OllamaOptions.builder() .model("qwen2.5:7b") .temperature(0.1) .build()) .build(); } @Bean CompiledGraph<AgentExecutor.State> agentExecutor(OllamaChatModel model) { return AgentExecutor.builder() .chatModel(model) .toolsFromObject(new CalculatorTools()) .build() .compile(); } } // Usage var result = agentExecutor.invoke(Map.of( "messages", "What is 15 multiplied by 23?" )); ``` ### State Persistence with Checkpoints Save and restore graph state using checkpoint savers for long-running workflows and recovery. ```java import org.bsc.langgraph4j.checkpoint.MemorySaver; import org.bsc.langgraph4j.RunnableConfig; import org.bsc.langgraph4j.CompileConfig; // Create checkpoint saver var checkpointer = new MemorySaver(); // Compile graph with checkpoint support var statefulGraph = new StateGraph<>(MyState.SCHEMA, MyState::new) .addNode("step1", state -> Map.of("counter", 1)) .addNode("step2", state -> Map.of("counter", 2)) .addEdge(StateGraph.START, "step1") .addEdge("step1", "step2") .addEdge("step2", StateGraph.END) .compile(CompileConfig.builder() .checkpointSaver(checkpointer) .build()); // Run with thread ID for state isolation var config = RunnableConfig.builder() .threadId("conversation-123") .build(); // Execute and save checkpoints for (var output : statefulGraph.stream(Map.of("input", "start"), config)) { System.out.println("Checkpoint: " + output.node()); } // Retrieve state history var history = statefulGraph.getStateHistory(config); history.forEach(snapshot -> System.out.println("Version: " + snapshot.config().checkPointId()) ); // Resume from last checkpoint var lastState = statefulGraph.getState(config); System.out.println("Resuming from: " + lastState.next()); // Continue execution from checkpoint statefulGraph.invoke(null, config); // null means resume ``` ### Interruption and Human-in-the-Loop Pause execution before or after specific nodes for human approval or intervention. ```java import org.bsc.langgraph4j.action.InterruptionMetadata; class ApprovalState extends AgentState { public static final String PROPOSAL = "proposal"; public static final String APPROVED = "approved"; public static final Map<String, Channel<?>> SCHEMA = Map.of( PROPOSAL, Channel.of(), APPROVED, Channel.of() ); public ApprovalState(Map<String, Object> init) { super(init); } } var approvalWorkflow = new StateGraph<>(ApprovalState.SCHEMA, ApprovalState::new) .addNode("generate_proposal", state -> Map.of(ApprovalState.PROPOSAL, "Deploy to production") ) .addNode("execute_action", state -> { System.out.println("Executing: " + state.<String>value(ApprovalState.PROPOSAL).orElse("")); return Map.of(); }) .addEdge(StateGraph.START, "generate_proposal") .addEdge("generate_proposal", "execute_action") .addEdge("execute_action", StateGraph.END) .compile(CompileConfig.builder() .checkpointSaver(new MemorySaver()) .interruptBefore("execute_action") // Pause before execution .build()); var config = RunnableConfig.builder().threadId("workflow-1").build(); // Run until interruption var outputs = new ArrayList<>(); for (var output : approvalWorkflow.stream(Map.of(), config)) { outputs.add(output); if (output instanceof InterruptionMetadata) { System.out.println("Paused for approval at: " + ((InterruptionMetadata<?>)output).nodeId()); break; } } // Get current state and proposal var currentState = approvalWorkflow.getState(config); var proposal = currentState.state().<String>value(ApprovalState.PROPOSAL); System.out.println("Please approve: " + proposal.orElse("")); // Human approves - update state and resume var updatedConfig = approvalWorkflow.updateState( config, Map.of(ApprovalState.APPROVED, true) ); // Resume from interruption for (var output : approvalWorkflow.stream(null, updatedConfig)) { System.out.println("Resumed: " + output.node()); } ``` ### Parallel Execution of Branches Execute multiple nodes concurrently using parallel edges. ```java import java.util.concurrent.Executors; class ParallelState extends AgentState { public static final String RESULTS = "results"; public static final Map<String, Channel<?>> SCHEMA = Map.of( RESULTS, Channels.appender(ArrayList::new) ); public ParallelState(Map<String, Object> init) { super(init); } } var parallelGraph = new StateGraph<>(ParallelState.SCHEMA, ParallelState::new) .addNode("fetch_data_a", state -> { Thread.sleep(100); // Simulate work return Map.of(ParallelState.RESULTS, "Data A loaded"); }) .addNode("fetch_data_b", state -> { Thread.sleep(100); // Simulate work return Map.of(ParallelState.RESULTS, "Data B loaded"); }) .addNode("fetch_data_c", state -> { Thread.sleep(100); // Simulate work return Map.of(ParallelState.RESULTS, "Data C loaded"); }) .addNode("merge", state -> { var results = state.<List<String>>value(ParallelState.RESULTS) .orElse(List.of()); return Map.of(ParallelState.RESULTS, "Merged: " + String.join(", ", results)); }) .addEdge(StateGraph.START, "fetch_data_a") .addEdge(StateGraph.START, "fetch_data_b") .addEdge(StateGraph.START, "fetch_data_c") .addEdge("fetch_data_a", "merge") .addEdge("fetch_data_b", "merge") .addEdge("fetch_data_c", "merge") .addEdge("merge", StateGraph.END) .compile(); // Execute with custom thread pool for parallel nodes var executor = Executors.newFixedThreadPool(3); var config = RunnableConfig.builder() .addParallelNodeExecutor("parallel_node", executor) .build(); long start = System.currentTimeMillis(); var result = parallelGraph.invoke(Map.of(), config); long elapsed = System.currentTimeMillis() - start; result.ifPresent(state -> { System.out.println("Results: " + state.<List<String>>value(ParallelState.RESULTS)); System.out.println("Completed in: " + elapsed + "ms"); // ~100ms not 300ms }); executor.shutdown(); ``` ### Subgraph Composition Nest graphs within nodes for modular, reusable workflow components. ```java // Define a reusable validation subgraph class ValidationState extends AgentState { public static final String DATA = "data"; public static final String IS_VALID = "is_valid"; public static final Map<String, Channel<?>> SCHEMA = Map.of( DATA, Channel.of(), IS_VALID, Channel.of() ); public ValidationState(Map<String, Object> init) { super(init); } } var validationSubgraph = new StateGraph<>( ValidationState.SCHEMA, ValidationState::new ) .addNode("check_format", state -> { String data = state.<String>value(ValidationState.DATA).orElse(""); boolean valid = data.length() > 5; return Map.of(ValidationState.IS_VALID, valid); }) .addNode("check_content", state -> { boolean currentValid = state.<Boolean>value(ValidationState.IS_VALID).orElse(false); String data = state.<String>value(ValidationState.DATA).orElse(""); boolean valid = currentValid && !data.contains("invalid"); return Map.of(ValidationState.IS_VALID, valid); }) .addEdge(StateGraph.START, "check_format") .addEdge("check_format", "check_content") .addEdge("check_content", StateGraph.END) .compile(); // Use subgraph in parent graph var mainGraph = new StateGraph<>(ValidationState.SCHEMA, ValidationState::new) .addNode("prepare", state -> Map.of(ValidationState.DATA, "test data here") ) .addNode("validate", validationSubgraph) // Embed compiled subgraph .addNode("process", state -> { boolean valid = state.<Boolean>value(ValidationState.IS_VALID).orElse(false); return Map.of(ValidationState.DATA, valid ? "Processed successfully" : "Failed validation"); }) .addEdge(StateGraph.START, "prepare") .addEdge("prepare", "validate") .addEdge("validate", "process") .addEdge("process", StateGraph.END) .compile(); var result = mainGraph.invoke(Map.of()); result.ifPresent(state -> System.out.println("Result: " + state.<String>value(ValidationState.DATA)) ); ``` ### Graph Visualization Generate PlantUML or Mermaid diagrams to visualize graph structure. ```java import org.bsc.langgraph4j.GraphRepresentation; // Build a complex graph var graph = new StateGraph<>(MyState.SCHEMA, MyState::new) .addNode("start_node", state -> Map.of("step", 1)) .addNode("process_a", state -> Map.of("step", 2)) .addNode("process_b", state -> Map.of("step", 3)) .addNode("decision", state -> Map.of("decision", "route_a")) .addNode("end_node", state -> Map.of("step", 4)) .addEdge(StateGraph.START, "start_node") .addEdge("start_node", "decision") .addConditionalEdges("decision", (state, config) -> CompletableFuture.completedFuture( state.<String>value("decision").orElse("route_a") ), Map.of( "route_a", "process_a", "route_b", "process_b" ) ) .addEdge("process_a", "end_node") .addEdge("process_b", "end_node") .addEdge("end_node", StateGraph.END); // Generate PlantUML diagram var plantUml = graph.getGraph( GraphRepresentation.Type.PLANTUML, "My Workflow", true // include conditional edges ); System.out.println(plantUml.content()); // Generate Mermaid diagram var mermaid = graph.getGraph( GraphRepresentation.Type.MERMAID, "My Workflow" ); System.out.println(mermaid.content()); // Save to file java.nio.file.Files.writeString( java.nio.file.Path.of("workflow.puml"), plantUml.content() ); ``` ### Streaming with LLM Token Generation Stream LLM responses in real-time using streaming chat models. ```java import org.bsc.langgraph4j.langchain4j.generators.StreamingChatGenerator; import dev.langchain4j.model.StreamingResponseHandler; import dev.langchain4j.model.ollama.OllamaStreamingChatModel; import org.bsc.async.AsyncGenerator; class ChatState extends AgentState { public static final String MESSAGES = "messages"; public static final Map<String, Channel<?>> SCHEMA = Map.of( MESSAGES, MessageChannel.appender() ); public ChatState(Map<String, Object> init) { super(init); } } var streamingModel = OllamaStreamingChatModel.builder() .modelName("qwen2.5:7b") .baseUrl("http://localhost:11434") .build(); var graph = new StateGraph<>(ChatState.SCHEMA, ChatState::new) .addNode("chat", (state, config) -> { var messages = state.<List<ChatMessage>>value(ChatState.MESSAGES) .orElse(List.of()); // Use StreamingChatGenerator for token-by-token streaming AsyncGenerator<String> tokenStream = StreamingChatGenerator.generate( streamingModel, messages, null // no tools ); // Return the stream wrapped in state update return CompletableFuture.completedFuture( Map.of(ChatState.MESSAGES, tokenStream) ); }) .addEdge(StateGraph.START, "chat") .addEdge("chat", StateGraph.END) .compile(); // Stream tokens as they arrive for (var output : graph.stream(Map.of( ChatState.MESSAGES, List.of(new UserMessage("Tell me a story")) ))) { var msgs = output.state().<List<ChatMessage>>value(ChatState.MESSAGES); if (msgs.isPresent() && !msgs.get().isEmpty()) { var lastMsg = msgs.get().get(msgs.get().size() - 1); System.out.print(lastMsg.text()); // Print tokens incrementally } } ``` ### Multi-Agent Supervisor Pattern Coordinate multiple specialized agents with a supervisor that routes tasks. ```java class SupervisorState extends AgentState { public static final String MESSAGES = "messages"; public static final String NEXT_AGENT = "next_agent"; public static final Map<String, Channel<?>> SCHEMA = Map.of( MESSAGES, Channels.appender(ArrayList::new), NEXT_AGENT, Channel.of() ); public SupervisorState(Map<String, Object> init) { super(init); } } // Specialized agents var researchAgent = new StateGraph<>(SupervisorState.SCHEMA, SupervisorState::new) .addNode("research", state -> Map.of(SupervisorState.MESSAGES, "Research completed: Found 5 sources") ) .addEdge(StateGraph.START, "research") .addEdge("research", StateGraph.END) .compile(); var writerAgent = new StateGraph<>(SupervisorState.SCHEMA, SupervisorState::new) .addNode("write", state -> Map.of(SupervisorState.MESSAGES, "Article written based on research") ) .addEdge(StateGraph.START, "write") .addEdge("write", StateGraph.END) .compile(); // Supervisor orchestrates agents var supervisor = new StateGraph<>(SupervisorState.SCHEMA, SupervisorState::new) .addNode("supervisor", state -> { var messages = state.<List<String>>value(SupervisorState.MESSAGES) .orElse(List.of()); String nextAgent = messages.isEmpty() ? "research" : messages.size() == 1 ? "writer" : "FINISH"; return Map.of( SupervisorState.NEXT_AGENT, nextAgent, SupervisorState.MESSAGES, "Supervisor: Routing to " + nextAgent ); }) .addNode("research", researchAgent) .addNode("writer", writerAgent) .addEdge(StateGraph.START, "supervisor") .addConditionalEdges("supervisor", (state, config) -> CompletableFuture.completedFuture( state.<String>value(SupervisorState.NEXT_AGENT).orElse("FINISH") ), Map.of( "research", "research", "writer", "writer", "FINISH", StateGraph.END ) ) .addEdge("research", "supervisor") .addEdge("writer", "supervisor") .compile(); supervisor.invoke(Map.of(SupervisorState.MESSAGES, "Write article about AI")); ``` ### PostgreSQL Checkpoint Persistence Use PostgreSQL for production-grade checkpoint persistence across restarts. ```java import org.bsc.langgraph4j.checkpoint.postgres.PostgresCheckpointSaver; import javax.sql.DataSource; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; // Setup PostgreSQL connection pool var hikariConfig = new HikariConfig(); hikariConfig.setJdbcUrl("jdbc:postgresql://localhost:5432/langgraph"); hikariConfig.setUsername("user"); hikariConfig.setPassword("password"); hikariConfig.setMaximumPoolSize(10); DataSource dataSource = new HikariDataSource(hikariConfig); // Create checkpoint saver with automatic table creation var pgCheckpointer = PostgresCheckpointSaver.builder() .dataSource(dataSource) .createSchema(true) // Auto-create tables if needed .build(); // Compile graph with PostgreSQL persistence var persistentGraph = new StateGraph<>(MyState.SCHEMA, MyState::new) .addNode("long_running_task", state -> { // Simulate long-running work Thread.sleep(5000); return Map.of("progress", "50%"); }) .addNode("another_task", state -> { Thread.sleep(5000); return Map.of("progress", "100%"); }) .addEdge(StateGraph.START, "long_running_task") .addEdge("long_running_task", "another_task") .addEdge("another_task", StateGraph.END) .compile(CompileConfig.builder() .checkpointSaver(pgCheckpointer) .build()); var config = RunnableConfig.builder() .threadId("production-job-456") .build(); // Run - state persisted to PostgreSQL after each node try { persistentGraph.invoke(Map.of("input", "start"), config); } catch (Exception e) { // If server crashes, resume from last checkpoint System.err.println("Resuming from last checkpoint..."); persistentGraph.invoke(null, config); // Resume } ``` ### LangGraph Studio Integration Embed visual debugging interface for development and monitoring. ```java import org.bsc.langgraph4j.studio.jetty.LangGraphStudioServer4Jetty; import java.util.Map; // Create your graph var debugGraph = new StateGraph<>(MyState.SCHEMA, MyState::new) .addNode("step1", state -> Map.of("data", "Step 1 complete")) .addNode("step2", state -> Map.of("data", "Step 2 complete")) .addEdge(StateGraph.START, "step1") .addEdge("step1", "step2") .addEdge("step2", StateGraph.END) .compile(); // Start Studio server var studio = LangGraphStudioServer4Jetty.builder() .port(8080) .title("My LangGraph Application") .addInputStringArg("query", "User Query", true) .addInputStringArg("context", "Additional Context", false) .build(); studio.start(() -> debugGraph); System.out.println("Studio running at http://localhost:8080"); System.out.println("Open browser to visualize and debug graph execution"); // Keep server running Thread.currentThread().join(); ``` ### Channel Reducers and State Management Define custom channel reducers for complex state update logic. ```java import org.bsc.langgraph4j.state.Channel; import org.bsc.langgraph4j.state.Reducer; class CustomState extends AgentState { public static final String COUNTER = "counter"; public static final String MAX_VALUE = "max_value"; public static final String ITEMS = "items"; // Custom reducer: keep maximum value static final Reducer<Integer> maxReducer = (oldVal, newVal) -> oldVal == null ? newVal : Math.max(oldVal, newVal); // Custom reducer: deduplicate items static final Reducer<List<String>> deduplicatingAppender = (oldVal, newVal) -> { Set<String> combined = new HashSet<>(); if (oldVal != null) combined.addAll(oldVal); if (newVal instanceof List) combined.addAll((List<String>)newVal); else if (newVal != null) combined.add(newVal.toString()); return new ArrayList<>(combined); }; public static final Map<String, Channel<?>> SCHEMA = Map.of( COUNTER, Channel.of(() -> 0), // Default value MAX_VALUE, Channel.of(maxReducer, () -> 0), ITEMS, Channel.of(deduplicatingAppender, ArrayList::new) ); public CustomState(Map<String, Object> init) { super(init); } } var graph = new StateGraph<>(CustomState.SCHEMA, CustomState::new) .addNode("node1", state -> Map.of( CustomState.MAX_VALUE, 42, CustomState.ITEMS, "item1" )) .addNode("node2", state -> Map.of( CustomState.MAX_VALUE, 35, // Won't replace 42 (max reducer) CustomState.ITEMS, List.of("item1", "item2") // item1 deduplicated )) .addNode("node3", state -> Map.of( CustomState.ITEMS, "item3" )) .addEdge(StateGraph.START, "node1") .addEdge("node1", "node2") .addEdge("node2", "node3") .addEdge("node3", StateGraph.END) .compile(); var result = graph.invoke(Map.of()); result.ifPresent(state -> { System.out.println("Max value: " + state.<Integer>value(CustomState.MAX_VALUE)); // 42 System.out.println("Items: " + state.<List<String>>value(CustomState.ITEMS)); // [item1, item2, item3] }); ``` ## Use Cases and Integration Patterns LangGraph4j excels at building production AI agent systems that require sophisticated control flow, state management, and reliability. Common use cases include conversational AI assistants with tool access (search, databases, APIs), multi-agent collaboration systems where specialized agents handle different tasks (research, analysis, writing), autonomous workflow automation with decision points and error handling, RAG (Retrieval Augmented Generation) pipelines with dynamic retrieval strategies, and human-in-the-loop approval workflows for sensitive operations. The library's checkpoint system enables long-running agent tasks to survive restarts, making it suitable for batch processing, scheduled jobs, and serverless deployments where execution may be interrupted. Integration patterns typically involve combining LangGraph4j's graph orchestration with existing Java frameworks and tools. For web applications, graphs can be embedded in Spring Boot or Quarkus services with the Studio module providing real-time visualization and debugging. The checkpoint persistence layer integrates with PostgreSQL or custom storage backends for production deployments requiring durability and multi-instance coordination. LangGraph4j works seamlessly with LangChain4j and Spring AI for LLM access, supporting both synchronous and streaming responses. Tools and functions integrate via standard Java annotations, making it easy to expose REST APIs, database queries, or business logic to agents. The modular subgraph system promotes reusability across projects, while the channel-based state management provides type-safe access to shared data. For monitoring and observability, the library supports custom serializers (JSON/binary), checkpoint inspection APIs, and integration with the visual Studio interface for development and production debugging.