
Resources
Building Effective AI Workflows with OpenAI Agents: A Practical Guide to Agent Orchestration
Building vertical agents with OpenAI Agents SDK
.png)
April 9, 2025
4 Minutes
Last month OpenAI released their agents SDK, which empowers developers to create modular AI systems that can handle complex tasks through intelligent orchestration. This article demonstrates how to build practical, real-world agentic workflows using the OpenAI Agents Python library, focusing on a step-by-step approach that even beginners can follow. By examining concrete examples, we will explore how agent orchestration can transform the way vertical agents can be built, providing personalized, multilingual support with domain-specific capabilities.
🧠 Understanding the Fundamentals of OpenAI Agents
Agents are the core building blocks of sophisticated AI applications. According to the OpenAI documentation, an agent is essentially “a large language model (LLM), configured with instructions and tools”. This simple but powerful concept allows developers to create specialized AI components that can be orchestrated to work together seamlessly.
The basic configuration of an agent typically includes several key elements:
- 📜 Instructions: Also known as a developer message or system prompt
- 🤖 Model: Which LLM to use (such as o3-mini/gpt-4o)
- 🧰 Tools: Functions that the agent can use to accomplish tasks[2]
Creating a basic agent is straightforward, as shown in this example from the documentation:
.jpeg)
As demonstrated above, the OpenAI Agents SDK provides a straightforward and intuitive API that makes implementation quick and simple.
🏗️ System Architecture
This guide demonstrates how to build an AI Agent workflow that orchestrates multiple specialized agents, as illustrated in the diagram below.

The building blocks of our system are:
> Common Agents:
- 🛡️ Guard Agent: Responsible for user authentication and interaction control.
- 🚦 Router/Triage Agent: A supervisor agent that directs users to appropriate vertical agents.
> Vertical Agents:
Our system includes two main vertical categories:
- Language Agents: Specialized agents trained to handle language-specific interactions and nuances.
- English Speaker Agent: Handles English communication with expertise in language conventions and cultural nuances.
- Hindi Speaker Agent: Manages Hindi communication with proficiency in language-specific expressions and cultural context.
2. Product Agents: Specialized in product-related operations and tools.
- Product Information Agent: Handles product searches and provides detailed product information.
- Product Feedback Agent: Manages user feedback and distributes it through various channels like email and Slack.
> Tools
The above agents will have access to one or more tools:
- get_user_info - The Auth agent uses this to look up user information in the system through database queries or API calls.
- check_product_info - The Product Info agent uses this to verify product information and ownership, which can interface with CRM systems or databases.
- create_crm_ticket - The Product Feedback agent uses this to create tickets in the CRM system or send notifications to Slack channels.
- send_feedback_email - The Product Feedback agent uses this to send email messages containing user feedback.
Step 1: Building an Authentication Guard Agent
Security is a critical concern for any AI system interacting with users. Our first example demonstrates how to implement an authentication guard agent that verifies user identity before allowing access to the system.This agent is the gatekeeper of our agentic workflow.

