<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: bsorrentino</title>
    <description>The latest articles on DEV Community by bsorrentino (@bsorrentino).</description>
    <link>https://dev.to/bsorrentino</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F53518%2Ff0ff1ba7-a659-49a7-92e4-4fac9e829916.png</url>
      <title>DEV Community: bsorrentino</title>
      <link>https://dev.to/bsorrentino</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bsorrentino"/>
    <language>en</language>
    <item>
      <title>LangGraph4j 1.9.0: What's new and the roadmap</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Sat, 19 Sep 2026 11:51:59 +0000</pubDate>
      <link>https://dev.to/bsorrentino/langgraph4j-190-whats-new-and-the-roadmap-3f1l</link>
      <guid>https://dev.to/bsorrentino/langgraph4j-190-whats-new-and-the-roadmap-3f1l</guid>
      <description>&lt;h2&gt;
  
  
  LangGraph4j 1.9.0 is out!
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; 1.9.0 is out!&lt;/strong&gt; With the &lt;code&gt;1.8.x&lt;/code&gt; release stream now in LTS, development moves forward on &lt;code&gt;1.9&lt;/code&gt; with improvements to streaming, persistence, and the infrastructure for building agents in Java.&lt;/p&gt;

&lt;p&gt;This release is an important step toward workflows that are easier to follow while they run and easier to inspect after they finish. A node can now report progress output before returning its result, checkpoint savers have a richer execution lifecycle, and Studio gets an UI refresh.&lt;/p&gt;

&lt;p&gt;Let's look at what changes for an application built on &lt;code&gt;1.8.x&lt;/code&gt;, with particular attention to &lt;strong&gt;custom output from nodes&lt;/strong&gt; and &lt;strong&gt;checkpoint management&lt;/strong&gt;. The &lt;a href="https://langgraph4j.github.io/langgraph4j/1.9/whats-new-v1.9" rel="noopener noreferrer"&gt;official migration guide&lt;/a&gt; provides details API reference for the changes discussed here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emit custom output while a node is running
&lt;/h2&gt;

&lt;p&gt;Imagine a node that retrieves documents, processes them, and prepares a response. The caller may want to show progress throughout that work. Waiting for the final node output gives the user minimal information about what is happening in the meantime.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;1.9&lt;/code&gt;, a node action can emit its own typed &lt;code&gt;NodeOutput&lt;/code&gt; values through the active graph execution stream (&lt;code&gt;graph.stream(...)&lt;/code&gt;). They arrive alongside the usual START, node, and END outputs, before the node has necessarily finished.&lt;/p&gt;

&lt;p&gt;The mechanism is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define a subclass of &lt;code&gt;NodeOutput&lt;/code&gt; carrying the information you want to expose.&lt;/li&gt;
&lt;li&gt;Obtain a dispatcher from the node's &lt;code&gt;RunnableConfig&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Dispatch custom outputs during execution.&lt;/li&gt;
&lt;li&gt;Handle those types when consuming the graph stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a small example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProgressOutput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;NodeOutput&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AgentState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ProgressOutput&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AgentState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the node, the dispatcher publishes a message while the returned map still provides the normal state update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AsyncNodeActionWithConfig&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AgentState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dispatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AgentState&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProgressOutput&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;customDispatcher&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;dispatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dispatchAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProgressOutput&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nodeId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Processing documents"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Perform the node's work here.&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;completedFuture&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"result"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Documents processed"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this action is registered in a compiled graph, the caller can recognize its progress messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;noArgs&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;RunnableConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;forEachAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;ProgressOutput&lt;/span&gt; &lt;span class="n"&gt;progress&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;progress&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Graph step: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us a useful channel for progress notifications, intermediate results, and application-specific events. &lt;strong&gt;Dispatching a custom output does not update graph state.&lt;/strong&gt; If a value must influence routing, be available to later nodes, or become part of a persisted result, include it in the node's returned state update.&lt;/p&gt;

&lt;p&gt;There are two dispatch options. &lt;code&gt;dispatchSync(...)&lt;/code&gt; waits until the stream accepts the value and can throw &lt;code&gt;InterruptedException&lt;/code&gt;; &lt;code&gt;dispatchAsync(...)&lt;/code&gt; submits without waiting. The dispatcher is available during node execution in an active graph stream, including the supported hook context. Keep its use within that lifecycle: accessing it outside has an unpredicable result.&lt;/p&gt;

&lt;p&gt;Custom events are intended for &lt;code&gt;graph.stream(...)&lt;/code&gt; consumers; they do not change the result returned by &lt;code&gt;graph.invoke(...)&lt;/code&gt;. See the &lt;a href="https://langgraph4j.github.io/langgraph4j/1.9/core/emit-custom-output/" rel="noopener noreferrer"&gt;custom-output tutorial&lt;/a&gt; for the complete contract and a &lt;strong&gt;hook-based&lt;/strong&gt; example.&lt;/p&gt;

&lt;h3&gt;
  
  
  The streaming engine behind it
&lt;/h3&gt;

&lt;p&gt;This capability comes with a refactoring of the internal streaming engine around &lt;code&gt;AsyncGeneratorFlow&lt;/code&gt; from &lt;a href="https://github.com/bsorrentino/java-async-generator" rel="noopener noreferrer"&gt;&lt;code&gt;async-generator 5.0&lt;/code&gt;&lt;/a&gt; project.&lt;/p&gt;

&lt;p&gt;Ordinary iteration over &lt;code&gt;stream()&lt;/code&gt; remains source-compatible. Applications extending directly streaming generators, supplying a &lt;code&gt;BlockingQueue&lt;/code&gt;, or depending on &lt;code&gt;AsyncGenerator.WithResult&lt;/code&gt; need migration work. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; 👀&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This refactoring is a groundwork for Reactor Flow support in a future LangGraph4j &lt;code&gt;2.0&lt;/code&gt;; that support is a future direction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Checkpoint Savers: A richer execution lifecycle
&lt;/h2&gt;

&lt;p&gt;Checkpoint persistence is central to long-running agents and Human-in-the-Loop workflows. In &lt;code&gt;1.9&lt;/code&gt;, the changes cover what happens when execution finishes, how released runs can be retrieved, how subgraph savers participate, and how interruptions and failures are recorded. These are the &lt;a href="https://langgraph4j.github.io/langgraph4j/1.9/whats-new-v1.9" rel="noopener noreferrer"&gt;checkpoint changes&lt;/a&gt; I would review first when upgrading an existing application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Completed runs release their thread by default
&lt;/h3&gt;

&lt;p&gt;With a checkpoint saver configured, &lt;strong&gt;a graph that completes normally now releases its active thread automatically&lt;/strong&gt;. Depending on the saver, release archives or tags its checkpoints and removes the active checkpoint set.&lt;/p&gt;

