<?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>Forem: Shane Johnson</title>
    <description>The latest articles on Forem by Shane Johnson (@shanekj).</description>
    <link>https://forem.com/shanekj</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3889281%2F3adf37fc-f4c8-4779-b08c-8f220c45c059.jpg</url>
      <title>Forem: Shane Johnson</title>
      <link>https://forem.com/shanekj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shanekj"/>
    <language>en</language>
    <item>
      <title>Getting started with agentic workflows in Java and Quarkus</title>
      <dc:creator>Shane Johnson</dc:creator>
      <pubDate>Mon, 11 May 2026 17:08:00 +0000</pubDate>
      <link>https://forem.com/shanekj/getting-started-with-agentic-workflows-in-java-and-quarkus-323f</link>
      <guid>https://forem.com/shanekj/getting-started-with-agentic-workflows-in-java-and-quarkus-323f</guid>
      <description>&lt;p&gt;This post walks through building and running a real-world agentic workflow with Agentican and Quarkus.&lt;/p&gt;

&lt;p&gt;Specifically, an agentic workflow to automate market research and information sharing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify&lt;/strong&gt; the top vendors within a market category.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research&lt;/strong&gt; the positioning and strengths of each vendor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classify&lt;/strong&gt; the findings as either standard or urgent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Draft&lt;/strong&gt; a brief to share with others in the company.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quarkus&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java 25&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maven&lt;/strong&gt; (or Gradle)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM provider API key&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Add the dependency
&lt;/h2&gt;

&lt;p&gt;Create a Quarkus app, and add the Agentican Quarkus runtime module:&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;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;ai.agentican&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;agentican-quarkus-runtime&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;0.1.0-alpha.4&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Define agents, skills and the workflow
&lt;/h2&gt;

&lt;p&gt;Create an &lt;code&gt;agentican-catalog.yaml&lt;/code&gt; file on the classpath. &lt;/p&gt;

&lt;p&gt;This is where you describe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Who&lt;/strong&gt; does the work (agents).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What&lt;/strong&gt; they need to do it (skills).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How&lt;/strong&gt; they will do it (workflows).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;agents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;researcher&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;researcher&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Expert at finding accurate, sourced information about &lt;/span&gt;
      &lt;span class="s"&gt;companies and markets. Quotes sources. Distinguishes &lt;/span&gt;
      &lt;span class="s"&gt;opinion from fact.&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;writer&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;writer&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Synthesizes research into structured, concise briefs. &lt;/span&gt;
      &lt;span class="s"&gt;Avoids hedging language. Cites concrete evidence.&lt;/span&gt;

&lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-search&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;web-search&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;When a question requires external information, call the &lt;/span&gt;
      &lt;span class="s"&gt;search tool first. Quote sources in your answer.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the &lt;code&gt;agentican-catalog.yaml&lt;/code&gt; file to define the workflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;market-brief&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;market-brief&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Research vendors in a market and produce a&lt;/span&gt; 
       &lt;span class="s"&gt;structured brief&lt;/span&gt;
    &lt;span class="na"&gt;outputStep&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deliver&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&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;topic&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Market to research&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

      &lt;span class="pi"&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;vendor_count&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Number of vendors&lt;/span&gt;
        &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5"&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&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;identify&lt;/span&gt;
        &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;researcher&lt;/span&gt;
        &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;web-search&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;Identify the top {{param.vendor_count}} vendors in &lt;/span&gt;
          &lt;span class="s"&gt;{{param.topic}}. Return a JSON array of vendor names&lt;/span&gt;
          &lt;span class="s"&gt;— names only, no commentary.&lt;/span&gt;

      &lt;span class="pi"&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;deep-dive&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;loop&lt;/span&gt;
        &lt;span class="na"&gt;over&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;identify&lt;/span&gt;
        &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&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;analyze&lt;/span&gt;
            &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;researcher&lt;/span&gt;
            &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;web-search&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
            &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;Deep-dive vendor {{item}}: positioning, key &lt;/span&gt;
              &lt;span class="s"&gt;strengths, recent news. Quote sources.&lt;/span&gt;

      &lt;span class="pi"&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;classify&lt;/span&gt;
        &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;writer&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;Read the per-vendor deep-dives below. If any vendor has &lt;/span&gt;
          &lt;span class="s"&gt;launched a competitive feature in the last 30 days, &lt;/span&gt;
          &lt;span class="s"&gt;return the single word 'urgent'. Otherwise return &lt;/span&gt;
          &lt;span class="s"&gt;'standard'.&lt;/span&gt;

          &lt;span class="s"&gt;Deep-dives: {{step.deep-dive.output}}&lt;/span&gt;
        &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;deep-dive&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

      &lt;span class="pi"&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;deliver&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;branch&lt;/span&gt;
        &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classify&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;standard&lt;/span&gt;
        &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&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;urgent&lt;/span&gt;
            &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&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;urgent-brief&lt;/span&gt;
                &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;writer&lt;/span&gt;
                &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                  &lt;span class="s"&gt;Synthesize a vendor brief flagged URGENT for &lt;/span&gt;
                  &lt;span class="s"&gt;executive review. Lead with the recent &lt;/span&gt;
                  &lt;span class="s"&gt;competitive moves.&lt;/span&gt;

                  &lt;span class="s"&gt;Topic: {{param.topic}}&lt;/span&gt;
                  &lt;span class="s"&gt;Deep-dives: {{step.deep-dive.output}}&lt;/span&gt;

          &lt;span class="pi"&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;standard&lt;/span&gt;
            &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&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;standard-brief&lt;/span&gt;
                &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;writer&lt;/span&gt;
                &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                  &lt;span class="s"&gt;Synthesize a vendor brief.&lt;/span&gt;

                  &lt;span class="s"&gt;Topic: {{param.topic}}&lt;/span&gt;
                  &lt;span class="s"&gt;Deep-dives: {{step.deep-dive.output}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth flagging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agent: researcher&lt;/code&gt; references the agent for an agent step.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;outputStep&lt;/code&gt; designates the step whose output becomes the workflow's result.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{{param.X}}&lt;/code&gt; interpolates workflow inputs into step instructions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{{step.X.output}}&lt;/code&gt; interpolates an upstream step's output.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{{item}}&lt;/code&gt; is the current value inside a loop iteration.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type: loop&lt;/code&gt; steps take an &lt;code&gt;over&lt;/code&gt; reference (a step that produced a list).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type: loop&lt;/code&gt; steps run their nested &lt;code&gt;steps&lt;/code&gt; once per item, and in parallel.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type: branch&lt;/code&gt; steps take a &lt;code&gt;from&lt;/code&gt; reference (step used to select branch).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;branches:&lt;/code&gt; mutually exclusive steps with &lt;code&gt;default&lt;/code&gt; for unrecognized values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The framework loads &lt;code&gt;agentican-catalog.yaml&lt;/code&gt; from the classpath at boot, or you can define where it's loaded from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;agentican.catalog-config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/agentican/agentican-catalog.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: Agents, skills and workflows can be defined via a fluent builder API as well.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Configure the models
&lt;/h2&gt;

&lt;p&gt;Agentican loads runtime configuration from &lt;code&gt;application.properties&lt;/code&gt;. The minimum is one LLM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;agentican.llm[0]&lt;/span&gt;&lt;span class="py"&gt;.api-key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${ANTHROPIC_API_KEY}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provider defaults to &lt;code&gt;anthropic&lt;/code&gt;, and the model defaults to &lt;code&gt;claude-sonnet-4-5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;OpenAI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;agentican.llm[0]&lt;/span&gt;&lt;span class="py"&gt;.provider&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;
&lt;span class="err"&gt;agentican.llm[0]&lt;/span&gt;&lt;span class="py"&gt;.api-key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${OPENAI_API_KEY}&lt;/span&gt;
&lt;span class="err"&gt;agentican.llm[0]&lt;/span&gt;&lt;span class="py"&gt;.model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple, named LLMs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;agentican.llm[0]&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="err"&gt;agentican.llm[0]&lt;/span&gt;&lt;span class="py"&gt;.api-key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${ANTHROPIC_API_KEY}&lt;/span&gt;

&lt;span class="err"&gt;agentican.llm[1]&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;efficient&lt;/span&gt;
&lt;span class="err"&gt;agentican.llm[1]&lt;/span&gt;&lt;span class="py"&gt;.provider&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;
&lt;span class="err"&gt;agentican.llm[1]&lt;/span&gt;&lt;span class="py"&gt;.api-key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${OPENAI_API_KEY}&lt;/span&gt;
&lt;span class="err"&gt;agentican.llm[1]&lt;/span&gt;&lt;span class="py"&gt;.model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Create a typed workflow instance
&lt;/h2&gt;

&lt;p&gt;Define the workflow input and output records:&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="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;ResearchParams&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;topic&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;vendorCount&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="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;VendorBrief&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;topic&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Vendor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;vendors&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="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Vendor&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;name&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;positioning&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
                         &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;strengths&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;Then inject the typed workflow, and call it from a REST endpoint:&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="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/market-brief"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VendorBriefResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="nd"&gt;@AgenticanWorkflow&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"market-brief"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Workflow&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ResearchParams&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;VendorBrief&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;brief&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{topic}"&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;VendorBrief&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"topic"&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;topic&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;brief&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&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;ResearchParams&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;await&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;Now, test the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8080/market-brief/databases 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth flagging — they're what set this apart from a generic "call an LLM" library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ResearchParams.vendorCount&lt;/code&gt; becomes the workflow parameter &lt;code&gt;vendor_count&lt;/code&gt; via &lt;code&gt;SNAKE_CASE&lt;/code&gt; mapping.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;start()&lt;/code&gt; returns a &lt;code&gt;WorkflowRun&amp;lt;VendorBrief&amp;gt;&lt;/code&gt;. &lt;code&gt;await()&lt;/code&gt; parses the output step's text into a &lt;code&gt;VendorBrief&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@AgenticanWorkflow(name = "vendor-brief")&lt;/code&gt; resolves the registered workflow at injection time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;WorkflowRun&lt;/code&gt; itself exposes &lt;code&gt;future()&lt;/code&gt; for a &lt;code&gt;CompletableFuture&amp;lt;R&amp;gt;&lt;/code&gt;, and there's a &lt;code&gt;ReactiveWorkflow&amp;lt;P, R&amp;gt;&lt;/code&gt; Mutiny variant for Vert.x stacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Add tools (MCP and Composio).
&lt;/h2&gt;

&lt;p&gt;Agentican ships two integrations out of the box:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is one config block per server. Tools are auto-discovered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;agentican.mcp[0]&lt;/span&gt;&lt;span class="py"&gt;.slug&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;github&lt;/span&gt;
&lt;span class="err"&gt;agentican.mcp[0]&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;
&lt;span class="err"&gt;agentican.mcp[0]&lt;/span&gt;&lt;span class="py"&gt;.url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://mcp.github.com/sse&lt;/span&gt;
&lt;span class="err"&gt;agentican.mcp[0]&lt;/span&gt;&lt;span class="py"&gt;.headers.Authorization&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Bearer ${GITHUB_TOKEN}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Composio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;100+ SaaS toolkits — Slack, Notion, Linear, Salesforce, GitHub, Google Workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;agentican.composio.api-key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${COMPOSIO_API_KEY}&lt;/span&gt;
&lt;span class="py"&gt;agentican.composio.user-id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;user-123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tools are reference by name within agent steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&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;research&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;researcher&lt;/span&gt;
    &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;github_search_repositories&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Profile&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;open-source&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;vendors&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{param.topic}}."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Agentican/agentican-framework/blob/main/docs/getting-started.md" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt; — install, configure and run workflows&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Agentican/agentican-framework/blob/main/docs/concepts.md" rel="noopener noreferrer"&gt;Core Concepts&lt;/a&gt; — architecture, terminology and data flow&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Agentican/agentican-framework/blob/main/docs/tasks.md" rel="noopener noreferrer"&gt;Workflows &amp;amp; Steps&lt;/a&gt; — CDI surface, beans, qualifiers, override patterns.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Agentican/agentican-framework/blob/main/docs/agents.md" rel="noopener noreferrer"&gt;Agents&lt;/a&gt; — defining agents, skills and roles&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Agentican/agentican-framework/blob/main/docs/quarkus/getting-started.md" rel="noopener noreferrer"&gt;Getting Started (Quarkus)&lt;/a&gt; — dependency setup, config, first task&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Agentican/agentican-framework/blob/main/docs/quarkus/cdi.md" rel="noopener noreferrer"&gt;CDI Integration&lt;/a&gt; — injection, qualifiers, lifecycle events, bean overrides&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Agentican/agentican-framework/blob/main/docs/quarkus/rest.md" rel="noopener noreferrer"&gt;REST API&lt;/a&gt; — endpoints, SSE streaming, WebSocket, error codes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Agentican/agentican-framework/blob/main/docs/quarkus/observability.md" rel="noopener noreferrer"&gt;Observability&lt;/a&gt; — Micrometer metrics, OTel tracing, Prometheus queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Structured agentic workflows for the JVM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Agentican/agentican-framework" rel="noopener noreferrer"&gt;GitHub →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>java</category>
      <category>quarkus</category>
    </item>
    <item>
      <title>Java Agent Frameworks Are Code-First. Agentican Isn't.</title>
      <dc:creator>Shane Johnson</dc:creator>
      <pubDate>Tue, 21 Apr 2026 17:04:59 +0000</pubDate>
      <link>https://forem.com/shanekj/java-agent-frameworks-are-code-first-agentican-isnt-5f5d</link>
      <guid>https://forem.com/shanekj/java-agent-frameworks-are-code-first-agentican-isnt-5f5d</guid>
      <description>&lt;p&gt;I've been immersed in agentic workflows for the past year, and I had a persistent feeling that something was missing — not in any particular product, but in how the field was thinking about agents.&lt;/p&gt;

