<?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: srijithunni7182</title>
    <description>The latest articles on DEV Community by srijithunni7182 (@srijithunni7182).</description>
    <link>https://dev.to/srijithunni7182</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%2F3894712%2Fe640fd49-f998-4aa5-be79-97f01e45044e.jpeg</url>
      <title>DEV Community: srijithunni7182</title>
      <link>https://dev.to/srijithunni7182</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/srijithunni7182"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>srijithunni7182</dc:creator>
      <pubDate>Sat, 10 Oct 2026 11:07:24 +0000</pubDate>
      <link>https://dev.to/srijithunni7182/-g65</link>
      <guid>https://dev.to/srijithunni7182/-g65</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/srijithunni7182/your-coding-agent-shouldnt-write-ai-workflow-plumbing-it-should-write-the-workflow-1om" class="crayons-story__hidden-navigation-link"&gt;Your coding agent shouldn’t write AI workflow plumbing. It should write the workflow.”&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="/srijithunni7182" 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%2F3894712%2Fe640fd49-f998-4aa5-be79-97f01e45044e.jpeg" alt="srijithunni7182 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/srijithunni7182" class="crayons-story__secondary fw-medium m:hidden"&gt;
              srijithunni7182
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                srijithunni7182
                
                
              
              &lt;div id="story-author-preview-content-4827900" 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="/srijithunni7182" 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%2F3894712%2Fe640fd49-f998-4aa5-be79-97f01e45044e.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;srijithunni7182&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/srijithunni7182/your-coding-agent-shouldnt-write-ai-workflow-plumbing-it-should-write-the-workflow-1om" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 10&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/srijithunni7182/your-coding-agent-shouldnt-write-ai-workflow-plumbing-it-should-write-the-workflow-1om" id="article-link-4827900"&gt;
          Your coding agent shouldn’t write AI workflow plumbing. It should write the workflow.”
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&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/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&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;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/srijithunni7182/your-coding-agent-shouldnt-write-ai-workflow-plumbing-it-should-write-the-workflow-1om#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;
            17 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>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Your coding agent shouldn’t write AI workflow plumbing. It should write the workflow.”</title>
      <dc:creator>srijithunni7182</dc:creator>
      <pubDate>Sat, 10 Oct 2026 10:23:44 +0000</pubDate>
      <link>https://dev.to/srijithunni7182/your-coding-agent-shouldnt-write-ai-workflow-plumbing-it-should-write-the-workflow-1om</link>
      <guid>https://dev.to/srijithunni7182/your-coding-agent-shouldnt-write-ai-workflow-plumbing-it-should-write-the-workflow-1om</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://medium.com/@srijith.unni/loom-the-common-language-for-generative-ai-f33c06c99a5c" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AI fatigue is real. New technologies have always pulled developers in. Ruby on Rails, Go, Rust: we'd write a quick "Hello World," play with it for a while, and over months or years settle on what it was actually good for. With AI, that cycle has shrunk to days. There's a new framework, or a new way of doing things, almost every week, and &lt;a href="https://medium.com/@writertripathi/langchain-and-langgraph-are-dead-so-what-to-use-4a6033621fce" rel="noopener noreferrer"&gt;keeping up is so tiring&lt;/a&gt; that many developers stop trying.&lt;/p&gt;

&lt;p&gt;Generative AI brought something traditional programming never had: reasoning. Life is never a straight line, and the beauty of machine learning is that it doesn't assume the rules of life are written in advance. Our own rules aren't either. They come from history and practice etched into our brains, the neural patterns of our ancestors, reinterpreted from time to time. AI offers us a glimpse, a shimmer of hope, that this accumulated human knowledge can be codified to automate workflows that until now needed a chain of people in between.&lt;/p&gt;

&lt;p&gt;Look closely, though, and most of what we're building with it is the same thing. Those workflows are graphs. Coding agents, support bots, personal assistants like OpenClaw all follow that shape. A human workflow is replaced by a network of agents.&lt;/p&gt;

&lt;p&gt;What's being commoditized is the framework for building that graph. LangChain and Spring AI pioneered it, and I have a lot of respect for both. At work we've been using LangChain and LangGraph, and I've come to know them well. When LangChain introduced &lt;a href="https://github.com/langchain-ai/deepagents" rel="noopener noreferrer"&gt;Deep Agents&lt;/a&gt;, a "batteries included" agent harness, I realised it was, underneath, a more opinionated, standardized graph. CrewAI, n8n and the rest &lt;a href="https://www.aibuilderclub.com/blog/is-graph-engineering-just-langgraph" rel="noopener noreferrer"&gt;each offer their own opinion on how a workflow should be built&lt;/a&gt;. As an industry, we're still early in finding the right abstraction.&lt;/p&gt;

&lt;p&gt;Meanwhile the work takes several translations. We picture a workflow, describe it to a coding agent in English, and the agent writes Python or Java that we then have to review to check it built what we pictured. Along the way we have to answer the hard questions in framework terms: what should be a node, what should be an agent, when to ask a person, how much is safe to automate. No wonder so many developers give up on frameworks and hand-roll their own loops.&lt;/p&gt;

&lt;p&gt;It doesn't have to work this way. What if the coding agent didn't write the implementation at all? What if it wrote the workflow itself, in a language you can read in a minute and see as a graph, and the framework turned that into a running system? You would decide what the workflow is and what it must guarantee, and stop reviewing pages of generated plumbing.&lt;/p&gt;

&lt;p&gt;So yes, this is another tool. But it isn't another way to wire graphs. It's a way to stop writing the wiring at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Re-introducing Loom&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I first wrote about Loom's design &lt;a href="https://medium.com/@srijith.unni/i-built-a-programming-language-for-ai-agents-heres-why-715ec7d35bcd" rel="noopener noreferrer"&gt;earlier this year&lt;/a&gt;. That piece covers the compiler side of the story. Since then Loom has grown a security audit, an eval harness, tasks and a CLI. This one is about developer experience.&lt;/p&gt;

&lt;p&gt;Say I want an automated workflow where I describe a problem on my Ubuntu laptop, and the workflow researches it on the web and writes a script to fix it. I describe that in English to my coding agent, and using the Loom guide skill (more on that at the end), it drafts this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// laptop-doctor: describe a laptop problem in English. Agents diagnose it and research it, another writes a fix
// script, a code check blocks anything that deletes data, you approve twice, and only then is the script run.

budget { tokens: 400000  calls: 60 }

tool Inspect {
    use: shell
    allow: "df, free, uptime, lsblk, ps, lscpu, uname, journalctl, dmesg, systemd-analyze, ip, ss"
    timeout: 20s
    unattended: true
}

