What Should Botie Do After a Tool Replies?
One search may reveal that a second search is needed. Follow the observations through an agent loop, then give that loop explicit ways to finish or ask for help.
15 sections · 4 questions · 1 coding challenge
The arrow keys move between sections too.
An Observation Could Change the Next Action
BOTIE
Our controller is smaller than a full agent implementation. It consumes supplied proposals and results so we can inspect termination behaviour. The workshop does not run a language model or reveal its private reasoning. It shows the operational decisions an application must make around whatever model supplies the proposals.
Sources for this history
The Manual Search Returns the Wrong Version
Botie searches for an inspection interval and finds a manual for an older pump. The excerpt is readable and the source is real, but it does not settle the technician’s question. A useful next step might be another search using the exact model number.
This is where selecting a next action can help. The model receives the search result and proposes what to do next. We still need a program that validates the proposal, records what happened, and stops when further attempts are no longer justified.
All actions and observations are scripted offline fixtures. No model or external tool is called.
Step 1 of 2
Follow a question through observable events
The next proposal can depend on what the previous search actually returned.
| Event | Observed information | Next allowed decision |
|---|---|---|
| Question | Interval for P-2 model B | Search model B manual |
| Search result | Only model A manual was found | Try a corrected search or report missing evidence |
| Second result | Model B interval with source M2B | Prepare a sourced answer |
| Application check | Reply meets the response contract | Return the answer |
Question
- Observed information
- Interval for P-2 model B
- Next allowed decision
- Search model B manual
Search result
- Observed information
- Only model A manual was found
- Next allowed decision
- Try a corrected search or report missing evidence
Second result
- Observed information
- Model B interval with source M2B
- Next allowed decision
- Prepare a sourced answer
Application check
- Observed information
- Reply meets the response contract
- Next allowed decision
- Return the answer
Step 2 of 2
Stop for different reasons
The same construction must distinguish an answer from a request to keep searching.
| Proposal | Tool attempts used / limit | Last tool status | Controller result |
|---|---|---|---|
| finish | 2 / 2 | ok | answer |
| tool | 2 / 2 | ok | handoff |
| tool | 1 / 3 | permanent_error | handoff |
| tool | 1 / 3 | ok | continue |
finish
- Tool attempts used / limit
- 2 / 2
- Last tool status
- ok
- Controller result
- answer
tool
- Tool attempts used / limit
- 2 / 2
- Last tool status
- ok
- Controller result
- handoff
tool
- Tool attempts used / limit
- 1 / 3
- Last tool status
- permanent_error
- Controller result
- handoff
tool
- Tool attempts used / limit
- 1 / 3
- Last tool status
- ok
- Controller result
- continue
Let observations affect the route without losing control
The next operation depends on the supplied observation and the remaining tool budget. A final response uses a different branch from another tool request.
When should the next operation be allowed?
Try a successful final answer, a request for another tool at the limit, and a permanent tool failure. Build the decision from comparisons and branches.
BOTIE
answer, continue, or handoff
continue
Follow the operations
1. Validated next proposal: tool or finish
tool
2. Text value
finish
3. Compare complete values
false
4. Text value
answer
5. Tool attempts already used
1
6. Maximum tool attempts
3
7. Check below a limit
true
8. Last tool status: ok or permanent_error
ok
9. Text value
ok
10. Compare complete values
true
11. Require both checks
true
12. Text value
continue
13. Text value
handoff
14. Choose from a check
continue
15. Choose from a check
continue
16. Result
continue
This is one deterministic controller decision, not a model inference. The host must separately validate final answers and enforce time, token, permission and side-effect limits.
1. Name the state that must survive between calls
The next model request needs the task and the observations gathered so far. The application also needs control state: which tools are allowed, how many attempts have been made, and whether an earlier operation failed. These serve different purposes.
Store tool-call IDs with their results so parallel or delayed responses can be matched to the right proposal. Keep error results distinct from successful results. A timeout is evidence that a response was not received in time; it does not necessarily prove that a side effect never happened.
The previous lesson’s context selection decides what the model sees. The controller’s budgets and permissions remain authoritative even if older messages are summarised.
2. Separate proposing, executing and observing
At each turn, the model may propose a tool call or a final response. The application validates the proposal. If it is a tool call, the server checks the tool name and arguments, checks permission, then executes and records its result. A later model request receives that observation.
This cycle is what makes a tool-using agent different from simply displaying model text. The software decides what actually executes. A proposal containing a function name must never be evaluated as arbitrary code. Look up the name in a bounded registry of approved implementations.
An unknown tool name should produce a controlled failure or repair opportunity. It should not become a shell command.
3. Give the loop an explicit termination decision
Our controller accepts a validated action of finish or tool. A finish action can return a checked final response, including an honest statement that evidence was unavailable. A tool action may continue only while the tool-attempt count is below its limit and the last status is ok. Otherwise control passes to a handoff response.
The workshop uses comparisons and Choose from a check to make this decision visible. It has no feedback wire: the real loop lives in the host program, which calls the controller again after the next observation. The Python exercise replays that host loop using a supplied list of events.
If action is finish: return answer
Otherwise, if used < limit and status is ok: return continue
Otherwise: return handoffThis order permits a final response after the last allowed tool attempt. It does not permit another tool just because the model says it is almost done.
4. Count attempts at the boundary you intend to limit
A tool-attempt budget counts calls that were attempted, including calls that failed. Counting only successes allows an unreliable tool to be retried indefinitely. Increment the attempt counter when the call is issued, and persist it if execution can resume after a restart.
A tool limit does not bound every expense. One tool may take a long time, and the model may consume tokens between tool calls. A deployed system normally needs additional limits on elapsed time, model requests and token or monetary usage. Name each counter and decide what happens at its boundary.
This workshop’s used and limit refer only to tool attempts. We will not describe it as a complete cost limiter.
Failures still consume a tool attempt
This program demonstrates counting observations from attempted tools. It does not decide whether to retry them.
observations = ["ok", "timeout", "ok"]
attempts = 0
for status in observations:
attempts += 1
print(attempts, status)1 ok
2 timeout
3 okThe timeout is the second attempt. Its cost and uncertain outcome do not disappear because it failed to return useful information.
Check yourself
Questions on Parts 1 to 4
Question 1 of 2
What does this controller return?
Use the information below.
Two tool attempts have been used and the limit is two. The validated next proposal is finish. The response itself has already passed the required checks.5. Classify errors before offering a retry
A temporary connection failure may justify a retry with a limit and delay. A permanent permission denial will not become authorised because the model repeats it. A schema error may need corrected arguments. Keep these categories distinguishable in the tool result.
Our workshop controller treats permanent_error as a stop for further tools. It can still return an already prepared and checked final response explaining the limitation. The Python replay uses a more immediate policy: it hands off as soon as that error arrives, before accepting another proposal. These are deliberately different application choices; neither may execute another tool after the denial.
If a production service retries a side effect after an uncertain outcome, it must reuse the original idempotency identity and follow the tool contract. Otherwise an apparently helpful retry can duplicate an inspection.
Neither example implements timed retries. Repeated calls that return no new information can also justify a stop, but a progress detector needs a defined comparison of inputs and outcomes.
6. A final response is still a candidate answer
When the model requests finish, validate its response schema and evidence before showing it as a successful answer. It may correctly report that the available manuals are insufficient. That is different from claiming the maintenance question was resolved.
Record why execution ended: answered, missing evidence, denied action, exhausted budget or a service failure. A single completed flag loses those distinctions. Evaluations should distinguish successfully answering a question from merely reaching a terminal state.
In the workshop, answer means return the supplied final-response branch. The fixture does not include its text or verify factual correctness.
7. Preserve a useful handoff
If the loop cannot continue, tell the technician what remains unresolved and which relevant sources were checked. Where appropriate, suggest the missing asset number or manual version that would let work resume. Avoid claiming that an unconfirmed tool action succeeded.
A human review path needs the actual proposed action and its evidence. A generic “approve agent” button provides too little information. When review changes a request, revalidate its payload and permissions before execution, just as in the tool-contract lesson.
The handoff is part of the task’s designed behaviour. It should be understandable even when the underlying service error was technical.
8. Replay the controller before paying for live runs
Scripted events let us test control logic cheaply and consistently. Include a normal path, a failure and a boundary where no further tool may run. The fixtures can prove that the counter and branching rules behave as specified on those inputs.
They cannot establish that a model selects useful tools, understands observations, or writes supported answers. Those require representative model runs and evaluation. Keeping these tests separate makes a failure easier to locate: deterministic control code, model behaviour, or the external service.
The following exercise returns both the terminal reason and attempted-tool count. Change a replay to predict where the host loop stops before running it.
Check yourself
Questions on Parts 1 to 8
Question 1 of 2
What should this lesson’s controller do?
Use the information below.
A tool returns permanent_error because the caller is denied access. The model proposes the same tool again; one of three attempts has been used.Try it with Botie
Build with Botie
Write and run Python