Class: SwarmSDK::V3::Loop::Executor

Inherits:
Object
  • Object
show all
Includes:
Memory::Adapters::VectorUtils
Defined in:
lib/swarm_sdk/v3/loop/executor.rb

Overview

Core loop engine for iterative refinement

Runs a sequence of ask() calls through a provided callable, optionally checking for convergence via embedding similarity between consecutive responses. Emits events for each iteration and the overall loop lifecycle.

The Executor is stateless between runs — all configuration is passed to #run. The ask callable and embedder are injected at construction for testability.

Examples:

Basic usage (called internally by Agent#loop)

executor = Executor.new(
  ask_callable: ->(prompt) { agent.ask(prompt) },
  embedder: Memory::Embedder.new,
  agent_id: "writer_abc123",
)
result = executor.run(
  kickoff: "Write a poem about the sea",
  iterate: "Improve the poem",
  max_iterations: 5,
  convergence_threshold: 0.95,
  converge: true,
)

Instance Method Summary collapse

Methods included from Memory::Adapters::VectorUtils

#similarity

Constructor Details

#initialize(ask_callable:, embedder:, agent_id:) ⇒ Executor

Create a new Executor

Parameters:

  • ask_callable (#call)

    Lambda wrapping agent.ask(prompt)

  • embedder (Memory::Embedder, nil)

    Embedder for convergence detection (nil if converge: false)

  • agent_id (String)

    Agent identifier for event emission



38
39
40
41
42
# File 'lib/swarm_sdk/v3/loop/executor.rb', line 38

def initialize(ask_callable:, embedder:, agent_id:)
  @ask_callable = ask_callable
  @embedder = embedder
  @agent_id = agent_id
end

Instance Method Details

#run(kickoff:, iterate:, max_iterations:, convergence_threshold:, converge:) ⇒ Result

Execute the iterative loop

Runs the kickoff prompt first, then the iterate prompt for subsequent iterations. If convergence checking is enabled, computes embedding similarity between consecutive responses and stops when it exceeds the threshold.

Examples:

Run with convergence

result = executor.run(
  kickoff: "Draft an essay",
  iterate: "Revise and improve",
  max_iterations: 10,
  convergence_threshold: 0.95,
  converge: true,
)
result.converged?  #=> true or false

Parameters:

  • kickoff (String)

    Prompt for the first iteration

  • iterate (String)

    Prompt for subsequent iterations

  • max_iterations (Integer)

    Maximum number of iterations (>= 1)

  • convergence_threshold (Float)

    Similarity threshold for convergence (0.0..1.0)

  • converge (Boolean)

    Whether to check for convergence

Returns:

  • (Result)

    Aggregate result with all iterations



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/swarm_sdk/v3/loop/executor.rb', line 67

def run(kickoff:, iterate:, max_iterations:, convergence_threshold:, converge:)
  EventStream.emit(
    type: "loop_started",
    agent: @agent_id,
    max_iterations: max_iterations,
    convergence_threshold: convergence_threshold,
  )

  iterations = []
  converged = false
  previous_content = nil

  max_iterations.times do |index|
    prompt = index.zero? ? kickoff : iterate
    response = @ask_callable.call(prompt)

    # Handle interrupted agent (ask() returns nil)
    break if response.nil?

    current_content = response.content.to_s
    input_tokens = response.respond_to?(:input_tokens) ? (response.input_tokens || 0) : 0
    output_tokens = response.respond_to?(:output_tokens) ? (response.output_tokens || 0) : 0

    delta_score = nil
    if converge && previous_content && @embedder
      delta_score = compute_delta(previous_content, current_content)
    end

    iteration = Iteration.new(
      number: index + 1,
      response: response,
      prompt: prompt,
      tokens: { input: input_tokens, output: output_tokens },
      delta_score: delta_score,
    )
    iterations << iteration

    EventStream.emit(
      type: "loop_iteration_completed",
      agent: @agent_id,
      iteration: index + 1,
      delta_score: delta_score,
      converged: false,
    )

    if delta_score && delta_score >= convergence_threshold
      converged = true
      break
    end

    previous_content = current_content
  end

  EventStream.emit(
    type: "loop_completed",
    agent: @agent_id,
    iterations: iterations.size,
    converged: converged,
  )

  Result.new(iterations: iterations, converged: converged)
end