Hi, I'm Lena. I was watching an agent handle a tool call last week when something felt slightly off.
The call failed. The agent just moved on — no retry, no log, nothing. It kept going like nothing happened.
I sat with that for a moment. Not because it crashed, but because there was nowhere obvious to put the logic that should have caught it.
That's when I started looking more carefully at hooks.
What Is an Agent Hook?
An agent hook is a predefined interception point in an agent's execution lifecycle where custom logic can be inserted. It doesn't change the agent's core reasoning loop. It attaches to specific moments — before a tool call, after a model response, when an error surfaces — and lets you do something there without touching the agent itself.
If you're already building with agent frameworks, this is probably a pattern you recognize even if you haven't named it. You've written something that runs before the model call to inject context, or something that catches outputs before they propagate. That's a hook.
Pre-hooks vs. Post-hooks vs. Error Hooks
The terminology varies across frameworks, but the structure is consistent.
Pre-hooks run before a step executes. They can inspect or modify the input — inject auth headers, append context, validate that required fields are present. A before_model hook might check whether the conversation has grown too long and truncate it before the model ever sees it.
Post-hooks run after a step completes. They receive the output and can transform it, log it, or trigger downstream actions. An after_tool_call hook might record what the tool returned before the agent continues.
Error hooks are the ones I find most interesting to observe. They fire when something in the chain fails. A tool times out. An API returns a 429. The model produces output that fails a validation check. Error hooks are where retry logic, fallback routing, and graceful degradation live — when they exist at all.
How Hooks Differ from Middleware
The distinction is subtle and frameworks don't always use these terms consistently, which has confused me more than once.
Middleware wraps the entire execution or a major layer of it — the request goes in, passes through the middleware stack, and comes out the other side. Hooks are more granular. They attach to specific named events within the execution cycle rather than wrapping the whole thing.
In LangChain's agent architecture, wrap_model_call behaves like middleware — it nests around the model call itself. But before_tool_call and after_tool_call are hooks in the narrower sense: they fire at specific points without wrapping. The LangChain agent documentation covers both patterns together, which is accurate to how they're used in practice even if the concepts differ.
Where Hooks Are Used
Tool Call Interception
This is probably the most common place I've seen hooks deployed. Tool calls are where agents interact with external systems — APIs, databases, file systems — and external systems fail in ways the agent doesn't always handle gracefully on its own.
A before_tool_call hook can validate inputs before the call goes out. A wrap_tool_call hook can catch failures and retry with backoff. An after_tool_call hook can normalize inconsistent response shapes before the agent processes them.
Without these, tool failures tend to propagate silently.
Memory Read/Write Hooks
Memory operations are another place where hooks create useful leverage. Before a memory read, a hook can filter what gets surfaced based on the current task context. After a memory write, a hook can flag entries that look inconsistent with existing records.
The Microsoft agent pipeline architecture uses this approach via AIContextProviders — injectable components that run before each LLM call to enrich the message history. It's not called a hook explicitly, but the pattern is identical: custom logic at a specific execution point, composable and replaceable.
Output Validation Hooks
Post-model hooks for output validation are underused, from what I've observed. The agent produces a response — but does it match the expected schema? Does it contain values outside acceptable ranges? Is it safe to pass downstream?
A validation hook that catches these issues before they propagate is much cheaper than diagnosing them after the fact.
Designing Hooks That Don't Break Agent Flow
Stateless vs. Stateful Hooks
A stateless hook reads inputs and produces outputs without depending on or modifying anything outside its own scope. It's predictable. A stateful hook — one that reads from or writes to shared state — introduces ordering dependencies and potential race conditions, especially in multi-agent or parallel execution scenarios.
Stateless hooks are easier to test, easier to reason about, and safer to compose. Stateful hooks are sometimes necessary, but they're also where most of the subtle bugs I've encountered with hook-based systems originate. LangGraph's extensibility patterns recommend keeping state mutations explicit and scoped to avoid this class of issue.
Side Effect Containment
A hook that makes an external API call, writes to a database, or triggers a notification is a hook with side effects. Side effects aren't inherently wrong, but they need to be contained — clearly bounded, idempotent where possible, and isolated from the core execution path so a failure in the hook doesn't cascade into the agent.
This is the part I still find slightly unclear in practice. The boundary between "the agent's logic" and "what the hook is allowed to do" isn't always well-defined.
Hook Patterns Worth Reusing
Retry Hooks for Tool Failures
A retry hook wraps a tool call and re-executes it on specific failure conditions — timeouts, rate limit errors, transient network issues. The key design decisions are: which errors warrant a retry, how many times, and with what backoff strategy.
wrap_tool_call in LangChain's middleware protocol handles exactly this. The handler callback pattern means each middleware layer receives a reference to the inner execution, enabling retry logic without modifying the tool itself.
Audit Hooks for Execution Logging
An audit hook fires after each significant step and records what happened: which tool was called, what it returned, how long it took, whether it succeeded. Over time, this log becomes the primary artifact for debugging agent behavior in production.
The Microsoft Agent Governance Toolkit structures this as a full governance gate — every tool call, output, and agent-to-agent interaction passes through a policy engine before it executes and leaves an audit trail. That's a more opinionated version of the same underlying pattern.
Rate-Limit Hooks for API Calls
A rate-limit hook tracks outgoing API calls and introduces delays or queuing when thresholds are approached. It's straightforward in concept and easy to underinvest in until an agent workflow starts hitting limits at scale.
The placement matters. A hook that runs too early can throttle unnecessarily. One that runs too late has already made the call. Pre-hooks on tool invocations are typically the right insertion point for rate-limit logic — close enough to the call to be accurate, early enough to intervene.
Limits and Tradeoffs
Hooks solve a real problem: injecting logic into an execution chain without rewriting the core. But they introduce their own surface area.
Execution order becomes important. Hooks often run in a defined sequence — all before hooks in order, then the call, then after hooks in reverse. When multiple hooks modify the same state, the order of operations matters and isn't always obvious.
Hooks can silently swallow errors. An error hook that catches and handles a failure might prevent the agent from knowing the failure happened. Whether that's the right behavior depends entirely on the context, and it's easy to get wrong.
Debugging a hook chain is harder than debugging linear code. When something behaves unexpectedly, the cause could be in any layer of a composed hook stack. Observability — traces, structured logs, explicit hook IDs — is not optional in production. The OpenTelemetry specification is worth understanding here; most production agent frameworks now emit traces in this format, and hooks are typically the instrumentation layer that feeds them.
I'm still working out where hooks are the right abstraction versus where they add too much. That line isn't fixed.
FAQ
- What is an agent hook in AI?
An agent hook is a custom logic insertion point at a specific stage of an agent's execution cycle — before a tool call, after a model response, or when an error occurs. Hooks let developers intercept, inspect, modify, or extend agent behavior without altering the core agent loop. They're supported across major frameworks including LangChain, LangGraph, and the Microsoft Agent Framework.
- What is the difference between a hook and middleware in an agent?
Middleware typically wraps a full layer of execution — like a pipeline decorator that every request passes through. Hooks are more granular, attaching to specific named events within the cycle. In practice, many frameworks use both: middleware-style wrappers for model calls, hook-style listeners for discrete events like before_tool_call or after_model. The distinction blurs at the edges.
- How do pre-hooks and post-hooks work in AI agent execution?
Pre-hooks run before a step — they can modify inputs, inject context, or block execution based on conditions. Post-hooks run after a step completes — they receive the output and can log it, validate it, or trigger downstream actions. Execution order is fixed: all pre-hooks fire in sequence, then the step executes, then post-hooks fire in reverse order. This pattern mirrors the request/response cycle in web server middleware.
- How do I add custom logic to an AI agent without modifying its core?
The standard approach is registering hooks or middleware with the agent's execution pipeline. In LangChain, this means passing middleware objects or decorated functions to create_agent. In the Microsoft Agent Framework, it means using .Use() decorators or AIContextProviders. The principle is consistent across frameworks: define the interception point, write the logic, register it. The agent's core loop stays untouched.
I'll keep watching how hook patterns evolve in production systems. There's something interesting about a mechanism that's designed to stay invisible — you only notice it when it fails, or when it isn't there and something else fails instead.