agent Diagnostician { model: "gemini-2.5-flash"  prompt: "diagnostician"  temperature: 0.1  tools: [Inspect]  max_iterations: 10  guard { pii: mask } }
agent Researcher    { model: "gemini-2.5-flash"  prompt: "researcher"     temperature: 0.2  tools: [web_search] }
agent ScriptAuthor  { model: "gemini-2.5-flash"  prompt: "scriptauthor"   temperature: 0.2  tools: [web_search] }
agent Remediator    { model: "gemini-2.5-flash"  prompt: "remediator"     temperature: 0.1  tools: [Inspect]  max_iterations: 6  guard { pii: mask } }

workflow Main(laptop_issue) {
    // 1. Look at the machine, then research it. The researcher may send the diagnostician back for more (at most 3 extra rounds).
    delegate "Problem: {laptop_issue}\nCollect evidence." to Diagnostician -&amp;gt; evidence_text

    loop until (research_result.verdict == "ENOUGH") max 4 {
        delegate "Round {_loopRound} of 4. Problem: {laptop_issue}\nEvidence:\n{evidence_text}" to Researcher -&amp;gt; research_result expecting { verdict: enum["ENOUGH", "NEED_MORE"], findings: string, request: string }
        alt (research_result.verdict == "NEED_MORE") {
            // the 4th round is the last: whatever it asks for is not collected (so at most 3 extra rounds of evidence)
            alt (_loopRound != "4") {
                delegate "Problem: {laptop_issue}\nEarlier evidence:\n{evidence_text}\nExtra checks requested: {research_result.request}" to Diagnostician -&amp;gt; evidence_text
            }
        }
    }

    // 2. Write a fix script. If no safe fix is supported, stop here.
    delegate "Problem: {laptop_issue}\nResearch findings:\n{research_result.findings}" to ScriptAuthor -&amp;gt; fix_draft expecting { has_script: enum["YES", "NO"], script: string, summary: string }

    alt (fix_draft.has_script == "NO") {
        note "No safe fix was found: {fix_draft.summary}\n\nWhat the research found:\n{research_result.findings}"
    } else {
        // 3. The safety check is code, not a model. A blocked script goes back to the author, at most 2 rewrites.
        run ValidateScript(script = fix_draft.script) -&amp;gt; check_result
        alt (check_result.outcome == "blocked") {
            delegate "Rewrite 1 of 2. Your script was blocked by the safety check: {check_result.reason}\nRewrite it so this cannot happen. Problem: {laptop_issue}\nResearch findings:\n{research_result.findings}" to ScriptAuthor -&amp;gt; fix_draft expecting { script: string, summary: string }
            run ValidateScript(script = fix_draft.script) -&amp;gt; check_result
            alt (check_result.outcome == "blocked") {
                delegate "Rewrite 2 of 2. Your script was blocked again by the safety check: {check_result.reason}\nRewrite it so this cannot happen. Problem: {laptop_issue}\nResearch findings:\n{research_result.findings}" to ScriptAuthor -&amp;gt; fix_draft expecting { script: string, summary: string }
                run ValidateScript(script = fix_draft.script) -&amp;gt; check_result
            }
        }

        alt (check_result.outcome == "blocked") {
            note "Stopped. The safety check blocked every draft: {check_result.reason}\nNothing was saved or run."
        } else {
            // 4. First approval: the person reads the script. A script the check was unsure about says so.
            alt (check_result.outcome == "ask") {
                human_prompt "THE SAFETY CHECK COULD NOT TELL if this script is safe: {check_result.reason}\n\n{fix_draft.script}\n\nWhat it does: {fix_draft.summary}\n\nApprove it anyway? (type yes or no)" -&amp;gt; first_approval
            } else {
                human_prompt "The safety check found nothing that deletes data.\n\n{fix_draft.script}\n\nWhat it does: {fix_draft.summary}\n\nApprove this script? (type yes or no)" -&amp;gt; first_approval
            }

            alt (first_approval == "yes") {
                run SaveScript(script = fix_draft.script, label = "fix") -&amp;gt; saved_file
                // 5. Second approval, just before running: the person may keep the saved file and run it later.
                human_prompt "Saved to {saved_file.value}\nRun it now? (type yes or no)" -&amp;gt; second_approval
                alt (second_approval == "yes") {
                    // 6. Running is code: only the saved file, checked against the approved checksum, 2 minutes at most.
                    run RunScript(path = saved_file.value, sha256 = saved_file.sha256) -&amp;gt; run_outcome
                    // shown at once, so the result is never lost if the spend cap stops the remediator's report
                    note "The script finished ({run_outcome.outcome}):\n{run_outcome.value}"
                    delegate "Original problem: {laptop_issue}\nRun result: {run_outcome.outcome}\nScript output:\n{run_outcome.value}" to Remediator -&amp;gt; final_text
                    note "{final_text}"
                } else {
                    note "Not run. The script is saved at {saved_file.value}; you can read it and run it yourself later."
                }
            } else {
                note "Not approved. Nothing was saved or run."
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The VS Code extension validates it as you type and draws the network of agents beside the editor:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7jwkojtw57163dx8wafo.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7jwkojtw57163dx8wafo.png" alt="loom vscode extension" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What each line buys you&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;budget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tokens:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;calls:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;is a hard ceiling on what one run can spend. It isn't a dashboard you check afterwards. When the allowance is gone, the run stops. The same limits can sit on a single agent or a single step.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tool Inspect { use: shell  allow: "df, free, uptime, ..." }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;gives the agents a shell, but only for the commands you list, all of which look rather than change. Each call has twenty seconds. Because nothing on the list can modify the machine, the agents may use it without asking you each time.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;tools:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Inspect&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;tools:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;web_search&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;decide who can do what. The Diagnostician and the Remediator can look at the machine. The Researcher and the ScriptAuthor can search the web. No agent gets both, and none of them can run the fix.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;guard { pii: mask }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;matters more than it looks. System logs are full of usernames, email addresses and IP addresses. They're replaced with placeholders before anything reaches the model.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;research_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"ENOUGH"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;lets the Researcher send the Diagnostician back for more evidence, at most three extra times. The loop can't run away, and the agents don't decide when to stop. The script does.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;expecting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;verdict:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ENOUGH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NEED_MORE"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;makes each answer arrive in a fixed shape, so the next step branches on verdict or has_script instead of guessing at free text. If the ScriptAuthor finds no safe fix, it says so with&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;has_script:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NO"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;, and the workflow stops honestly instead of inventing one.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run ValidateScript(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;is the heart of it. That isn't a model; it's Java code. The model writes the script, and code decides whether it's safe. A blocked script goes back to the author with the reason, at most twice. If the check can't tell, it says so, and your approval prompt tells you plainly. Same input, same answer, no tokens spent, and no clever wording in a log file can talk it into anything.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;human_prompt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;First you read the script and approve it. Then it's saved, and you're asked again whether to run it now or keep it for later. If nobody is at a console, the workflow pauses without tying up a thread and picks up when the answer arrives, without repeating the work already done.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run RunScript(path = ..., sha256 = ...) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;runs only the file you approved, checked against its checksum so nothing can swap it in between, for two minutes at most. The result is shown immediately, so even if the spend cap stops the Remediator's report, you still see what happened.&lt;/p&gt;

&lt;p&gt;Around the script there's more you didn't have to write: a security audit that reads this file and flags any agent that can read untrusted text, reach private data and act; an evaluation harness for testing it against your own examples; a journal that lets a stopped run resume instead of starting over; a graph of the workflow drawn straight from the script; and a guide your coding agent can follow to build in the same way.&lt;/p&gt;

&lt;p&gt;That's the point of Loom: the workflow is the interesting part, so everything else is built in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One tool, two ways in&lt;/strong&gt;: a command line and VS Code&lt;/p&gt;

&lt;p&gt;Loom ships as a single jar, the weave command line, and a VS Code extension that carries it. There is no server to stand up and no platform to sign in to.&lt;/p&gt;

&lt;p&gt;The command line is how you work in a terminal and how your pipeline works with your workflow:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;weave check laptop-doctor.loom                   # every problem, with its line, without running or spending&lt;br&gt;
weave graph laptop-doctor.loom --format mermaid  # the workflow as a diagram&lt;br&gt;
weave explain laptop-doctor.loom                 # the script described back to you in plain English&lt;br&gt;
weave audit laptop-doctor.loom                   # a security review; exits 1 on a high finding&lt;br&gt;
weave run laptop-doctor.loom --max-tokens 50000  # run it, with a cap&lt;br&gt;
weave package laptop-doctor.loom --fat           # a standalone jar&lt;br&gt;
weave next                                       # what to do next in this project&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It exits with the codes your CI expects.&lt;/p&gt;

&lt;p&gt;The VS Code extension is where most of the writing happens: syntax highlighting, problems underlined as you save, an outline of your workflows, snippets that expand the common statements, a live graph of the workflow that follows your cursor, a one-click run, and the guide one command away.&lt;/p&gt;

&lt;p&gt;Same project, same files, either way in. Check it from the terminal in CI, build it in the editor, and nobody has to translate between the two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Personal data and guardrails, in one line&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A laptop's logs say more about you than you'd think: your username, your email address, the IP addresses you've connected from. Before laptop-doctor lets a model read any of that, the question is what the model gets to see. In Loom, that's a property of the agent, written next to its model:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;agent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Diagnostician&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;model:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gemini-2.5-flash"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;prompt:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"diagnostician"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;temperature:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;tools:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Inspect&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;max_iterations:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;guard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;pii:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;mask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;


&lt;p&gt;The guard has three settings, plus a bias check:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mask&lt;/code&gt;: emails, phone numbers, social security numbers, card numbers and IP addresses become placeholders like [EMAIL] before anything reaches the model. That covers the task, the context, the agent's memory and the results of its tools, so whatever journalctl prints is masked before the Diagnostician sees it. The answer is masked before it's stored too, so the evidence the Researcher takes to the web carries no trace of you.&lt;br&gt;
&lt;code&gt;block:&lt;/code&gt; if a task or an answer contains personal data, the step fails, and the error names the kinds of data, never the values.&lt;br&gt;
&lt;code&gt;warn:&lt;/code&gt; it records the finding and carries on.&lt;br&gt;
&lt;code&gt;bias:&lt;/code&gt; warn or bias: block checks the answer for bias, with simple rules by default, or a model you name as the judge.&lt;/p&gt;

&lt;p&gt;Want to protect just part of a workflow? Wrap those steps in a guardrail and say what happens if it trips:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;guardrail (PII) {&lt;br&gt;
    delegate "Problem: {laptop_issue}\nEvidence:\n{evidence_text}" to Researcher -&amp;gt; research_result&lt;br&gt;
} on_violation {&lt;br&gt;
    note "Personal data reached the research step. Stopped before anything was searched."&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The audit trail follows the same rule: counts and kinds, never the data. Secrets such as API keys, webhook URLs and passwords can't be written into a script at all. They come from the environment or a secret store, and they're scrubbed from results, errors, traces and logs.&lt;/p&gt;

&lt;p&gt;A security review that fits in CI. A built-in audit reads your workflow without running it and shows what each agent can touch. It asks the question that matters most for agents: does any single agent read untrusted content, reach private data and have a way to send or act? That combination is &lt;a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/" rel="noopener noreferrer"&gt;Simon Willison's lethal trifecta&lt;/a&gt;, and it's how a hidden instruction in a log file or a web page becomes a leak or a harmful action. Loom flags it, along with unapproved side effects, a missing budget, or personal data with no guard. Every finding is mapped to the OWASP Top 10 for LLM applications and says what to change.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg1ry3qzgkd0lkms1bsfe.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg1ry3qzgkd0lkms1bsfe.png" alt="weave audit report" width="799" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In laptop-doctor, the agents that look at the machine can't search the web, the ones that search can't touch the machine, and none of them can run the fix. The steps that must always happen aren't prompts at all. They're code. That's next.&lt;/p&gt;

&lt;p&gt;Write your own tools, and make the critical steps unskippable&lt;/p&gt;

&lt;p&gt;Real workflows have to do things: look at a system, call an internal API, run a script. Loom gives you a clear way to think about it.&lt;/p&gt;

&lt;p&gt;In laptop-doctor, Inspect is a tool. The Diagnostician decides which commands to run and when. ValidateScript, SaveScript and RunScript are tasks. No agent decides whether the safety check happens. It happens every time a script is written.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools.&lt;/strong&gt; You get a lot for free: &lt;em&gt;web search&lt;/em&gt;, a &lt;em&gt;calculator&lt;/em&gt;, an &lt;em&gt;OpenAPI tool&lt;/em&gt; that exposes an API from its spec, &lt;em&gt;MCP servers&lt;/em&gt;, and six general-purpose tools: &lt;em&gt;webhook (Slack, Discord, Teams), email, http, file, shell and sql&lt;/em&gt;. They come with safe defaults: https only, no calls to internal or cloud-metadata addresses, the agent picks a path but never the host, credentials only from the environment or a secret store, SQL that only reads, and a shell tool that runs programs directly (no shell syntax) and needs a person's approval. Inspect is that shell tool narrowed to a list of commands that only look, which is why it may run unattended. When you need something that isn't there, a tool is a small Java class:&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;class&lt;/span&gt; &lt;span class="nc"&gt;WordCount&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Tool&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;String&lt;/span&gt; &lt;span class="nf"&gt;getName&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="s"&gt;"word_count"&lt;/span&gt;&lt;span class="o"&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;String&lt;/span&gt; &lt;span class="nf"&gt;getDescription&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="s"&gt;"Counts the words in the text argument."&lt;/span&gt;&lt;span class="o"&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;String&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;Map&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;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&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;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrDefault&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;strip&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;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\s+"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;length&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;It's an ordinary class, which makes it exactly the kind of code your coding agent writes well for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tasks&lt;/strong&gt;. For the steps that must happen, you write a task, plain Java run with a run step:&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="cm"&gt;/** The mandatory safety check. Plain code, no model: outcome is "pass", "ask" (a person decides) or "blocked". */&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;ValidateScript&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Task&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;String&lt;/span&gt; &lt;span class="nf"&gt;getName&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="s"&gt;"ValidateScript"&lt;/span&gt;&lt;span class="o"&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;TaskEffect&lt;/span&gt; &lt;span class="nf"&gt;effect&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="nc"&gt;TaskEffect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NONE&lt;/span&gt;&lt;span class="o"&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;TaskResult&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TaskContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&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;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requireArg&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"script"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;ScriptSafety&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Verdict&lt;/span&gt; &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ScriptSafety&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;check&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;TaskResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TaskResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verdict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;outcome&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="na"&gt;toLowerCase&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;verdict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reasons&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&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;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verdict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reasonText&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;And refer to it in the Loom file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run ValidateScript(script = fix_draft.script) -&amp;gt; check_result
alt (check_result.outcome == "blocked") {
    delegate "Your script was blocked by the safety check: {check_result.reason}\nRewrite it so this cannot happen." to ScriptAuthor -&amp;gt; fix_draft expecting { script: string, summary: string }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Same input, same output, no tokens spent, and no prompt injection can change what it does. A task that changes the outside world says so, gets an idempotency key so a restarted run doesn't do it twice, and can require a person's approval. That's why SaveScript and RunScript are tasks: if a run stops after your approval and resumes the next day, the script isn't saved twice or run twice. It's also what lets you put a safety check, a refund policy or a payment inside an AI workflow. The model writes, and code decides.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autonomy, earned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;laptop-doctor asks you twice, every time. For a script about to run on your own machine, that's right. But picture a decision your team makes fifty times a day: approve this refund, grant this access, escalate this ticket. Ask a person to confirm every one, and within a week nobody is reading the prompt.&lt;/p&gt;

&lt;p&gt;Jairo Cadena makes this point well in &lt;a href="https://www.linkedin.com/pulse/human-in-the-loop-authorization-primitive-jairo-cadena-plh4e/" rel="noopener noreferrer"&gt;Human-in-the-Loop as an Authorization Primitive&lt;/a&gt;. Consent fatigue, he argues, isn't people failing. It's what you should expect from a control designed with no regard for how much attention a person actually has. His remedy is to treat each approval as a real grant of authority: narrowly scoped, used up by the action it allows, recorded with who approved and exactly what they saw, and required only where the risk earns it. He also gives a test for a control that has decayed: approval rates near 100% and decisions made in no time at all mean nobody is reading.&lt;/p&gt;

&lt;p&gt;Loom’s answer to that is &lt;strong&gt;earned autonomy&lt;/strong&gt;. Every organisation asks the same question before it lets an agent act alone: has this agent earned it? Usually that’s a gut call made once, before launch. Loom turns it into a record. The agent proposes, a person decides, and the runtime keeps a ledger of both.&lt;/p&gt;

&lt;p&gt;The model is a &lt;strong&gt;ladder&lt;/strong&gt;. An agent doesn’t get autonomy; it climbs to it. You define the rungs. At the bottom, people decide without seeing what the agent proposed. Higher up, they see the proposal and confirm or override it. At the top, the agent decides alone. On every rung you write what the agent must show to climb, and what makes it slip back.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
ladder RefundLadder {
    rung watch { mode: blind }

    rung suggest {
        mode: confirm
        climb when history.count &amp;gt;= 100 and history.span &amp;gt;= 14d
               and history.rate(agrees).lower95 &amp;gt;= 0.90
    }

    rung act {
        mode: autonomous
        climb when history.count &amp;gt;= 300 and history.rate(agrees).lower95 &amp;gt;= 97%
               and history.count(dangerous) == 0
        slip to suggest when history.last(50).count(dangerous) &amp;gt;= 2,
                             history.last(100).count(reversed) &amp;gt;= 2,
                             history.rate(agrees) &amp;lt; 0.92
        sample 5% blind
        escalate when case.amount &amp;gt; 200, history.since(1d).count &amp;gt;= 50
    }

    climbing needs approval from risk-owner
}

decision Refund {
    proposed by:            Triager
    choices:                approve, reject, escalate
    group cases by:         tier
    remember:               amount: number, tier: text, reason: text
    dangerous mistake:      propose approve, person decides reject
    ask:                    support-lead
    ladder:                 RefundLadder
    when the agent changes: test it on past cases
}

workflow Triage(ticket, tier, amount, reason) {
    decide Refund -&amp;gt; verdict
    alt (verdict == "approve") { note "refund {ticket}" }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Loom supplies the machinery: the ledger, the rung types, and the guarantee that a blind rung really is blind. You supply the policy. Loom can’t know what “good enough” means for your refunds, so you write it down, and the evidence is your own history. Three things keep it honest:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Blind evidence. If people can see the agent’s answer, high agreement may only mean they were nodding along. So only cases decided without the proposal in view count, and that sampling continues after the agent is trusted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slipping is automatic. A dangerous mistake or a run of reversals drops the agent a rung from the next case, with no meeting. Big cases and heavy days still go to a person.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A changed agent starts again. Edit the prompt or swap the model and the ledger treats it as a new agent. Loom replays the old agent’s blind cases under the new one first, and it starts only on the rung that replay earns. You can do the same by hand before you ship:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;weave replay refund.loom --decision Refund --candidate refund-v2.loom --since 14d --limit 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It doesn’t learn, it doesn’t find out by itself that a verdict was wrong (you record that), and it doesn’t certify compliance. It produces evidence, and only the runtime can write to the ledger.&lt;/p&gt;

&lt;p&gt;Don’t trust the agent to be autonomous. Make it earn it, with your own history as the exam, and write the exam yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let your coding agent do the rest&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started by asking why a coding agent should write a workflow in Java or Python at all. It's the question Loom answers, and the last piece is what makes the answer practical.&lt;/p&gt;

&lt;p&gt;The repository carries a skill for coding agents. One command installs it into your project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;weave guide --install-skill .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That puts the Loom guide and a set of instructions in .claude/skills/llm4j-workflow-guide, so your coding agent knows how to build a Loom workflow the way this article does. It starts from a ready-made project, writes the workflow in Loom, puts the steps that must always happen in Java tasks, adds a budget and the guards, and checks the script before anything spends a cent. You describe what you want in English. The agent writes the workflow you can read in a minute and see as a graph, not pages of plumbing for you to review.&lt;/p&gt;

&lt;p&gt;Try it in a few minutes&lt;br&gt;
Clone the repository and run one script:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/srijithunni7182/llm4j.git
&lt;span class="nb"&gt;cd &lt;/span&gt;llm4j
scripts/quick-build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's the whole setup. It builds and tests the libraries, packages the skill as dist/llm4j-workflow-guide.zip, and builds and installs the VS Code extension. If VS Code's code command isn't on your PATH, the script tells you how to install the extension by hand.&lt;/p&gt;

&lt;p&gt;Then add the packaged skill to your coding agent, describe a workflow you've been meaning to automate, and let it build your own version, with guardrails, spending caps, an evaluation report and security built in.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/srijithunni7182" rel="noopener noreferrer"&gt;
        srijithunni7182
      &lt;/a&gt; / &lt;a href="https://github.com/srijithunni7182/llm4j" rel="noopener noreferrer"&gt;
        llm4j
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Native Java Ecosystem for AI - Agents, Tools, Harness and Workflows
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/srijithunni7182/llm4j/docs/images/hero_llm4j.svg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsrijithunni7182%2Fllm4j%2FHEAD%2Fdocs%2Fimages%2Fhero_llm4j.svg" width="640" alt="llm4j: ai-agent4j builds agents, Loom orchestrates them, eval4j tests them"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;llm4j&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;b&gt;AI agents, written the Java way.&lt;/b&gt;&lt;br&gt;
  Typed, testable, observable: from a single tool call to autonomous workflows that run for days
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://central.sonatype.com/artifact/io.github.srijithunni7182/ai-agent4j" rel="nofollow noopener noreferrer"&gt;&lt;img alt="Maven Central" src="https://camo.githubusercontent.com/8b02999761f08ccd5701720e33922effbf277cfda437e4a13e3fae5a1293e751/68747470733a2f2f696d672e736869656c64732e696f2f6d6176656e2d63656e7472616c2f762f696f2e6769746875622e7372696a697468756e6e69373138322f61692d6167656e74346a2e7376673f6c6162656c3d4d6176656e25323043656e7472616c"&gt;&lt;/a&gt;
  &lt;a href="https://www.oracle.com/java/technologies/downloads/#java17" rel="nofollow noopener noreferrer"&gt;&lt;img alt="Java 17+" src="https://camo.githubusercontent.com/e333ccf68c010bb70cf2e21d3a8e801d8df7aa38f9c63a548538f2afd9b445b4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4a6176612d31372532422d6f72616e6765"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/srijithunni7182/llm4j/LICENSE" rel="noopener noreferrer"&gt;&lt;img alt="License: Apache 2.0" src="https://camo.githubusercontent.com/5b60841bea9e11d9d0b0950d690c9bc554e06385634056a7d5d62a15d1a4eabe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4170616368655f322e302d626c75652e737667"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/bf06a8804813f1119a46e8025a778d090ece9820165088f4dd9fb30b85139628/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c4c4d732d47656d696e6925323025374325323053617276616d2532302537432532304f6c6c616d61253230253743253230436c617564652d626c756576696f6c6574"&gt;&lt;img alt="Providers" src="https://camo.githubusercontent.com/bf06a8804813f1119a46e8025a778d090ece9820165088f4dd9fb30b85139628/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c4c4d732d47656d696e6925323025374325323053617276616d2532302537432532304f6c6c616d61253230253743253230436c617564652d626c756576696f6c6574"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/srijithunni7182/llm4j/src/ai-agent4j/wiki/Getting-Started.md" rel="noopener noreferrer"&gt;Get started&lt;/a&gt; ·
  &lt;a href="https://github.com/srijithunni7182/llm4j/docs/THE_STACK.md" rel="noopener noreferrer"&gt;The stack&lt;/a&gt; ·
  &lt;a href="https://github.com/srijithunni7182/llm4j/docs/EXAMPLES.md" rel="noopener noreferrer"&gt;Examples&lt;/a&gt; ·
  &lt;a href="https://github.com/srijithunni7182/llm4j/docs/README.md" rel="noopener noreferrer"&gt;Docs&lt;/a&gt; ·
  &lt;a href="https://github.com/srijithunni7182/llm4j/src/ai-agent4j/wiki/WHY_AI_AGENT4J.md" rel="noopener noreferrer"&gt;Why ai-agent4j?&lt;/a&gt; ·
  &lt;a href="https://github.com/srijithunni7182/llm4j/src/loom/ai-agent4j-loom/WHY_LOOM.md" rel="noopener noreferrer"&gt;Why Loom?&lt;/a&gt; ·
  &lt;a href="https://github.com/srijithunni7182/llm4j/SECURITY.md" rel="noopener noreferrer"&gt;&lt;b&gt;Security&lt;/b&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/srijithunni7182/llm4j/src/ai-agent4j/wiki/xAI_BEYOND_BLACK_BOXES.md" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsrijithunni7182%2Fllm4j%2FHEAD%2Fdocs%2Fimages%2Fxai_banner.svg" alt="The most complete explainable-AI toolkit for Java agents: traceability, confidence and escalation, PII privacy, fairness" width="100%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-alert markdown-alert-important"&gt;
&lt;p class="markdown-alert-title"&gt;Important&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The most complete explainable-AI toolkit for Java agents.&lt;/strong&gt; Every agent step is recorded as an immutable audit event (&lt;strong&gt;traceability&lt;/strong&gt;)
each run carries a &lt;strong&gt;confidence score&lt;/strong&gt; with &lt;code&gt;shouldEscalateToHuman()&lt;/code&gt;, &lt;strong&gt;PII is masked&lt;/strong&gt; before it reaches a model
or a log, and &lt;strong&gt;bias monitors&lt;/strong&gt; can flag or block a response. The four pillars are mapped to GDPR, the EU AI Act and
the NIST AI RMF in &lt;strong&gt;&lt;a href="https://github.com/srijithunni7182/llm4j/src/ai-agent4j/wiki/xAI_BEYOND_BLACK_BOXES.md" rel="noopener noreferrer"&gt;xAI: Beyond Black Boxes&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;These ship in the box. With Spring AI or LangChain4j you get observability and PII guardrails, but you build confidence scoring, bias monitoring and the audit trail yourself.&lt;/p&gt;
&lt;/div&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why llm4j&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Java runs the systems that can't go down…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/srijithunni7182/llm4j" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Clone it, try it, and if you like the idea, give the repo a star. It's still young, so you'll likely find rough edges and bugs. Tell me about them in the comments or open an issue, and I'll keep improving it with your feedback.&lt;/p&gt;

&lt;p&gt;What's the part of your current agent workflow you'd most like to stop hand-wiring? I'd love to hear in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>opensource</category>
      <category>llm</category>
    </item>
    <item>
      <title>Building Production AI Agents in Java: Introducing llm4j and the Loom Orchestration DSL</title>
      <dc:creator>srijithunni7182</dc:creator>
      <pubDate>Thu, 23 Apr 2026 17:17:02 +0000</pubDate>
      <link>https://dev.to/srijithunni7182/building-production-ai-agents-in-java-introducing-llm4j-and-the-loom-orchestration-dsl-10bi</link>
      <guid>https://dev.to/srijithunni7182/building-production-ai-agents-in-java-introducing-llm4j-and-the-loom-orchestration-dsl-10bi</guid>
      <description>&lt;p&gt;The AI agent ecosystem has a Python problem.&lt;/p&gt;

&lt;p&gt;Not that Python is bad — it's a great language. But when you're building production systems that need type safety, JVM performance, Spring Boot integration, enterprise security controls, and the ability to onboard a team of Java engineers without retraining them on a new ecosystem — the Python-first assumption of most AI frameworks starts to feel like a tax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;llm4j&lt;/strong&gt; is a Java-native AI agent framework that doesn't ask you to pay that tax. It's built from the ground up for the JVM, ships on Maven Central, and includes a purpose-built orchestration DSL called &lt;strong&gt;Loom&lt;/strong&gt; that solves a problem most frameworks don't even acknowledge: the coordination layer of a multi-agent system should be symbolic, not probabilistic.&lt;/p&gt;

&lt;p&gt;Let me show you what that means in practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stack at a Glance
&lt;/h2&gt;

&lt;p&gt;llm4j is organized into three layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;llm4j/
├── ai-agent4j          # Core library — LLM clients, ReAct agents, RAG, memory
├── ai-agent4j-addons   # Local embeddings (ONNX/DJL), pgvector, Pinecone
└── loom/
    ├── ai-agent4j-loom # Loom DSL runtime — Lexer, Parser, AST, HarnessExecutor
    ├── vscode-loom     # VS Code extension for .loom files
    └── ctk             # Conformance Test Kit — behavioral contracts for all runtimes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;code&gt;ai-agent4j&lt;/code&gt; standalone as a lightweight LLM client and ReAct agent framework. Or you can layer Loom on top for complex multi-agent orchestration. They're designed to work together but are independently useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: ai-agent4j — The Core Library
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&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;io.github.srijithunni7182&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;ai-agent4j&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;5.0&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;h3&gt;
  
  
  A Minimal LLM Client
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;LLMConfig&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;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;"GOOGLE_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;defaultModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash"&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="nc"&gt;LLMClient&lt;/span&gt; &lt;span class="n"&gt;client&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;DefaultLLMClient&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;GoogleProvider&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="nc"&gt;LLMResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LLMRequest&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;addUserMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Summarize the key risks in this contract: {text}"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No decorators, no async event loops, no Python runtime embedded in your JVM process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider Support
&lt;/h3&gt;

&lt;p&gt;ai-agent4j ships with first-class support for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Gemini&lt;/strong&gt; (1.5 Flash, Pro, 2.x) — the primary target, deeply optimized&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sarvam AI&lt;/strong&gt; — Indian language voice agents with TTS, STT, and translation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama&lt;/strong&gt; — local models (Gemma, Llama, Mistral) with zero cost and zero internet dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a routing layer that makes provider switching transparent:&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;LLMConfig&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;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;primaryModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fallbackModels&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;.&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;"claude-3-haiku"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gemini-1.5-flash"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RoutingStrategy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;COST_AWARE&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your primary provider rate-limits or fails, the router falls over to the next option automatically. No retry boilerplate in your application code.&lt;/p&gt;

&lt;h3&gt;
  
  
  ReAct Agents with Tool Use
&lt;/h3&gt;

&lt;p&gt;The ReAct (Reasoning + Acting) loop is a first-class primitive:&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;ReActAgent&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ReActAgent&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;client&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;systemPrompt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You are a financial analyst."&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSearchTool&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CalculatorTool&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxIterations&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&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="nc"&gt;AgentResult&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;agent&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;"What is the current P/E ratio of NVDA?"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent reasons, decides which tool to call, observes the result, and iterates until it has a final answer — or hits the iteration limit. The Thought-Action-Observation loop is fully transparent and auditable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory: Short-Term and Long-Term
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Conversation history&lt;/strong&gt; (short-term) is managed automatically. &lt;strong&gt;Semantic memory&lt;/strong&gt; (long-term) uses vector embeddings to recall relevant facts across sessions:&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;ReActAgent&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ReActAgent&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;client&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SemanticMemory&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;embeddingProvider&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;OnnxEmbeddingProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"models/onnx/model.onnx"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"models/onnx/tokenizer.json"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;vectorStore&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;PGVectorStore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jdbc:postgresql://localhost/mydb"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pass"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"agent_memory"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;384&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;similarityThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxResults&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent will automatically retrieve relevant memories before each response. No manual retrieval logic in your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  RAG Pipelines
&lt;/h3&gt;

&lt;p&gt;The addons module adds local embeddings and persistent vector stores:&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;io.github.srijithunni7182&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;ai-agent4j-addons&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;5.0&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Local embeddings — no API calls, no cost&lt;/span&gt;
&lt;span class="nc"&gt;OnnxEmbeddingProvider&lt;/span&gt; &lt;span class="n"&gt;embedder&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;OnnxEmbeddingProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"models/onnx/model.onnx"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"models/onnx/tokenizer.json"&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// pgvector for production persistence&lt;/span&gt;
&lt;span class="nc"&gt;PGVectorStore&lt;/span&gt; &lt;span class="n"&gt;store&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;PGVectorStore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"jdbc:postgresql://localhost:5432/postgres"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"postgres"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"secret"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"document_embeddings"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;384&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;embed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your document chunk here"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doc_001"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector&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;"source"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"contracts"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"year"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2024"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Semantic search&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;SearchResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryVector&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use Pinecone for managed cloud storage — same API, different backend.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: Loom — The Orchestration DSL
&lt;/h2&gt;

&lt;p&gt;Here's where llm4j diverges from every other Java AI framework.&lt;/p&gt;

&lt;p&gt;Most multi-agent frameworks — in any language — ask you to express coordination logic in the same language as your application. Python functions, YAML configs, JSON schemas. The routing rules, the retry logic, the branching conditions — all of it lives in your application code, tightly coupled to the framework's execution model.&lt;/p&gt;

&lt;p&gt;This creates a problem that gets worse as your system grows: &lt;strong&gt;you can't tell where the AI ends and the application begins.&lt;/strong&gt; The coordination logic bleeds into your business logic. The retry handling bleeds into your prompt engineering. The whole thing becomes a tangle that's hard to read, hard to audit, and hard to hand to a new engineer.&lt;/p&gt;

&lt;p&gt;Loom's answer is a hard architectural boundary: &lt;strong&gt;the coordination layer is a separate language with its own runtime.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Idea: Neuro-Symbolic AI
&lt;/h3&gt;

&lt;p&gt;The term "neuro-symbolic" describes a system that combines neural components (LLMs — probabilistic, reasoning about content) with symbolic components (deterministic rules — routing, branching, safety constraints).&lt;/p&gt;

&lt;p&gt;The key insight is that these two things should not be mixed. The model should reason about content. The harness should govern coordination. And the boundary between them should be explicit, auditable, and enforced by a runtime — not inferred from a prompt.&lt;/p&gt;

&lt;p&gt;Loom is that harness, expressed as a DSL.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Real Loom Script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// agents.loom

routing CostAware {
    strategy: "COST_AWARE"
    primary: "gpt-4o"
    fallback: ["claude-3-haiku", "gemini-1.5-flash"]
}

agent Researcher {
    model: "gpt-4o"
    system: "You are a rigorous research analyst."
    routing: CostAware
    knowledge {
        type: "RAG"
        path: "data/research_kb/"
        embedding: "text-embedding-3-small"
    }
}

agent Writer {
    model: "claude-3-opus"
    system: "You write clear, compelling technical articles."
}

agent Auditor {
    model: "gemini-1.5-pro"
    system: "You audit content for accuracy and compliance."
    output_schema: {
        status: enum["APPROVED", "NEEDS_REVISION"],
        issues: list&amp;lt;string&amp;gt;
    }
}

workflow ResearchAndPublish(topic) {

    // Retry with structured failure handling
    delegate "Research: {topic}" to Researcher -&amp;gt; findings
        retry 3
        on_failure {
            note "Research failed: {_error}"
            handoff "Manual research needed for: {topic}" to Writer
        }

    // Typed symbolic branching — not a string match
    alt (findings.status == "SUFFICIENT") {

        // Parallel execution — both run concurrently
        parallel {
            delegate "Write article: {findings}" to Writer -&amp;gt; draft
            delegate "Audit findings: {findings}" to Auditor -&amp;gt; audit
        }

        alt (audit.status == "APPROVED") {
            handoff "Publish: {draft}" to Writer
        } else {
            loop until (approved == "true") {
                delegate "Revise based on: {audit.issues}" to Writer -&amp;gt; draft
                human_prompt "Approve revised draft? (true/false)" -&amp;gt; approved
            }
        }

    } else {
        handoff "Insufficient research for: {topic}" to Researcher
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me walk through what's happening here, because every construct is doing something specific.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;routing CostAware&lt;/code&gt;&lt;/strong&gt; — a global policy. When the Researcher's primary model rate-limits, the runtime falls over to the fallback list automatically. This is declared once and applied by name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;knowledge { type: "RAG" }&lt;/code&gt;&lt;/strong&gt; — the Researcher has RAG-backed context. Before each call, the runtime retrieves relevant documents from the knowledge base and injects them into the prompt. Configured in the DSL, not in Java code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;output_schema&lt;/code&gt;&lt;/strong&gt; — the Auditor is constrained to return structured JSON. The runtime coerces the model's response before any condition is evaluated. &lt;code&gt;audit.status == "APPROVED"&lt;/code&gt; is a typed enum check, not a string match against free text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;delegate ... retry 3 on_failure { }&lt;/code&gt;&lt;/strong&gt; — resilience contracts in the DSL. If the Researcher fails three times, the runtime executes the &lt;code&gt;on_failure&lt;/code&gt; block. The model never sees this logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;parallel { }&lt;/code&gt;&lt;/strong&gt; — concurrent execution. Both the Writer and Auditor run in separate threads. Execution waits at the closing brace for both to complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;loop until&lt;/code&gt;&lt;/strong&gt; — symbolic retry. The runtime evaluates the condition. The model doesn't decide when to stop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;human_prompt&lt;/code&gt;&lt;/strong&gt; — first-class human-in-the-loop. Execution suspends until input arrives.&lt;/p&gt;

&lt;p&gt;The entire coordination graph — retries, parallelism, branching, human approval gates — is in the &lt;code&gt;.loom&lt;/code&gt; file. The models only see their individual task strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build and alias the CLI&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;loom/ai-agent4j-loom &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; mvn clean &lt;span class="nb"&gt;install
alias &lt;/span&gt;&lt;span class="nv"&gt;weave&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'java -cp "target/classes:target/lib/*" io.github.llm4j.loom.cli.WeaveCLI'&lt;/span&gt;

&lt;span class="c"&gt;# Run&lt;/span&gt;
weave run research.loom &lt;span class="nt"&gt;--loot&lt;/span&gt; tools.loot &lt;span class="nt"&gt;--input&lt;/span&gt; &lt;span class="nv"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Neuro-Symbolic AI"&lt;/span&gt;

&lt;span class="c"&gt;# Package for deployment&lt;/span&gt;
weave package research.loom &lt;span class="nt"&gt;--loot&lt;/span&gt; tools.loot &lt;span class="nt"&gt;--fat&lt;/span&gt; &lt;span class="nt"&gt;--out&lt;/span&gt; research-app.jar
java &lt;span class="nt"&gt;-jar&lt;/span&gt; research-app.jar &lt;span class="nv"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Quantum Computing"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Embedding in a Spring Boot Application
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&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;ResearchService&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;HarnessExecutor&lt;/span&gt; &lt;span class="n"&gt;executor&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;ResearchService&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;String&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&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;"workflows/research.loom"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="nc"&gt;LoomScript&lt;/span&gt; &lt;span class="n"&gt;script&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;LoomParser&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;Lexer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;parseScript&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;ToolRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&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;ToolRegistry&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;LootLoader&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;loadIntoRegistry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tools.loot"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&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;executor&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;HarnessExecutor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&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;DefaultLLMClientFactory&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;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initialize&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;research&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;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeWorkflow&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ResearchAndPublish"&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;"topic"&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="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Full Primitive Set
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delegate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Call one agent, await result, bind to variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;broadcast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fan out to multiple agents in parallel, collect combined result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;handoff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Terminal node — pass control to an agent and end the current branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;parallel { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Concurrent execution block — every statement runs in its own thread&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;alt&lt;/code&gt; / &lt;code&gt;else&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Symbolic conditional branching on typed variable values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;loop until&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retry block — executes until a symbolic condition is met&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;human_prompt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocking suspension — parks execution until human input arrives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;guardrail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wraps a block — intercepts output before it escapes (e.g. PII detection)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;call&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invoke a sub-workflow with isolated variable scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;observe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Emit a structured trace event without affecting control flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;note&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inline documentation — ignored by the runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;import&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split large workflows across files — merged into a flat namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Part 3: The Tooling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  VS Code Extension
&lt;/h3&gt;

&lt;p&gt;Writing &lt;code&gt;.loom&lt;/code&gt; files without tooling is painful. The VS Code extension provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax highlighting for &lt;code&gt;.loom&lt;/code&gt; and &lt;code&gt;.loot&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Real-time LSP diagnostics — errors as you type, with precise line/column&lt;/li&gt;
&lt;li&gt;Hover documentation and go-to-definition for agents and workflows&lt;/li&gt;
&lt;li&gt;Completion for all Loom keywords and identifiers&lt;/li&gt;
&lt;li&gt;Workflow Outline sidebar — see the full structure of a script at a glance&lt;/li&gt;
&lt;li&gt;Run Workflow command — execute the current file via &lt;code&gt;weave&lt;/code&gt; without leaving the editor&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conformance Test Kit (CTK)
&lt;/h3&gt;

&lt;p&gt;This is the part that makes Loom a language rather than a library.&lt;/p&gt;

&lt;p&gt;The CTK is a suite of 15 canonical &lt;code&gt;.loom&lt;/code&gt; scripts — one per primitive — paired with expected execution traces and mock agent fixtures. Any Loom runtime (Java, Python, or future implementations) must pass the CTK to be considered conformant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;loom/ctk
mvn &lt;span class="nb"&gt;test
&lt;/span&gt;mvn &lt;span class="nb"&gt;exec&lt;/span&gt;:java &lt;span class="nt"&gt;-Dexec&lt;/span&gt;.mainClass&lt;span class="o"&gt;=&lt;/span&gt;io.github.loom.ctk.CtkMain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trace comparison algorithm checks structure, not values — it verifies that the right agents were called in the right order with the right statement types, without being brittle to changes in model output. This is what makes behavioral parity across runtimes verifiable rather than assumed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Java for AI Agents?
&lt;/h2&gt;

&lt;p&gt;This is the question I get most often, so let me answer it directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type safety at the boundary.&lt;/strong&gt; The hardest problem in multi-agent systems is the boundary between model output (free text) and application logic (typed values). Java's type system, combined with Loom's output schemas, makes this boundary explicit and enforced at compile time where possible and at runtime where not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JVM performance.&lt;/strong&gt; The HarnessExecutor uses Java's &lt;code&gt;CompletableFuture&lt;/code&gt; and thread pools for parallel execution. For workflows with many concurrent branches, the JVM's threading model is a genuine advantage over Python's GIL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enterprise integration.&lt;/strong&gt; Spring Boot, Quarkus, Jakarta EE, JDBC, JPA — the Java ecosystem for enterprise integration is unmatched. If your AI agents need to talk to Oracle databases, SAP systems, or internal REST APIs with complex auth flows, you want to be in Java.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Existing teams.&lt;/strong&gt; Most enterprise engineering teams are Java shops. Asking them to adopt Python for AI work means new tooling, new CI/CD pipelines, new security reviews, and a split codebase. llm4j lets them build AI agents in the language they already know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auditability.&lt;/strong&gt; The Loom DSL produces a complete execution trace for every workflow run. Every &lt;code&gt;delegate&lt;/code&gt;, &lt;code&gt;broadcast&lt;/code&gt;, &lt;code&gt;call&lt;/code&gt;, and &lt;code&gt;handoff&lt;/code&gt; is recorded with its agent, payload, and output variable. This is not optional — it's built into the runtime. For regulated industries, this matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Stats
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lines of Code&lt;/td&gt;
&lt;td&gt;13,700+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Cases&lt;/td&gt;
&lt;td&gt;438+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maven Central&lt;/td&gt;
&lt;td&gt;v5.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java Version&lt;/td&gt;
&lt;td&gt;17+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Library Size&lt;/td&gt;
&lt;td&gt;~308 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/srijithunni7182/llm4j.git
&lt;span class="nb"&gt;cd &lt;/span&gt;llm4j

&lt;span class="c"&gt;# Build the core library&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ai-agent4j &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; mvn clean &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Build and run the Loom sample&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../loom/ai-agent4j-loom &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; mvn clean &lt;span class="nb"&gt;install
alias &lt;/span&gt;&lt;span class="nv"&gt;weave&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'java -cp "target/classes:target/lib/*" io.github.llm4j.loom.cli.WeaveCLI'&lt;/span&gt;
weave run samples/boardroom/main.loom &lt;span class="nt"&gt;--input&lt;/span&gt; &lt;span class="nv"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"The future of AI agents"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or add the Maven dependency and start with the &lt;a href="https://github.com/srijithunni7182/llm4j/wiki/Getting-Started" rel="noopener noreferrer"&gt;Quick Start Guide&lt;/a&gt;.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;loom4py&lt;/strong&gt; is in active development — a native Python implementation of the Loom runtime that will be validated against the CTK for full behavioral parity with the Java reference. The goal: write a &lt;code&gt;.loom&lt;/code&gt; script once, run it on any conformant runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contribute
&lt;/h2&gt;

&lt;p&gt;llm4j is MIT licensed and actively developed. The repo is at:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/srijithunni7182/llm4j" rel="noopener noreferrer"&gt;https://github.com/srijithunni7182/llm4j&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Good first contributions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New Loom samples&lt;/strong&gt; — real-world workflows in &lt;code&gt;loom/ai-agent4j-loom/samples/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New tool implementations&lt;/strong&gt; — anything that implements the &lt;code&gt;Tool&lt;/code&gt; interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New provider integrations&lt;/strong&gt; — OpenAI, Anthropic, Cohere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;loom4py&lt;/strong&gt; — the Python runtime is the most impactful open contribution right now&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CTK runtime ports&lt;/strong&gt; — build a conformant Loom runtime in Go, Rust, or TypeScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building multi-agent systems in Java and you've been waiting for a framework that takes the JVM seriously — this is it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Star the repo, open an issue, or just say hello. The model reasons. The harness governs.&lt;/em&gt;&lt;/p&gt;

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