Implementation:Openai Openai agents python Lifecycle Hooks Pattern
| Knowledge Sources | |
|---|---|
| Domains | Agent_Configuration, Observability, Design_Pattern |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Demonstrates both run-level RunHooks and agent-level AgentHooks for observing and logging lifecycle events during agent execution, including agent start/end, LLM calls, tool invocations, and handoffs.
Description
The lifecycle_example.py example demonstrates the SDK's two-tier hooks system for observing and reacting to events during agent execution. The run-level ExampleHooks(RunHooks) class provides comprehensive visibility across an entire run, tracking seven event types: on_agent_start, on_agent_end, on_llm_start, on_llm_end, on_tool_start, on_tool_end, and on_handoff. It maintains an event counter and prints detailed information including agent names, tool call IDs, arguments, results, and cumulative usage statistics (requests, input/output/total tokens) at each event.
The agent-level LoggingHooks(AgentHooks) class provides per-agent visibility with on_start and on_end callbacks. The on_start method receives an AgentHookContext with access to the turn_input showing what input the agent received, while on_end receives the agent's output. Each agent can have its own AgentHooks instance, enabling agent-specific logging or behavior.
The example sets up a two-agent pipeline: a start_agent that generates a random number and hands off to a multiply_agent if the number is odd. Both agents are configured with LoggingHooks() for agent-level tracking, and the run is executed with ExampleHooks() for run-level tracking. The tool hooks note that they apply only to local tools and do not fire for hosted tools (WebSearchTool, FileSearchTool, CodeInterpreterTool, HostedMCPTool).
Usage
Use this pattern when you need to observe, log, measure, or react to lifecycle events during agent execution. Common use cases include logging and debugging, usage tracking (token consumption per step), latency monitoring, audit trails for compliance, custom metrics collection, and building reactive systems that respond to specific lifecycle events.
Code Reference
Source Location
- Repository: Openai_Openai_agents_python
- File: examples/basic/lifecycle_example.py
- Lines: 1-189
Signature
class ExampleHooks(RunHooks):
def __init__(self): ...
async def on_agent_start(self, context: AgentHookContext, agent: Agent) -> None: ...
async def on_llm_start(self, context: RunContextWrapper, agent: Agent, system_prompt: Optional[str], input_items: list[TResponseInputItem]) -> None: ...
async def on_llm_end(self, context: RunContextWrapper, agent: Agent, response: ModelResponse) -> None: ...
async def on_agent_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None: ...
async def on_tool_start(self, context: RunContextWrapper, agent: Agent, tool: Tool) -> None: ...
async def on_tool_end(self, context: RunContextWrapper, agent: Agent, tool: Tool, result: str) -> None: ...
async def on_handoff(self, context: RunContextWrapper, from_agent: Agent, to_agent: Agent) -> None: ...
class LoggingHooks(AgentHooks[Any]):
async def on_start(self, context: AgentHookContext[Any], agent: Agent[Any]) -> None: ...
async def on_end(self, context: RunContextWrapper[Any], agent: Agent[Any], output: Any) -> None: ...
Import
from agents import (
Agent,
AgentHookContext,
AgentHooks,
RunContextWrapper,
RunHooks,
Runner,
Tool,
Usage,
function_tool,
)
from agents.items import ModelResponse, TResponseInputItem
from agents.tool_context import ToolContext
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| hooks (run-level) | RunHooks | No | Passed to Runner.run() to observe run-wide lifecycle events |
| hooks (agent-level) | AgentHooks | No | Passed to Agent() constructor to observe per-agent lifecycle events |
| context (on_agent_start) | AgentHookContext | Yes | Provides turn_input and usage data at agent start |
| context (on_tool_start/end) | RunContextWrapper (castable to ToolContext) | Yes | Provides tool_name, tool_call_id, tool_arguments, and usage data |
Outputs
| Name | Type | Description |
|---|---|---|
| Event log lines | stdout | Numbered event entries with agent name, tool details, usage stats, and handoff information |
| Usage stats | Usage | Cumulative request count, input_tokens, output_tokens, and total_tokens at each event |
Usage Examples
Define Run-Level Hooks
from agents import RunHooks, RunContextWrapper, Agent, AgentHookContext, Tool, Usage
class MyRunHooks(RunHooks):
async def on_agent_start(self, context: AgentHookContext, agent: Agent) -> None:
print(f"Agent {agent.name} started with input: {context.turn_input}")
async def on_tool_start(self, context: RunContextWrapper, agent: Agent, tool: Tool) -> None:
print(f"Tool {tool.name} invoked")
async def on_handoff(self, context: RunContextWrapper, from_agent: Agent, to_agent: Agent) -> None:
print(f"Handoff: {from_agent.name} -> {to_agent.name}")
Define Agent-Level Hooks
from agents import AgentHooks, AgentHookContext, RunContextWrapper, Agent
class MyAgentHooks(AgentHooks):
async def on_start(self, context: AgentHookContext, agent: Agent) -> None:
print(f"{agent.name} is starting")
async def on_end(self, context: RunContextWrapper, agent: Agent, output) -> None:
print(f"{agent.name} produced: {output}")
Attach Hooks and Run
import asyncio
from agents import Agent, Runner, function_tool
from pydantic import BaseModel
class FinalResult(BaseModel):
number: int
agent = Agent(
name="My Agent",
instructions="Do something useful.",
hooks=MyAgentHooks(),
)
hooks = MyRunHooks()
async def main():
await Runner.run(agent, hooks=hooks, input="Process this request.")
asyncio.run(main())