Skip to main content

Overview

LoopAgent runs its sub-agents in a loop, allowing for iterative refinement and multi-pass processing. The loop continues until:
  1. A sub-agent escalates (signals completion)
  2. Maximum iterations are reached
  3. An error occurs

Basic Usage

import { AgentBuilder, LlmAgent } from '@iqai/adk';

const analyzer = new LlmAgent({
  name: 'analyzer',
  description: 'Analyzes data quality',
  instruction: 'Check if data quality is sufficient',
  outputKey: 'quality_score',
  model: 'gemini-2.5-flash'
});

const improver = new LlmAgent({
  name: 'improver',
  description: 'Improves data',
  instruction: 'Improve data based on quality score: {quality_score}',
  model: 'gemini-2.5-flash'
});

const { runner } = await AgentBuilder
  .create('improvement-loop')
  .asLoop([analyzer, improver], 3) // Max 3 iterations
  .build();

const result = await runner.ask('Improve this text: ...');

Creating a LoopAgent

Using AgentBuilder

const { runner } = await AgentBuilder
  .create('loop-workflow')
  .asLoop(
    [agent1, agent2, agent3],
    5  // Maximum 5 iterations
  )
  .build();

Direct Construction

import { LoopAgent, LlmAgent } from '@iqai/adk';

const loop = new LoopAgent({
  name: 'iterative-processor',
  description: 'Processes data iteratively',
  subAgents: [
    evaluatorAgent,
    refinementAgent
  ],
  maxIterations: 10
});

Configuration

interface LoopAgentConfig {
  // Required
  name: string;
  description: string;
  
  // Sub-agents to loop through
  subAgents?: BaseAgent[];
  
  // Maximum iterations (optional)
  maxIterations?: number;
}

Max Iterations

// Infinite loop (until escalate)
const infiniteLoop = new LoopAgent({
  name: 'infinite',
  description: 'Loops until done',
  subAgents: [agent1, agent2]
  // No maxIterations - runs until escalate
});

// Limited iterations
const limitedLoop = new LoopAgent({
  name: 'limited',
  description: 'Loops up to 5 times',
  subAgents: [agent1, agent2],
  maxIterations: 5 // Stops after 5 iterations
});
Without maxIterations, the loop runs indefinitely until a sub-agent escalates. Always consider adding a maximum to prevent infinite loops.

Escalation (Early Exit)

Sub-agents can signal completion by escalating:
import { LlmAgent } from '@iqai/adk';

const checker = new LlmAgent({
  name: 'quality_checker',
  description: 'Checks if quality is sufficient',
  instruction: `
    Check the quality score in state.
    If quality > 0.8, escalate to signal completion.
    Otherwise, continue processing.
  `,
  model: 'gemini-2.5-flash'
});

const improver = new LlmAgent({
  name: 'improver',
  description: 'Improves quality',
  instruction: 'Improve the content',
  outputKey: 'quality_score',
  model: 'gemini-2.5-flash'
});

const { runner } = await AgentBuilder
  .create('quality-loop')
  .asLoop([checker, improver], 10)
  .build();
Escalation is triggered when an event has actions.escalate set to true. This is typically done through specific tool calls or model instructions.

Execution Flow

// Iteration 1:
//   - agent1 runs
//   - agent2 runs
//   - agent3 runs
// Iteration 2:
//   - agent1 runs
//   - agent2 runs
//   - agent3 runs (escalates)
// Loop stops
The loop executes all sub-agents in sequence, then repeats until:
1
Check for Escalation
2
After each sub-agent runs, check if it escalated
3
Check Max Iterations
4
After completing all sub-agents, check if maxIterations reached
5
Continue or Stop
6
If neither condition met, start next iteration

Use Cases

Iterative Refinement

const drafter = new LlmAgent({
  name: 'drafter',
  description: 'Drafts content',
  instruction: 'Create a draft or improve existing draft',
  outputKey: 'draft',
  model: 'gemini-2.5-flash'
});

const critic = new LlmAgent({
  name: 'critic',
  description: 'Critiques content',
  instruction: 'Critique this draft: {draft}. If excellent, escalate.',
  outputKey: 'feedback',
  model: 'gpt-4'
});

const { runner } = await AgentBuilder
  .create('writing-loop')
  .asLoop([drafter, critic], 5)
  .withQuickSession({ state: { draft: '' } })
  .build();

const result = await runner.ask('Write an article about AI');

Quality Checking

const processor = new LlmAgent({
  name: 'processor',
  description: 'Processes data',
  instruction: 'Process the input data',
  outputKey: 'processed',
  model: 'gemini-2.5-flash'
});

const validator = new LlmAgent({
  name: 'validator',
  description: 'Validates output',
  instruction: 'Validate quality. If > 90%, escalate.',
  outputKey: 'quality',
  model: 'gemini-2.5-flash'
});

const { runner } = await AgentBuilder
  .create('validation-loop')
  .asLoop([processor, validator], 3)
  .build();

Search and Verify

import { createTool } from '@iqai/adk';
import { z } from 'zod';

const searchTool = createTool({
  name: 'search',
  description: 'Search for information',
  schema: z.object({
    query: z.string()
  }),
  fn: async ({ query }) => {
    // Search implementation
    return { results: [] };
  }
});

