An assistant looks up a room, finds that it is unavailable, and searches for another. Where did the second search come from? That decision is the useful place to start understanding agents.
Before you begin: Complete Chains, agents, and tools. You should understand a function call and a saved conversation.
Find the decision in the loop
In a fixed workflow, your program determines the steps. In an agent loop, a model can propose the next step using the information available so far. The surrounding program still decides which proposals are allowed and executes the tools. A model that prints “reservation confirmed” has not reserved anything.
Think of a run as a sequence of observations and decisions. The user supplies a goal. The model sees instructions and available tool descriptions. It proposes a call. Your program checks the arguments and permission, executes the function, and returns its result. The model can then answer, ask for clarification, or propose another call. The original ReAct research explored this combination of reasoning and interaction; it did not establish that any unrestricted loop becomes reliable.
Follow one request
Our fictional request is “Find a room for 12 people after 2 pm.” The available tools are list_rooms and read_availability. Neither tool can create a booking.
| Step | New evidence | Reasonable next step |
|---|---|---|
| List rooms | A seats 8; B seats 16 | Discard A for this request |
| Read B | Free from 3 pm | Report B at 3 pm |
| User asks to book | No booking tool exists | Explain the limit |
The model is useful for interpreting the request and selecting a relevant lookup. Capacity checks can remain ordinary code. Making every comparison another model call would add cost and another opportunity for error.
Now change one observation: B's availability service times out. “No room is free” is no longer supported. A failed lookup and a successful empty result are different states. Preserve that distinction in tool output and in the answer.
What changed in the world after this action?
A tool call is an event, not necessarily progress. If a search returns the same passages as before, the agent’s evidence may be unchanged. Represent the goal in terms of required facts or completed actions, then compare the current state with that goal after each step.
Set budgets for time, calls and repeated failures. When a loop cannot reduce uncertainty, the useful response may be to ask for missing information or return a partial result. The challenge makes the stopping decision part of the design instead of hoping that another iteration will solve the problem.
Forty searches return overlapping evidence and no supported conclusion.
Compare each result with the unresolved evidence requirements and remaining budget.
The system can recognize stalled progress and stop with an honest explanation.
Define progress before adding autonomy
A state is the information the application keeps about the run: request, evidence, pending calls, results, and status. A policy is the rule for choosing or allowing a next action. Some policy lives in instructions; hard limits belong in code.
For this task, progress means learning something new about a suitable room. Repeating the same failed call without a changed condition is not progress. Set a deadline and a bounded retry rule. Count model turns, tool calls, and elapsed time separately. One model response can contain more than one tool call.
An illustrative control policy, not executable Python, is:
Validate the request.
While time and call budgets remain:
Ask for an answer or a permitted lookup.
Reject invalid or repeated calls without new evidence.
Record the lookup result, including failures.
Stop when an answer is supported or clarification is needed.
Return the answer, its evidence, and the run status.
Decide how this run should end
The model calls read_availability("B") three times and receives the same timeout. It then proposes book_room("B"). What should the application do, and what should the user see?
Inspect the decision
Reject the unavailable booking tool and stop the repeated lookup under the retry policy. Report that B fits the size requirement but availability could not be confirmed. Do not imply a booking happened. The useful record includes the attempted lookup and failure status, not a fabricated successful outcome.
Sketch a second run where the user never supplied the number of people. A good first step is a question, because room capacity cannot be evaluated yet. This is an agent doing less work to reach a better result.
Practice with feedback
Define progress before adding autonomy
An agent has looped 40 times, calling search with slightly different phrasings, and has produced nothing.
Locate the decision in a loop and state how a run should end.
Check your understanding
Your task
Add a progress test and a termination rule to a looping agent.
These notes stay on this page. Download them before leaving.
What to include
- A new fact is defined mechanically, for example a source id not seen before
- The no-progress rule has a number and an action
- All three terminations produce distinct outputs
- The trace inspection targets the first divergence
Compare with a worked answer
Here is one way to answer. Check how it uses the information in the task.
The loop's goal: answer 'which classes have places left this week' from the notices. What counts as a new fact: an observation containing a source id not already in the trace, or a value for a sub-question that is still open. Progress test after each observation: did the set of resolved sub-questions grow, or did the set of seen source ids grow? If neither, the step made no progress. On 3 consecutive no-progress steps: stop the loop, return partial with the open sub-questions, and log the three repeated calls. Three rather than one, because a single redundant search is normal exploration.
Termination conditions: success: every sub-question has at least one cited observation. Returns claims plus sources. partial: some resolved, some open, either from the no-progress rule or the budget. Returns what is grounded and names what is not. exhausted: 12 steps spent with nothing resolved. Returns not_finished with the trace, and never a paragraph that reads like an answer.
What I inspect first in a failed trace: the first observation that contradicted the plan. In the 40-step run, step 2 returned an empty list, and the planner treated empty as 'try different words' instead of 'this class does not exist'. Everything after step 2 was noise generated by that one misreading.
When you are signed in, opening the challenge carries your edited working notes into its draft in this browser. The challenge has its own completion record. Practising here does not award points or mark it complete.
Next, put this boundary into a framework and inspect which parts it makes easier to express.
Sources
ReAct paper introduces interleaved reasoning and action. OpenAI Agents SDK running guide documents one concrete runner loop and its limits.