&lt;p&gt;This matters if your application completes a graph, updates the same active thread's state, and executes it again. To retain the previous lifecycle, explicitly disable automatic release when compiling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;compileConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CompileConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checkpointSaver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;saver&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;releaseThread&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// backward compatible option&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compileConfig&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;An interruption or an exception does not automatically release the thread.&lt;/strong&gt; Interrupted runs remain available for Human-in-the-Loop continuation, and failed runs remain available for the application's recovery strategy. &lt;code&gt;graph.updateState(...)&lt;/code&gt; continues to work in those situations as before.&lt;/p&gt;

&lt;p&gt;The practical upgrade check is to examine what your code does &lt;em&gt;after successful completion&lt;/em&gt;. Any inspection, replay, resume, or manual-release logic that assumes an active checkpoint set needs attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experimental versioning for released runs
&lt;/h3&gt;

&lt;p&gt;Checkpoint tags gain optional version information. &lt;code&gt;BaseCheckpointSaver.Tag&lt;/code&gt; exposes &lt;code&gt;threadId()&lt;/code&gt;, &lt;code&gt;version()&lt;/code&gt; as an &lt;code&gt;Optional&amp;lt;Integer&amp;gt;&lt;/code&gt;, &lt;code&gt;checkpoints()&lt;/code&gt;, and &lt;code&gt;lastCheckpoint()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two lookup methods make released runs accessible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RunnableConfig&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;lastTag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RunnableConfig&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;version&lt;/code&gt; argument to &lt;code&gt;tag(...)&lt;/code&gt; is nullable. &lt;code&gt;lastTag(...)&lt;/code&gt; retrieves the latest tag regardless of whether it is versioned.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GraphResult.asLastCheckpointStateData()&lt;/code&gt; now obtains its data through &lt;code&gt;Tag.lastCheckpoint()&lt;/code&gt;, preserving its observable result. This is useful continuity for callers retrieving the final checkpoint state while the underlying release model evolves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parent and Subgraph savers work together
&lt;/h3&gt;

&lt;p&gt;Subgraphs increasingly carry real agent behavior, so their persistence lifecycle must follow the parent execution.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BaseCheckpointSaver&lt;/code&gt; now supports &lt;code&gt;putSubGraphSaver(...)&lt;/code&gt; to register a subgraph saver and &lt;code&gt;listSubGraphSaver(...)&lt;/code&gt; to retrieve registrations associated with a parent configuration. &lt;code&gt;SubCompiledGraphNodeAction&lt;/code&gt; registers its saver with the parent; &lt;code&gt;AbstractCheckpointSaver.release(...)&lt;/code&gt; then cascades release to the registered subgraph threads.&lt;/p&gt;

&lt;p&gt;If you implement a custom saver, &lt;code&gt;AbstractCheckpointSaver&lt;/code&gt; provides a reference implementation of the registration mechanism. Review that lifecycle when adapting your implementation, especially if your agents contain nested compiled graphs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interruption and Error Metadata
&lt;/h3&gt;

&lt;p&gt;The saver contract also exposes explicit lifecycle hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;registerInterruption&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;RunnableConfig&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;InterruptionMetadata&lt;/span&gt; &lt;span class="n"&gt;interruptionMetadata&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Tag&lt;/span&gt; &lt;span class="nf"&gt;releaseCheckpointsOnError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;RunnableConfig&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These allow a saver to retain interruption information and distinguish an error release from normal completion. &lt;code&gt;InterruptionMetadata&lt;/code&gt; now also exposes the interruption reason. That gives later analysis more context about why an execution stopped.&lt;/p&gt;

&lt;p&gt;The error-release hook does not change the default described above: an exception leaves the thread available, and the caller chooses its recovery or release strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; 👀&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To avoid leaving lots of unfinished records in the checkpoint persistence layer, we recommend calling &lt;code&gt;saver.releaseOnError(RunnableConfig, Throwable)&lt;/code&gt; within exception handling logic. We are currently considering automating this  in a future release by having the graph execution engine invoke the method whenever an error occurs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The in-memory, file-system, Redis, Postgres, Oracle, MySQL, CockroachDB, DynamoDB, and Hazelcast savers have been aligned with this contract through default implementations. The amount of information retained remains a concern of the specific saver implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  SQLite and PostgreSQL V2 savers
&lt;/h3&gt;

&lt;p&gt;Both relational integrations add implementations backed by versioned SQL resources:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Integration&lt;/th&gt;
&lt;th&gt;New implementation&lt;/th&gt;
&lt;th&gt;Existing implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQLite&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SQLiteSaverV2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SQLiteSaver&lt;/code&gt;, for the V1 schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostgresSaverV2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PostgresSaver&lt;/code&gt;, for the V1 schema&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The V2 implementations align with the updated release, error, and interruption contract. The original classes remain available for their V1 schemas. Treat adoption of a V2 saver as a persistence change to review against your existing database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other changes worth knowing before upgrading
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Explicit Graph Inputs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GraphInput&lt;/code&gt; becomes the preferred API for expressing whether an execution is starting or resuming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="o"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resume&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GraphInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resume&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"approval"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"APPROVED"&lt;/span&gt;&lt;span class="o"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;GraphInput.noArgs()&lt;/code&gt; for a new execution without arguments. The older map-based stream and invoke overloads remain available in &lt;code&gt;1.9&lt;/code&gt;, but are deprecated for removal. Moving away from a null map makes the intended operation explicit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Diagnostics and State Serialization
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;RunnableConfig.nodePath()&lt;/code&gt; supplies the current node path for regular nodes and subgraphs, replacing the older partial &lt;code&gt;graphPath()&lt;/code&gt; usage. A node can also intentionally interrupt execution by raising &lt;code&gt;GraphInterruptException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;State cloning remains enabled by default. &lt;code&gt;RunnableConfig.builder().disableCloneState()&lt;/code&gt; opts out, which can help during early development with objects that are not yet serializable. Exposed state may then reflect later mutations, so consider how consumers retain or modify it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;StateSerializer.declareTransientAttributes(...)&lt;/code&gt; excludes named attributes from serialized data and restores them from the same serializer's in-memory transient storage. Those values do not survive a restart, a different process, or a new serializer instance. Keep anything needed for durable resume in persisted state. &lt;code&gt;GsonStateSerializer&lt;/code&gt; is deprecated for removal; move toward Jackson-based serialization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent infrastructure
&lt;/h3&gt;