const searcher = new LlmAgent({
  name: 'searcher',
  description: 'Searches for information',
  instruction: 'Search for relevant information',
  tools: [searchTool],
  outputKey: 'search_results',
  model: 'gemini-2.5-flash'
});

const verifier = new LlmAgent({
  name: 'verifier',
  description: 'Verifies information',
  instruction: 'Verify results: {search_results}. If sufficient, escalate.',
  model: 'gpt-4'
});

const { runner } = await AgentBuilder
  .create('search-verify-loop')
  .asLoop([searcher, verifier], 5)
  .build();

State Management

Loop agents share state across iterations:
const iteration1 = new LlmAgent({
  name: 'counter',
  description: 'Increments counter',
  instruction: `
    Get the current count from state (default 0).
    Increment by 1.
    If count >= 3, escalate.
  `,
  outputKey: 'count',
  model: 'gemini-2.5-flash'
});

const { runner } = await AgentBuilder
  .create('counter-loop')
  .asLoop([iteration1], 10)
  .withQuickSession({ state: { count: 0 } })
  .build();

// Loop will run 3 times:
// Iteration 1: count = 1
// Iteration 2: count = 2  
// Iteration 3: count = 3, escalates

Differences from Other Agents

FeatureLoopAgentSequentialAgentParallelAgent
ExecutionLoops until doneRuns onceRuns once
IterationsMultipleSingleSingle
StateAccumulatesPasses throughIsolated
Early ExitEscalateNoNo
Use CaseRefinementPipelineConcurrency

Live Mode

Live mode (audio/video) is not yet supported for LoopAgent. Attempting to use runLive() will throw an error:
const loop = new LoopAgent({
  name: 'loop',
  description: 'Loop agent',
  subAgents: [agent1]
});

// This will throw an error
for await (const event of loop.runLive(context)) {
  // Error: "This is not supported yet for LoopAgent."
}

Best Practices

1
Always Set Max Iterations
2
Prevent infinite loops:
3
// Good
const loop = new LoopAgent({
  name: 'loop',
  description: 'Safe loop',
  subAgents: [agent1, agent2],
  maxIterations: 10 // Safety limit
});

// Risky
const loop = new LoopAgent({
  name: 'loop',
  description: 'Risky loop',
  subAgents: [agent1, agent2]
  // No max - could loop forever
});
4
Implement Clear Exit Conditions
5
Make escalation conditions explicit:
6
const checker = new LlmAgent({
  name: 'checker',
  description: 'Checks completion',
  instruction: `
    Check the quality score in state.
    If score >= 0.9:
      - Set 'completed' to true in state
      - Escalate to exit the loop
    Otherwise:
      - Continue to next iteration
  `,
  model: 'gemini-2.5-flash'
});
7
Use Output Keys for Iteration
8
Track iteration state:
9
const agents = [
  new LlmAgent({
    name: 'processor',
    description: 'Processes data',
    outputKey: 'iteration_result',
    model: 'gemini-2.5-flash'
  }),
  new LlmAgent({
    name: 'evaluator',
    description: 'Evaluates result',
    instruction: 'Evaluate: {iteration_result}',
    outputKey: 'evaluation',
    model: 'gemini-2.5-flash'
  })
];

const { runner } = await AgentBuilder
  .create('loop')
  .asLoop(agents, 5)
  .build();
10
Monitor Iterations
11
Track how many iterations run:
12
const counter = new LlmAgent({
  name: 'counter',
  description: 'Tracks iterations',
  instruction: `
    Get current iteration count from state.
    Increment by 1.
    Log the iteration number.
    Check if work is complete.
  `,
  outputKey: 'iteration_count',
  model: 'gemini-2.5-flash'
});

Advanced Example

import { AgentBuilder, LlmAgent, createTool } from '@iqai/adk';
import { z } from 'zod';

// Tool to mark completion
const completeTool = createTool({
  name: 'mark_complete',
  description: 'Mark the task as complete',
  fn: (_, context) => {
    context.state.set('completed', true);
    return { success: true };
  }
});

// Researcher agent
const researcher = new LlmAgent({
  name: 'researcher',
  description: 'Researches and gathers information',
  instruction: `
    Research the topic and gather information.
    Add findings to the 'research' state.
  `,
  outputKey: 'research',
  model: 'gemini-2.5-flash'
});

// Analyzer agent
const analyzer = new LlmAgent({
  name: 'analyzer',
  description: 'Analyzes research completeness',
  instruction: `
    Review the research: {research}
    Check if we have enough information.
    
    If sufficient:
      - Call mark_complete tool
      - Escalate to end the loop
    
    If insufficient:
      - Identify gaps
      - Continue to next iteration
  `,
  tools: [completeTool],
  model: 'gpt-4'
});

// Create loop
const { runner } = await AgentBuilder
  .create('research-loop')
  .asLoop([researcher, analyzer], 5)
  .withQuickSession({
    state: {
      research: '',
      completed: false
    }
  })
  .build();

// Execute
const result = await runner.ask('Research quantum computing applications');

Next Steps

Workflow Agents

Other workflow patterns

LlmAgent

Single agent documentation

State Management

Managing state across iterations

Tools

Creating tools for loop control

Build docs developers (and LLMs) love