ExAthena.Loop.Mode behaviour (ExAthena v0.13.0)

Copy Markdown View Source

Pluggable control-flow strategy for the agent loop.

Modes decide what to do each turn. The kernel handles everything else — tool dispatch, permissions, hooks, events, accounting. Modes sit on top and shape the iteration: ReAct is "infer → tool → loop", Plan-and-Solve is "plan once → execute many", Reflexion adds a self-critique pass.

Writing a mode

defmodule MyMode do
  @behaviour ExAthena.Loop.Mode

  @impl true
  def init(state), do: {:ok, state}

  @impl true
  def iterate(state) do
    # Run inference, handle tool calls, update state, decide continue/halt.
    # Return {:continue, state} to keep looping, {:halt, state} to stop.
    {:continue, state}
  end
end

The builtin ExAthena.Modes.ReAct is a reference implementation.

Atom shortcuts

:react, :plan_and_solve, :reflexion resolve to the builtin modules. Any other atom or a module reference is used verbatim.

Summary

Callbacks

Called once before the first iteration. Use to prime mode-specific state.

Drive one iteration. Return one of

Optional. Called after each {:continue, new_state} to determine whether the iteration was productive. Return true if the run made progress (new tool name+args pair OR new assistant text), false otherwise. When not implemented, the kernel uses its own default check.

Functions

Resolve an atom shortcut or module to the Mode module.

Callbacks

init(t)

@callback init(ExAthena.Loop.State.t()) ::
  {:ok, ExAthena.Loop.State.t()} | {:error, term()}

Called once before the first iteration. Use to prime mode-specific state.

iterate(t)

@callback iterate(ExAthena.Loop.State.t()) ::
  {:continue, ExAthena.Loop.State.t()}
  | {:halt, ExAthena.Loop.State.t()}
  | {:error, term()}

Drive one iteration. Return one of:

  • {:continue, State.t()} — keep looping (kernel checks caps + budget).
  • {:halt, State.t()} — stop looping; the kernel produces a Result from the terminal state's finish_reason (which the mode should set via ExAthena.Loop.set_finish_reason/2 before returning).
  • {:error, reason} — abort with an unrecoverable error. The kernel wraps this in :error_during_execution.

productivity_signal(prev_state, new_state)

(optional)
@callback productivity_signal(
  prev_state :: ExAthena.Loop.State.t(),
  new_state :: ExAthena.Loop.State.t()
) ::
  boolean()

Optional. Called after each {:continue, new_state} to determine whether the iteration was productive. Return true if the run made progress (new tool name+args pair OR new assistant text), false otherwise. When not implemented, the kernel uses its own default check.

Functions

resolve(mode)

@spec resolve(atom() | module()) :: module()

Resolve an atom shortcut or module to the Mode module.