> Implementation Details
The authentication guard agent uses a get_user_info tool to validate users against a database. Here's how you can implement this crucial first layer:
class UserAuthOutput(BaseModel):
is_authenticated: bool
user_id: str | None = None
user_name: str | None = None
class UserInfoContext(BaseModel):
user_id: str | None = None
user_name: str | None = None
auth_attempts: int = 0
max_auth_attempts: int = 3
@function_tool
async def get_user_info(wrapper: RunContextWrapper[UserInfoContext], user_id: str, otp: str) -> str:
"""
Check user authentication and attach additional information
Args:
user_id: User ID
otp: OTP for the user
Returns:
str: Authentication status message
"""
print(f"Tool called with user_id: {user_id} and otp: {otp}")
try:
users_map = [
{
"user_id": "Mohit21",
"otp": "1234",
"user_name": "Mohit Yadav"
},
{
"user_id": "Mohit22",
"otp": "1235",
"user_name": "Mohit Y"
}
]
user = next(user for user in users_map if user["user_id"] == user_id and user["otp"] == otp)
if user:
try:
wrapper.context.user_id = user_id
wrapper.context.user_name = user["user_name"]
wrapper.context.auth_attempts = 0 # Reset attempts on success
except Exception as e:
print(f"Error while setting context: {str(e)}")
print(f"Authentication successful. Context: {wrapper.context}")
return f"Authentication successful. Welcome, {user['user_name']}!"
wrapper.context.auth_attempts += 1
remaining_attempts = wrapper.context.max_auth_attempts - wrapper.context.auth_attempts
return f"Authentication failed. {remaining_attempts} attempts remaining."
except Exception as error:
print(f"Authentication error: {str(error)}")
return "Authentication failed. Please try again with valid credentials."
user_auth_check_agent = Agent(
name="Auth Check Agent",
instructions="""
You are a professional authentication agent responsible for user verification.
# Core Responsibilities
- Verify user credentials securely and efficiently
- Track authentication attempts
- Provide clear feedback on authentication status
- Handle authentication failures gracefully
# Authentication Protocol
1. Initial Greeting:
- Introduce yourself as the authentication agent
- Request user credentials (user_id and OTP)
2. Credential Verification:
- Use get_user_info tool to verify credentials
- Track number of authentication attempts
- Provide clear feedback on success/failure
3. Success Handling:
- Welcome authenticated users professionally
- Transfer to router agent when available
- Include user's name in welcome message
4. Failure Handling:
- Clearly communicate remaining attempts
- Provide guidance on correct credential format
- Offer another attempt if maximum not reached
5. Security Measures:
- Never reveal existing user IDs
- Don't provide hints about valid credentials
- Maintain professional tone throughout
Remember: Security and user experience must be balanced appropriately.
""",
model="gpt-4", # Upgraded to GPT-4 for better security handling
tools=[
get_user_info
]
)
The key components of this authentication agent are:
- System prompts that serve as instructions, defining the agent’s clear responsibilities.
- get_user_info - A tool that verifies user access. While this example uses in-memory checking, it could connect to a database or API in production.
Step 2. Creating a Triage Router with Multilingual Support
After authentication, users are directed to specialized agents through a triage router agent. This router evaluates users’ language preferences and needs to connect them with the most appropriate agent.

> Multilingual Agent Implementation
Building on our authenticated system, we can create a triage router that supports multiple languages:
english_speaker_agent = Agent[UserInfoContext](
name="English Speaker Agent",
handoff_description="Professional English language support agent for customer interactions.",
instructions=f"""
{RECOMMENDED_PROMPT_PREFIX}
You are a professional English language customer service agent.
# Core Responsibilities
- Communicate in clear, professional British English
- Maintain formal yet friendly tone
- Use simple, unambiguous language
- Avoid technical jargon unless specifically requested
# Response Protocol
1. First verify if user's query is in English
2. If unclear, ask specific clarifying questions
3. For product queries, transfer to requirements agent
4. For non-English queries, transfer to appropriate language agent
5. For unrelated topics, transfer to router agent
# Context Awareness
- Always check user authentication status before providing sensitive information
- Reference user's name when available in context
- Keep track of previous interactions in the conversation
""",
model="gpt-4o",
)
hindi_speaker_agent = Agent[UserInfoContext](
name="Hindi Speaker Agent",
handoff_description="Professional Hindi language support agent for customer interactions.",
instructions=f"""
{RECOMMENDED_PROMPT_PREFIX}
You are a professional Hindi language customer service agent.
# Core Responsibilities
- Communicate exclusively in formal Hindi (शुद्ध हिंदी)
- Maintain professional yet approachable tone
- Transliterate Hindi text to English for TTS compatibility
- Preserve cultural context and respect
# Response Protocol
1. First verify if user's query is in Hindi
2. Always provide responses in this format:
Hindi: [Hindi text]
Transliteration: [English transliteration]
3. For product queries, transfer to requirements agent
4. For non-Hindi queries, transfer to appropriate language agent
5. For unrelated topics, transfer to router agent
# Context Awareness
- Always check user authentication status before providing sensitive information
- Use appropriate Hindi honorifics based on context
- Keep track of previous interactions in the conversation
""",
model="gpt-4o-mini",
)
router_agent = Agent[UserInfoContext](
name="Router Agent",
handoff_description="Central routing agent for language and service delegation.",
instructions=f"""
{RECOMMENDED_PROMPT_PREFIX}
You are the central routing agent responsible for request delegation.
# Core Responsibilities
- Language detection and appropriate agent routing
- Service type classification
- Authentication status verification
# Routing Protocol
1. Analyze user input for:
- Primary language (English/Hindi)
- Request type (Product/Service/Support)
- Authentication requirements
2. Route based on priority:
- Authentication needs -> Auth Check agent
- If User input is in Hindi -> Hindi Speaker agent
- If User input is in English -> English Speaker agent
- If User input is neither Hindi nor English -> English Speaker agent
- If User input is related to product information -> Gather Requirements Agent
- If User input is related to product feedback -> Product Feedback Agent
3. Monitor conversation flow:
- Track handoffs
- Ensure proper agent transitions
- Maintain context continuity
""",
handoffs=[
hindi_speaker_agent,
english_speaker_agent,
],
)
user_auth_check_agent.handoffs.append(router_agent)
The key components of these agents are:
- System prompts that serve as instructions, defining each agent’s specific responsibilities.
- Handoffs: A mechanism that allows one agent to delegate communication to another agent. There are two ways to implement agent transfers:
.jpeg)
This implementation demonstrates how agent handoffs create an elegant, modular system. The simplicity of OpenAI Agent handoffs allows each agent to focus on a specific responsibility, making the system both maintainable and extensible. After authentication, the router agent determines the user’s language preference and seamlessly connects them with an agent who speaks their language.
Step 3. Building a Comprehensive Product Support System
Now, let’s expand our system further to include domain-specific functionality for product support. This example demonstrates a more complex orchestration that combines language routing with domain expertise.

