CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-dev-langchain4j--langchain4j-agentic

LangChain4j Agentic Framework provides a comprehensive Java library for building multi-agent AI systems with support for workflow orchestration, supervisor agents, planning-based execution, declarative configuration, agent-to-agent communication, and human-in-the-loop workflows.

Overview
Eval results
Files

loop.mddocs/workflows/

Loop Workflows

Execute agents iteratively with configurable exit conditions, iteration limits, and loop counter tracking.

Overview

Loop workflows enable:

  • Retry logic with automatic iteration tracking
  • Iterative refinement until quality thresholds are met
  • Convergence-based optimization
  • Configurable exit conditions (predicate or iteration-aware)

Factory Methods

static LoopAgentService<UntypedAgent> loopBuilder();
static <T> LoopAgentService<T> loopBuilder(Class<T> agentServiceClass);

Quick Start:

UntypedAgent loop = AgenticServices.loopBuilder()
    .subAgents(refinementAgent)
    .maxIterations(10)
    .exitCondition(scope -> (Double) scope.readState("quality") >= 0.95)
    .build();

Configuration

Maximum Iterations

LoopAgentService<T> maxIterations(int maxIterations);

Required setting that defines the upper limit for loop execution.

Exit Conditions

LoopAgentService<T> exitCondition(Predicate<AgenticScope> exitCondition);
LoopAgentService<T> exitCondition(BiPredicate<AgenticScope, Integer> exitCondition);
LoopAgentService<T> exitCondition(String description, Predicate<AgenticScope> exitCondition);
LoopAgentService<T> exitCondition(String description, BiPredicate<AgenticScope, Integer> exitCondition);
LoopAgentService<T> testExitAtLoopEnd(boolean checkAtEnd);

Simple predicate:

.exitCondition(scope -> (Boolean) scope.readState("is_valid"))

With iteration count:

.exitCondition((scope, iteration) -> {
    double quality = (Double) scope.readState("quality_score", 0.0);
    return quality >= 0.95 || iteration >= 8;
})

Test at loop end (do-while style):

.exitCondition(scope -> (Boolean) scope.readState("is_complete"))
.testExitAtLoopEnd(true)  // Test after running subagents

Loop Counter

LoopAgentService<T> loopCounterName(String counterName);

Default counter name is "loopCounter". Counter starts at 1 and increments after each iteration.

.loopCounterName("attempt_number")
.beforeCall(scope -> scope.writeState("max_attempts", 10))
.exitCondition(scope -> {
    Boolean success = (Boolean) scope.readState("success");
    return success != null && success;
})

Execution Flow

  1. Loop counter initialized to 1
  2. beforeCall callback executed (if configured)
  3. For each iteration:
    • Sub-agents invoked sequentially
    • Loop counter incremented
    • Exit condition evaluated
  4. Loop terminates when:
    • Exit condition returns true
    • Max iterations reached
    • Error occurs (unless handled)

Common Patterns

Retry with Backoff

UntypedAgent retryLoop = AgenticServices.loopBuilder()
    .subAgents(apiCallAgent)
    .maxIterations(5)
    .loopCounterName("attempt")
    .beforeCall(scope -> {
        int attempt = scope.readState("attempt", 0);
        if (attempt > 1) {
            long backoffMs = (long) Math.pow(2, attempt - 1) * 1000;
            Thread.sleep(backoffMs);
        }
    })
    .exitCondition(scope -> scope.readState("api_response") != null)
    .build();

Iterative Refinement

UntypedAgent refinementLoop = AgenticServices.loopBuilder()
    .subAgents(critic, improver)
    .maxIterations(5)
    .loopCounterName("round")
    .exitCondition(scope -> {
        Critique critique = (Critique) scope.readState("critique");
        return critique != null && critique.getScore() >= 9.0;
    })
    .beforeCall(scope -> {
        String initial = generator.invoke("Create article").toString();
        scope.writeState("content", initial);
    })
    .output(scope -> Map.of(
        "content", scope.readState("content"),
        "rounds", scope.readState("round"),
        "score", ((Critique) scope.readState("critique")).getScore()
    ))
    .build();

Convergence Detection

UntypedAgent optimizer = AgenticServices.agentBuilder()
    .chatModel(chatModel)
    .beforeCall(scope -> {
        Object current = scope.readState("current_solution");
        scope.writeState("previous_solution", current);
    })
    .outputKey("current_solution")
    .build();

UntypedAgent optimizationLoop = AgenticServices.loopBuilder()
    .subAgents(optimizer)
    .maxIterations(100)
    .exitCondition(scope -> {
        Double current = (Double) scope.readState("current_solution");
        Double previous = (Double) scope.readState("previous_solution");
        if (current == null || previous == null) return false;
        return Math.abs(current - previous) < 0.0001;
    })
    .build();

Declarative API

Example:

interface RefinementSystem {
    @LoopAgent(
        name = "content-refiner",
        maxIterations = 10,
        exitConditionMethod = "isQualityAcceptable",
        loopCounterName = "refinement_iteration",
        subAgents = {ContentRefiner.class}
    )
    String refineContent(String initialContent);

    default boolean isQualityAcceptable(AgenticScope scope) {
        Double quality = (Double) scope.readState("quality_score");
        Integer iteration = (Integer) scope.readState("refinement_iteration");
        return (quality != null && quality >= 0.9) ||
               (iteration != null && iteration >= 8);
    }
}

Error Handling

UntypedAgent loop = AgenticServices.loopBuilder()
    .subAgents(processorAgent)
    .maxIterations(10)
    .loopCounterName("retry_attempt")
    .errorHandler(errorContext -> {
        Throwable error = errorContext.error();
        AgenticScope scope = errorContext.agenticScope();
        Integer attempt = (Integer) scope.readState("retry_attempt");

        if (error instanceof TimeoutException && attempt < 5) {
            scope.writeState("last_error", error.getMessage());
            return new ErrorRecoveryResult("retry", true);
        }

        return new ErrorRecoveryResult(null, false);
    })
    .build();

See Also

  • Conditional Workflows - Conditional agent execution
  • Sequential Workflows - Sequential execution
  • Error Handling - Comprehensive error handling

Install with Tessl CLI

npx tessl i tessl/maven-dev-langchain4j--langchain4j-agentic

docs

index.md

tile.json