Lifecycle Hooks in the OpenAI Agents SDK allow you to execute custom code at specific points during an agent run. Think of Lifecycle Hooks as "event listeners" or "callbacks" that let you hook into the execution flow - you can run custom logic when an agent starts, when a tool is called, when the LLM generates a response, and more. This is essential for logging, monitoring, custom business logic, and extending agent behavior.
Core Concepts
What are Lifecycle Hooks?
Lifecycle Hooks are functions that are called at specific points during agent execution:
Agent lifecycle - When an agent starts or ends
LLM lifecycle - Before and after LLM calls
Tool lifecycle - Before and after tool execution
Handoff lifecycle - When agents hand off to each other
Custom Logic - Implement business rules at specific points
Analytics - Collect data on agent usage
Validation - Add custom validation at key points
Extension - Extend agent behavior without modifying core code
Hook Types
Run Hooks
RunHooks are called for the entire run, across all agents:
Python
from agents import RunHooks
classMyRunHooks(RunHooks[TContext]):
asyncdefon_agent_start(self, context, agent):
"""Called when any agent starts."""print(f"Agent {agent.name} starting")
asyncdefon_agent_end(self, context, agent, output):
"""Called when any agent ends."""print(f"Agent {agent.name} finished")
When to use:
Global logging for all agents
Cross-agent monitoring
Run-level analytics
Global business logic
Agent Hooks
AgentHooks are called for a specific agent:
Python
from agents import AgentHooks
classMyAgentHooks(AgentHooks[TContext]):
asyncdefon_start(self, context, agent):
"""Called when this specific agent starts."""print(f"My agent starting")
asyncdefon_end(self, context, agent, output):
"""Called when this specific agent ends."""print(f"My agent finished")
When to use:
Agent-specific logging
Per-agent monitoring
Agent-specific business logic
Custom agent behavior
Run Hooks Methods
on_agent_start
Called when an agent starts (any agent in the run):
Python
asyncdefon_agent_start(
self,
context: AgentHookContext[TContext],
agent: TAgent,
) -> None:
"""Called before the agent is invoked.
Called each time the current agent changes.
"""print(f"Agent {agent.name} starting")
Use cases:
Log agent starts
Initialize agent-specific state
Track agent usage
Validate agent configuration
on_agent_end
Called when an agent produces a final output:
Python
asyncdefon_agent_end(
self,
context: AgentHookContext[TContext],
agent: TAgent,
output: Any,
) -> None:
"""Called when the agent produces a final output."""print(f"Agent {agent.name} finished with output: {output}")
Use cases:
Log agent outputs
Track agent performance
Validate output
Trigger follow-up actions
on_handoff
Called when a handoff occurs:
Python
asyncdefon_handoff(
self,
context: RunContextWrapper[TContext],
from_agent: TAgent,
to_agent: TAgent,
) -> None:
"""Called when a handoff occurs."""print(f"Handoff from {from_agent.name} to {to_agent.name}")
Use cases:
Log handoffs
Track handoff patterns
Validate handoffs
Trigger handoff-specific logic
on_llm_start
Called just before invoking the LLM:
Python
asyncdefon_llm_start(
self,
context: RunContextWrapper[TContext],
agent: Agent[TContext],
system_prompt: str | None,
input_items: list[TResponseInputItem],
) -> None:
"""Called just before invoking the LLM for this agent."""print(f"LLM call starting for {agent.name}")
Use cases:
Log LLM calls
Track prompt usage
Validate prompts
Modify prompts (with care)
on_llm_end
Called immediately after the LLM call returns:
Python
asyncdefon_llm_end(
self,
context: RunContextWrapper[TContext],
agent: Agent[TContext],
response: ModelResponse,
) -> None:
"""Called immediately after the LLM call returns for this agent."""print(f"LLM call completed for {agent.name}")
Use cases:
Log LLM responses
Track token usage
Validate responses
Analyze model behavior
on_tool_start
Called immediately before a local tool is invoked:
Python
asyncdefon_tool_start(
self,
context: RunContextWrapper[TContext],
agent: TAgent,
tool: Tool,
) -> None:
"""Called immediately before a local tool is invoked.
For function-tool invocations, context is typically a ToolContext instance.
"""print(f"Tool {tool.name} starting")
Use cases:
Log tool calls
Track tool usage
Validate tool inputs
Modify tool inputs (with care)
on_tool_end
Called immediately after a local tool is invoked:
Python
asyncdefon_tool_end(
self,
context: RunContextWrapper[TContext],
agent: TAgent,
tool: Tool,
result: str,
) -> None:
"""Called immediately after a local tool is invoked."""print(f"Tool {tool.name} finished")
Use cases:
Log tool results
Track tool performance
Validate tool outputs
Analyze tool behavior
Agent Hooks Methods
on_start
Called when this specific agent starts:
Python
asyncdefon_start(
self,
context: AgentHookContext[TContext],
agent: TAgent,
) -> None:
"""Called before the agent is invoked.
Called each time the running agent is changed to this agent.
"""print(f"This agent starting")
Use cases:
Agent-specific initialization
Agent-specific logging
Validate agent state
Load agent-specific resources
on_end
Called when this specific agent produces a final output:
Python
asyncdefon_end(
self,
context: AgentHookContext[TContext],
agent: TAgent,
output: Any,
) -> None:
"""Called when the agent produces a final output."""print(f"This agent finished")
Use cases:
Agent-specific cleanup
Agent-specific output processing
Save agent-specific state
Trigger agent-specific follow-ups
on_handoff
Called when the agent is being handed off to:
Python
asyncdefon_handoff(
self,
context: RunContextWrapper[TContext],
agent: TAgent,
source: TAgent,
) -> None:
"""Called when the agent is being handed off to.
The source is the agent that is handing off to this agent.
"""print(f"Handed off from {source.name} to {agent.name}")
Use cases:
Log handoff reception
Prepare agent for handoff
Validate handoff context
Initialize agent state
on_tool_start
Called immediately before a local tool is invoked:
Python
asyncdefon_tool_start(
self,
context: RunContextWrapper[TContext],
agent: TAgent,
tool: Tool,
) -> None:
"""Called immediately before a local tool is invoked."""print(f"Tool {tool.name} starting in this agent")
Use cases:
Agent-specific tool logging
Track tool usage by agent
Agent-specific tool validation
on_tool_end
Called immediately after a local tool is invoked:
Python
asyncdefon_tool_end(
self,
context: RunContextWrapper[TContext],
agent: TAgent,
tool: Tool,
result: str,
) -> None:
"""Called immediately after a local tool is invoked."""print(f"Tool {tool.name} finished in this agent")
Use cases:
Agent-specific tool result logging
Track tool performance by agent
Agent-specific tool result validation
on_llm_start
Called immediately before the agent issues an LLM call:
Python
asyncdefon_llm_start(
self,
context: RunContextWrapper[TContext],
agent: Agent[TContext],
system_prompt: str | None,
input_items: list[TResponseInputItem],
) -> None:
"""Called immediately before the agent issues an LLM call."""print(f"LLM call starting for this agent")
Use cases:
Agent-specific LLM logging
Track LLM usage by agent
Agent-specific prompt validation
on_llm_end
Called immediately after the agent receives the LLM response:
Python
asyncdefon_llm_end(
self,
context: RunContextWrapper[TContext],
agent: Agent[TContext],
response: ModelResponse,
) -> None:
"""Called immediately after the agent receives the LLM response."""print(f"LLM call completed for this agent")
# Good - logging is cross-cuttingclassLoggingHooks(RunHooks):
asyncdefon_agent_start(self, context, agent):
logger.info(f"Agent {agent.name} starting")
# Avoid - business logic in hooksclassBusinessLogicHooks(RunHooks):
asyncdefon_agent_start(self, context, agent):
if complex_business_rule(agent):
do_something() # Business logic should be in tools/agents