In short
- AI agents are autonomous systems that can perform tasks without human intervention, using reasoning and tool calling to achieve goals.
- The OpenAI Agents SDK is a lightweight Python framework that simplifies building agentic applications.
- Five architectural patterns are demonstrated: deterministic workflows, routing for task delegation, agents-as-tools for orchestration, judge patterns for evaluation, and parallelization for performance.
- The SDK seamlessly integrates with the Model Context Protocol (MCP) for tool calling.
- OpenAI’s tracing capabilities provide detailed insights into agent flows,
The OpenAI Agents SDK is an open-source, lightweight Python SDK that can be used to build agentic applications. The SDK was launched March this year (2025) and the successor to OpenAI’s earlier initiative, Swarm, which has been deprecated. It’s an easy-to-understand SDK without too many abstractions. The problem with many agent frameworks and libraries is that they include too many abstractions created with good intentions but leading to unnecessary complexity. At its core, an agentic application is simply calling an LLM with context. I’ll cover this later, but before I dive into how the OpenAI Agents SDK works, let’s first explore what an agent really is and what you can build with them.
Overview of Agents
“An artificial intelligence (AI) agent refers to a system or program that is capable of autonomously performing tasks on behalf of a user or another system.” – IBM
I took the above quote from IBM’s website, but honestly, the definitions of AI agents are pretty similar across sources. So there seems to be consensus on what an AI agent is. A few key points about AI agents:
- Operate without human intervention
- Goal directed and complete tasks
- Work autonomously
- Interact with tools and services if it’s needed
- Reason and plan next steps
To explain how agents operate it’s best to compare it with a rule-based e-mail filter system. Traditionally, you would have something below to automatically move emails based on their content to the correct folder.
IF email contains "discount" OR "sale"
THEN move to "Promotions" folder
IF sender is "boss@company.com"
THEN mark as "High Priority"
IF attachment > 10MB
THEN reject email
This basically means that you develop rules (if-then, etc.) for all scenarios. It can’t adapt or handle emails that can’t be processed by the filter.
An AI agent would try to understand the intent of the email and, based on that understanding, categorize and move it to the correct folder or create a new folder if that seems more logical (if instructed to do so). It can learn from patterns in emails and adapt its behavior over time. Below, you can see how an AI agent would try to understand the intent of the email and likely make a different decision than the rule-based filter.
Incoming Email:
From: john.doe@client.com
Subject: "Project Phoenix - 50% discount on implementation"
Agent's Reasoning:
"I see 'discount' but this is from a known client about Project Phoenix,
not a promotion. Previous emails from John are in 'Active Projects'.
This is a business proposal requiring response, not a promotion."
Decision Process:
- Context: Client email → Not promotional
- History: Past emails → Active Projects folder
- Priority: Project Phoenix + pricing → Urgent
- Action needed: Proposal → Requires response
Actions:
→ Move to: Active Projects/Project Phoenix
→ Flag as: Important, Requires Response
→ Create reminder: "Review proposal by EOD"
Basic agents vs Autonomous agents
A basic agent is essentially a thin layer around a language model API. It relies on the language model to return an answer. In comparison, an autonomous agent plans and uses function calling to reach its goal. It can reason through complex tasks and maintains state and memory.
You can assign an agent a task and it will work autonomously to complete it without human intervention. Because it works autonomously, it’s important to describe the task or rather the end goal as clearly and specifically as possible. If you don’t, it will hallucinate or get off track pretty quickly. An AI agent is powered by an LLM that supports function calling to communicate with other systems and tools.
An AI agent is usually part of some kind of application or tool. It can be part of a broader backend application where it serves as a component that performs specific tasks, or it can be a bot in a chat application. The reason for this is that it usually needs context to complete a task.
Some use cases for agents:
- Research topics online and summarize.
- Email filters.
- Customer support
- Analyze data and create reports
- …
Let’s continue with how to create an autonomous agent using the OpenAI Agents SDK.
What is the OpenAI Agents SDK?
Building an autonomous agent doesn’t necessarily mean a lot of complexity. There are a few basics that need to be implemented: use a language model, decide on the agent architecture, define agents and their tasks (system prompts), provide context (message history and other relevant information), and implement function calling (I’ll focus on using MCP in this article). With these steps, you’ll most likely have a solid foundation for your agent. With that in mind, you can either directly call the language model API, or use an agents framework. The OpenAI Agents Python SDK is super lightweight and straight forward to use.
Let’s take a look at a super straightforward example of using the SDK. In this example, I’ve created three agents. The Triage agent is the orchestrator and will hand off the task to either the Winter or Summer Shopping agent depending on the input message. In this example, the input parameter is “winter,” so the Triage agent ‘should’ call the Winter Shopping agent, because it’s instructed like that.
class ShoppingItem(BaseModel):
name: str
price: float
url: str
class ShoppingList(BaseModel):
items: list[ShoppingItem]
async def main():
summer_shopping_agent=Agent(
name="Summer Shopping agent",
instructions=("""
You are a shopping assistant specializing in summer fashion. Your task is to find the best price for a product.
- Come up with fake products and their prices.
"""),
handoffs=[],
output_type=ShoppingList
)
winter_shopping_agent=Agent(
name="Winter Shopping agent",
instructions=("""
You are a shopping assistant specializing in winter fashion. Your task is to find the best price for a product.
- Come up with fake products and their prices.
"""),
handoffs=[],
output_type=ShoppingList
)
triage_agent = Agent(
name="Triage agent",
instructions=("""
You are an orchestrator.
- Based on the provided season create a shopping list.
- Delegate the task to the correct Shopping agent to find a matching outfit.
"""),
handoffs=[winter_shopping_agent, summer_shopping_agent],
)
result = await Runner.run(triage_agent, input="Winter")
print(result.final_output)
draw_graph(triage_agent, filename="agent_graph1")
if __name__ == "__main__":
asyncio.run(main())
The draw_graph
method (part of the SDK) generates an image of the relationship between agents. Below, you can see that the Triage agent hands off to either the Winter Shopping or Summer Shopping agent, which returns a final output.