&lt;p&gt;The experimental core skills API introduces &lt;code&gt;SkillSource&lt;/code&gt;, &lt;code&gt;SkillPath&lt;/code&gt;, and &lt;code&gt;SkillParser&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Spring AI builds on this with &lt;code&gt;SubAgent&lt;/code&gt;, &lt;code&gt;CustomSubAgent&lt;/code&gt;, &lt;code&gt;SkilledReactSubAgent&lt;/code&gt;, and &lt;code&gt;SkillResource&lt;/code&gt;, making compiled agents reusable as tools. I covered the pattern in &lt;a href="https://bsorrentino.github.io/bsorrentino/ai/2026/04/28/LangGraph4j-SubAgent.html" rel="noopener noreferrer"&gt;Skill-Based Sub-Agents with LangGraph4j and Spring AI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  LangGraph4j Studio gets a visual refresh
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Studio 1.8
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64xiuq245rv505aon0pn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64xiuq245rv505aon0pn.gif" alt="LangGraph4j Studio 1.8" width="720" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Studio 1.9
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb3z3zgx1ogs51zg1am12.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb3z3zgx1ogs51zg1am12.gif" alt="LangGraph4j Studio 1.9" width="560" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the new Studio the active node receives a blue highlight and a loading indicator, making execution easier to follow at a glance.&lt;/p&gt;

&lt;h2&gt;
  
  
  LangGraph4j DSL
&lt;/h2&gt;

&lt;p&gt;The release also adds &lt;code&gt;langgraph4j-dsl&lt;/code&gt;, whose &lt;code&gt;JsonDslGenerator&lt;/code&gt; exports graphs, including parallel nodes and nested subgraphs, as JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compiledGraph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JsonDslGenerator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This representation has been used for the Studio refactoring toward &lt;a href="https://reactflow.dev" rel="noopener noreferrer"&gt;React Flow&lt;/a&gt; library.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next for LangGraph4j
&lt;/h2&gt;

&lt;p&gt;The next phase of development will focus on three areas. These are roadmap priorities, with scope and delivery to evolve as implementation progresses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Persist more of the execution context
&lt;/h3&gt;

&lt;p&gt;I want to improve graph execution-context persistence so that it supports monitoring and post-processing analysis more effectively. The checkpoint lifecycle work in &lt;code&gt;1.9&lt;/code&gt; is a step in that direction: understanding completed, interrupted, and failed runs is essential when agentic workflows move into production.&lt;/p&gt;

&lt;p&gt;The aim is to make retained execution information more useful for understanding behavior and for subsequent analysis and processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redesign parallel node execution
&lt;/h3&gt;

&lt;p&gt;Another priority is to redesign the parallel-node implementation, removing current limitations and improving efficiency. As workflows grow, parallel branches need to become easier to compose and manage.&lt;/p&gt;

&lt;p&gt;This is upcoming work. Applications using &lt;code&gt;1.9&lt;/code&gt; should continue to account for the currently documented parallel-execution limitations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improve built-in Agents for Spring AI and LangChain4j
&lt;/h3&gt;

&lt;p&gt;Finally, I want to enhance the built-in agent infrastructure for both &lt;strong&gt;Spring AI&lt;/strong&gt; and &lt;strong&gt;LangChain4j&lt;/strong&gt;. The skills and sub-agent work demonstrates how much can be built on top of graph execution; the goal is to make those building blocks more useful and easier to compose across both integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the Release
&lt;/h2&gt;

&lt;p&gt;Update the LangGraph4j BOM to &lt;code&gt;1.9.0&lt;/code&gt; to keep module versions aligned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.bsc.langgraph4j&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;langgraph4j-bom&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.9.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;For an existing &lt;code&gt;1.8.x&lt;/code&gt; application, start with the &lt;a href="https://langgraph4j.github.io/langgraph4j/1.9/whats-new-v1.9" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;, review thread release and stored checkpoint compatibility, then exercise interruption, resume, and completion paths with your chosen saver.&lt;/p&gt;

&lt;p&gt;I'm interested in feedback from real workflows, especially around custom progress output and checkpoint management. Check out &lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt;, try the release, and let me know what would help your next agentic application. If you find the project useful, leave a star ⭐️ and... happy AI coding! 👋&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://bsorrentino.github.io/bsorrentino/ai/2026/09/18/LangGraph4j-Whats-new-in-1.9.0.html" rel="noopener noreferrer"&gt;https://bsorrentino.github.io&lt;/a&gt; on September 18, 2026.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>workflow</category>
      <category>langgraph4j</category>
      <category>agents</category>
    </item>
    <item>
      <title>Interesting experimentation using Spring AI and LangGraph4j</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Wed, 19 Aug 2026 09:26:37 +0000</pubDate>
      <link>https://dev.to/bsorrentino/interesting-experimentation-using-spring-ai-and-langgraph4j-52lp</link>
      <guid>https://dev.to/bsorrentino/interesting-experimentation-using-spring-ai-and-langgraph4j-52lp</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/lbobylev/building-an-agentic-harness-with-spring-ai-2j1p" class="crayons-story__hidden-navigation-link"&gt;Building an Agentic Harness with Spring AI and LangGraph4j&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/lbobylev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4036707%2F743b294b-bc81-4119-96a7-dec0a4250647.jpg" alt="lbobylev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/lbobylev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              l3o6
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                l3o6
                
                
              
              &lt;div id="story-author-preview-content-4346862" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/lbobylev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4036707%2F743b294b-bc81-4119-96a7-dec0a4250647.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;l3o6&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/lbobylev/building-an-agentic-harness-with-spring-ai-2j1p" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 8&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/lbobylev/building-an-agentic-harness-with-spring-ai-2j1p" id="article-link-4346862"&gt;
          Building an Agentic Harness with Spring AI and LangGraph4j
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/springboot"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;springboot&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/java"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;java&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/langgraph4j"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;langgraph4j&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/lbobylev/building-an-agentic-harness-with-spring-ai-2j1p" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/lbobylev/building-an-agentic-harness-with-spring-ai-2j1p#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              5&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>java</category>
      <category>springboot</category>
    </item>
    <item>
      <title>Interesting AI use case, using LangGraph4j to orchestrate and AiService by LangChain4j, it's a great match</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Fri, 07 Aug 2026 20:42:35 +0000</pubDate>
      <link>https://dev.to/bsorrentino/interesting-ai-use-case-using-langgraph4j-to-orchestrate-and-aiservice-by-langchain4j-its-a-4o4a</link>
      <guid>https://dev.to/bsorrentino/interesting-ai-use-case-using-langgraph4j-to-orchestrate-and-aiservice-by-langchain4j-its-a-4o4a</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ykpraveen/building-a-multi-agent-hiring-workflow-with-langchain4j-and-langgraph4j-on-spring-boot-2a15" class="crayons-story__hidden-navigation-link"&gt;Building a Multi-Agent Hiring Workflow with LangChain4j and LangGraph4j on Spring Boot&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ykpraveen" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F569972%2F089d56a9-b0f6-4d91-ba28-0b5cb92248cd.png" alt="ykpraveen profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ykpraveen" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Praveen Yadav
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Praveen Yadav
                
                
              
              &lt;div id="story-author-preview-content-4313923" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ykpraveen" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F569972%2F089d56a9-b0f6-4d91-ba28-0b5cb92248cd.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Praveen Yadav&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ykpraveen/building-a-multi-agent-hiring-workflow-with-langchain4j-and-langgraph4j-on-spring-boot-2a15" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 5&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ykpraveen/building-a-multi-agent-hiring-workflow-with-langchain4j-and-langgraph4j-on-spring-boot-2a15" id="article-link-4313923"&gt;
          Building a Multi-Agent Hiring Workflow with LangChain4j and LangGraph4j on Spring Boot
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/java"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;java&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/llm"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;llm&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/springboot"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;springboot&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ykpraveen/building-a-multi-agent-hiring-workflow-with-langchain4j-and-langgraph4j-on-spring-boot-2a15" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ykpraveen/building-a-multi-agent-hiring-workflow-with-langchain4j-and-langgraph4j-on-spring-boot-2a15#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              3&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            10 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>java</category>
      <category>springboot</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Skill-Based Sub-Agents with LangGraph4j and Spring AI</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Mon, 04 May 2026 20:27:26 +0000</pubDate>
      <link>https://dev.to/bsorrentino/skill-based-sub-agents-with-langgraph4j-and-spring-ai-52b0</link>
      <guid>https://dev.to/bsorrentino/skill-based-sub-agents-with-langgraph4j-and-spring-ai-52b0</guid>
      <description>&lt;h2&gt;
  
  
  Combine Tools &amp;amp; Skill to create Agent