&lt;p&gt;Most of the conversation was about chatbots and assistants. Which is fine, but it's a narrow slice of what agents can do. What I kept coming back to was the idea of an &lt;em&gt;AI-native workforce&lt;/em&gt; — not AI as a tool you prompt, but AI as colleagues you delegate to. Agents that can handle complex, multi-step tasks, coordinate with each other, use tools as needed, check in with humans, and work reliably.&lt;/p&gt;

&lt;p&gt;So I started building an AI workforce platform. I kept running into problems that existing frameworks hadn't addressed — not because they were bad, but because they were built with different goals in mind. I open-sourced what I'd learned as the &lt;a href="https://github.com/Agentican/agentican-framework" rel="noopener noreferrer"&gt;Agentican Framework&lt;/a&gt;, an open source multi-agent framework for Java.&lt;/p&gt;

&lt;p&gt;Java was a no-brainer. I'm a former Red Hatter, so Java is home. And Java has always lagged Python in AI — I want to help change that.&lt;/p&gt;

&lt;p&gt;There are agent frameworks for Java. They just weren't what I had in mind.&lt;/p&gt;

&lt;p&gt;Most of them — LangChain4j and Embabel are good examples — are code-first. Agents are annotated classes or interfaces. Workflows are built programmatically. Everything lives in the application. That's a reasonable approach, but it couples the definition of agents and workflows to the applications that run them. Every service that needs a &lt;code&gt;Market Analyst&lt;/code&gt; defines its own. Every workflow that involves one is a new class hierarchy. The more you build, the more you duplicate.&lt;/p&gt;

