AI Agents Need an Execution Architecture, Not Just More Tools

Calling a language-model workflow an agent does not make it more capable. It only makes the design question easier to avoid.

An agent is a system that observes an environment and can act on it. For a customer-support product, the environment may include a conversation, a knowledge base, account data, and a case-management API. For a coding product, it may include a repository, test runner, terminal, and pull-request service. The set of tools defines what the system can do; the environment defines what those actions mean.

That is a larger responsibility than generating text. A wrong sentence may be corrected by a person. A wrong action can send an email, change a record, spend money, or leak data. The useful architectural question is not whether a model can plan. It is which decisions must remain visible, validated, and reversible when that plan meets the real world.

An Agent Is Defined by Its Environment and Action Set

The word agent has become broad enough to obscure the boundary that matters. A RAG assistant that retrieves documentation is agentic in the modest sense that it uses a retrieval tool. An assistant that retrieves records and issues refunds has a far more consequential action space.

Before choosing a framework, write down the operating envelope:

BoundaryQuestions worth answering
EnvironmentWhat state can change? What sources can be stale, adversarial, or incomplete?
ObservationWhich data may the system read for this user and task?
ActionsWhich tools are read-only, which have external effects, and which are irreversible?
AuthorityWhat does the signed-in user permit? What requires a separate human approval?
CompletionWhat objective evidence shows the task actually succeeded?
RecoveryHow can the product cancel, retry, compensate, or hand work to a person?

This is not paperwork. It reveals when a vague agent capability is really several products with different risk. "Resolve a billing request" might mean look up a payment, explain a charge, update a plan, issue a credit, or open a human escalation. Each needs different data and authority.

For browser applications, do not let the client become the policy engine. The frontend should request a bounded workflow, display sources and proposed effects, and provide cancellation or confirmation. A server-side boundary should authenticate the caller, choose tools, authorize each effect, apply rate and cost limits, and record the result. The model is an input to that boundary, not its administrator.

Separate Planning From Execution

An agent often needs to turn one request into several steps: identify intent, retrieve facts, choose a tool, inspect its result, and decide what remains. A single model call can attempt all of that. It is appealing because the demo is short. It is difficult to debug because a failure has no location.

Make the stages explicit instead:

  1. Classify intent: identify the job and the requested scope.
  2. Generate a plan: propose the necessary steps and inputs.
  3. Validate the plan: check whether the requested tools, order, and arguments are allowed and feasible.
  4. Execute bounded steps: call tools through normal application code.
  5. Evaluate outcomes: decide whether evidence shows completion, a recoverable error, or a need to revise the plan.

The model can participate in several stages, but it should not collapse them into one opaque authority. A planner may propose retrieving recent payments for a billing request. The executor must still apply the current user's account scope. An evaluator may notice that a required record is missing. It should not compensate by inventing it.

This separation also improves performance choices. The system can generate several low-cost plan candidates in parallel and select one, but that spends more tokens and creates another selection problem. It can cache an intent classification, but only when that cache does not cross users, permissions, or materially changing state. Architecture makes these tradeoffs visible instead of letting them hide in an agent loop.

Tool Definitions Are an Application Interface

Function calling gives a model a structured way to request a tool and arguments. It is valuable because a schema can reject malformed JSON before an action runs. It is not a guarantee that the requested action is correct, authorized, or safe.

Treat a tool declaration as an internal API contract. It needs a purpose narrow enough to understand, inputs constrained enough to validate, and an output shape the next stage can reason about.

type ProposedPlan = {
  intent: 'explain-payment' | 'request-refund'
  steps: Array<
    | { tool: 'getRecentPayments'; accountId: string }
    | { tool: 'createRefundRequest'; paymentId: string; reason: string }
  >
}

async function executeStep(
  actor: AuthenticatedUser,
  step: ProposedPlan['steps'][number],
) {
  if (step.tool === 'getRecentPayments') {
    await assertAccountAccess(actor.id, step.accountId)
    return getRecentPayments(step.accountId)
  }

  await assertRefundAuthority(actor, step.paymentId)
  return createRefundRequest(step.paymentId, step.reason)
}

