Skip to content

std.agent_loop

std.agent_loop provides loop_until — the functional form of Sema’s declarative loop … until construct. It runs a pure step function over an immutable state until a done predicate holds or a max_iters bound is reached. The termination policy is data, not a tangle of breaks.

from std.agent_loop import loop_until

The agentic loop — “keep taking a step until you are confident enough or you run out of budget” — is the backbone of every agent, and it is usually a long, hand-rolled while (a _run_deep loop can run ~300 lines). loop_until distills it to one combinator whose step and done are ordinary lambdas, so the loop’s structure is fixed and correct while its policy is a parameter.

def loop_until(init: any, max_iters: int, step: fn, done: fn) -> any !{}

init is the starting state; step: state -> state advances it; done: state -> bool decides when to stop; max_iters caps the iterations. It returns the final state (whether it stopped because done held or the cap was hit):

from std.agent_loop import loop_until
from std.belief import prior
# Gather evidence until we are confident, or after at most 8 rounds.
final = loop_until(
prior(),
max_iters=8,
step=(b => b.bumped(next_observation())),
done=(b => b.confidence() > 0.9),
)

step and done are pure functions over an immutable state. Sema forbids a lambda from mutating captured state, which is why std.belief offers bumped (returns a new belief) alongside update (mutates in place): inside a loop_until step you thread state functionally rather than mutating it. This discipline is what keeps the loop safe to reason about and parallelize.

See the exact signature and effect row in the generated std.agent_loop API reference.