&lt;p&gt;The model I kept coming back to was simpler: agents, skills, and plans should exist in repositories, independent of any application. Define a &lt;code&gt;Market Analyst&lt;/code&gt;. Let it participate in any workflow that needs it, across any service in the organization. Same for plans — a &lt;code&gt;Competitive Research&lt;/code&gt; workflow shouldn't be owned by the application that first needed it. It should live somewhere it can be versioned, reused, and evolved independently. Separation of concerns, applied to agentic systems.&lt;/p&gt;

&lt;p&gt;This is the core idea behind Agentican. Agents, skills, and plans — and increasingly tools, via MCP and platforms like Composio — are managed as first-class artifacts outside the application. The framework accesses them through repositories (in-memory or persistent) and makes embedding and running them in your service nearly codeless. Extension points exist where they should — custom code steps, custom tools, custom agent loops, custom event listeners — but orchestration is the framework's job. The focus shifts from building agents to embedding them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentican
&lt;/h2&gt;

&lt;p&gt;Here's the fast path to see it in action:&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="k"&gt;try&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;agentican&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AgenticanRuntime&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;llm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LlmConfig&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;apiKey&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;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ANTHROPIC_API_KEY"&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="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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;researchTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agentican&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Research the top 5 LLMs"&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;researchTask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;output&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;No agents, skills or tools are defined. The built-in Planner reads the task description, creates or reuses the agents and skills needed, chooses from available tools, builds a plan and executes it.&lt;/p&gt;

&lt;p&gt;It's the shortest path from "I wish agents could do this" to agents doing it — but it's not how most teams will use the framework in production. In production, you maintain a catalog of agents, skills, and plans — and embed them into your services with next to no code:&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="nd"&gt;@Inject&lt;/span&gt; &lt;span class="nd"&gt;@AgenticanPlan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Competitive Research"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Agentican&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ResearchParams&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ResearchSummary&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;competitiveResearch&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;researchParams&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;ResearchParams&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"realtime CDC"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;ResearchSummary&lt;/span&gt; &lt;span class="n"&gt;researchSummary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="n"&gt;competitiveResearch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runAndAwait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;researchParams&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how the &lt;code&gt;Competitive Research&lt;/code&gt; plan is defined.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic workflows
&lt;/h2&gt;

&lt;p&gt;A competitive research workflow that serves product, marketing, and sales. Product wants to know if market changes warrant a strategic response. Marketing wants per-competitor positioning intel. Sales wants to know who they're likely to run into and what to say.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agents and skills
&lt;/h3&gt;

&lt;p&gt;Agents and skills are defined independently. Multiple agents can share the same skill, and an agent doesn't need every skill for every task — skills are assigned at the step level when defining the plan.&lt;/p&gt;

