Tools, Agent Loops, and Middleware

Objectives

See the minimal LangChain Agent loop clearly, learn to express tool schemas, execution permissions, errors, and side effects as code contracts, and understand the current boundaries of create_agent and middleware.

The Agent loop is not mysterious

user message -> model
                | no tool call -> final message
                | tool call    -> validate parameters -> execute tool -> ToolMessage -> model
                                               \______________________________/ bounded loop

The model only proposes an action. The real executor must validate the tool name, parameters, caller identity, and resource scope again. After observing a tool result, the model can call another tool or finish. The runtime must limit maximum rounds, total elapsed time, cost, duplicate calls, and human-takeover conditions.

LangChain v1 uses from langchain.agents import create_agent as the standard high-level Agent entry point, replacing the former preferred langgraph.prebuilt.create_react_agent position. High-level Agents are built on LangGraph, but a standard tool loop does not require a beginner to hand-write a StateGraph first.

Start with a read-only tool

The current official Python entry point for tools is from langchain.tools import tool. By default, the decorator uses the function name and docstring to create the tool description, while type hints participate in the parameter schema. Illustrative code:

from langchain.tools import tool  # Import the decorator that declares an ordinary Python function as a LangChain Tool.
 
@tool  # Generate the tool description and parameter schema from the function name, docstring, and type hints.
def lookup_order(order_id: str) -> dict[str, str]:  # Read-only lookup interface whose input is an order identifier.
    """Look up the current caller's authorized order summary by order ID."""
    # Business code must validate identity and resource authorization; the decorator itself provides no access control.
    return {"order_id": order_id, "status": "pending"}  # Return the minimal order summary without exposing unrelated fields.

Tool descriptions should explain when to use the tool and its boundaries, but they are not an authorization system. An order_id, path, URL, or SQL statement generated by the model is untrusted input.

A good tool contract

DimensionQuestion to answer
NameIs it one action, such as lookup_order rather than manage_order?
InputWhat are the types, lengths, enums, resource scope, and required fields?
OutputWhat does the model see, what artifact does the application retain, and how is provenance tracked?
ErrorsHow are invalid input, permission denial, transient failure, and permanent failure distinguished?
Side effectsDoes it write data, incur a charge, or send a message? Can it preview and require approval?
IdempotencyWould retrying the same request execute it twice? Where is the idempotency key stored?
BudgetWhat are the limits for timeouts, rate, call count, and result size?

Separate read-only and write tools. For high-risk actions such as sending email, deleting files, or executing SQL, return a preview first and execute only with an external approval token. Bind the approval to the action, parameters, state version, and expiration time.

What create_agent handles

A high-level Agent handles the standard model–tool loop, message state, and middleware extension points. A typical shape includes model, tools, optional system_prompt, middleware, response_format, and checkpoint-related configuration. Model identifiers, provider packages, and some parameters change by version, so code is verified only after the project locks its dependencies.

It does not automatically guarantee that:

  • Tools access only resources the current user is authorized to access.
  • External actions are idempotent.
  • Prompt injection cannot influence actions.
  • Every tool has sensible timeouts and error categories.
  • The final answer has factual provenance.

Before obtaining provider credentials, run the keyless `create_agent` runtime contract. It sends a fixed message trace through real create_agent and ToolNode instances, verifying normal behavior, unknown tools, and strict-parameter rejection. Its fake shim intentionally does not validate provider bind_tools serialization, real model decisions, networking, or authorization, so separate smoke and integration tests must still cover those areas.

Where middleware fits

Current official documentation positions middleware as the primary create_agent extension surface. It can handle dynamic prompts, context trimming, tool selection, retries, fallbacks, rate limiting, PII handling, guardrails, human approval, and logging. Middleware hooks run inside the LangGraph compiled by create_agent; the entire Agent can also become a node in a larger StateGraph.

Treat middleware as a set of composable policies in engineering work; do not put all business logic into one hook. Enforce authorization at tool execution, redact sensitive logs when they are produced, and define a clear terminal state if middleware fails.

Failure and stopping policy

  • Parameters do not meet the schema: do not execute the tool; return a recoverable error to the model or fail directly.
  • Permission denied: retries cannot bypass it; record an audit event and escalate or reject.
  • Transient network error: use bounded exponential backoff under a total-time budget.
  • Permanent business error: stop immediately; do not classify “order not found” as a network error.
  • Repeated identical call: check whether an observation was lost; stop at the threshold.
  • Unknown result of a write: check the idempotency receipt first; never replay blindly.

Practice

Design three tools for “look up a policy and create a ticket”: search_policy, preview_ticket, and create_ticket. For each, specify the input schema, output, errors, side effects, authorization, idempotency key, and approval requirement. Then draw a termination graph with no more than six model calls, three tool calls, and a 30-second total budget.

Self-check

  • Restate the responsibility boundaries among the model, Agent runtime, executor, and tools.
  • Explain why an @tool schema cannot replace object-level authorization.
  • Design preview, approval, idempotency, and unknown-result recovery for a write tool.
  • Explain that middleware fits cross-cutting policies but is not the only security-boundary implementation.

Next

Continue to Retrieval and RAG Components to learn how an Agent can use traceable knowledge.

Source baseline

Official facts checked on 2026-07-21.