The dispatch code needs to authorize the actual resource, not trust an accountId chosen in model output. High-impact tools should prefer a narrow operation, read-only defaults, limited scopes, idempotency where appropriate, and an explicit confirmation that displays the actual effect. Generated arguments are proposals; deterministic code decides whether they can become a state change.

This boundary protects against ordinary mistakes and prompt injection alike. A retrieved message can influence a plan, but it cannot use prose to grant permission to itself.

Control Flow Is Product Behavior

Plans are not always a list of serial calls. They may need parallel work, branches, and loops:

  • sequential: translate a request into a constrained query, then execute it;
  • parallel: retrieve several independent sources at once to reduce perceived latency;
  • conditional: route a request to a human when the available evidence or authorization is insufficient;
  • bounded iteration: inspect a paginated source until a condition is met or a budget is exhausted.

Traditional code evaluates control-flow conditions exactly. Agentic workflows often let a model classify a condition whose meaning is fuzzy. That is workable only if the decision has a contract and a fallback.

For example, "is this refund request eligible?" should be converted into concrete policy checks wherever possible. A model may extract facts or summarize an ambiguous request, but account status, payment date, product rules, and approval requirements belong in deterministic code. When an agent does need judgment, the product should preserve the evidence, expose the decision to an authorized reviewer, and avoid silently converting uncertainty into a completed action.

Parallelism deserves the same care. It can make a research interface feel dramatically faster, yet it can also multiply rate-limit pressure and cause several in-flight operations to outlive a cancelled request. Use an AbortSignal through the server workflow, cap concurrency, and ensure the UI distinguishes "cancelled" from "completed with no result." The user experiences the end-to-end graph, not the elegance of individual calls.

Pick Planning Granularity for the Failure You Need to Catch

A detailed plan is easier to execute because each step is concrete. It is harder to generate accurately because there are more chances for a model to select a wrong tool, parameter, or ordering. A high-level plan is easier to form but pushes ambiguity into execution.

The right granularity follows the consequence and observability of the work:

  • use small, explicit steps for money movement, account changes, production deployment, and regulated data;
  • use higher-level plans for exploratory research where the product can show sources and ask the user to refine the direction;
  • stop a loop when the task has a defined budget, retry limit, or handoff condition;
  • require each plan step to name an expected result, not only an action.

The last point prevents a common failure. An agent may call a tool successfully yet make no progress because the tool returned an empty or irrelevant result. A plan with expected observations can decide whether to revise, ask a question, or stop. A plan that only counts successful calls can wander indefinitely.

Design for Compound Mistakes

Each additional action adds a chance to fail. In a simple approximation, if every independent step has a probability $p$ of behaving correctly, a workflow with $n$ required steps has success probability $p^n$. Even high per-step reliability degrades quickly across a long chain.

That is not an argument against agents. It is an argument against treating more autonomy as free capability. Reduce the number of uncertain transformations. Prefer direct APIs over asking a model to interpret a human-formatted page. Make tool outputs structured. Validate before effects. Add checkpoints around expensive or irreversible work. Let a person take over when the system lacks evidence.

The legal and social stakes become concrete here. A system that misroutes a promotional suggestion is annoying. A system that changes a benefit, exposes an account record, or sends a message on a person's behalf can cause a real loss. The design must identify who can contest the action, what record explains it, and how the effect can be reversed. "The agent decided" is not an acceptable operational explanation.

Build Agents That Can Be Examined

Good agent architecture does not ask a model to impersonate an application runtime. It uses a model where language and judgment are useful, then keeps authority, state transitions, and policy in systems that can enforce them.

Define the environment and action set. Separate planning, validation, execution, and evaluation. Treat tool definitions as contracts. Make control flow, budgets, and recovery visible. Match plan granularity to consequence. Let the UI show sources, pending effects, confirmations, and failures instead of inventing a smooth narrative around uncertainty.

That is how an agent becomes an operable product workflow rather than a chain of impressive tool calls.