&lt;p&gt;Define agents and skills in &lt;code&gt;application.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;agentican&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;llm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&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;default&lt;/span&gt;
      &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic&lt;/span&gt;
      &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ANTHROPIC_API_KEY}&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-sonnet-4-5&lt;/span&gt;

  &lt;span class="na"&gt;agents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;market-analyst&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;Market Analyst&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;Analyst on a corporate research team. Profiles companies, maps&lt;/span&gt;
        &lt;span class="s"&gt;market categories, and produces structured findings for internal&lt;/span&gt;
        &lt;span class="s"&gt;decision-makers. Works from evidence; comfortable saying "I don't&lt;/span&gt;
        &lt;span class="s"&gt;know" when the data is thin.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;research-manager&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;Research Manager&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;Senior contributor on a research team. Takes multiple analysts'&lt;/span&gt;
        &lt;span class="s"&gt;findings and combines them into briefings fit for leadership&lt;/span&gt;
        &lt;span class="s"&gt;consumption. Prioritizes signal and structure over&lt;/span&gt;
        &lt;span class="s"&gt;comprehensiveness.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;product-strategy-manager&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;Product Strategy Manager&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;Strategic partner embedded with product leadership. Evaluates the&lt;/span&gt;
        &lt;span class="s"&gt;competitive landscape and translates observation into actionable&lt;/span&gt;
        &lt;span class="s"&gt;recommendations on roadmap, positioning, and bets. Names tradeoffs&lt;/span&gt;
        &lt;span class="s"&gt;explicitly; does not hedge when a call is needed.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;communications-manager&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;Communications Manager&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;Owns outbound messaging on behalf of leadership. Decides how&lt;/span&gt;
        &lt;span class="s"&gt;findings reach stakeholders — who, when, through what channel, in&lt;/span&gt;
        &lt;span class="s"&gt;what form. Final quality gate before anything ships externally.&lt;/span&gt;

  &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;primary-source-preference&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;Primary Source Preference&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;Prefer primary sources: press releases, product pages, engineering&lt;/span&gt;
        &lt;span class="s"&gt;blogs, SEC filings, analyst-report abstracts. Cite sources inline&lt;/span&gt;
        &lt;span class="s"&gt;with URLs when possible. Only use secondary coverage when no primary&lt;/span&gt;
        &lt;span class="s"&gt;source exists, and label it as such. Do not fabricate sources or&lt;/span&gt;
        &lt;span class="s"&gt;quotes.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;momentum-signals&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;Momentum Signals&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;Evaluate momentum from concrete signals: funding rounds in the last&lt;/span&gt;
        &lt;span class="s"&gt;12 months, headcount growth, named customer logos, analyst coverage,&lt;/span&gt;
        &lt;span class="s"&gt;meaningful product releases. Discount vanity signals (press mentions,&lt;/span&gt;
        &lt;span class="s"&gt;award lists, "fastest-growing" claims) unless corroborated.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tiered-comparison&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;Tiered Comparison&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;When comparing multiple entities, organize them into meaningful tiers&lt;/span&gt;
        &lt;span class="s"&gt;before narrating. Group items that share a core property, surface&lt;/span&gt;
        &lt;span class="s"&gt;what distinguishes one tier from another, and call out outliers&lt;/span&gt;
        &lt;span class="s"&gt;within a tier. Favor bullets or tables for the inventory; reserve&lt;/span&gt;
        &lt;span class="s"&gt;prose for synthesis.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;threat-rubric&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;Threat Rubric&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;When classifying competitive threat level, apply this rubric:&lt;/span&gt;
          &lt;span class="s"&gt;high-threat: clear product overlap with our roadmap AND at least&lt;/span&gt;
            &lt;span class="s"&gt;one of (recent Series B+ funding, named enterprise wins,&lt;/span&gt;
            &lt;span class="s"&gt;2× headcount growth in the past year)&lt;/span&gt;
          &lt;span class="s"&gt;low-threat: otherwise&lt;/span&gt;
        &lt;span class="s"&gt;When in doubt, choose low-threat.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;exec-memo-voice&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;Exec Memo Voice&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;When writing for a busy executive audience: one page max, open with&lt;/span&gt;
        &lt;span class="s"&gt;the bottom line in a single sentence, bullets for evidence, close&lt;/span&gt;
        &lt;span class="s"&gt;with concrete recommendations when the memo calls for action. No&lt;/span&gt;
        &lt;span class="s"&gt;hedging, no marketing language.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;multi-channel-publishing&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;Multi Channel Publishing&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;Adapt content to the delivery channel. Email: full artifact,&lt;/span&gt;
        &lt;span class="s"&gt;professional tone. Slack: 1-2 sentence teaser plus a link to the&lt;/span&gt;
        &lt;span class="s"&gt;archive; conversational but concrete. Notion (or other archival&lt;/span&gt;
        &lt;span class="s"&gt;surface): full artifact, titled for searchability. Never send the&lt;/span&gt;
        &lt;span class="s"&gt;same text verbatim across surfaces.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;p&gt;Agents are only as capable as the tools available to them. Agentican supports four kinds of tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Provider tools.&lt;/strong&gt; Most major providers — Anthropic, OpenAI, Google — now ship built-in tools like web search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP servers.&lt;/strong&gt; Model Context Protocol has become the standard for tools (e.g., Notion, Slack, Linear, GitHub and more).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composio.&lt;/strong&gt; Gives agents access to 100s of tools across the SaaS ecosystem — Gmail, Salesforce, HubSpot, Jira and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom toolkits.&lt;/strong&gt; A &lt;code&gt;Toolkit&lt;/code&gt; API lets developers build their own tools and make them available to any agent.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;agentican&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# llms...&lt;/span&gt;
  &lt;span class="c1"&gt;# agents...&lt;/span&gt;
  &lt;span class="c1"&gt;# skills...&lt;/span&gt;

  &lt;span class="na"&gt;mcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;slack&lt;/span&gt;
      &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://mcp.slack.com/mcp&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer ${SLACK_MCP_TOKEN}&lt;/span&gt;

  &lt;span class="na"&gt;composio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${COMPOSIO_API_KEY}&lt;/span&gt;
    &lt;span class="na"&gt;user-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${COMPOSIO_USER_ID}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With agents, skills, and tools configured, it's time to define the plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plan
&lt;/h3&gt;

&lt;p&gt;A plan is a named, versioned workflow — params, a sequence of steps, and an &lt;code&gt;output-step&lt;/code&gt; that designates which step's result becomes the typed return value. Plans are registered at runtime and can be invoked by name, reused across the application, and updated through config without code changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;agentican&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# llms...&lt;/span&gt;
  &lt;span class="c1"&gt;# agents...&lt;/span&gt;
  &lt;span class="c1"&gt;# skills...&lt;/span&gt;
  &lt;span class="c1"&gt;# tools...&lt;/span&gt;

  &lt;span class="na"&gt;plans&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;external-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;competitive-research&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;Competitive Research&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Research the competitive landscape for a category&lt;/span&gt;
      &lt;span class="na"&gt;output-step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Draft Memo&lt;/span&gt;   &lt;span class="c1"&gt;# forward reference&lt;/span&gt;
      &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&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;category&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Category to analyze&lt;/span&gt;
          &lt;span class="na"&gt;default-value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;realtime CDC&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# filled in below&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent steps
&lt;/h3&gt;