&lt;/h2&gt;

&lt;p&gt;Modern agentic systems usually expose two strong but separate abstractions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt;, which give an LLM executable capabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt;, which package reusable instructions for a narrow task&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article shows how to combine both ideas in &lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; so that a skill is not just prompt text, but a fully operational &lt;strong&gt;sub-agent exposed as a tool&lt;/strong&gt;. The result is a practical pattern for building modular multi-agent systems in Java with &lt;strong&gt;&lt;a href="https://spring.io/projects/spring-ai" rel="noopener noreferrer"&gt;Spring AI&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt;&lt;/strong&gt;, while keeping each agent focused, reusable, and low in context cost.&lt;/p&gt;

&lt;p&gt;Starting from a standard ReACT agent, the approach implemented is quite simple.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load a &lt;code&gt;SKILL.md&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Read its front matter to define the tool contract ( name and argument description)&lt;/li&gt;
&lt;li&gt;Use the skill body as the sub-agent system prompt&lt;/li&gt;
&lt;li&gt;Restrict the sub-agent to only the tools declared by the skill&lt;/li&gt;
&lt;li&gt;Register the sub-agent in a parent ReAct agent as if it were a normal tool&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words, a skill becomes an &lt;em&gt;executable tool-backed agent&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Pattern Matters
&lt;/h2&gt;

&lt;p&gt;The standard ReAct pattern already gives an LLM a strong execution model: reason, choose a tool, inspect the result, and continue until the task is complete. That model is useful because the LLM does not need to commit to a fixed plan up front. It can adapt after every tool call.&lt;/p&gt;

&lt;p&gt;The next step is to let a tool itself be implemented by another agent.&lt;/p&gt;

&lt;p&gt;Once that happens, the parent agent is no longer forced to know every operational detail. It only needs to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which specialized capability exists&lt;/li&gt;
&lt;li&gt;when to invoke it&lt;/li&gt;
&lt;li&gt;what input to pass&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The specialized logic is delegated to a narrower agent with its own instructions and its own restricted toolset. This gives us a clean &lt;strong&gt;sub-agent architecture&lt;/strong&gt; without inventing a new orchestration primitive. We reuse the same tool-calling semantics that ReAct agents already understand well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Reusing Tool Semantics Is Powerful
&lt;/h2&gt;

&lt;p&gt;One of the most valuable properties of LLM-based agents is their ability to build and revise a tool execution plan while reasoning over intermediate results. ReAct made this practical by combining thought, action, and observation in a loop.&lt;/p&gt;

&lt;p&gt;When a tool call can transparently invoke another agent, we preserve that planning model instead of replacing it.&lt;/p&gt;

&lt;p&gt;The parent agent can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;decide that a specialized capability is needed&lt;/li&gt;
&lt;li&gt;provide only the task-relevant context&lt;/li&gt;
&lt;li&gt;wait for a result exactly as it would for a standard tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The child agent can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run its own reasoning loop&lt;/li&gt;
&lt;li&gt;invoke its own allowed tools&lt;/li&gt;
&lt;li&gt;return a compact outcome to the parent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives hierarchical coordination without exposing the full inner workflow to the parent prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimizing Context and Token Usage
&lt;/h2&gt;

&lt;p&gt;This pattern also improves context discipline.&lt;/p&gt;

&lt;p&gt;Without sub-agents, a single generalist agent often needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every tool definition&lt;/li&gt;
&lt;li&gt;all operating instructions for every domain&lt;/li&gt;
&lt;li&gt;every procedural rule in one large system prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That quickly becomes expensive and unreliable.&lt;/p&gt;

&lt;p&gt;With skill-based sub-agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the parent only sees high-level capabilities&lt;/li&gt;
&lt;li&gt;each child sees only the instructions relevant to its task&lt;/li&gt;
&lt;li&gt;each child receives only the tools it is allowed to use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This narrower context has practical benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fewer tokens sent to the model&lt;/li&gt;
&lt;li&gt;lower prompt interference between unrelated domains&lt;/li&gt;
&lt;li&gt;clearer tool choice&lt;/li&gt;
&lt;li&gt;better modularity as the system grows&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;allowed-tools&lt;/code&gt; skill property plays an important role here anabling tool filtering. It turns tool access into an explicit design decision instead of an accidental side effect of what happened to be registered globally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Reference implementation
&lt;/h2&gt;

