OpenAI Realtime Agents

repository·main·Indexed 27 days ago

https://github.com/openai/openai-realtime-agents

A demonstration repository for advanced voice agent patterns using the OpenAI Realtime API and the OpenAI Agents SDK. It showcases architectures such as Chat-Supervisor and Sequential Handoffs to build multi-agent conversational experiences, featuring a Next.js TypeScript application with built-in guardrail classification and a scenario-based UI.

Tokens
4.3K
Snippets
5
Records
24
Agent score
88%

What's inside openai-realtime-agents

  1. Create a custom multi-agent voice app template

    main

    To build your own multi-agent voice application using this project's templates:

    1. Create a new agent set configuration.
    2. Register the new configuration in src/app/agentConfigs/index.ts.
    3. The new agent will then be available for selection in the UI via the 'Scenario' dropdown menu.

    Each agentConfig can define:

    • instructions: The system prompt for the agent.
    • tools: The list of available tools.
    • toolLogic: Custom logic that runs when a tool is called. By default, tool calls return True. If you define toolLogic, it will execute your specific code and return an object to the conversation (useful for RAG context or external API data).
  2. Install and Setup the Realtime API Agents Demo

    main

    This project is a Next.js TypeScript application. To set up the development environment:

    1. Install dependencies:
      npm i
    2. Configure your OpenAI API Key. You can either add OPENAI_API_KEY to your shell environment (e.g., .bash_profile) or create a .env file by copying .env.sample:
      cp .env.sample .env
      Then, add your key to the .env file.
    3. Start the development server:
      npm run dev
    4. Access the application at http://localhost:3000.
  3. Navigate the Realtime Agents Demo UI

    main

    The demo interface provides several controls and views:

    • Scenario Dropdown: Select different agent scenarios. Selecting a scenario automatically switches the active agent in the Agent dropdown.
    • Conversation Transcript (Left): Displays the dialogue, including tool calls, tool call responses, and agent handoffs. Click non-message elements to expand them.
    • Event Log (Right): Shows real-time client and server events. Click an event to view the full JSON payload.
    • Bottom Controls:
      • Disconnect: End the session.
      • Voice Activity Detection: Toggle between automated VAD or Push-to-Talk (PTT).
      • Audio Playback: Toggle audio output on/off.
      • Logs: Toggle visibility of logs.
  4. Implement the Chat-Supervisor Agentic Pattern

    main

    The Chat-Supervisor pattern uses a low-latency realtime model (e.g., gpt-4o-realtime-mini) for basic conversation and a high-intelligence text-based model (e.g., gpt-4.1) to handle complex tool calls and reasoning. This provides a natural voice experience while maintaining high intelligence.

    To modify this pattern for your own agent:

    1. Update the Supervisor Agent (src/app/agentConfigs/chatSupervisorDemo/supervisorAgent.ts):
      • Add your existing text agent prompt and tools.
      • Place domain-specific logic below the ==== Domain-Specific Agent Instructions ==== marker.
      • Optimize instructions for voice (e.g., request conciseness).
    2. Update the Chat Agent (src/app/agentConfigs/chatSupervisor/index.ts):
      • Customize chatAgentInstructions for tone and greeting.
      • Add tool definitions to chatAgentInstructions. Using a brief YAML description is recommended over JSON to prevent the model from attempting to call the tool directly.
      • Control the decision boundary by updating the # Allow List of Permitted Actions section.
  5. Use getNextResponseFromSupervisor tool in Chat-Supervisor pattern

    main

    The getNextResponseFromSupervisor tool is the primary mechanism for the junior agent to interact with the supervisor agent.

    Usage Requirements:

    1. Filler Phrase: Before calling the tool, the agent MUST say a neutral filler phrase to the user (e.g., "Just a second.", "Let me check.", "One moment."). Filler phrases must not imply whether the request can be fulfilled.
    2. Context Provision: When calling the tool, you must provide relevantContextFromLastUserMessage. This should be a concise summary of salient information from the most recent user message to assist the supervisor.
    3. Verbatim Response: Once the supervisor returns a response, the junior agent should read it to the user.

    Example Workflow:

    • User: "Can you tell me what my current plan includes?"
    • Assistant: "One moment."
    • Tool Call: getNextResponseFromSupervisor(relevantContextFromLastUserMessage="Wants to know what their current plan includes")
    • Supervisor Output: # Message\nYour current plan includes...
    • Assistant (Final): "Your current plan includes..."
  6. Configure Complex Agent Graphs for Sequential Handoffs

    main

    For complex flows (like CustomerServiceRetail), you can define an agent graph by assigning downstreamAgents to each agent and then using the injectTransferTools utility to wrap them with the necessary transfer capabilities.

    import authentication from "./authentication";
    import returns from "./returns";
    import sales from "./sales";
    import simulatedHuman from "./simulatedHuman";
    import { injectTransferTools } from "../utils";
    
    authentication.downstreamAgents = [returns, sales, simulatedHuman];
    returns.downstreamAgents = [authentication, sales, simulatedHuman];
    sales.downstreamAgents = [authentication, returns, simulatedHuman];
    simulatedHuman.downstreamAgents = [authentication, returns, sales];
    
    const agents = injectTransferTools([
      authentication,
      returns,
      sales,
      simulatedHuman,
    ]);
    
    export default agents;
  7. Implement the Sequential Handoff Agentic Pattern

    main

    The Sequential Handoff pattern (inspired by OpenAI Swarm) allows specialized agents to transfer control to one another via tool calls. This is ideal for complex workflows like customer service where different intents require different specialist models.

    To implement this using the RealtimeAgent class from @openai/agents/realtime:

    1. Define each agent with a name, handoffDescription (context for the transfer tool), instructions, tools, and handoffs (an array of agents it can transfer to).
    2. Export an array containing all participating agents.
    import { RealtimeAgent } from '@openai/agents/realtime';
    
    // Define agents using the OpenAI Agents SDK
    export const haikuWriterAgent = new RealtimeAgent({
      name: 'haikuWriter',
      handoffDescription: 'Agent that writes haikus.', // Context for the agent_transfer tool
      instructions: 
        'Ask the user for a topic, then reply with a haiku about that topic.',
      tools: [],
      handoffs: [],
    });
    
    export const greeterAgent = new RealtimeAgent({
      name: 'greeter',
      handoffDescription: 'Agent that greets the user.',
      instructions: 
        "Please greet the user and ask them if they'd like a haiku. If yes, hand off to the 'haikuWriter' agent.",
      tools: [],
      handoffs: [haikuWriterAgent], // Define which agents this agent can hand off to
    });
    
    // An Agent Set is just an array of the agents that participate in the scenario
    export default [greeterAgent, haikuWriterAgent];
  8. Customize Output Guardrails

    main

    Assistant messages are checked for safety and compliance. The guardrail logic is implemented in src/app/App.tsx.

    Workflow:

    1. When a response.text.delta stream starts, the message is marked as IN_PROGRESS.
    2. If the server emits guardrail_tripped, the message is marked as FAIL.
    3. If the server emits response.done, the message is marked as PASS.

    To modify how moderation is triggered or displayed, search for the guardrail_tripped token within src/app/App.tsx and update the logic there.

  9. Access available agent sets via allAgentSets

    main

    The allAgentSets object provides a registry of pre-configured agent scenarios. Each key in the object corresponds to a specific agentic pattern, and the value is an array of RealtimeAgent objects.

    Available scenario keys:

    • simpleHandoff: A basic handoff scenario.
    • customerServiceRetail: A retail-focused customer service flow.
    • chatSupervisor: A chat supervisor pattern.

    You can use these keys to load specific sets of agents for your application.

  10. Define Tool structures

    main

    Tools are defined using the Tool interface, which follows the standard function calling schema. A Tool must have a type of "function", a name, a description, and a parameters object of type ToolParameters.

    export interface Tool {
      type: "function";
      name: string;
      description: string;
      parameters: ToolParameters;
    }
    
    export interface ToolParameters {
      type: string;
      properties: Record<string, ToolParameterProperty>;
      required?: string[];
      additionalProperties?: boolean;
    }