&lt;p&gt;Agent steps are the core building block. Each one assigns a task to a named agent, optionally activating a subset of that agent's skills for that step only. Steps with no dependencies — or whose dependencies are already satisfied — run in parallel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&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;Find Leaders&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Market Analyst&lt;/span&gt;
    &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Primary Source Preference&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Identify 3-5 established leaders in {{param.category}}.&lt;/span&gt;
      &lt;span class="s"&gt;Respond with a JSON array of objects with two fields each:&lt;/span&gt;
        &lt;span class="s"&gt;{ "name": "Company", "summary": "1-2 lines on what they sell and market position" }&lt;/span&gt;

  &lt;span class="pi"&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;Find Challengers&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Market Analyst&lt;/span&gt;
    &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Primary Source Preference&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Momentum Signals&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Identify 3-5 emerging or recently-funded challengers in {{param.category}}.&lt;/span&gt;
      &lt;span class="s"&gt;Respond with the same JSON shape as above:&lt;/span&gt;
        &lt;span class="s"&gt;{ "name": "Company", "summary": "1-2 lines on what makes them notable" }&lt;/span&gt;

  &lt;span class="pi"&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;Synthesize Findings&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Research Manager&lt;/span&gt;
    &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Tiered Comparison&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Find Leaders&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Find Challengers&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Synthesize findings on {{param.category}}.&lt;/span&gt;

      &lt;span class="s"&gt;Leaders (JSON):&lt;/span&gt;
      &lt;span class="s"&gt;{{step.Find Leaders.output}}&lt;/span&gt;

      &lt;span class="s"&gt;Emerging (JSON):&lt;/span&gt;
      &lt;span class="s"&gt;{{step.Find Challengers.output}}&lt;/span&gt;

      &lt;span class="s"&gt;Synthesize into a coherent one-page overview, grouping by leader vs emerging.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Find Leaders&lt;/code&gt; and &lt;code&gt;Find Challengers&lt;/code&gt; have no &lt;code&gt;dependencies&lt;/code&gt;, so they run in parallel. &lt;code&gt;Synthesize Findings&lt;/code&gt; waits for both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code steps
&lt;/h3&gt;

&lt;p&gt;Not everything should go through an LLM. Code steps let you drop into typed Java for deterministic work — data transformation, API calls, computation — and wire the result back into the plan graph. They implement a simple &lt;code&gt;CodeStep&amp;lt;I, O&amp;gt;&lt;/code&gt; interface; input and output are any Jackson-serializable types.&lt;/p&gt;

&lt;p&gt;Here we merge two separate JSON arrays into one deduplicated list, ready to iterate. An LLM could do this, but you'd pay for a model call per run and risk hallucinated duplicates.&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;record&lt;/span&gt; &lt;span class="nf"&gt;MergeLists&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;leaders&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;emerging&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Competitor&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;name&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;summary&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MergeCompetitorLists&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CodeStep&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MergeLists&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Competitor&lt;/span&gt;&lt;span class="o"&gt;&amp;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;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="no"&gt;MAPPER&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;ObjectMapper&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;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;TypeReference&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Competitor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;TYPE&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;TypeReference&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{};&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Competitor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MergeLists&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StepContext&lt;/span&gt; &lt;span class="n"&gt;ctx&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;merged&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;LinkedHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Competitor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="no"&gt;MAPPER&lt;/span&gt;&lt;span class="o"&gt;.&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Competitor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;leaders&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;  &lt;span class="no"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putIfAbsent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="no"&gt;MAPPER&lt;/span&gt;&lt;span class="o"&gt;.&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Competitor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emerging&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putIfAbsent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&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;Reference it from the plan by slug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Leaders&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Challengers&lt;/span&gt;
  &lt;span class="c1"&gt;# Synthesize Findings&lt;/span&gt;

  &lt;span class="pi"&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;Merge Competitors&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code&lt;/span&gt;
    &lt;span class="na"&gt;code-slug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;merge-lists&lt;/span&gt;
    &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Find Leaders&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Find Challengers&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;code-input&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;leaders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{step.Find&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Leaders.output}}"&lt;/span&gt;
      &lt;span class="na"&gt;emerging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{step.Find&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Challengers.output}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Merge Competitors&lt;/code&gt; runs concurrently with &lt;code&gt;Synthesize Findings&lt;/code&gt; — both depend on the research steps and nothing else. Its output is a &lt;code&gt;List&amp;lt;Competitor&amp;gt;&lt;/code&gt; serialized as a JSON array, ready to iterate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loop steps
&lt;/h3&gt;

&lt;p&gt;Loop steps fan out over a JSON array, running the body once per element — all in parallel. Inside the body, &lt;code&gt;{{item}}&lt;/code&gt; resolves to the current element and &lt;code&gt;{{item.field}}&lt;/code&gt; to a specific field. The loop's aggregate output is available downstream as a single step reference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Leaders&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Challengers&lt;/span&gt;
  &lt;span class="c1"&gt;# Synthesize Findings&lt;/span&gt;
  &lt;span class="c1"&gt;# Merge Competitors&lt;/span&gt;

  &lt;span class="pi"&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;Research Competitors&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;loop&lt;/span&gt;
    &lt;span class="na"&gt;over&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Merge Competitors&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&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;Profile Competitor&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
        &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Market Analyst&lt;/span&gt;
        &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Primary Source Preference&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Momentum Signals&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;Research {{item.name}} in the context of {{param.category}}:&lt;/span&gt;
          &lt;span class="s"&gt;pricing model, GTM motion, recent funding or headcount signals,&lt;/span&gt;
          &lt;span class="s"&gt;product differentiators.&lt;/span&gt;

          &lt;span class="s"&gt;Build on what we already know:&lt;/span&gt;
          &lt;span class="s"&gt;{{item.summary}}&lt;/span&gt;

          &lt;span class="s"&gt;Write 3-5 sentences.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each competitor gets its own &lt;code&gt;Market Analyst&lt;/code&gt; run in parallel, seeded with what we already know from the merge step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branch steps
&lt;/h3&gt;

&lt;p&gt;Branch steps execute one of several named paths based on an upstream step's output. Each path is its own subgraph. When paths share a terminal step name — as they do here — downstream steps and &lt;code&gt;output-step&lt;/code&gt; resolve cleanly regardless of which path ran.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Leaders&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Challengers&lt;/span&gt;
  &lt;span class="c1"&gt;# Synthesize Findings&lt;/span&gt;
  &lt;span class="c1"&gt;# Merge Competitors&lt;/span&gt;
  &lt;span class="c1"&gt;# Research Competitors&lt;/span&gt;

  &lt;span class="pi"&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;Assess Threat&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Product Strategy Manager&lt;/span&gt;
    &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Threat Rubric&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Research Competitors&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Given these competitor deep dives:&lt;/span&gt;
      &lt;span class="s"&gt;{{step.Research Competitors.output}}&lt;/span&gt;

      &lt;span class="s"&gt;Reply with exactly two words: 'High Threat' or 'Low Threat'.&lt;/span&gt;

  &lt;span class="pi"&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;Route By Threat&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;branch&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Assess Threat&lt;/span&gt;
    &lt;span class="na"&gt;default-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Low&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Threat"&lt;/span&gt;
    &lt;span class="na"&gt;path-configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;High&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Threat"&lt;/span&gt;
        &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&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;Draft Memo&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
            &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Product Strategy Manager&lt;/span&gt;
            &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Exec Memo Voice&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
            &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;Write an urgent strategic memo.&lt;/span&gt;
              &lt;span class="s"&gt;Research: {{step.Research Competitors.output}}&lt;/span&gt;

              &lt;span class="s"&gt;Call out the 2-3 most pressing threats, why they matter now,&lt;/span&gt;
              &lt;span class="s"&gt;and finish with 3 concrete recommendations for our product team.&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Low&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Threat"&lt;/span&gt;
        &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&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;Draft Memo&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
            &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Product Strategy Manager&lt;/span&gt;
            &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Exec Memo Voice&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
            &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;Write a brief routine update.&lt;/span&gt;
              &lt;span class="s"&gt;Research: {{step.Research Competitors.output}}&lt;/span&gt;

              &lt;span class="s"&gt;Two paragraphs — no recommendations needed.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Human-in-the-loop
&lt;/h3&gt;

&lt;p&gt;HITL in Agentican is a checkpoint, not a callback. Flag any agent step with &lt;code&gt;hitl: true&lt;/code&gt; and the workflow suspends &lt;em&gt;after&lt;/em&gt; that step completes — the step's output sits waiting, state is persisted, a checkpoint event fires over SSE, and nothing downstream proceeds until a human approves or rejects via the REST API. Suspended tasks survive restarts and resume exactly where they left off. No custom state management; just a flag.&lt;/p&gt;

&lt;p&gt;Because HITL suspends &lt;em&gt;after&lt;/em&gt; a step runs, the flag has to go on the step whose output you want approved — not on the step that acts on that output. Two different responsibilities, two steps:&lt;/p&gt;

&lt;p&gt;First, a review step. Communications Manager applies the exec-comms guidelines (the &lt;code&gt;Exec Memo Voice&lt;/code&gt; skill), produces the final shape of the brief, and the task suspends for human sign-off before anything goes further:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Leaders&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Challengers&lt;/span&gt;
  &lt;span class="c1"&gt;# Synthesize Findings&lt;/span&gt;
  &lt;span class="c1"&gt;# Merge Competitors&lt;/span&gt;
  &lt;span class="c1"&gt;# Research Competitors&lt;/span&gt;
  &lt;span class="c1"&gt;# Assess Threat&lt;/span&gt;
  &lt;span class="c1"&gt;# Route Threat&lt;/span&gt;
  &lt;span class="c1"&gt;# Draft Memo&lt;/span&gt;

  &lt;span class="pi"&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;Review Brief&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Communications Manager&lt;/span&gt;
    &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Exec Memo Voice&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Draft Memo&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;hitl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Review the draft memo against our exec-comms guidelines: one-line&lt;/span&gt;
      &lt;span class="s"&gt;bottom line, specific-named evidence, no hedging, recommendations&lt;/span&gt;
      &lt;span class="s"&gt;only when a call to action is warranted.&lt;/span&gt;

      &lt;span class="s"&gt;Draft: {{step.Draft Memo.output}}&lt;/span&gt;

      &lt;span class="s"&gt;Output the final brief as a CompetitiveResearchSummary&lt;/span&gt;
      &lt;span class="s"&gt;({headline, assessment, recommendations}). If the draft already&lt;/span&gt;
      &lt;span class="s"&gt;conforms, pass it through; otherwise, correct it.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;Review Brief&lt;/code&gt; finishes, the task transitions to &lt;code&gt;SUSPENDED&lt;/code&gt;, its full state (plan graph, turn history, the draft itself) is persisted to Postgres, and a checkpoint event fires over the Quarkus REST SSE stream. A reviewer sees the draft in whatever UI you've built — the framework ships 18 REST endpoints exactly for this — and either approves or rejects with feedback. On rejection, &lt;code&gt;Review Brief&lt;/code&gt; re-runs with the reviewer's feedback appended to its instructions. A task can sit suspended across multiple deploys for days and resume the moment a human clicks approve.&lt;/p&gt;

