When an agent helps and when a workflow is enough
Decide using control flow, task uncertainty, and failure cost, then separate model decisions from program rules.
Choose a workflow or agent deliberately and define enforceable stopping conditions.
An agent is not a requirement to hand every step to a model. It is useful when the next action cannot be fully determined in advance. A task with a known path and clear rules often fits an explicit workflow better.
Look at control flow before counting model calls. Three model calls in a fixed sequence do not necessarily make an agent necessary.
Decide whether the path needs to change
Extracting fields from a description, validating them, and saving a result usually has a known sequence. The program owns the flow and the model handles a limited text-understanding step.
Dynamic decisions are more valuable when search results determine the next query or tool feedback changes the plan. Work such as ReAct explores alternating reasoning and action, but applications still need permission checks, state, and stopping conditions.
A task involving judgment does not require an unlimited loop. A fixed workflow can use a model for classification while the program limits possible branches.
Account for the cost of failure
A wrong suggestion and a wrong external action have different consequences. A model can propose an action without being authorized to execute it.
Sending messages, modifying resources, or placing orders should pass application-level permission and confirmation rules. Read operations also need resource boundaries rather than unlimited access.
Enforce step limits, elapsed-time limits, budgets, and allowed tools in code. A stopping rule should be something the executor can enforce, not merely a sentence in a prompt.
Practice with a task router
The attributes in this example come from trusted application code. The router demonstrates choosing an execution mode; it does not call a model or implement a complete agent framework.
def choose_execution(task):
if task["changes_external_state"]:
return "approval-required"
if task["known_steps"]:
return "workflow"
return "bounded-agent"
assert choose_execution({
"changes_external_state": False, "known_steps": True
}) == "workflow"
assert choose_execution({
"changes_external_state": True, "known_steps": False
}) == "approval-required"
assert choose_execution({
"changes_external_state": False, "known_steps": False
}) == "bounded-agent"
print("Execution boundaries checked")
Do not let a model mark its own action as having no side effects and thereby bypass permission checks. Task labels, tool capabilities, and authorization must come from trusted configuration.
Retain auditable decisions
Record the goal, selected tool, validated arguments, tool-result status, and stopping reason. These explain execution without requesting or storing a model's private chain of thought.
Repeated failures should end the loop or enter an explicit recovery branch. Rephrasing a denied operation is not a valid way around an authorization failure.
Try it next
Draw the task flow and mark known steps separately from steps needing dynamic choice. Implement the known portion as a workflow first.
Add a bounded agent only where uncertainty warrants it. Define behavior for timeout, repeated actions, and lack of progress. The next guide gives the tools themselves a validation and execution contract.
Sources & further reading
Sources checked on 2026-11-10. Original engineering explanation; examples are teaching data, not production measurements.