> Product Domain Agents with Specialized Tools
In this enhanced system, we’ll add product domain agents with tools for querying product information and sharing feedback:
@function_tool
async def check_product_info(wrapper: RunContextWrapper[UserInfoContext], product_id: str) -> str:
"""
Checks if the product exists and is authorized to be queried by the user
Args:
product_id (str): The product id to check
"""
products_map = {
"product_1": {
"product_owner": "Mohit21",
"name": "Blender from Amazon"
},
"product_2": {
"product_owner": "Mohit22",
"name": "Joystick from Noon.com"
}
}
if wrapper.context.user_id is None:
return "User must be authenticated"
if not products_map[product_id]:
return "Product does not exist"
if products_map[product_id]["product_owner"] != wrapper.context.user_id:
print(f"Product ownership mismatch. Expected '{wrapper.context.user_id}', found '{products_map[product_id]['product_owner']}'")
return f"Product {product_id} does not belong to user {wrapper.context.user_id}"
return f"The product name is {products_map[product_id]['name']}"
gather_requirements_agent = Agent[UserInfoContext](
name="Gather Requirements Agent",
handoff_description="Product information specialist agent that handles product queries and validations.",
instructions=f"""
{RECOMMENDED_PROMPT_PREFIX}
You are a specialized product information agent with access to product validation tools.# Core Responsibilities
- Handle all product-related queries and validations
- Extract product IDs from user conversations
- Verify product ownership and access rights
- Provide clear product information to users
# Query Processing Protocol
1. Product ID Detection:
- Look for product IDs in user messages (format: product_1, product_2, etc.)
- If no product ID found, ask user to provide one
- Guide users on correct product ID format
2. Authentication Check:
- Verify user authentication status before product lookup
- If user not authenticated, inform and route back to auth agent
3. Product Validation:
- Use check_product_info tool to validate product access
- Handle ownership verification
- Present product details when authorized
4. Response Handling:
- Provide clear feedback on product status
- Format product information in a readable manner
- Handle errors with clear explanations
- Use translate_to_hindi tool if user input is in Hindi
- Use translate_to_english tool if user input is in English or any other language
5. Routing Rules:
- For non-product queries -> Route to router agent
Remember: Always maintain context of the conversation and user's authentication status.
""",
model="gpt-4o",
tools=[
check_product_info,
hindi_speaker_agent.as_tool(
tool_name="translate_to_hindi",
tool_description="Translate the agent response to Spanish",
),
english_speaker_agent.as_tool(
tool_name="translate_to_english",
tool_description="Translate the agent response to English",
)
]
)
gather_requirements_agent.handoffs.append(router_agent)
gather_requirements_agent.handoffs.append(hindi_speaker_agent)
gather_requirements_agent.handoffs.append(english_speaker_agent)
hindi_speaker_agent.handoffs.append(gather_requirements_agent)
english_speaker_agent.handoffs.append(gather_requirements_agent)
@function_tool
async def send_feedback_email(wrapper: RunContextWrapper[UserInfoContext], product_id: str, feedback: str) -> str:
"""
Sends product feedback via email
Args:
product_id (str): The product ID
feedback (str): The feedback content
"""
# Simulating email sending
print(f"Sending email feedback for product {product_id}")
return f"Email feedback sent successfully for product {product_id}"
@function_tool
async def create_crm_ticket(wrapper: RunContextWrapper[UserInfoContext], product_id: str, feedback: str) -> str:
"""
Creates a CRM ticket for product feedback
Args:
product_id (str): The product ID
feedback (str): The feedback content
"""
# Simulating CRM ticket creation
ticket_id = f"TICKET-{product_id}-{hash(feedback) % 1000}"
return f"CRM ticket created successfully. Ticket ID: {ticket_id}"
product_feedback_agent = Agent[UserInfoContext](
name="Product Feedback Agent",
handoff_description="Specialized agent for collecting and processing product feedback through various channels.",
instructions="""
You are a product feedback collection specialist that can process feedback through multiple channels.
# Core Responsibilities
- Collect product feedback from users
- Validate product IDs before processing feedback
- Offer multiple feedback submission methods (email, CRM ticket)
- Guide users through the feedback submission process
# Feedback Collection Protocol
1. Initial Information Gathering:
- Request product ID if not provided
- Ask for feedback content
- Confirm feedback channel preference (email or CRM ticket)
2. Feedback Processing:
- Validate product exists using check_product_info
- Process feedback through chosen channel
- Provide confirmation and reference numbers
3. Channel-Specific Handling:
- Email: Format and send feedback via email
- CRM: Create ticket and provide ticket ID
4. Response Protocol:
- Confirm successful submission
- Provide reference numbers or IDs
- Handle any submission errors
- Use translation tools for responding to users in their preferred language.
Remember: Always verify product existence before processing feedback.
""",
model="gpt-4o",
tools=[
check_product_info,
send_feedback_email,
create_crm_ticket,
hindi_speaker_agent.as_tool(
tool_name="translate_to_hindi",
tool_description="Translate the agent response to Spanish",
),
english_speaker_agent.as_tool(
tool_name="translate_to_english",
tool_description="Translate the agent response to English",
)
]
)
# Add handoffs
product_feedback_agent.handoffs.append(router_agent)
gather_requirements_agent.handoffs.append(product_feedback_agent)
router_agent.handoffs.append(product_feedback_agent)
This comprehensive system demonstrates sophisticated agent orchestration with multiple layers:
- The entry point agent verifies that users are authenticated
- The authentication guard validates user identities
- The advanced triage router determines both language preference and support needs
- Specialized product agents handle domain-specific queries with appropriate tools
The system is modular and extensible, allowing you to add new languages, domains, or tools as needed without disrupting the existing workflow.
The codebase for above agentic workflow is available as Jupyter notebooks in github repo. Please feel free to contribute a ⭐ and fork the repo to run it locally and share the feedback.
🏆 Best Practices for Agent Orchestration
Based on the examples above, here are some best practices for creating effective agent orchestration workflows:
🧩 Design with Modularity in Mind
Each agent should have a clear, focused purpose. The authentication guard handles security, the triage router handles routing decisions, and domain specialists handle specific tasks. This modularity makes your system easier to maintain and extend.
🤝 Provide Clear Handoff Instructions
When designing agents that delegate to other agents, include clear instructions about when and why to make handoffs. This ensures that conversations flow smoothly between agents without confusing the user.
🧰 Create Focused, Reusable Tools
Tools should be atomic and focused on specific tasks. This makes them more reusable across different agents and simplifies debugging and maintenance.
🐛 Implement Robust Error Handling
Both tools and agents should include clear error handling protocols. Tools should return structured error responses, and agents should be instructed on how to handle these errors gracefully.
💻 Test Components Individually
Test each agent and tool individually before integrating them into your complete system. This helps identify and resolve issues early in the development process.
🔒 Prioritize Security
Always implement proper authentication and authorization checks, especially for tools that access sensitive data or perform actions on behalf of users.
Conclusion
OpenAI’s Agents framework provides a powerful platform for building sophisticated AI workflows through intelligent agent orchestration. By breaking down complex problems into specialized agents with clear responsibilities, you can create systems that are both powerful and maintainable.
The examples we explored — from authentication to multilingual product support — demonstrate how easily developers can implement complex workflows using the OpenAI Agents framework. Each example builds on the previous one, showcasing how you can start simple and gradually add complexity as your needs evolve.
As agent technology continues to advance, we can expect even more powerful orchestration capabilities that will enable increasingly sophisticated AI applications. By embracing the modular, orchestrated approach demonstrated in this article, you will be well-positioned to leverage these advancements and create AI systems that truly transform how businesses and customers interact.