Introduction & Context

In the previous lesson, you learned how to securely inject sensitive data into your agent workflows by managing the execution context. Now it's time to take your agent control skills to the next level by learning how to monitor and control the entire lifecycle of your agent workflows using event listeners.

When you build real-world AI applications, you need visibility into what your agents are doing. You might want to know when agents start and stop, which tools they're using, when handoffs occur between agents, and how long different operations take. This kind of observability is crucial for debugging, performance monitoring, compliance logging, and understanding how your AI system behaves in production.

By the end of this lesson, you will be able to create and attach event listeners to both individual agents and the runner system, giving you comprehensive monitoring and control over your agent workflows.

Understanding Event-Driven Monitoring

Before diving into the specifics of the OpenAI Agents SDK, let's establish the foundational concepts that make agent monitoring possible.

Event listeners are functions that "listen" for specific events to occur and automatically execute when those events happen. Think of them as watchers that sit quietly in the background until something interesting occurs, then spring into action.

In programming, event listeners follow a simple pattern: you register a function to be called when a particular event fires. The system then automatically invokes your function at the right moment, passing along relevant data about what happened.

For example, in web development you might register an event listener for button clicks:

In the context of AI agents, event listeners work the same way but for agent-related events like "agent started", "tool executed", or "handoff occurred". Instead of manually checking if these events happened, you register listeners that the SDK calls automatically.

Event listeners are non-intrusive – your main agent logic doesn't need to know about monitoring or logging concerns. The listeners operate independently, keeping your code clean and focused while providing powerful observability and control capabilities.

The OpenAI Agents SDK Event System

The OpenAI Agents SDK provides a flexible event system that allows you to attach listeners at two different levels:

LevelRegistration pointBest for
Runner eventsrunner.on('<event>')System‑wide monitoring, logging, analytics
Agent eventsagent.on('<event>')Agent‑specific monitoring, context setup

Runner events are emitted by the Runner instance while an agent workflow is executing. You register listeners with runner.on(), and those listeners receive notifications about everything that happens during the run – no matter which agent is active. This is perfect for global monitoring, compliance logging, and system-wide analytics.

Agent events are registered directly on individual agents using agent.on(). These listeners only fire for events related to that specific agent, making them ideal for targeted monitoring and agent-specific behaviors like dynamic context injection.

These two mechanisms work together seamlessly: you might maintain a global trace with runner events while simultaneously using agent events to perform specialized tasks for particular agents. This layered approach gives you both broad visibility and fine-grained control.

Runner Events: Your Global Monitoring System

A Runner emits events that cover all the critical lifecycle points you typically want to monitor. Here are the core events available:

Event nameListener signatureFires when
agent_start(ctx, agent)Before any agent runs
agent_end(ctx, agent, output)After an agent produces its final output
agent_handoff(ctx, fromAgent, toAgent)When one agent hands off to another
agent_tool_start(ctx, agent, tool)Before any tool is executed
agent_tool_end(ctx, agent, tool, result)After a tool returns its result

Every listener receives the shared context object as its first parameter, ensuring all monitoring code has access to the same runtime data.

The key characteristic of runner events is their global scope – when you register a listener on a runner, it will fire for every agent that executes within that runner's workflow, regardless of which specific agent triggers the event. This makes runner events perfect for system-wide monitoring, compliance logging, and analytics that need to capture the complete picture of your agent system's behavior.

Setting Up Global Event Listeners

Let's see how to set up comprehensive global monitoring by registering event listeners on a runner instance:

This setup provides complete visibility into your agent system's behavior. Notice how each listener serves a specific monitoring purpose:

  • agent_start captures when agents begin work and can initialize timing or logging
  • agent_tool_end records all tool executions for audit trails and debugging
  • agent_handoff tracks workflow routing for understanding system behavior
  • agent_end measures performance and captures final outputs

The global nature of these events means you get comprehensive coverage without needing to attach listeners to every individual agent in your system. This is particularly valuable in complex multi-agent workflows where you want centralized monitoring and logging.

Here's a practical example of runner-level monitoring:

Agent Events: Fine-Grained Per-Agent Control

While runner events excel at global monitoring, agent events give you surgical precision for individual agent behavior. You attach listeners directly to specific agents using the .on() method, and these listeners only fire for that particular agent.

The available agent events mirror the runner events but fire only for the attached agent:

Event nameListener signatureFires when
agent_start(ctx, agent)Before this agent is invoked
agent_end(ctx, agent, output)After this agent produces final output
agent_handoff(ctx, nextAgent)When this agent hands off to another
agent_tool_start(ctx, tool)Before this agent calls a tool
agent_tool_end(ctx, tool, result)After the tool returns

The key advantage of agent events is specificity – you get fine‑grained control without needing conditional logic to filter events for the agent you care about.

Implementing Dynamic Context Injection

Let's implement a practical example that demonstrates dynamic context injection using agent events:

This example shows how agent events enable you to inject fresh data right before an agent starts working, ensuring it always has the most up-to-date information available. The agent-specific nature of these listeners means they only execute for the Travel Genie agent, not for other agents in your system.

Shared Context Across All Events

One of the most powerful aspects of the OpenAI Agents SDK's event system is how context flows through your entire workflow. Every listener – both runner events and agent events – receives the same context instance that was passed to Runner.run().

This means all event listeners inside a single run share a mutable context object. Anything you store in ctx.context is immediately visible to subsequent listeners and components in that run. This pattern makes it easy to coordinate complex workflows and share state without exposing sensitive details to the language model.

For example, if your travelGenie agent's agent_start listener injects user data into the context, that same data will be available to:

  • All subsequent runner event listeners
  • Other agents' listeners if they receive a handoff
  • Any tools that get executed

This shared context approach eliminates the need for complex state management while maintaining clean separation between your monitoring code and your core agent logic.

Creating Your Complete Agent System

Let's build a complete multi-agent system with comprehensive event monitoring:

Executing the Complete Workflow

Finally, we execute previously defined runner instance with all event listeners active:

The SDK automatically coordinates all the event listeners you've registered, ensuring they fire at the correct time and in the proper sequence. When you call runner.run(), the system orchestrates all the monitoring mechanisms you've set up, giving you complete visibility into your agent workflow.

Observing the Complete Workflow

When you run a workflow with both types of event listeners attached, you'll see comprehensive monitoring output that shows the complete lifecycle of your agent system:

This output demonstrates the complete flow: the triage agent starting, the handoff to Travel Genie, Travel Genie starting (with context injection happening via the agent event listener), the tool execution result, and finally the agent's completion output. Notice how the global events provide system-wide visibility while the agent-specific events capture targeted information.

Summary & Next Steps

In this lesson, you've mastered the OpenAI Agents SDK's comprehensive event system. You learned how runner events provide global visibility across your entire agent system, while agent events enable fine-grained monitoring and control over individual agents. You've seen how shared context flows through all event listeners, creating a powerful foundation for coordination and state management.

The combination of these event mechanisms gives you everything you need to build sophisticated monitoring, implement dynamic context injection, and maintain robust observability in your agent workflows. Whether you need system-wide analytics or agent-specific customization, you now have the tools to implement both seamlessly.

Now that you understand these event listener fundamentals, you're ready to experiment with creating your own custom monitoring and control solutions in the following practice exercises. These hands-on activities will deepen your expertise in agent workflow control and help you build more sophisticated AI systems with robust observability.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal