Skip to content

Commit f93c854

Browse files
committed
Copy customer_service sample over from OpenAI SDK
1 parent fe83da2 commit f93c854

File tree

1 file changed

+176
-0
lines changed
  • samples-v2/openai_agents/customer_service

1 file changed

+176
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
from __future__ import annotations as _annotations
2+
3+
import asyncio
4+
import random
5+
import uuid
6+
7+
from pydantic import BaseModel
8+
9+
from agents import (
10+
Agent,
11+
HandoffOutputItem,
12+
ItemHelpers,
13+
MessageOutputItem,
14+
RunContextWrapper,
15+
Runner,
16+
ToolCallItem,
17+
ToolCallOutputItem,
18+
TResponseInputItem,
19+
function_tool,
20+
handoff,
21+
trace,
22+
)
23+
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
24+
25+
### CONTEXT
26+
27+
28+
class AirlineAgentContext(BaseModel):
29+
passenger_name: str | None = None
30+
confirmation_number: str | None = None
31+
seat_number: str | None = None
32+
flight_number: str | None = None
33+
34+
35+
### TOOLS
36+
37+
38+
@function_tool(
39+
name_override="faq_lookup_tool", description_override="Lookup frequently asked questions."
40+
)
41+
async def faq_lookup_tool(question: str) -> str:
42+
question_lower = question.lower()
43+
if any(
44+
keyword in question_lower
45+
for keyword in ["bag", "baggage", "luggage", "carry-on", "hand luggage", "hand carry"]
46+
):
47+
return (
48+
"You are allowed to bring one bag on the plane. "
49+
"It must be under 50 pounds and 22 inches x 14 inches x 9 inches."
50+
)
51+
elif any(keyword in question_lower for keyword in ["seat", "seats", "seating", "plane"]):
52+
return (
53+
"There are 120 seats on the plane. "
54+
"There are 22 business class seats and 98 economy seats. "
55+
"Exit rows are rows 4 and 16. "
56+
"Rows 5-8 are Economy Plus, with extra legroom. "
57+
)
58+
elif any(
59+
keyword in question_lower
60+
for keyword in ["wifi", "internet", "wireless", "connectivity", "network", "online"]
61+
):
62+
return "We have free wifi on the plane, join Airline-Wifi"
63+
return "I'm sorry, I don't know the answer to that question."
64+
65+
66+
@function_tool
67+
async def update_seat(
68+
context: RunContextWrapper[AirlineAgentContext], confirmation_number: str, new_seat: str
69+
) -> str:
70+
"""
71+
Update the seat for a given confirmation number.
72+
73+
Args:
74+
confirmation_number: The confirmation number for the flight.
75+
new_seat: The new seat to update to.
76+
"""
77+
# Update the context based on the customer's input
78+
context.context.confirmation_number = confirmation_number
79+
context.context.seat_number = new_seat
80+
# Ensure that the flight number has been set by the incoming handoff
81+
assert context.context.flight_number is not None, "Flight number is required"
82+
return f"Updated seat to {new_seat} for confirmation number {confirmation_number}"
83+
84+
85+
### HOOKS
86+
87+
88+
async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext]) -> None:
89+
flight_number = f"FLT-{random.randint(100, 999)}"
90+
context.context.flight_number = flight_number
91+
92+
93+
### AGENTS
94+
95+
faq_agent = Agent[AirlineAgentContext](
96+
name="FAQ Agent",
97+
handoff_description="A helpful agent that can answer questions about the airline.",
98+
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
99+
You are an FAQ agent. If you are speaking to a customer, you probably were transferred to from the triage agent.
100+
Use the following routine to support the customer.
101+
# Routine
102+
1. Identify the last question asked by the customer.
103+
2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge.
104+
3. If you cannot answer the question, transfer back to the triage agent.""",
105+
tools=[faq_lookup_tool],
106+
)
107+
108+
seat_booking_agent = Agent[AirlineAgentContext](
109+
name="Seat Booking Agent",
110+
handoff_description="A helpful agent that can update a seat on a flight.",
111+
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
112+
You are a seat booking agent. If you are speaking to a customer, you probably were transferred to from the triage agent.
113+
Use the following routine to support the customer.
114+
# Routine
115+
1. Ask for their confirmation number.
116+
2. Ask the customer what their desired seat number is.
117+
3. Use the update seat tool to update the seat on the flight.
118+
If the customer asks a question that is not related to the routine, transfer back to the triage agent. """,
119+
tools=[update_seat],
120+
)
121+
122+
triage_agent = Agent[AirlineAgentContext](
123+
name="Triage Agent",
124+
handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.",
125+
instructions=(
126+
f"{RECOMMENDED_PROMPT_PREFIX} "
127+
"You are a helpful triaging agent. You can use your tools to delegate questions to other appropriate agents."
128+
),
129+
handoffs=[
130+
faq_agent,
131+
handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff),
132+
],
133+
)
134+
135+
faq_agent.handoffs.append(triage_agent)
136+
seat_booking_agent.handoffs.append(triage_agent)
137+
138+
139+
### RUN
140+
141+
142+
async def main():
143+
current_agent: Agent[AirlineAgentContext] = triage_agent
144+
input_items: list[TResponseInputItem] = []
145+
context = AirlineAgentContext()
146+
147+
# Normally, each input from the user would be an API request to your app, and you can wrap the request in a trace()
148+
# Here, we'll just use a random UUID for the conversation ID
149+
conversation_id = uuid.uuid4().hex[:16]
150+
151+
while True:
152+
user_input = input("Enter your message: ")
153+
with trace("Customer service", group_id=conversation_id):
154+
input_items.append({"content": user_input, "role": "user"})
155+
result = await Runner.run(current_agent, input_items, context=context)
156+
157+
for new_item in result.new_items:
158+
agent_name = new_item.agent.name
159+
if isinstance(new_item, MessageOutputItem):
160+
print(f"{agent_name}: {ItemHelpers.text_message_output(new_item)}")
161+
elif isinstance(new_item, HandoffOutputItem):
162+
print(
163+
f"Handed off from {new_item.source_agent.name} to {new_item.target_agent.name}"
164+
)
165+
elif isinstance(new_item, ToolCallItem):
166+
print(f"{agent_name}: Calling a tool")
167+
elif isinstance(new_item, ToolCallOutputItem):
168+
print(f"{agent_name}: Tool call output: {new_item.output}")
169+
else:
170+
print(f"{agent_name}: Skipping item: {new_item.__class__.__name__}")
171+
input_items = result.to_input_list()
172+
current_agent = result.last_agent
173+
174+
175+
if __name__ == "__main__":
176+
asyncio.run(main())

0 commit comments

Comments
 (0)