The terminal output:
items=[
ShoppingItem(
name='Cozy Wool Scarf',
price=39.99,
url='https://fashionstore.com/product/cozy-wool-scarf'),
ShoppingItem(
name='Insulated Ski Jacket',
price=129.99,
url='https://fashionstore.com/product/insulated-ski-jacket'),
ShoppingItem(
name='Winter Thermal Leggings',
price=24.99,
url='https://fashionstore.com/product/winter-thermal-leggings'),
ShoppingItem(
name='Waterproof Snow Boots',
price=89.99,
url='https://fashionstore.com/product/waterproof-snow-boots'),
ShoppingItem(
name='Knit Beanie Hat',
price=19.99,
url='https://fashionstore.com/product/knit-beanie-hat')
]
The Triage agent example demonstrates just one possible architecture. Let’s explore other patterns you can use to structure your agents.
Architectural Patterns for Agents
There are different architectures that can be used to implement agents. It all depends on the requirements. Picking the right architecture is crucial for getting the best results. I’ll cover a few architectures and how you can implement them with the OpenAI Agents SDK.
Deterministic Pattern
The deterministic workflow is the simplest one. All the steps are clear: first X, then Y. It’s important that you don’t overcomplicate things when the end goal and the path to that goal are clear.
- Fixed sequence of steps.
- Straight forward workflows with no variations.
Example:
Generate a shopping list for a complete winter outfit and provide a summary of the total cost.
class ShoppingItem(BaseModel):
name: str
price: float
url: str
class ShoppingList(BaseModel):
items: list[ShoppingItem]
class TotalCostSummary(BaseModel):
total: float
item_count: int
details: str
async def main():
winter_agent=Agent(
name="Winter Shopping List Agent",
instructions=(
"""
You are an agent that generates a random shopping list for a complete winter outfit.
Include various items like a jacket, scarf, gloves, hat, boots, and socks, or other appropriate winter clothing.
For each item, provide a name, a random but realistic price, and a placeholder URL (e.g., https://example.com/item-name).
Return the list as a ShoppingList.
"""
),
output_type=ShoppingList
)
cost_agent=Agent(
name="Total Cost Agent",
instructions=(
"""
You are a deterministic agent. Given a ShoppingList, calculate the total cost and item count, and return a TotalCostSummary with a details string in the format: 'Total for X items: $Y'.
"""
),
output_type=TotalCostSummary
)
# Step 1: Generate shopping list
shopping_list_result = await Runner.run(winter_agent, input="")
shopping_list = shopping_list_result.final_output
print("Winter Shopping List:")
for item in shopping_list.items:
print(f"- {item.name}: ${item.price:.2f} ({item.url})")
print()
cost_result = await Runner.run(cost_agent, input=shopping_list.model_dump_json())
summary = cost_result.final_output
print("Summary:")
print(summary.details)
The terminal output:
Winter Shopping List:
- Winter Jacket: $120.99 (https://example.com/winter-jacket)
- Wool Scarf: $25.50 (https://example.com/wool-scarf)
- Thermal Gloves: $30.00 (https://example.com/thermal-gloves)
- Knitted Hat: $18.75 (https://example.com/knitted-hat)
- Waterproof Boots: $89.99 (https://example.com/waterproof-boots)
- Warm Socks: $12.00 (https://example.com/warm-socks)
Summary:
Total for 6 items: $297.23
Routing pattern
The routing pattern allows you to create multiple routes in a workflow. The orchestrator decides which route to take and hands over the task to the next agent. This flow can have many subflows.
- Orchestrator acts as a router.
- The selected agent takes over the task and returns a final result
- Ideal when input determines the workflow branch
Example:
Create a shopping list for a winter outfit.
I demonstrated this pattern in the previous section (What is the OpenAI Agents SDK?)
Agents as tools
With agents as tools, you create an orchestrator that chooses which agent to call, and without handing over control, the orchestrator decides if the final result is met. In the example, I use reasoning, which means that the language model thinks before answering. This is useful in scenarios where you want an orchestrator to decide which agent/tool it should call to complete the task.
- The orchestrator never gives up control.
- Which agents are used depends on intermediate results.
- Multiple runs might use different agents.
- No fixed workflow path.
Example:
Create an affordable but stylish winter outfit based on the current trends and a budget of €400.
class ClothingItem(BaseModel):
name: str
price: float
description: str
category: str
trend_score: float
quality_score: float
sustainability_score: float
class OutfitRecommendation(BaseModel):
items: List[ClothingItem]
total_budget: float
remaining_budget: float
total_cost: float
async def main():
user_request = "Help me create an affordable but stylish winter outfit based on the current trends and my budget of 400 euro."
print(f"--- Starting Winter Outfit Creation ---\n")
trend_analyzer = Agent(
name="TrendAnalyzerAgent",
instructions=f"""
You are an expert fashion trend analyzer for winter clothing.
Based on current fashion trends, recommend 8-10 stylish winter clothing items across different categories:
- 2 outerwear options (coats/jackets)
- 2 tops (sweaters/shirts)
- 2 bottoms (pants/skirts)
- 2 footwear options
- 2 accessories (scarves, hats, gloves)
For each item, include:
- A specific name (e.g., "Oversized Wool Blend Coat" rather than just "Coat")
- An estimated market price in euros (€)
- A brief description highlighting why it's trendy this season
- The category it belongs to
- A trend_score from 0-10 indicating how fashionable it currently is
- Leave the quality_score and sustainability_score blank
""",
output_type=OutfitRecommendation
)
budget_optimizer = Agent(
name="BudgetOptimizerAgent",
instructions=f"""
You are a budget optimization expert for fashion.
You'll receive a list of trending winter clothing items and a budget.
Your task is to:
1. Select ONE item from each category (outerwear, top, bottom, footwear, accessory) that creates a cohesive outfit
2. Ensure the total cost stays within the budget
3. Try to maximize style while respecting the budget constraint
4. Calculate and include the remaining budget after your selections
- Leave the quality_score and sustainability_score blank
""",
output_type=OutfitRecommendation
)
quality_advisor = Agent(
name="QualityAdvisorAgent",
instructions=f"""
You are an expert in clothing quality assessment.
You'll receive a budget-optimized outfit selection and the remaining budget.
Your task is to:
1. Evaluate each selected item for quality based on materials, construction, and durability
2. Assign a quality_score from 0-10 for each item
- Leave the sustainability_score blank
""",
output_type=OutfitRecommendation
)
sustainability_checker = Agent(
name="SustainabilityCheckerAgent",
instructions=f"""
You are an expert in sustainable and eco-friendly fashion.
You'll receive a quality-assessed outfit selection.
Your task is to:
1. Evaluate each item for sustainability based on typical materials, production methods, and brand practices
2. Assign a sustainability_score from 0-10 for each item
""",
output_type=OutfitRecommendation
)
triage_agent = Agent(
name="Triage agent",
instructions=(
f"""
You are an orchestrator responsible for coordinating specialized agents through the tools available to you.
Your task is to:
1. Use the TrendAnalyzer tool to analyze current winter fashion trends and get 8-10 trendy items across different clothing categories (outerwear, tops, bottoms, footwear, accessories).
2. Use the BudgetOptimizer tool to select one item from each category to create a cohesive outfit while staying within the budget constraint.
3. Use the QualityAdvisor tool to evaluate the quality of each selected item.
4. Use the SustainabilityChecker tool to assess the sustainability of each item.
5. Combine all the insights from these tools to create a final outfit recommendation that balances style, budget, quality, and sustainability.
Rules:
- Always use the appropriate tool for each specific task rather than trying to perform the tasks yourself.
- You MUST only evaluate the output of the tools, NEVER modify the output.
- If results are weak or incorrect from any tool, request retries or clarifications.
- If the quality or sustainability scores of items are below 5, go back to the TrendAnalyzer tool to find better alternatives.
- Pass the complete output from one tool to the next tool in the sequence as needed.
"""
),
model="o4-mini",
model_settings=ModelSettings(reasoning={"summary": "auto"}),
tools=[
trend_analyzer.as_tool(
tool_name="TrendAnalyzer",
tool_description="Analyze current winter fashion trends and suggest trendy items"
),
budget_optimizer.as_tool(
tool_name="BudgetOptimizer",
tool_description="Select items to create a cohesive outfit within the budget"
),
quality_advisor.as_tool(
tool_name="QualityAdvisor",
tool_description="Evaluate item quality and suggest upgrades"
),
sustainability_checker.as_tool(
tool_name="SustainabilityChecker",
tool_description="Assess sustainability and suggest eco-friendly alternatives"
),
],
)
triage_result = await Runner.run(triage_agent, input=user_request)
print("Research result:")
print(triage_result.final_output)
The terminal output:
--- Starting Winter Outfit Creation ---
Research result:
Here’s a stylish, on-trend winter outfit under 400 € that balances style, quality and sustainability:
1. Outerwear: Teddy Bomber Jacket — 75 €
• Trend Score: 8
• Quality Score: 7
• Sustainability Score: 6
A plush, casual layer that’s both cozy and on-trend.
2. Top: Mock Neck Wool Sweater — 60 €
• Trend Score: 8
• Quality Score: 8
• Sustainability Score: 7
Versatile, warm and chic under any jacket.
3. Bottoms: Corduroy Wide-Leg Trousers — 45 €
• Trend Score: 7
• Quality Score: 7
• Sustainability Score: 6.5
Offers texture and a flattering silhouette.
4. Footwear: Faux Fur Lined Ankle Boots — 85 €
• Trend Score: 9
• Quality Score: 6
• Sustainability Score: 5
Keeps you warm and stylish on winter walks.
5. Accessory: Knit Infinity Scarf — 20 €
• Trend Score: 8
• Quality Score: 6
• Sustainability Score: 6.5
Adds extra warmth and a finishing touch.
Total Cost: 285 €
Remaining Budget: 115 € (for extras like gloves, layering pieces or a small bag)
This outfit delivers current winter trends in a cohesive look, all while staying well within your 400 € budget and ensuring good quality and sustainable choices. Enjoy your new winter wardrobe!
As mentioned, I configured the model to use reasoning. In the tracing screenshot below (I’ll cover tracing this in the next section), you can see that after every agent run, it evaluates if the output is good enough. If not, it decides which next steps to take. In the tracing, you can see it iterated a few times over the Budget agent because the total costs exceeded the budget. Eventually, it asks the Trend agent again to come up with new products because it can’t stay within budget. This is the strength of reasoning models.

Judge pattern
With the judge pattern, an evaluator agent is used to compare or score the output of another agent. This pattern can be used in a loop to ensure that an output is of a certain quality or that requirements are met.
- Multiple agents propose results
- A dedicated judge agent evaluates and selects the best option
Example:
Find me the best shopping list for a winter outfit based on style and affordability.
task_description = "Create an affordable but stylish winter outfit based on the current trends and a budget of €200. The outfit should be suitable for winter."
budget = 200.0
outfit_proposer_agent = Agent(
name="OutfitProposerAgent",
instructions=f"""
You are a fashion expert specializing in affordable and trendy outfits.
Your task is to design a winter outfit based on the following criteria:
- Task: {task_description}
You need to propose a complete outfit.
""",
output_type=ProposedOutfit
)
proposer_result = await Runner.run(outfit_proposer_agent, input=task_description)
print("\n--- Proposed Outfit ---")
for item in proposer_result.final_output.items:
print(f" - {item.name} (€{item.price:.2f}): {item.description}")
outfit_judge_agent = Agent(
name="OutfitJudgeAgent",
instructions=f"""
You are a fashion critic and budget analyst.
Your task is to evaluate a proposed winter outfit.
The input you receive will be a JSON object containing the 'proposed_outfit'.
Evaluation Criteria:
- Original Task: "{task_description}"
- Budget: €{budget}
Evaluate the 'proposed_outfit' against:
- Within budget.
- Style and alignment with current winter fashion trends.
- Completeness and practicality for winter.
""",
output_type=Judgement
)
judge_result = await Runner.run(outfit_judge_agent, input=proposer_result.final_output.model_dump_json())
print("\n--- Judgement ---")
print(f"Approved: {judge_result.final_output.is_approved}")
print(f"Score: {judge_result.final_output.score}/10")
print(f"Feedback: {judge_result.final_output.feedback}")
print(f"Reasoning: {judge_result.final_output.reasoning}")
The terminal output:
--- Proposed Outfit ---
- Wool Blend Coat (€75.00): A warm, knee-length wool blend coat in a neutral camel color for versatile styling.
- Chunky Knit Sweater (€30.00): A cozy, oversized chunky knit sweater in a trendy cream color.
- High-Waisted Skinny Jeans (€25.00): Classic high-waisted skinny jeans in dark wash for a slimming effect.
- Ankle Boots (€40.00): Stylish faux leather ankle boots with a sturdy, non-slip sole, perfect for winter.
- Beanie Hat (€10.00): A knitted beanie hat in a soft gray tone to keep warm while looking stylish.
- Wool Scarf (€20.00): A long, woolen scarf in a plaid pattern to add a pop of color.
--- Judgement ---
Approved: True
Score: 9.5/10
Feedback: The "Chic Winter Ensemble" is well within budget at a total of €200 and covers all essential winter components: warmth, style, and versatility. The outfit includes a warm coat, a cozy sweater, sturdy boots, and necessary accessories like a beanie and scarf. The color palette is trendy and easy to mix and match, aligning well with current winter fashion trends. The only slight drawback is the lack of thermal layering options, but overall, the outfit is both fashionable and practical. Great job!
Reasoning: The proposed outfit totals exactly €200, ensuring it is within the budget. The items selected—coat, chunky sweater, jeans, ankle boots, beanie, and scarf—provide practicality for winter with warmth and style. The neutral and trendy color choices in camel, cream, gray, and plaid allow for easy styling and fit well with current fashion trends. Each piece is versatile for various settings, from casual outings to slightly more dressed-up environments. The outfit effectively balances affordability, style, and functionality for winter.
Parallization pattern
With the parallelization pattern, you run multiple agents at the same time to improve performance or to get multiple variations and choose the best one. The orchestrator agent will combine the results. This pattern can also be used in combination with the judge pattern to choose the best results.
- Multiple agents work independently on subtasks.
- Orchestrator aggregates their results after completion.
- Reduces overall processing time through parallel execution.
Example:
Create a comprehensive shopping list for a winter outfit by gathering recommendations from multiple sources.
task_description = "Create a comprehensive shopping list for a winter outfit by gathering recommendations from multiple sources. The outfit should be stylish, warm, and suitable for casual outings. Budget is flexible but aim for value."
facebook_agent = Agent(
name="FacebookMarketplaceTrendAgent",
instructions=f"""
You are an agent that searches Facebook for winter outfit trends.
Based on the task: "{task_description}", provide 2-3 trendy winter clothing items.
Include their name, estimated price, a brief description, and mark the source as 'Facebook'.
""",
output_type=List[ShoppingItem]
)
google_agent = Agent(
name="GoogleShoppingExpertAgent",
instructions=f"""
You are an agent that searches Google Shopping for winter outfit items.
Based on the task: "{task_description}", suggest 2-3 trendy winter clothing items.
Include their name, typical price, a brief description, and mark the source as 'Google'.
""",
output_type=List[ShoppingItem]
)
reddit_agent = Agent(
name="RedditFashionAdviceAgent",
instructions=f"""
You are an agent that searches Reddit (e.g., r/femalefashionadvice, r/malefashionadvice) for winter outfit items.
Based on the task: "{task_description}", provide 2-3 community-recommended winter clothing items.
Include their name, estimated price, a brief description, and mark the source as 'Reddit'.
""",
output_type=List[ShoppingItem]
)
instagram_agent = Agent(
name="InstagramStyleInfluencerAgent",
instructions=f"""
You are an agent that searches an Instagram style influencer showcasing winter outfits.
Based on the task: "{task_description}", suggest 2-3 fashionable winter clothing items.
Include their name, approximate price, a brief description, and mark the source as 'Instagram'.
""",
output_type=List[ShoppingItem]
)
aggregation_agent = Agent(
name="OutfitAggregatorAgent",
instructions=f"""
You are an agent that aggregates a final shopping list from multiple sources.
You will receive an input object with item lists from 'facebook_items', 'google_items', 'reddit_items', and 'instagram_items'.
Your task is to select exactly one 'best' item from each source's list.
Ensure the 'source' field in each selected item is correctly preserved.
""",
output_type=List[ShoppingItem]
)
print("--- Gathering Winter Outfit Recommendations in Parallel ---")
results = await asyncio.gather(
Runner.run(facebook_agent, input=task_description),
Runner.run(google_agent, input=task_description),
Runner.run(reddit_agent, input=task_description),
Runner.run(instagram_agent, input=task_description)
)
facebook_results, google_results, reddit_results, instagram_results = results
all_recommendations: List[ShoppingItem] = []
print("\n--- Recommendations from Facebook ---")
for item in facebook_results.final_output:
print(f" - {item.name} (€{item.price:.2f}): {item.description} (Source: {item.source})")
all_recommendations.append(item)
print("\n--- Recommendations from Google ---")
for item in google_results.final_output:
print(f" - {item.name} (€{item.price:.2f}): {item.description} (Source: {item.source})")
all_recommendations.append(item)
print("\n--- Recommendations from Reddit ---")
for item in reddit_results.final_output:
print(f" - {item.name} (€{item.price:.2f}): {item.description} (Source: {item.source})")
all_recommendations.append(item)
print("\n--- Recommendations from Instagram ---")
for item in instagram_results.final_output:
print(f" - {item.name} (€{item.price:.2f}): {item.description} (Source: {item.source})")
all_recommendations.append(item)
aggregator_input_data = AllSourcesInput(
facebook_items=facebook_results.final_output if facebook_results and facebook_results.final_output else [],
google_items=google_results.final_output if google_results and google_results.final_output else [],
reddit_items=reddit_results.final_output if reddit_results and reddit_results.final_output else [],
instagram_items=instagram_results.final_output if instagram_results and instagram_results.final_output else []
)
print("\n\n--- Aggregating Recommendations ---")
aggregation_result = await Runner.run(aggregation_agent, input=aggregator_input_data.model_dump_json())
aggregated_list: List[ShoppingItem] = aggregation_result.final_output
print("\n\n--- Curated Shopping List (One Best Item Per Source) ---")
for i, item in enumerate(aggregated_list):
print(f"{i+1}. {item.name} (€{item.price:.2f}) - {item.description} (Source: {item.source})")
The terminal output:
--- Gathering Winter Outfit Recommendations in Parallel ---
--- Recommendations from Facebook ---
- Chunky Cable Knit Sweater (€75.00): A cozy, oversized cable knit sweater made from thick, soft yarn to keep you warm during chilly outings. Available in various neutral colors to match any wardrobe. (Source: Facebook)
- Plaid Wool Blend Coat (€150.00): A stylish wool blend coat featuring a classic plaid pattern, offering warmth and a fashionable look for any casual outing. (Source: Facebook)
- Faux Fur Lined Ankle Boots (€95.00): These trendy ankle boots feature a faux fur lining for extra warmth and comfort, with a durable outer sole perfect for winter walks. (Source: Facebook)
--- Recommendations from Google ---
- Patagonia Down Sweater Jacket (€229.00): Lightweight yet warm, this jacket features 800-fill-power down insulation and is perfect for casual outings. (Source: Google)
- Columbia Winter Boots (€120.00): Durable and waterproof, these boots offer excellent warmth and are ideal for winter weather. (Source: Google)
- Levi's Sherpa Trucker Jacket (€98.00): A classic denim jacket with cozy sherpa lining, providing style and warmth for casual wear. (Source: Google)
--- Recommendations from Reddit ---
- Uniqlo Ultra Light Down Jacket (€70.00): A lightweight yet warm down jacket, perfect for layering without bulk. Available in various colors. (Source: Reddit)
- Blundstone 550 Boots (€190.00): Durable and stylish leather boots with a weatherproof finish. Ideal for winter walks and casual outings. (Source: Reddit)
- Patagonia Better Sweater Fleece (€130.00): A cozy fleece pullover that's warm and versatile for winter layering. (Source: Reddit)
--- Recommendations from Instagram ---
- Cozy Wool Blend Overcoat (€120.00): A stylish, knee-length wool blend overcoat in a neutral color, perfect for layering and keeping warm during winter outings. (Source: Instagram)
- Chunky Knit Turtleneck Sweater (€75.00): A soft and comfortable chunky knit sweater with a turtleneck design to keep you cozy. It pairs well with both jeans and skirts. (Source: Instagram)
- Shearling-Lined Leather Ankle Boots (€150.00): Stylish leather ankle boots with a warm shearling lining, offering both style and comfort for cold weather adventures. (Source: Instagram)
--- Aggregating Recommendations ---
--- Curated Shopping List (One Best Item Per Source) ---
1. Plaid Wool Blend Coat (€150.00) - A stylish wool blend coat featuring a classic plaid pattern, offering warmth and a fashionable look for any casual outing. (Source: Facebook)
2. Patagonia Down Sweater Jacket (€229.00) - Lightweight yet warm, this jacket features 800-fill-power down insulation and is perfect for casual outings. (Source: Google)
3. Blundstone 550 Boots (€190.00) - Durable and stylish leather boots with a weatherproof finish. Ideal for winter walks and casual outings. (Source: Reddit)
4. Shearling-Lined Leather Ankle Boots (€150.00) - Stylish leather ankle boots with a warm shearling lining, offering both style and comfort for cold weather adventures. (Source: Instagram)
These are just a few examples, there are more patterns.
Use MCP for tool calling
In my previous article, I explained about the MCP protocol. This protocol is now being widely adopted by many companies. The OpenAI agents SDK also support this protocol. Below a simple example of how you can use the MCP protocol to call servers (tools) when building agents. As you can see I’m using stdio for connecting to the MCP server.
class ShoppingItem(BaseModel):
name: str
price: float
class ShoppingList(BaseModel):
items: list[ShoppingItem]
async def main():
product_search_server = MCPServerStdio(
name="ProductsSearchServer",
params={
"command": "uv",
"args": [
"run",
"--with",
"mcp[cli]",
"mcp",
"run",
"[ABSOLUTE_PATH]\\servers\\product_search_server.py"
],
},
cache_tools_list=False
)
async with product_search_server:
tools = await product_search_server.list_tools()
print(f"MCP server tools: {tools}")
shopping_agent=Agent(
name="Shopping Agent",
instructions=(
"""
You are a shopping assistant. Your task is to create a shopping list for clothing items.
- Use the MCP server to search for clothing products.
"""
),
mcp_servers=[product_search_server],
output_type=ShoppingList
)
shopping_list_result = await Runner.run(shopping_agent, input="")
shopping_list = shopping_list_result.final_output
print("--- Shopping List --- ")
for item in shopping_list.items:
print(f"- {item.name}: ${item.price:.2f}")
The terminal output:
MCP server tools: [Tool(name='search_products', description="Search for products based on the user's query.\n ", inputSchema={'properties': {}, 'title': 'search_productsArguments', 'type': 'object'})]
--- Shopping List ---
- Classic Cotton T-Shirt: $19.99
- Slim Fit Jeans: $49.99
- Wool Sweater: $59.99
- Athletic Shorts: $24.99
- Formal Button-up Shirt: $39.99
- Winter Jacket: $129.99
- Cotton Socks Set: $12.99
- Summer Dress: $45.99
- Leather Belt: $29.99
- Casual Hoodie: $34.99
The MCP server:
mcp = FastMCP("Product search server")
@mcp.tool()
def search_products() -> str:
"""Search for products based on the user's query.
"""
# Hardcoded clothing products
products = [
{
"name": "Classic Cotton T-Shirt",
"description": "Comfortable everyday t-shirt made from 100% organic cotton",
"category": "Clothing",
"price": 19.99
},
{
"name": "Slim Fit Jeans",
"description": "Modern slim fit jeans with stretch technology for maximum comfort",
"category": "Clothing",
"price": 49.99
},
{
"name": "Wool Sweater",
"description": "Warm wool sweater perfect for cold weather",
"category": "Clothing",
"price": 59.99
},
{
"name": "Athletic Shorts",
"description": "Lightweight shorts ideal for sports and running",
"category": "Clothing",
"price": 24.99
},
{
"name": "Formal Button-up Shirt",
"description": "Professional shirt suitable for office and formal occasions",
"category": "Clothing",
"price": 39.99
},
{
"name": "Winter Jacket",
"description": "Insulated jacket with water-resistant exterior for winter protection",
"category": "Clothing",
"price": 129.99
},
{
"name": "Cotton Socks Set",
"description": "Pack of 5 pairs of comfortable cotton socks",
"category": "Clothing",
"price": 12.99
},
{
"name": "Summer Dress",
"description": "Light and airy dress perfect for summer days",
"category": "Clothing",
"price": 45.99
},
{
"name": "Leather Belt",
"description": "Durable genuine leather belt with classic buckle",
"category": "Clothing",
"price": 29.99
},
{
"name": "Casual Hoodie",
"description": "Comfortable hoodie for everyday casual wear",
"category": "Clothing",
"price": 34.99
}
]
return products
Assigning a Dedicated Model to Each Agent
Choosing the right model is crucial for getting the best results. When choosing a model it’s important to consider these aspects: costs, latency, reasoning, capabilities, context window, etc. It often requires iterating a couple of times to find the best model for the job. The OpenAI Agents SDK uses gpt-4o by default, but you can configure it to use other models.
In the Agent as Tools example, I used a reasoning model so the orchestrator could ‘think’ before planning next steps. This is valuable when working with multiple agents, as the orchestrator must make decisions based on each agent’s output. For example, when the BudgetOptimizer agent reported that the outfit exceeded budget, the orchestrator called the TrendAnalyzer agent multiple times to find alternatives. Rather than stopping at the first failure, it iterates until finding an outfit that meets all requirements. However, this flexibility comes at a cost. Reasoning models increase both latency and expenses because they may trigger multiple language model calls. Fortunately, you can configure settings to control this behavior. Let’s look at a simple example.
Below, I’m using the gpt-4o-mini model for the Outfit Creator agent and o1-mini (reasoning) for the Triage agent. In the user_request
variable, which serves as input for the Triage agent, I’ve set a budget of 50 euros—intentionally low for a complete winter outfit to demonstrate the reasoning capabilities.
class ClothingItem(BaseModel):
name: str
price: float
description: str
category: str
trend_score: float
class OutfitRecommendation(BaseModel):
items: List[ClothingItem]
total_budget: float
remaining_budget: float
total_cost: float
async def main():
user_request = "Help me create an affordable but stylish winter outfit based on the current trends and my budget of 50 euro."
print(f"--- Starting Winter Outfit Creation ---\n")
outfit_creator = Agent(
name="Outfit creator agent",
instructions=f"""
You are a fashion expert specializing in winter outfits.
Create a stylish winter outfit that includes:
- 1 outerwear piece (coat/jacket)
- 1 top (sweater/shirt)
- 1 bottom (pants/skirt)
- 1 footwear option
- 1 accessory (scarf, hat, gloves)
For each item:
- Provide a specific name (e.g., "Oversized Wool Blend Coat")
- Estimate the market price in euros (€)
- Include a brief description highlighting its style
- List the category it belongs to
- Assign a trend_score from 0-10
Important:
- Ensure the total cost stays within the user's budget of 500 euro.
- Include the remaining budget in your response
""",
output_type=OutfitRecommendation,
model="gpt-4o-mini",
model_settings=ModelSettings(temperature=0.9),
)
triage_agent = Agent(
name="Triage agent",
instructions=(
f"""
You are an orchestrator responsible for coordinating specialized agents through the tools available to you.
Your task is to:
- Use the OutfitCreator tool to create a stylish winter outfit within the budget.
Rules:
- Always use the appropriate tool for each specific task rather than trying to perform the tasks yourself.
- You MUST only evaluate the output of the tools, NEVER modify the output.
- If results are weak or incorrect from any tool, request retries or clarifications.
"""
),
model="o4-mini",
model_settings=ModelSettings(reasoning={"summary": "auto"}),
tools=[
outfit_creator.as_tool(
tool_name="OutfitCreator",
tool_description="Create a stylish winter outfit within budget constraints"
)
],
)
triage_result = await Runner.run(triage_agent, input=user_request, max_turns=2)
print("Research result:")
print(triage_result)
print(triage_result.final_output)
When I execute this agent workflow, I get an agents.exceptions.MaxTurnsExceeded: Max turns (2) exceeded
error. As shown in the in the tracing screenshot below, the Outfit creator agent couldn’t find a complete outfit within the 50 euro budget. For that reason, the triage agent repeatedly called the Outfit creator agent. To prevent costly operations from running indefinitely, the SDK provides a max_turns
parameter for the Runner.run
method. This acts as a safety mechanism when agents get stuck in loops.
Below, you can see that the OutfitCreatorAgent was called twice but couldn’t find an outfit within budget. Since I configured max_turns=2
, the workflow was cancelled to prevent any further iterations.

Tracing and Observability
Creating AI applications that incorporate multiple agents can become complex. Especially when using handoffs or function calling, it’s useful to see the interactions between the LLM and agents. OpenAI created a tracing section in their online platform that displays a trace route for every workflow run.
The tracing interface shows the execution flow step by step, indicating which agent was executed and the LLM request/response. Also, it provides insights in the function calls. This provides a great way to debug and get insights in the agent workflow runs.

For every agent workflow run, a trace is created and displayed in the portal. However, in some cases you might want to generate a trace_id
yourself, for instance, when you need to call the run method multiple times. For this, you can use the gen_trace_id
method and pass it to the trace method as shown below.
trace_id=gen_trace_id()
with trace(workflow_name="Shopping agents", trace_id=trace_id):
result = await Runner.run(triage_agent, input="Winter")
print(result.final_output)
with trace(workflow_name="Shopping agents", trace_id=trace_id):
result = await Runner.run(triage_agent, input="Summer")
print(result.final_output)
This results in the following overview in the portal.

Summarize
An AI agent is a system that can autonomously perform tasks without human intervention. The OpenAI Agents SDK is a lightweight Python framework that makes it easy to build agentic applications using multiple architectural patterns like routing, parallelization, and judge patterns. The SDK seamlessly integrates with the Model Context Protocol (MCP) for tool calling and provides good tracing capabilities that give clear insights into agent workflow execution.