&lt;p&gt;This is also why &lt;code&gt;output-step: Review Brief&lt;/code&gt; on the plan (set in the skeleton at the top): the reviewed, approved version is what the typed invoker parses into &lt;code&gt;CompetitiveResearchSummary&lt;/code&gt;. The framework attaches a JSON Schema generated from that record to &lt;code&gt;Review Brief&lt;/code&gt;'s LLM call via the provider's native structured-output mode, so the output is schema-constrained and Jackson-parsable.&lt;/p&gt;

&lt;p&gt;Second, a publish step. No HITL — the brief has already been approved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Leaders&lt;/span&gt;
  &lt;span class="c1"&gt;# Find Challengers&lt;/span&gt;
  &lt;span class="c1"&gt;# Synthesize Findings&lt;/span&gt;
  &lt;span class="c1"&gt;# Merge Competitors&lt;/span&gt;
  &lt;span class="c1"&gt;# Research Competitors&lt;/span&gt;
  &lt;span class="c1"&gt;# Assess Threat&lt;/span&gt;
  &lt;span class="c1"&gt;# Route Threat&lt;/span&gt;
  &lt;span class="c1"&gt;# Draft Memo&lt;/span&gt;
  &lt;span class="c1"&gt;# Review Brief&lt;/span&gt;

  &lt;span class="pi"&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;Publish Brief&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Communications Manager&lt;/span&gt;
    &lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Multi Channel Publishing&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;gmail_send_email&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;slack_post_message&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;notion_create_page&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Review Brief&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;Distribute the approved brief across three channels:&lt;/span&gt;

      &lt;span class="s"&gt;1. Email the exec-distro@ list with the full memo.&lt;/span&gt;
      &lt;span class="s"&gt;2. Post a 1-2 sentence teaser to the #competitive-intel Slack channel.&lt;/span&gt;
      &lt;span class="s"&gt;3. Create a page in the "Competitive Briefs" Notion database titled&lt;/span&gt;
         &lt;span class="s"&gt;with today's date, body = the full memo.&lt;/span&gt;

      &lt;span class="s"&gt;Brief:&lt;/span&gt;
      &lt;span class="s"&gt;{{step.Review Brief.output}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;tools&lt;/code&gt; lists exactly which tool names this step is allowed to call — one from Composio (Gmail) and one from each of the two MCP servers (Slack and Notion). The registry resolves each tool name to its owning toolkit at dispatch, so the agent can't accidentally reach for any other tool in the registered toolkits.&lt;/p&gt;

&lt;p&gt;Two steps, two responsibilities, one HITL gate in the right place: humans approve &lt;em&gt;before&lt;/em&gt; any email lands in an inbox.&lt;/p&gt;

&lt;p&gt;That's the plan. The injection from the top of the post now has everything it needs:&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="nd"&gt;@Inject&lt;/span&gt; &lt;span class="nd"&gt;@AgenticanPlan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Competitive Research"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Agentican&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ResearchParams&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ResearchSummary&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;competitiveResearch&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;researchParams&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;ResearchParams&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"realtime CDC"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;ResearchSummary&lt;/span&gt; &lt;span class="n"&gt;researchSummary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="n"&gt;competitiveResearch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runAndAwait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;researchParams&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;The shift toward AI-native workflows is happening. Teams are already delegating real work to agents — research, analysis, drafting, routing, publishing. The question isn't whether to build agentic systems. It's how.&lt;/p&gt;

&lt;p&gt;For Java teams, the answer has mostly been "assemble it yourself from lower-level primitives" or "wait." Agentican is a bet on a third option: a framework built around the idea that agents, skills, and plans are durable organizational assets — not application scaffolding. That the right model is declarative, repository-based, and designed to support workflows that span services, teams, and time.&lt;/p&gt;

&lt;p&gt;It's alpha. The APIs are stabilizing. But the foundations are real and the direction is clear. If you're building agentic systems on the JVM — or thinking about it — I'd love for you to take a look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&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;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;ai.agentican&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;agentican-framework-core&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;0.1.0-alpha.2&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo, docs, and examples: &lt;a href="https://github.com/Agentican/agentican-framework" rel="noopener noreferrer"&gt;github.com/Agentican/agentican-framework&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback welcome — open an issue, drop a comment, or find me on GitHub.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>java</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
