Autonomous Agent Loops

Build robust ReAct autonomous agent loops with termination conditions, state history management, and recursion limits.

TL;DR

  1. Call the model, run its chosen tool, append the observation, repeat.
  2. Cap every loop with a maxIterations counter so runaway agents stop.
  3. Store each observation in a history array for the model to read.

ReAct Loop Architecture

    While Loop Framework

    Core iterative dispatch cycle powering autonomous reasoning.

    let iterations = 0;
    const maxIterations = 10;
    while (iterations < maxIterations) {
      iterations++;
      const res = await callModel(messages);
      if (!res.hasToolUse) return res.finalAnswer;
      await handleTools(res.toolUse);
    }
    Termination Evaluator

    Evaluate whether agent has satisfied original user objective.

    function isComplete(res: ModelResponse): boolean {
      // Stop reason is end_turn and no pending tools
      return res.stop_reason === 'end_turn' &&
             !res.content.some(b => b.type === 'tool_use');
    }
    Recursion Depth Guard

    Throw explicit boundary exception if iteration ceiling breached.

    if (iterations >= maxIterations) {
      logger.warn('Agent exceeded iteration ceiling');
      return 'Agent reached maximum iteration limit.';
    }

State And Memory Management

    Append-Only History

    Maintain chronological audit trail of agent steps and data.

    messages.push({
      role: 'assistant',
      content: step.content,
    });
    messages.push({
      role: 'user',
      content: step.toolResults,
    });
    Observation Compression

    Truncate oversized tool payloads to preserve context budget.

    function compressResult(raw: string, maxChars = 1500) {
      if (raw.length <= maxChars) return raw;
      return raw.slice(0, maxChars) + '... [truncated]';
    }
    Scratchpad Reflection

    Inject periodic evaluation directives into conversational state.

    if (iterations === 5) {
      messages.push({
        role: 'user',
        content: 'Pause and evaluate progress to goal.',
      });
    }

Loop Defenses And Safeguards

    Repetition Detector

    Detect cyclic tool calling and break repetitive loops.

    const isLooping = history.slice(-3).every(
      h => h.name === currentTool && h.args === currentArgs
    );
    if (isLooping) {
      injectWarning('Repeated action detected.');
    }
    Token Spend Cap

    Track cumulative token usage and abort if budget exceeded.

    totalTokens += res.usage.total_tokens;
    if (totalTokens > 50000) {
      throw new Error('Agent exceeded token budget');
    }
    Graceful Fallback Exit

    Return partial summary when iteration limits are encountered.

    return await synthesizeSummary({
      originalGoal: goal,
      completedActions: executedSteps,
    });

Production Agent Observability

    Step Telemetry Event

    Emit telemetry event after each completed reasoning turn.

    telemetry.track('agent_step', {
      iteration: iterations,
      tool: currentTool,
      tokens: res.usage.total_tokens,
      durationMs: stepDuration,
    });
    Agent Run State Machine

    Track lifecycle states: starting, running, paused, failed.

    type AgentState =
      'idle' | 'running' | 'paused' | 'done';
    let state: AgentState = 'running';
    // Expose state via websocket for UI visualization
    Cancellable AbortSignal

    Permit user to cancel running agent loop at any step.

    if (abortSignal.aborted) {
      throw new Error('Agent execution cancelled by user');
    }

Tips

  1. Cap every agent loop with a maxIterations counter so a confused agent stops before it burns through your token budget.
  2. Append each thought and tool result to a history array so the agent remembers what it already tried.

Warnings

  1. Never run a while loop without a hard iteration ceiling, because a reasoning cycle can drain your API credits in minutes.
  2. Avoid letting history grow forever, because long tool outputs eventually overflow the context window unless you summarize or prune them.

In Practice

FAQ