Skip to main content

Command Palette

Search for a command to run...

Workflow Composition: Parallel, Loop, and Conditional Patterns with LangChain4j

Updated
3 min readView as Markdown
P

I am a Software Engineer from Dallas, Texas, USA, developing cyber security softwares.

The orchestration trilogy is complete. After sequential chaining (#227) and goal-oriented graph planning (#228), this round adds the third pattern: workflow composition — building complex pipelines from parallel, loop, and conditional primitives.

The Three Building Blocks

LangChain4j's agentic module provides three workflow patterns that can be nested inside each other:

Parallel (parallelBuilder)

Runs sub-agents concurrently, then merges results via an output() function:

UntypedAgent parallelResearch = AgenticServices.<String>parallelBuilder()
        .subAgents(researchAgent1, researchAgent2)
        .outputKey("research")
        .output(scope -> {
            String r1 = scope.readState("research1", "");
            String r2 = scope.readState("research2", "");
            return r1 + "\n\n" + r2;
        })
        .build();

Loop (loopBuilder)

Runs sub-agents repeatedly until an exit condition is met:

UntypedAgent refinementLoop = AgenticServices.<String>loopBuilder()
        .subAgents(qualityScorer, improveAgent)
        .maxIterations(3)
        .exitCondition(scope -> scope.readState("score", 0.0) >= 0.8)
        .build();

Conditional (conditionalBuilder)

Routes to different sub-agents based on a predicate:

UntypedAgent conditionalFormatter = AgenticServices.<String>conditionalBuilder()
        .subAgents(
                scope -> "technical".equals(scope.readState("category", "")),
                technicalFormat)
        .subAgents(
                scope -> !"technical".equals(scope.readState("category", "")),
                generalFormat)
        .build();

The Demo Pipeline

The workflow generates a blog post with four phases:

flowchart TD
    subgraph P["Phase 1: Parallel Research"]
        R1[ResearchAgent1<br/>research1]
        R2[ResearchAgent2<br/>research2]
    end

    subgraph L["Phase 3: Refinement Loop"]
        QS[QualityScorerAgent<br/>score]
        IA[ImproveAgent<br/>draft]
    end

    P --> D[Phase 2: DraftAgent<br/>draft]
    D --> L
    L --> CA[Phase 4: CategoryAgent<br/>category]
    CA -->|technical| TF[TechnicalFormatAgent<br/>formatted]
    CA -->|general| GF[GeneralFormatAgent<br/>formatted]
Agent Reads Writes Pattern
ResearchAgent1 topic research1 parallel
ResearchAgent2 topic research2 parallel
WorkflowDraftAgent topic, research1, research2 draft sequential
QualityScorerAgent draft score loop
ImproveAgent draft draft loop
CategoryAgent topic category sequential
TechnicalFormatAgent draft formatted conditional
GeneralFormatAgent draft formatted conditional

All four patterns compose in a single sequence:

this.pipeline = AgenticServices.sequenceBuilder()
        .subAgents(parallelResearch, draftAgent, refinementLoop, categoryAgent)
        .subAgents(conditionalFormatter)
        .outputKey("formatted")
        .build();

Gotchas

Three things worth noting:

  1. Builders must call .build(). parallelBuilder(), loopBuilder(), and conditionalBuilder() return builder objects. You must call .build() to get the UntypedAgent that can be passed to sequenceBuilder().subAgents(). Forgetting .build() causes a cryptic "No agent method found" error.

  2. Parallel output() merges scope keys. The output() function on parallelBuilder reads sub-agent outputs from the scope and combines them. Set outputKey() to write the merged result back to the scope.

  3. Loop exitCondition is checked after each iteration. The predicate receives the live AgenticScope. The loop runs sub-agents sequentially within each iteration — so if you have [scorer, improver], iteration 1 runs scorer then improver, and iteration 2 runs scorer again (checking the exit condition).

What We Learned

The three workflow patterns cover the common orchestration cases:

  • Parallel for independent work that can run concurrently (research, brainstorming)

  • Loop for iterative refinement (quality scoring, style review)

  • Conditional for branching logic (formatting, routing)

  • Sequence for chaining them together

The key insight is that all three are composable. A loop can wrap a parallel workflow. A conditional can branch to a parallel or loop sub-graph. The sequenceBuilder orchestrates them in order.

This completes the orchestration feature set for the demo: crew (supervisor delegation), chain (sequential pipeline), graph (GOAP planning), and workflow (parallel/loop/conditional composition).


Next up: The demo now covers all core orchestration patterns. The remaining open idea is parallel agent execution — running multiple agents concurrently and merging their results, which is partially covered by the parallel pattern here.