&lt;p&gt;I've started implementation in the new &lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; release stream &lt;code&gt;1.9&lt;/code&gt; currently in active development. The implementation is centered on [&lt;code&gt;SkilledReactSubAgent&lt;/code&gt;], which turns a markdown skill into both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a compiled LangGraph4j sub-graph&lt;/li&gt;
&lt;li&gt;a Spring AI tool (i.e. &lt;code&gt;ToolCallback&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the same component can participate in the graph as an agent and present itself to the parent LLM as a callable tool.&lt;/p&gt;

&lt;p&gt;In particular the flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse the &lt;code&gt;SKILL.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Extract &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; from front matter&lt;/li&gt;
&lt;li&gt;Optionally filter tools using &lt;code&gt;allowed-tools&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use the markdown body as the sub-agent instructions&lt;/li&gt;
&lt;li&gt;Compile the agent&lt;/li&gt;
&lt;li&gt;Wrap it into a function tool whose input is a single &lt;code&gt;context&lt;/code&gt; field&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;The important design choice is that the &lt;strong&gt;tool definition is derived from the skill metadata&lt;/strong&gt;, while the &lt;strong&gt;agent behavior is derived from the skill content&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Skill Format
&lt;/h3&gt;

&lt;p&gt;The supported skill format is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;agent name&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;|"&lt;/span&gt;
        &lt;span class="s"&gt;&amp;lt;multi line description&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;list of tools&amp;gt;&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;skill&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  From Skill to Tool-Backed Agent
&lt;/h3&gt;

&lt;p&gt;The conversion happens in &lt;code&gt;SkilledReactSubAgent.Builder#build(...)&lt;/code&gt;. This is the crucial bridge where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the skill metadata defines the external API&lt;/li&gt;
&lt;li&gt;the skill body defines the internal operating behavior&lt;/li&gt;
&lt;li&gt;the allowed tools define the sub-agent execution boundary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resulting agent is then wrapped as a Spring AI function tool.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;From the parent agent point of view, this is just another tool. Internally, it is a complete ReAct-capable agent workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purchase Assistant Agent
&lt;/h3&gt;

&lt;p&gt;This is an agent that allow to select a produdct from a reference marketplace and purchase it. Below the simplified component diagram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffeiutlwe940j65rwdvyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffeiutlwe940j65rwdvyp.png" alt="diagram" width="778" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Marketplace Skill
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent-marketplace&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;marketplace agent, ask for information about products. This is agent that provides the information on the product marketplace.&lt;/span&gt;
    &lt;span class="s"&gt;required data are:&lt;/span&gt;
    &lt;span class="s"&gt;* product name&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;searchByProduct&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## Retrieve product information&lt;/span&gt;
We need all information about about the product of interest in particular is mandatory to have
&lt;span class="p"&gt;*&lt;/span&gt; product name

with such information we have to invoke tool &lt;span class="sb"&gt;`searchByProduct`&lt;/span&gt; and return the result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Payment Skill
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent-payment&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;payment agent, this is the agent that provides payment service.&lt;/span&gt;
    &lt;span class="s"&gt;required data are:&lt;/span&gt;
    &lt;span class="s"&gt;* Product name&lt;/span&gt;
    &lt;span class="s"&gt;* Product price&lt;/span&gt;
    &lt;span class="s"&gt;* Product currency&lt;/span&gt;
    &lt;span class="s"&gt;* the International Bank Account Number (IBAN) - optional&lt;/span&gt;

&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;submit-payment&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;retrieve-iban&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## submit a payment for purchasing a specific product&lt;/span&gt;
We need all information about purchasing to allow the payment
&lt;span class="p"&gt;
*&lt;/span&gt; Product name
&lt;span class="p"&gt;*&lt;/span&gt; Product price
&lt;span class="p"&gt;*&lt;/span&gt; Product price currency
&lt;span class="p"&gt;*&lt;/span&gt; International Bank Account Number (IBAN)

With such information you must invoke tool &lt;span class="sb"&gt;`submit-payment`&lt;/span&gt; and return the result of the payment transaction plus the IBAN.
If IBAN is not provided invoke tool &lt;span class="sb"&gt;`retrieve-iban`&lt;/span&gt; to retrieve the required IBAN.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observing the skills above they define three different concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: the tool name exposed to the parent agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: the tool description shown to the model&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allowed-tools&lt;/code&gt;: the only tools available inside the sub-agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything after the front matter becomes the sub-agent system prompt.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Again, this is a strong separation of concerns. The parent agent sees a concise tool contract, while the child agent receives richer operating instructions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Finally the code
&lt;/h2&gt;

&lt;p&gt;Below a representative code snippet that show how wires two skill-based sub-agents into a parent agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subAgentMarketplace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SkilledReactSubAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatModel&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Marketplace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compileConfig&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;SkillSource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Paths&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"skills/agent-marketplace/"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subAgentPayment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SkilledReactSubAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatModel&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compileConfig&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;SkillSource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Paths&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"skills/agent-payment/"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;purchaseAssistantAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AgentExecutorEx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatModel&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subAgentMarketplace&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subAgentPayment&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compileConfig&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""
        search for product 'X'.
        If found proceed to payment with IBAN US82WEST1234567890123456 
        to purchase it
        """&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;purchaseAssistantAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; 
        &lt;span class="nc"&gt;GraphInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"messages"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;runnableConfig&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This composition is interesting because the purchase agent (the parent) does not need detailed knowledge of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how product lookup works&lt;/li&gt;
&lt;li&gt;how payment submission works&lt;/li&gt;
&lt;li&gt;when IBAN retrieval is required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those rules are delegated to the sub agents themselves.&lt;/p&gt;

&lt;p&gt;That is a simple but good example of behavioral encapsulation. The parent agent only decides to involve marketplace agent to retrieve product and after that to involve payment agent to purchase.&lt;/p&gt;

&lt;p&gt;This is a very basic but representative example, it is useful because it shows the intended layering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the user gives a single business request&lt;/li&gt;
&lt;li&gt;the parent agent decomposes the work&lt;/li&gt;
&lt;li&gt;specialized sub-agents execute their own logic&lt;/li&gt;
&lt;li&gt;the final answer comes back through the normal tool-response path&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Interesting note that each sub agent, potentially, can relies on different LLM chat model so we can further optimize overall agent execution process&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Below a more detailed componet diagram generated by &lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7u6han2x5t14pgasky3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7u6han2x5t14pgasky3.png" alt="diagram" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From this diagram it is quite clear that the sub agents are nothing but sub-graphs&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Advantages
&lt;/h2&gt;

&lt;p&gt;This approach has several practical advantages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Reuse Existing Agent Patterns&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is no need to invent a separate orchestration protocol for sub-agents. A sub-agent is exposed as a tool, so the parent agent can use standard ReAct behavior without extra prompting complexity.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Better Modularity&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each skill owns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;its tool-facing identity&lt;/li&gt;
&lt;li&gt;its internal instructions&lt;/li&gt;
&lt;li&gt;its allowed tool boundary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes the system easier to evolve than a single monolithic agent prompt.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Smaller and Cleaner Context&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The child agent receives only what it needs. This reduces token usage and lowers the risk that unrelated instructions distort tool choice or task execution.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Safer Capability Scoping&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By filtering tools from &lt;code&gt;allowed-tools&lt;/code&gt;, the framework prevents a child agent from accidentally seeing tools outside its intended domain.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Better Reusability&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A skill can be packaged, versioned, and reused across different parent agents. The same marketplace or payment skill can participate in many workflows as long as the tool contract remains stable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tradeoffs and Implementation Considerations
&lt;/h2&gt;

&lt;p&gt;This pattern is strong, but it is not free of tradeoffs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Parent Must Summarize Well&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because the child receives a single &lt;code&gt;context&lt;/code&gt; field, the parent agent needs to pass the relevant facts clearly. If critical information is omitted, the child may need extra recovery steps or may fail to act.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Tool Descriptions Matter More&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since the parent sees the child as a tool, the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; fields in the skill front matter become part of the routing surface for the LLM. Weak metadata will reduce invocation quality.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prompt Boundaries Need Discipline&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The skill body is effectively the child system prompt. If it is vague, contradictory, or overloaded, the benefits of modularity disappear. Skills need to stay focused.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Observability Is Important&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nested agents can make execution harder to inspect if tracing is weak. LangGraph4j's graph structure and step streaming help here, but production systems should still invest in logging and traceability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why LangGraph4j Fits This Well
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; is a good fit for this pattern because it already models agent workflows as explicit graphs and integrates naturally with Spring AI tool abstractions.&lt;/p&gt;

&lt;p&gt;That matters for two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the sub-agent is not just prompt composition; it is a compiled graph with state and execution hooks&lt;/li&gt;
&lt;li&gt;the parent integration remains ergonomic because Spring AI already treats capabilities as tools&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; support interruptions in subgraph enabling &lt;strong&gt;Human In The Loop&lt;/strong&gt; also when involving sub agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the implementation is not forcing two incompatible ideas together. It is aligning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LangGraph4j graph execution&lt;/li&gt;
&lt;li&gt;Spring AI tool calling&lt;/li&gt;
&lt;li&gt;markdown-defined skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;into a single reusable agent-building pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Turning a skill into a sub-agent is a small change with a large impact.&lt;/p&gt;

&lt;p&gt;It lets us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preserve the strengths of the ReAct loop&lt;/li&gt;
&lt;li&gt;delegate specialized behavior cleanly&lt;/li&gt;
&lt;li&gt;reduce context size&lt;/li&gt;
&lt;li&gt;constrain tool access per domain&lt;/li&gt;
&lt;li&gt;build multi-agent systems out of reusable skill modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is implemented in a compact and pragmatic way: a &lt;code&gt;SKILL.md&lt;/code&gt; file becomes the source of both the sub-agent prompt and the tool contract exposed to the parent agent.&lt;/p&gt;

&lt;p&gt;For Java teams building agentic applications with Spring AI and LangGraph4j, this is a practical pattern for moving from a single large agent toward a modular multi-agent design without abandoning the tool semantics that current LLMs already handle well.&lt;/p&gt;

&lt;p&gt;The implementation is in progress on release &lt;code&gt;1.9-SNAPSHOT&lt;/code&gt; in develop branch of &lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; repository but the first result are very promising.&lt;/p&gt;

&lt;p&gt;Hope this could help and encourage usage of &lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; for your next Agentic Workflow. Checkout project, try it and let me know your feedback and... happy AI coding! 👋&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://bsorrentino.github.io/bsorrentino/ai/2026/04/28/LangGraph4j-SubAgent.html" rel="noopener noreferrer"&gt;https://bsorrentino.github.io&lt;/a&gt; on April 28, 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agentskills</category>
      <category>langgraph4j</category>
      <category>springai</category>
    </item>
    <item>
      <title>Interesting reading for who are approaching use AI for developing at scale</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Mon, 30 Mar 2026 07:48:38 +0000</pubDate>
      <link>https://dev.to/bsorrentino/interesting-reading-for-who-are-approaching-use-ai-for-developing-at-scale-2okf</link>
      <guid>https://dev.to/bsorrentino/interesting-reading-for-who-are-approaching-use-ai-for-developing-at-scale-2okf</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/incomplete_developer/spec-driven-development-return-of-best-practices-44b0" class="crayons-story__hidden-navigation-link"&gt;Spec-Driven Development: Return of Best Practices&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/SYNSj-nwZyo" title="Spec-Driven Development: Return of Best Practices"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/incomplete_developer" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1237870%2Fd4f4f10a-3000-409a-8c61-f712e1bb5b87.png" alt="incomplete_developer profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/incomplete_developer" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Incomplete Developer
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Incomplete Developer
                
              
              &lt;div id="story-author-preview-content-3409072" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/incomplete_developer" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1237870%2Fd4f4f10a-3000-409a-8c61-f712e1bb5b87.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Incomplete Developer&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/incomplete_developer/spec-driven-development-return-of-best-practices-44b0" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/incomplete_developer/spec-driven-development-return-of-best-practices-44b0" id="article-link-3409072"&gt;
          Spec-Driven Development: Return of Best Practices
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/specdrivendevelopment"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;specdrivendevelopment&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/vibecoding"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;vibecoding&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/incomplete_developer/spec-driven-development-return-of-best-practices-44b0" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;4&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/incomplete_developer/spec-driven-development-return-of-best-practices-44b0#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>specdrivendevelopment</category>
      <category>vibecoding</category>
      <category>ai</category>
    </item>
    <item>
      <title>Agent Definition Language (ADL): The Open Source Standard for Defining AI Agents 👀. Looks promising

https://www.nextmoca.com/blogs/agent-definition-language-adl-the-open-source-standard-for-defining-ai-agents</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Tue, 10 Feb 2026 09:28:44 +0000</pubDate>
      <link>https://dev.to/bsorrentino/agent-definition-language-adl-the-open-source-standard-for-defining-ai-agents-looks-ibd</link>
      <guid>https://dev.to/bsorrentino/agent-definition-language-adl-the-open-source-standard-for-defining-ai-agents-looks-ibd</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.nextmoca.com/blogs/agent-definition-language-adl-the-open-source-standard-for-defining-ai-agents" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fstatic1.squarespace.com%2Fstatic%2F68793d703bfc192900e52897%2Ft%2F69367d5c303f115f30ef293d%2F1765178760762%2FChatGPT%2BImage%2BDec%2B7%252C%2B2025%2Bat%2B10_59_56%2BPM.png%3Fformat%3D1500w" height="506" class="m-0" width="760"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.nextmoca.com/blogs/agent-definition-language-adl-the-open-source-standard-for-defining-ai-agents" rel="noopener noreferrer" class="c-link"&gt;
            Agent Definition Language (ADL): The Open Source Standard for Defining AI Agents — Next Moca 
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            GitHub Repository:     https://github.com/nextmoca/adl
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.squarespace-cdn.com%2Fcontent%2Fv1%2F68793d703bfc192900e52897%2F3868b990-79f4-4dc0-b01c-f46e74d833a2%2Ffavicon.ico%3Fformat%3D100w" width="100" height="100"&gt;
          nextmoca.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Observability matter</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Fri, 06 Feb 2026 17:47:43 +0000</pubDate>
      <link>https://dev.to/bsorrentino/observability-matter-24m3</link>
      <guid>https://dev.to/bsorrentino/observability-matter-24m3</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/bsorrentino/langgraph4j-hooks-and-opentelemetry-1egk" class="crayons-story__hidden-navigation-link"&gt;LangGraph4j Hooks and OpenTelemetry&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/bsorrentino" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F53518%2Ff0ff1ba7-a659-49a7-92e4-4fac9e829916.png" alt="bsorrentino profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/bsorrentino" class="crayons-story__secondary fw-medium m:hidden"&gt;
              bsorrentino
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                bsorrentino
                
              
              &lt;div id="story-author-preview-content-3237947" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/bsorrentino" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F53518%2Ff0ff1ba7-a659-49a7-92e4-4fac9e829916.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;bsorrentino&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/bsorrentino/langgraph4j-hooks-and-opentelemetry-1egk" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Feb 6&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/bsorrentino/langgraph4j-hooks-and-opentelemetry-1egk" id="article-link-3237947"&gt;
          LangGraph4j Hooks and OpenTelemetry
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/langgraph4j"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;langgraph4j&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/observability"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;observability&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opentelemetry"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opentelemetry&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/bsorrentino/langgraph4j-hooks-and-opentelemetry-1egk" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/bsorrentino/langgraph4j-hooks-and-opentelemetry-1egk#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>langgraph4j</category>
      <category>agents</category>
      <category>observability</category>
      <category>opentelemetry</category>
    </item>
    <item>
      <title>LangGraph4j Hooks and OpenTelemetry</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Fri, 06 Feb 2026 17:44:50 +0000</pubDate>
      <link>https://dev.to/bsorrentino/langgraph4j-hooks-and-opentelemetry-1egk</link>
      <guid>https://dev.to/bsorrentino/langgraph4j-hooks-and-opentelemetry-1egk</guid>
      <description>&lt;h2&gt;
  
  
  Why Observability Matters
&lt;/h2&gt;

&lt;p&gt;Agentic workflows are dynamic: nodes may run conditionally, in parallel, or be skipped entirely. When something fails, a simple stack trace is rarely enough. &lt;strong&gt;Observability&lt;/strong&gt; gives you a unified view of &lt;strong&gt;what&lt;/strong&gt; happened, &lt;strong&gt;where&lt;/strong&gt; it happened, and &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practice, observability helps you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand end-to-end execution flow.&lt;/li&gt;
&lt;li&gt;Detect performance bottlenecks early.&lt;/li&gt;
&lt;li&gt;Correlate errors with specific nodes, edges, and state transitions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Telemetry Matters
&lt;/h2&gt;

&lt;p&gt;Observability is only possible if you collect high‑quality telemetry. Telemetry is the raw signal: &lt;strong&gt;traces&lt;/strong&gt;, &lt;strong&gt;logs&lt;/strong&gt;, and &lt;strong&gt;metrics&lt;/strong&gt;. Without it, probably,  you are debugging your system partially.&lt;/p&gt;

&lt;p&gt;In agent graphs, telemetry becomes even more important because state and control flow are distributed across steps. The same workflow can behave differently depending on state, configuration, or external tool results.&lt;/p&gt;

&lt;h2&gt;
  
  
  The LangGraph4j hooks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://langgraph4j.github.io/langgraph4j/core/hooks/" rel="noopener noreferrer"&gt;LangGraph4j hooks&lt;/a&gt; are small, composable interceptors that sit around &lt;strong&gt;nodes&lt;/strong&gt; and &lt;strong&gt;conditional edges&lt;/strong&gt;. They let you run custom logic &lt;strong&gt;before&lt;/strong&gt;, &lt;strong&gt;after&lt;/strong&gt;, or &lt;strong&gt;wrapping&lt;/strong&gt; the core action, without modifying the node itself. &lt;br&gt;
Hooks can be registered globally (applies to all nodes/edges) or by ID (targeted to a specific node/edge), and their execution order is &lt;strong&gt;LIFO&lt;/strong&gt;(Last In First Out) for &lt;code&gt;BeforeCall&lt;/code&gt;/&lt;code&gt;AfterCall&lt;/code&gt;, &lt;strong&gt;FIFO&lt;/strong&gt;(First In First Out) for &lt;code&gt;WrapCall&lt;/code&gt;. &lt;br&gt;
This makes them a precise, low‑friction mechanism for cross‑cutting concerns like tracing, metrics, logging, or state inspection.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Hooks Enable Observability
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://langgraph4j.github.io/langgraph4j/core/hooks/" rel="noopener noreferrer"&gt;Hooks&lt;/a&gt; provide lifecycle interception points around node and edge execution. This is the ideal place to add logging, metrics, or traces without contaminating business logic.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; you can intercept execution &lt;strong&gt;before&lt;/strong&gt;, &lt;strong&gt;after&lt;/strong&gt;, or &lt;strong&gt;around&lt;/strong&gt; node/edge calls. Here is a minimal example that measures node execution time and logs it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SCHEMA&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addWrapCallNodeHook&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;nodeId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// add Wrap call node hook&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;whenComplete&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"node '{}' took {}ms"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodeId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"node1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action1&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"node2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action2&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;....&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern scales: you can plug in structured logs, metrics counters, or spans with the same hook mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenTelemetry in the Java Ecosystem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://opentelemetry.io/docs/what-is-opentelemetry/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; is the standard for observability across modern platforms. In Java it provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tracing&lt;/strong&gt; via spans&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics&lt;/strong&gt; via meters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt; via instrumentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You configure it once and export telemetry to a backend like &lt;strong&gt;&lt;a href="https://www.jaegertracing.io" rel="noopener noreferrer"&gt;Jaeger&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;Grafana&lt;/strong&gt;, or the &lt;strong&gt;OpenTelemetry Collector&lt;/strong&gt;. The key benefit here is &lt;strong&gt;portability&lt;/strong&gt;: you are not locked to a single vendor.&lt;/p&gt;

&lt;h2&gt;
  
  
  How LangGraph4j Hooks Enable OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt; includes a dedicated module, &lt;code&gt;langgraph4j-opentelemetry&lt;/code&gt;, that provides hook implementations ready for tracing node and edge execution. Consider it as a reference implementation about &lt;strong&gt;how to integrate OpenTelemetry in LangGraph4j&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The module includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;OTELWrapCallTraceHook&lt;/code&gt;: creates spans for each node and edge call, adding config/state attributes and start/end events.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OTELWrapCallTraceSetParentHook&lt;/code&gt;: creates a parent span so node/edge spans are grouped inside a workflow scope.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal integration looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;otelHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OTELWrapCallTraceHook&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parentHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OTELWrapCallTraceSetParentHook&lt;/span&gt;&lt;span class="o"&gt;.&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyWorkflow"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;groupName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"stream"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SCHEMA&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addWrapCallNodeHook&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otelHook&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addWrapCallEdgeHook&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otelHook&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addWrapCallNodeHook&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parentHook&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addWrapCallEdgeHook&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parentHook&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"node1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action1&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"node2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action2&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addContitionalEdges&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"node2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edgeAction&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
            &lt;span class="nc"&gt;EdgeMappings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"node1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toEnd&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;        
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps node logic clean while producing rich, correlated traces for the entire workflow.&lt;/p&gt;

&lt;p&gt;Below an example of output in &lt;a href="https://www.jaegertracing.io" rel="noopener noreferrer"&gt;Jaeger&lt;/a&gt; tracing platform&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwi3989g7rpnk6794wb5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwi3989g7rpnk6794wb5d.png" alt="jaeger" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Hooks are the foundation for observability in &lt;a href="https://github.com/langgraph4j/langgraph4j" rel="noopener noreferrer"&gt;LangGraph4j&lt;/a&gt;: they give you structured interception points without affect your main workflow code. OpenTelemetry builds on top of this by standardizing how telemetry is collected and exported in the Java ecosystem.&lt;br&gt;
Combined, they deliver production-grade visibility for complex agent workflows.&lt;br&gt;
Checkout project, try it and let me know your feedback and ... happy AI coding! 👋&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://opentelemetry.io/docs/what-is-opentelemetry/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://bsorrentino.github.io/bsorrentino/ai/2026/02/04/LangGraph4j-Hooks-and-OpenTelemertry.html" rel="noopener noreferrer"&gt;https://bsorrentino.github.io&lt;/a&gt; on February 4, 2026&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>langgraph4j</category>
      <category>agents</category>
      <category>observability</category>
      <category>opentelemetry</category>
    </item>
    <item>
      <title>I read this in a post: "AI agents are 5% models and 95% engineering glue." I completely agree, but I feel like it's not so clear among software engineers.
This helps us to understand that we need to focus on adding value around models.</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Sat, 31 Jan 2026 08:55:24 +0000</pubDate>
      <link>https://dev.to/bsorrentino/i-read-this-in-a-post-ai-agents-are-5-models-and-95-engineering-glue-i-completely-agree-but-23nl</link>
      <guid>https://dev.to/bsorrentino/i-read-this-in-a-post-ai-agents-are-5-models-and-95-engineering-glue-i-completely-agree-but-23nl</guid>
      <description></description>
      <category>ai</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Top GitHub Users By Public Contributions in Italy 

https://github.com/gayanvoice/top-github-users/blob/main/markdown/public_contributions/italy.md 

I'm at 26th place 👨‍💻 🚀😉</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Fri, 23 Jan 2026 15:04:34 +0000</pubDate>
      <link>https://dev.to/bsorrentino/top-github-users-by-public-contributions-in-italy-1o1i</link>
      <guid>https://dev.to/bsorrentino/top-github-users-by-public-contributions-in-italy-1o1i</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://github.com/gayanvoice/top-github-users/blob/main/markdown/public_contributions/italy.md" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frepository-images.githubusercontent.com%2F373383893%2F3812a400-d11b-11eb-8238-bd579de69457" height="640" class="m-0" width="1280"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://github.com/gayanvoice/top-github-users/blob/main/markdown/public_contributions/italy.md" rel="noopener noreferrer" class="c-link"&gt;
            top-github-users/markdown/public_contributions/italy.md at main · gayanvoice/top-github-users · GitHub
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Check your ranking in GitHub! Don't forget to star ⭐ this repository. - gayanvoice/top-github-users
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.githubassets.com%2Ffavicons%2Ffavicon.svg" width="32" height="32"&gt;
          github.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>[Top GitHub Users By Public Contributions in Italy]
(https://github.com/gayanvoice/top-github-users/blob/main/markdown/public_contributions/italy.md)

I'm at 26th place 👨‍💻 🚀😉</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Fri, 23 Jan 2026 14:58:26 +0000</pubDate>
      <link>https://dev.to/bsorrentino/top-github-users-by-public-contributions-in-italy-1jp</link>
      <guid>https://dev.to/bsorrentino/top-github-users-by-public-contributions-in-italy-1jp</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://github.com/gayanvoice/top-github-users/blob/main/markdown/public_contributions/italy.md" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frepository-images.githubusercontent.com%2F373383893%2F3812a400-d11b-11eb-8238-bd579de69457" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://github.com/gayanvoice/top-github-users/blob/main/markdown/public_contributions/italy.md" rel="noopener noreferrer" class="c-link"&gt;
            top-github-users/markdown/public_contributions/italy.md at main · gayanvoice/top-github-users · GitHub
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Check your ranking in GitHub! Don't forget to star ⭐ this repository. - gayanvoice/top-github-users
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.githubassets.com%2Ffavicons%2Ffavicon.svg"&gt;
          github.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;




</description>
      <category>community</category>
      <category>github</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Exploring Javelit, the Java counterpart to Streamlit, for rapid prototyping and AI agents using LangGraph4j.</title>
      <dc:creator>bsorrentino</dc:creator>
      <pubDate>Sat, 20 Dec 2025 20:42:38 +0000</pubDate>
      <link>https://dev.to/bsorrentino/exploring-javelit-the-java-counterpart-to-streamlit-for-rapid-prototyping-and-ai-agents-using-5gmi</link>
      <guid>https://dev.to/bsorrentino/exploring-javelit-the-java-counterpart-to-streamlit-for-rapid-prototyping-and-ai-agents-using-5gmi</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/bsorrentino/experimenting-with-javelit-the-streamlit-for-java-bll" class="crayons-story__hidden-navigation-link"&gt;Experimenting with Javelit - The Streamlit for Java&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/bsorrentino" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F53518%2Ff0ff1ba7-a659-49a7-92e4-4fac9e829916.png" alt="bsorrentino profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/bsorrentino" class="crayons-story__secondary fw-medium m:hidden"&gt;
              bsorrentino
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                bsorrentino
                
              
              &lt;div id="story-author-preview-content-3117821" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/bsorrentino" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F53518%2Ff0ff1ba7-a659-49a7-92e4-4fac9e829916.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;bsorrentino&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/bsorrentino/experimenting-with-javelit-the-streamlit-for-java-bll" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Dec 20 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/bsorrentino/experimenting-with-javelit-the-streamlit-for-java-bll" id="article-link-3117821"&gt;
          Experimenting with Javelit - The Streamlit for Java
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/java"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;java&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ui"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ui&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javelit"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javelit&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/bsorrentino/experimenting-with-javelit-the-streamlit-for-java-bll#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>java</category>
      <category>ui</category>
      <category>ux</category>
      <category>javelit</category>
    </item>
  </channel>
</rss>
