12.3 Tools and Agents: Execution State Machines Controlled by Host Programs
The Prediction Hall receives a task: "Identify the service with the highest latency and generate a maintenance ticket." The model correctly selects the monitoring query tool, but mistakenly writes natural-language timestamps into integer parameters. On retry, the query succeeds, yet the model duplicates the same ticket twice.
An agent is not an autonomous digital employee granted independent permissions. Instead, it is a loop governed by the host application: the model proposes the next action, the host validates, authorizes, and executes it, then returns a bounded result to the model. As long as an action produces side effects, the reliability boundary must be enforced by deterministic code.
Learning Objectives
- Implement agents as bounded state machines;
- Design typed tool schemas, authorization, and parameter validation;
- Handle idempotency, retries, timeouts, and partial failures;
- Distinguish between conversation history, work state, and long-term memory;
- Evaluate tool selection, parameters, results, and side effects.
1. Models Can Only Initiate Tool Calls
Tool calling responses typically include a tool name and arguments. Both are untrusted inputs: the model might invent tool names, omit fields, provide incorrect types, or copy prompt injection patterns directly into the arguments.
The tool registry must define:
- Stable tool names, versions, and purposes;
- JSON Schema or equivalent typed input/output;
- Required fields, enumerations, length, numeric, and format constraints;
- Read-only or side-effect classifications;
- Timeout limits, rate limits, cost implications, and data sensitivity;
- Required permissions for callers;
- Retry and idempotency contracts.
Unknown fields must be rejected by default to prevent the model from passing unaudited parameters directly to the underlying SDK. Tool outputs are also untrusted and must be constrained in size, escaped to prevent injection, and strictly separated between data and instructions.
2. Permissions Won't Expand Due to Model Suggestions
What an agent can do for a user is determined by the current principal and business policies, not by how firmly the prompt is written. The host application must validate each call before execution by checking:
- The current user or service identity;
- Tenant, resource scope, and operation context;
- Data classification and usage restrictions;
- Whether secondary approval or confirmation is required;
- Whether budget, rate, or time window limits have been exceeded.
For irreversible or externally visible actions (such as deletion, payment, messaging, deployment, or permission changes) first present the specific target and impact. The user must confirm the structured, parsed operation, not a vague natural language instruction.
Never pass database superuser credentials or production deployment tokens into the model's context. Instead, credentials should be held by a restricted executor and used only in response to authorized tool calls.
3. Expressing Loops with a State Machine
def run_agent(task, principal, registry, limits):
state = new_run(task=task, principal=principal, limits=limits)
while state.steps < limits.max_steps:
if state.deadline_exceeded() or state.cost_exceeded():
return state.stop("budget_exhausted")
reply = model.respond(
messages=state.visible_messages(),
tools=registry.schemas_for(principal),
)
state.record_model_reply(reply)
if reply.final_answer is not None:
return state.finish(reply.final_answer)
calls = registry.parse_and_validate(reply.tool_calls)
if not calls:
return state.stop("invalid_or_empty_action")
for call in dependency_order(calls):
policy.authorize(principal, call)
approval.require_if_needed(call, principal)
result = executor.execute(
call,
timeout=registry.timeout(call.name),
idempotency_key=state.idempotency_key(call),
)
state.record_tool_result(call, sanitize(result))
return state.stop("max_steps_reached")A real implementation must capture exceptions from the model, validator, and tools, and categorize them as recoverable, unrecoverable, or requiring manual intervention. Do not infinitely feed error messages back into the model for retry.
4. Planning is Strategy, Not a Permission Mechanism
Some tasks are well-suited to model invocation one step at a time. Others require first generating a plan, then executing it step by step. Still others can be handled with fixed workflows, where the model only fills in local parameters. The more stable and high-risk the process, the more appropriate deterministic orchestration becomes.
Publicly exposing a model’s full internal “Thought” process is not a necessary feature of an Agent. The system can retain structured plans, decision summaries, tool traces, and verifiable evidence, without relying on free-form chain-of-thought text for audit trails.
Parallel tool invocations are only suitable for independent actions. If one invocation depends on the output of another, an explicit dependency graph must be established; otherwise, the model might generate non-existent IDs or proceed with failed results.
5. Side Effects Require Idempotency and Compensation
A network timeout does not mean the server failed to execute. If the client simply retries a "create ticket" request, it may inadvertently generate duplicate side effects.
Prioritize design with the following principles:
- Derive an idempotency key from the run/action identity;
- Have the server store the key along with the outcome of the operation;
- On retry, return the previously recorded result instead of re-executing;
- For non-idempotent APIs, first check the current state, or implement business-level deduplication keys;
- Define compensation actions and human intervention points for multi-step transactions;
- Separate the "request accepted" state from the "business operation completed" state.
Distributed systems typically cannot achieve universal exactly-once guarantees through a single HTTP call. Applications must instead handle at-least-once delivery and duplicate results at the business logic level.
6. Set Budgets for Each Path
At a minimum, configure the following:
- max model steps;
- max tool calls and individual tool invocation counts;
- token, monetary, and wall-clock budgets;
- output size per invocation and total output size;
- query and data scan limits;
- retry counts and backoff strategies;
- an allowlist of accessible tools.
Termination conditions are not limited to "the model says it's done." They also include: the target has been verified, no valid actions remain, repeated states, budget exhaustion, user cancellation, and irrecoverable errors.
When detecting duplicate calls, compare normalized arguments with the associated state, do not rely solely on comparing natural language responses.
7. Do Not Mix Three States Together
Conversation History
Used to understand the current interaction. History that is too long incurs token costs and introduces contamination from outdated instructions; summaries are lossy compressions, and critical constraints should be preserved as structured fields.
Work State
Includes task graph, completed actions, artifact IDs, approvals, budget, and errors. It must be persisted and validated by the application, not relied upon by the model to "remember."
Long-Term Memory
When storing user preferences or facts across sessions, consent, source, validity period, correctability/deletability, and access control are required. Summaries generated by the model cannot become long-term facts without independent verification.
8. Error Recovery: Errors Must Be Explainable
Establish limited, well-defined recovery strategies for failures:
- Schema error: Return specific field-level errors and allow at most a few correction attempts;
- Auth denied: Terminate the operation, no workaround via alternative tools is permitted;
- Rate limit or transient error: Apply backoff according to defined policy;
- Timeout or unknown outcome: First query the operation’s current status before deciding whether to retry;
- Partial success: Log completed sub-steps and either trigger compensation logic or request manual intervention;
- Unsafe output: Isolate the content and halt further propagation to high-privilege tools.
Each recovery action must record the triggering error, the applied strategy, the version of the call, and the final outcome. Agent "figuring things out on its own" is not a recovery strategy.
9. Evaluate the Full Execution Trace
A correct final answer does not guarantee absence of extraneous queries, unauthorized actions, or redundant side effects. Agent evaluation must be layered:
- Tool selection accuracy;
- Argument schema and semantic correctness;
- Authorization decision validity;
- Task completion success;
- Side-effect correctness and duplication rate;
- Recovery success;
- Execution steps, latency, token consumption, and cost;
- Rate of unsafe action attempts;
- Whether the trace provides sufficient information for post-hoc debugging.
The test suite should include scenarios such as unavailable tools, malformed output, timeout, insufficient permissions, prompt injection, entity name collisions, and unknown execution results. Real-world side effects should be simulated using sandboxed or fake tools, or isolated execution environments.
Common Misconceptions
- Agent equals a thinking LLM: Product behavior is determined by a combination of model, tools, state machines, and policies, rather than the model itself alone.
- Tool schema ensures safety: It only provides partial structural validation and cannot replace authorization checks or business-level constraints.
- Retrying after timeout is sufficient: The original operation may have already succeeded; idempotency keys or state queries are required.
- Putting all history back is equivalent to memory: Doing so increases cost, introduces data pollution, and poses privacy risks.
- Setting
max_stepsprevents uncontrolled behavior: Additional controls (such as tool access, permissions, cost limits, data scope, and side effects) are still necessary.
Exercise
- Design a strict input schema, permissions, and an idempotency contract for "create ticket".
- Draw a state transition diagram showing the flow of tool calls from proposed to validated, authorized, approved, and executed.
- Write a recovery procedure for timeouts with unknown outcomes.
- Distinguish between conversation history, work state, and long-term memory within a single task.
- Construct five agent test cases that expose repeated calls or permission circumvention.
Summary
An agent's core isn't giving models more autonomy, but rather placing uncertain action suggestions into a controlled execution loop. Typed schemas, per-call authorization, explicit state, idempotency, budgeting, and trajectory evaluation collectively constrain risk; the host application remains ultimately responsible for actual side effects.
The next lesson moves into the prophecy hall's evaluation station: instead of asking "does the response look good?", we define tasks, evidence, trajectories, statistical units, and release thresholds.