Step Limits, Budget Limits, and Full-Fidelity Logs
Explore how to apply deterministic controls to AI workflow loops by setting step limits and budget caps, enforcing strict validation, and generating comprehensive per-step logs. Understand how to handle stop reasons explicitly and safely manage tool executions to create predictable, auditable, and bounded agent-like AI workflows.
We'll cover the following...
A tool-using loop becomes predictable when deterministic controls wrap the probabilistic part. The model proposes the next action, then the workflow either executes it or stops for a named reason.
This lesson stays platform-agnostic and focuses on the loop contract around triage that can call 0–N tools, then produce a final draft plus internal rationale fields.
With this diagram, we can step through one loop iteration and watch state, counters, and logs change in a controlled way.
The loop shape stays the same even as we add more tools and more steps. We initialize a state object, ask the model for next_action JSON, validate that proposal against an allowlist, execute a tool only if validation passes, append a step log, then check a stop condition that returns control back to the platform.
A minimal pseudo-workflow looks like this:
state = {input_id,triage_json, // category, priority, next_action, needs_humanstep_count: 0,budget_remaining: 5, // budget unitsmessages: [],run_log: []}ALLOWED_TOOLS = ["kb_search"]while true:proposal = model_propose_next_action(state) // probabilistic JSONvalidation = validate(proposal, ALLOWED_TOOLS) // deterministicif !validation.ok:append_step_log(state, proposal, validation, tool_call=null, tool_result=null)stop_reason = "INVALID_ACTION"breaktool_result = call_tool(proposal.tool_name, proposal.args) // deterministic side effectstate = apply_tool_result(state, tool_result) // deterministic state updateappend_step_log(state, proposal, validation, tool_call=proposal, tool_result=tool_result)state.step_count += 1if should_stop(state):stop_reason = compute_stop_reason(state)break
The important boundary is that only model_propose_next_action(state) is non-deterministic. Validation, counters, state updates, and stop reason selection must behave the same way on every run given the same inputs.
Step limits and stop reasons
Once the loop exists, the next failure mode is an endless sequence of plausible tool calls. A step limit prevents that by forcing a stop after max_steps tool executions, even if the model keeps proposing actions.
Stop reasons work best when they are explicit and mutually exclusive, because logs and dashboards can group by stop_reason without guessing. A compact set that stays readable in audits is:
DONEwhen the model proposes a terminal action and the workflow accepts itMAX_STEPSwhenstep_count >= max_stepsBUDGET_EXHAUSTEDwhenbudget_remaining <= 0INVALID_ACTIONwhen validation fails...