Wei Chen
Back to Blog

Tool-Calling Agents Need Explicit Execution Boundaries

Wei Chen · Published · ~2 min read

Design tools as contracts: validate arguments, enforce authorization, bound retries, and inspect outcomes outside the language model.

Eyeglasses in front of computer screens displaying source code
Cover photo: Unsplash · Unsplash License · Illustrative photograph

A model selecting a tool is proposing an action. The application still has to decide whether the action is valid, authorized, and useful. Treating those responsibilities as ordinary backend engineering makes an agent easier to inspect and recover when something goes wrong.

The key boundary sits between the model's requested call and the executor that can affect the outside world.

Start with the smallest useful control flow

Anthropic's Building effective agents, published December 19, 2024, distinguishes workflows with predefined code paths from agents that dynamically choose their process and tool use. It recommends beginning with simple approaches and adding complexity when it improves results. The article also emphasizes clear tool interfaces and environmental feedback. Read the original article.

That distinction is useful when designing a feature. If every request follows the same sequence—look up an order, format the result, return it—a deterministic workflow may be enough. Dynamic planning becomes useful when the next step genuinely depends on what previous steps discover.

Describe a narrow contract

Prefer a tool such as get_order_status(order_id) over a general-purpose endpoint that accepts arbitrary SQL or a free-form command. Specify required fields, allowed values, return fields, and recognizable failure states.

The executor should derive the current user and account scope from trusted application state. A model-supplied account ID is input, not proof of authorization. Check access before executing even a read operation.

Separate validation from execution

A practical executor follows a sequence like this:

model proposes a tool call → resolve tool name against an allowlist → validate arguments against the tool's schema → check the authenticated user's permissions → apply timeout and execution budget → execute the operation → normalize the result or error → record the outcome for the next step

This is an architectural sketch, not a complete implementation. Each boundary needs tests against the application's actual data and failure modes.

For write operations, decide how duplicate requests are handled. Retrying a status lookup is different from retrying a payment or sending a message. Use the target service's idempotency mechanism when available, and reconcile uncertain outcomes before assuming a timed-out request did nothing.

Bound the loop

Set limits on steps, elapsed time, and repeated calls with unchanged arguments. Return explicit errors the planner can interpret, rather than hiding failures inside a successful response envelope. When the budget is exhausted, surface an incomplete result that explains what remains unresolved.

Keep tool output as data. Text returned from a document or external page should not acquire the authority to change the application's permissions or execution policy.

Evaluate the action, not just the explanation

Test valid calls, unknown tools, malformed arguments, inaccessible records, timeouts, and repeated writes. Check whether the intended state change occurred and whether any unintended action happened. A fluent explanation is not evidence that an operation succeeded.

A useful agent is one whose decisions can be traced and whose effects can be controlled. Narrow tools and explicit execution boundaries provide that foundation before more elaborate planning is added.