Before you start
Track 17 switched the base class to DesignExecute to allow loops. This
tutorial shows what keeps those loops from running forever: an AST-injected
iteration counter that stops the loop if it exceeds max_loop_iterations.
The plan
The sum-of-squares plan from Track 17 loops exactly 100 times. Pass it
directly to execute() so the result is deterministic regardless of what
model you use.
PLAN = """\
total = 0
for i in range(1, 101):
sq = square(i)
total = add(total, sq)
return format_result("Sum of squares 1 to 100", total)
"""Setting the limit
from opensymbolicai.models import DesignExecuteConfig
config = DesignExecuteConfig(max_loop_iterations=N)
agent = Accumulator(llm=llm, config=config)
result = agent.execute(PLAN)Tripping the guard
With max_loop_iterations=50, the loop body runs 50 times and fails on
iteration 51:
config = DesignExecuteConfig(max_loop_iterations=50)
agent = Accumulator(llm=llm, config=config)
result = agent.execute(PLAN)
last_step = result.trace.steps[-1]
print(last_step.error)Loop exceeded maximum iterations (50)Letting it finish
With max_loop_iterations=100, all 100 iterations fit:
config = DesignExecuteConfig(max_loop_iterations=100)
agent = Accumulator(llm=llm, config=config)
result = agent.execute(PLAN)
if result.trace.all_succeeded:
print(result.get_value())
print(f"primitive calls: {len(result.trace.steps)}")Sum of squares 1 to 100: 338350
primitive calls: 201Full script
# main.py
from accumulator import Accumulator
from opensymbolicai.llm import LLMConfig
from opensymbolicai.models import DesignExecuteConfig
PLAN = """\
total = 0
for i in range(1, 101):
sq = square(i)
total = add(total, sq)
return format_result("Sum of squares 1 to 100", total)
"""
def run(label: str, max_loop_iterations: int) -> None:
llm = LLMConfig(provider="ollama", model="qwen2.5-coder:7b")
config = DesignExecuteConfig(max_loop_iterations=max_loop_iterations)
agent = Accumulator(llm=llm, config=config)
result = agent.execute(PLAN)
last_step = result.trace.steps[-1]
print(f"--- {label} (max_loop_iterations={max_loop_iterations}) ---")
if result.trace.all_succeeded:
print("result:", result.get_value())
print(f"primitive calls: {len(result.trace.steps)}")
else:
print("error:", last_step.error)
print()
run("limit too small", max_loop_iterations=50)
run("limit sufficient", max_loop_iterations=100)uv run main.pyThe default is 100. Pick a limit that fits the largest range your tasks will ever iterate.