# How to Use RunHooks and AgentHooks to Intercept Agent Lifecycle Events in openai-agents-python

> Intercept agent lifecycle events in openai-agents-python using RunHooks and AgentHooks. Easily customize agent behavior without altering core logic. Learn how now!

- Repository: [OpenAI/openai-agents-python](https://github.com/openai/openai-agents-python)
- Tags: how-to-guide
- Published: 2026-04-17

---

**Subclass `RunHooksBase` or `AgentHooksBase` from [`src/agents/lifecycle.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/lifecycle.py), override the specific lifecycle methods you need, and pass the instance to `AgentRunner.run()` or `Runner.run()` via the `hooks=` parameter to intercept execution without modifying core agent logic.**

The `openai-agents-python` SDK exposes two distinct hook systems that let you tap into agent execution flows at different granularities. **RunHooks and AgentHooks** provide a non-intrusive way to log, monitor, and modify behavior during agent lifecycle events. Both systems are defined in [`src/agents/lifecycle.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/lifecycle.py) and integrate with the execution loop through `RunContextWrapper` in [`src/agents/run_context.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_context.py).

## Understanding RunHooks vs. AgentHooks

The SDK distinguishes between run-level and agent-level interception points:

**RunHooks** operate at the **run level**, managing the lifecycle of a single execution flow from creation through completion. These hooks fire during turn execution, tool calls, and error handling within [`src/agents/run_internal/run_loop.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/run_loop.py).

**AgentHooks** operate at the **agent instance level**, capturing global events like initialization, shutdown, run creation, and state rewinds. These hooks manage cross-run concerns and agent lifespan events.

Both classes are generic (type-parameterized) and preserve your custom `RunContext` types throughout the execution chain, ensuring type-safe access to contextual data within hook implementations.

## How the Hook Mechanism Works Internally

When you invoke `AgentRunner.run()` or `Runner.run()` as defined in [`src/agents/run.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run.py), the execution flow enters [`src/agents/run_internal/run_loop.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/run_loop.py). At predetermined interception points, the runner checks for registered hooks via `RunContextWrapper` in [`src/agents/run_context.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_context.py).

If hooks are provided, the wrapper forwards calls to your implementations. If no hooks are registered, the runner uses default no-op implementations from the base classes, ensuring **zero performance overhead** when interception is not needed.

## Implementing RunHooks for Run-Level Interception

To intercept individual turns, tool executions, and run completion, subclass `RunHooksBase` from [`src/agents/lifecycle.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/lifecycle.py) and implement the methods corresponding to the events you need.

```python
from agents import AgentRunner, RunContext, RunHooksBase
from agents.llm import OpenAIChat
from agents.agent import SimpleAgent

# Define a custom context to carry request metadata

class MyRunContext(RunContext):
    request_id: str = "unknown"

# Implement run-level hooks

class LoggingRunHooks(RunHooksBase[MyRunContext, SimpleAgent]):
    async def on_start(self, ctx: MyRunContext) -> None:
        print(f"[Run start] request_id={ctx.request_id}")

    async def on_before_turn(self, ctx: MyRunContext) -> None:
        print(f"[Turn {ctx.turn}] about to call model…")

    async def on_after_turn(self, ctx: MyRunContext) -> None:
        print(f"[Turn {ctx.turn}] completed, tokens used={ctx.last_response.usage.total_tokens}")

    async def on_tool_error(self, ctx: MyRunContext, exc: Exception) -> None:
        print(f"[Tool error] {exc!r}")

# Build and run the agent with hooks

llm = OpenAIChat(model="gpt-4o-mini")
agent = SimpleAgent(llm=llm)
runner = AgentRunner(agent)

await runner.run(
    messages=[{"role": "user", "content": "Explain the Fibonacci sequence"}],
    context=MyRunContext(request_id="req-12345"),
    hooks=LoggingRunHooks(),
)

```

The `RunHooksBase` class provides methods including `on_start`, `on_before_turn`, `on_after_turn`, `on_tool_start`, `on_tool_success`, `on_tool_error`, `on_finish`, and `on_error`. Each receives the current `RunContext`, allowing you to inspect or log state without blocking the main execution flow.

## Implementing AgentHooks for Agent-Level Interception

For global agent lifecycle events, use `AgentHooksBase` from [`src/agents/lifecycle.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/lifecycle.py). This class provides hooks for initialization, shutdown, run creation, and state rewinds.

```python
from agents import AgentRunner, AgentHooksBase, AgentHookContext
from agents.llm import OpenAIChat
from agents.agent import SimpleAgent

class MetricsAgentHooks(AgentHooksBase[object, SimpleAgent]):
    async def on_agent_init(self, ctx: AgentHookContext) -> None:
        print("[Agent] initialising – allocate metrics bucket")

    async def on_agent_shutdown(self, ctx: AgentHookContext) -> None:
        print("[Agent] shutting down – flush metrics")

    async def on_run_created(self, ctx: AgentHookContext) -> None:
        print("[Run] new run started – start timer")

    async def on_run_rewind(self, ctx: AgentHookContext) -> None:
        print("[Run] rewind requested – reset timer")

# Build and run with agent-level hooks

llm = OpenAIChat(model="gpt-4o-mini")
agent = SimpleAgent(llm=llm)
runner = AgentRunner(agent)

await runner.run(
    messages=[{"role": "user", "content": "What is the weather today?"}],
    hooks=MetricsAgentHooks(),
)

```

`AgentHooksBase` methods include `on_agent_init`, `on_agent_shutdown`, `on_run_created`, `on_run_rewind`, and `on_global_error`. These fire outside the scope of any single run, making them ideal for resource management and cross-run persistence.

## When to Use RunHooks vs. AgentHooks

Choose your hook type based on the scope of the event you need to intercept:

- **Use `RunHooksBase`** when you need to instrument **individual execution turns**, handle **tool-level failures**, or log **per-run metrics** like token usage. These hooks fire within the single execution flow managed by [`src/agents/run_internal/run_loop.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/run_loop.py).

- **Use `AgentHooksBase`** when you need to manage **global agent state**, allocate resources during **agent initialization**, clean up during **shutdown**, or handle **run rewinds** and cross-run persistence. These hooks operate at the agent instance level independent of any single run.

Both systems are **composable**—you can provide instances of both hook types simultaneously to the same runner invocation. The `RunContextWrapper` in [`src/agents/run_context.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_context.py) manages the delegation chain, ensuring both run-level and agent-level callbacks fire in the correct order without interference.

## Summary

- **RunHooks and AgentHooks** provide a non-intrusive mechanism to intercept agent lifecycle events in `openai-agents-python` without modifying core agent logic.
- **RunHooksBase** (in [`src/agents/lifecycle.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/lifecycle.py)) manages run-level events including turns, tool execution, and errors via methods like `on_before_turn`, `on_tool_success`, and `on_finish`.
- **AgentHooksBase** (in [`src/agents/lifecycle.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/lifecycle.py)) handles agent-level lifecycle events including initialization, shutdown, and run rewinds via `on_agent_init`, `on_run_created`, and `on_run_rewind`.
- Hooks integrate through `RunContextWrapper` in [`src/agents/run_context.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_context.py) and are invoked by the execution loop in [`src/agents/run_internal/run_loop.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/run_loop.py).
- Both hook types are generic and preserve custom `RunContext` types for type-safe access to contextual data.
- Hooks incur zero performance overhead when not registered, as the runner uses default no-op implementations.

## Frequently Asked Questions

### What is the difference between RunHooks and AgentHooks in openai-agents-python?

**RunHooks** intercept events within a single execution flow or "run," such as individual model turns, tool executions, and run completion. **AgentHooks** operate at the agent instance level, handling global lifecycle events like agent initialization, shutdown, run creation, and state rewinds. According to the source code in [`src/agents/lifecycle.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/lifecycle.py), `RunHooksBase` focuses on per-run instrumentation while `AgentHooksBase` manages cross-run state and resource lifecycle.

### How do I pass custom hooks to an agent runner?

You pass hook instances via the `hooks=` parameter when calling `AgentRunner.run()` or `Runner.run()`. As shown in [`src/agents/run.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run.py), the runner accepts subclasses of either `RunHooksBase` or `AgentHooksBase`. The execution loop in [`src/agents/run_internal/run_loop.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/run_loop.py) then invokes your hook methods at the appropriate lifecycle points, wrapping them via `RunContextWrapper` from [`src/agents/run_context.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_context.py).

### Can I use both RunHooks and AgentHooks simultaneously?

Yes, the hook systems are composable and designed to work together. You can provide instances of both `RunHooksBase` and `AgentHooksBase` to the same runner invocation. The `RunContextWrapper` class in [`src/agents/run_context.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_context.py) manages the delegation chain, ensuring that both run-level and agent-level callbacks fire in the correct sequence without interfering with each other or blocking the main execution flow.

### Do hooks impact performance if I don't register any?

No. When no hooks are provided, the runner uses default no-op implementations defined in `RunHooksBase` and `AgentHooksBase`. As implemented in `openai-agents-python`, the core execution loop in [`src/agents/run_internal/run_loop.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/run_loop.py) checks for hook presence and skips invocation overhead entirely when no hooks are registered, ensuring zero performance penalty for the opt-in interception mechanism.