Skip to content

Commit cfee829

Browse files
committed
separate customer service workflow from helper functions
1 parent d4aab6c commit cfee829

2 files changed

Lines changed: 135 additions & 127 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
from __future__ import annotations as _annotations
2+
3+
from agents import Agent, RunContextWrapper, function_tool, handoff
4+
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
5+
from pydantic import BaseModel
6+
from temporalio import workflow
7+
8+
9+
### CONTEXT
10+
11+
12+
class AirlineAgentContext(BaseModel):
13+
passenger_name: str | None = None
14+
confirmation_number: str | None = None
15+
seat_number: str | None = None
16+
flight_number: str | None = None
17+
18+
19+
### TOOLS
20+
21+
22+
@function_tool(
23+
name_override="faq_lookup_tool",
24+
description_override="Lookup frequently asked questions.",
25+
)
26+
async def faq_lookup_tool(question: str) -> str:
27+
if "bag" in question or "baggage" in question:
28+
return (
29+
"You are allowed to bring one bag on the plane. "
30+
"It must be under 50 pounds and 22 inches x 14 inches x 9 inches."
31+
)
32+
elif "seats" in question or "plane" in question:
33+
return (
34+
"There are 120 seats on the plane. "
35+
"There are 22 business class seats and 98 economy seats. "
36+
"Exit rows are rows 4 and 16. "
37+
"Rows 5-8 are Economy Plus, with extra legroom. "
38+
)
39+
elif "wifi" in question:
40+
return "We have free wifi on the plane, join Airline-Wifi"
41+
return "I'm sorry, I don't know the answer to that question."
42+
43+
44+
@function_tool
45+
async def update_seat(
46+
context: RunContextWrapper[AirlineAgentContext],
47+
confirmation_number: str,
48+
new_seat: str,
49+
) -> str:
50+
"""
51+
Update the seat for a given confirmation number.
52+
53+
Args:
54+
confirmation_number: The confirmation number for the flight.
55+
new_seat: The new seat to update to.
56+
"""
57+
# Update the context based on the customer's input
58+
context.context.confirmation_number = confirmation_number
59+
context.context.seat_number = new_seat
60+
# Ensure that the flight number has been set by the incoming handoff
61+
assert context.context.flight_number is not None, "Flight number is required"
62+
return f"Updated seat to {new_seat} for confirmation number {confirmation_number}"
63+
64+
65+
### HOOKS
66+
67+
68+
async def on_seat_booking_handoff(
69+
context: RunContextWrapper[AirlineAgentContext],
70+
) -> None:
71+
flight_number = f"FLT-{workflow.random().randint(100, 999)}"
72+
context.context.flight_number = flight_number
73+
74+
75+
### AGENTS
76+
77+
78+
def init_agents() -> Agent[AirlineAgentContext]:
79+
"""
80+
Initialize the agents for the airline customer service workflow.
81+
:return: triage agent
82+
"""
83+
faq_agent = Agent[AirlineAgentContext](
84+
name="FAQ Agent",
85+
handoff_description="A helpful agent that can answer questions about the airline.",
86+
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
87+
You are an FAQ agent. If you are speaking to a customer, you probably were transferred to from the triage agent.
88+
Use the following routine to support the customer.
89+
# Routine
90+
1. Identify the last question asked by the customer.
91+
2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge.
92+
3. If you cannot answer the question, transfer back to the triage agent.""",
93+
tools=[faq_lookup_tool],
94+
)
95+
96+
seat_booking_agent = Agent[AirlineAgentContext](
97+
name="Seat Booking Agent",
98+
handoff_description="A helpful agent that can update a seat on a flight.",
99+
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
100+
You are a seat booking agent. If you are speaking to a customer, you probably were transferred to from the triage agent.
101+
Use the following routine to support the customer.
102+
# Routine
103+
1. Ask for their confirmation number.
104+
2. Ask the customer what their desired seat number is.
105+
3. Use the update seat tool to update the seat on the flight.
106+
If the customer asks a question that is not related to the routine, transfer back to the triage agent. """,
107+
tools=[update_seat],
108+
)
109+
110+
triage_agent = Agent[AirlineAgentContext](
111+
name="Triage Agent",
112+
handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.",
113+
instructions=(
114+
f"{RECOMMENDED_PROMPT_PREFIX} "
115+
"You are a helpful triaging agent. You can use your tools to delegate questions to other appropriate agents."
116+
),
117+
handoffs=[
118+
faq_agent,
119+
handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff),
120+
],
121+
)
122+
123+
faq_agent.handoffs.append(triage_agent)
124+
seat_booking_agent.handoffs.append(triage_agent)
125+
return triage_agent
126+
127+
128+
class ProcessUserMessageInput(BaseModel):
129+
user_input: str
130+
chat_length: int

openai_agents/workflows/customer_service_workflow.py

Lines changed: 5 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -9,145 +9,23 @@
99
ItemHelpers,
1010
MessageOutputItem,
1111
RunConfig,
12-
RunContextWrapper,
1312
Runner,
1413
ToolCallItem,
1514
ToolCallOutputItem,
1615
TResponseInputItem,
17-
function_tool,
18-
handoff,
1916
trace,
2017
)
21-
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
22-
from pydantic import BaseModel
2318

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",
40-
description_override="Lookup frequently asked questions.",
41-
)
42-
async def faq_lookup_tool(question: str) -> str:
43-
if "bag" in question or "baggage" in question:
44-
return (
45-
"You are allowed to bring one bag on the plane. "
46-
"It must be under 50 pounds and 22 inches x 14 inches x 9 inches."
47-
)
48-
elif "seats" in question or "plane" in question:
49-
return (
50-
"There are 120 seats on the plane. "
51-
"There are 22 business class seats and 98 economy seats. "
52-
"Exit rows are rows 4 and 16. "
53-
"Rows 5-8 are Economy Plus, with extra legroom. "
54-
)
55-
elif "wifi" in question:
56-
return "We have free wifi on the plane, join Airline-Wifi"
57-
return "I'm sorry, I don't know the answer to that question."
58-
59-
60-
@function_tool
61-
async def update_seat(
62-
context: RunContextWrapper[AirlineAgentContext],
63-
confirmation_number: str,
64-
new_seat: str,
65-
) -> str:
66-
"""
67-
Update the seat for a given confirmation number.
68-
69-
Args:
70-
confirmation_number: The confirmation number for the flight.
71-
new_seat: The new seat to update to.
72-
"""
73-
# Update the context based on the customer's input
74-
context.context.confirmation_number = confirmation_number
75-
context.context.seat_number = new_seat
76-
# Ensure that the flight number has been set by the incoming handoff
77-
assert context.context.flight_number is not None, "Flight number is required"
78-
return f"Updated seat to {new_seat} for confirmation number {confirmation_number}"
79-
80-
81-
### HOOKS
82-
83-
84-
async def on_seat_booking_handoff(
85-
context: RunContextWrapper[AirlineAgentContext],
86-
) -> None:
87-
flight_number = f"FLT-{workflow.random().randint(100, 999)}"
88-
context.context.flight_number = flight_number
89-
90-
91-
### AGENTS
92-
93-
94-
def init_agents() -> Agent[AirlineAgentContext]:
95-
"""
96-
Initialize the agents for the airline customer service workflow.
97-
:return: triage agent
98-
"""
99-
faq_agent = Agent[AirlineAgentContext](
100-
name="FAQ Agent",
101-
handoff_description="A helpful agent that can answer questions about the airline.",
102-
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
103-
You are an FAQ agent. If you are speaking to a customer, you probably were transferred to from the triage agent.
104-
Use the following routine to support the customer.
105-
# Routine
106-
1. Identify the last question asked by the customer.
107-
2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge.
108-
3. If you cannot answer the question, transfer back to the triage agent.""",
109-
tools=[faq_lookup_tool],
19+
from openai_agents.workflows.customer_service import (
20+
AirlineAgentContext,
21+
ProcessUserMessageInput,
22+
init_agents,
11023
)
11124

112-
seat_booking_agent = Agent[AirlineAgentContext](
113-
name="Seat Booking Agent",
114-
handoff_description="A helpful agent that can update a seat on a flight.",
115-
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
116-
You are a seat booking agent. If you are speaking to a customer, you probably were transferred to from the triage agent.
117-
Use the following routine to support the customer.
118-
# Routine
119-
1. Ask for their confirmation number.
120-
2. Ask the customer what their desired seat number is.
121-
3. Use the update seat tool to update the seat on the flight.
122-
If the customer asks a question that is not related to the routine, transfer back to the triage agent. """,
123-
tools=[update_seat],
124-
)
125-
126-
triage_agent = Agent[AirlineAgentContext](
127-
name="Triage Agent",
128-
handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.",
129-
instructions=(
130-
f"{RECOMMENDED_PROMPT_PREFIX} "
131-
"You are a helpful triaging agent. You can use your tools to delegate questions to other appropriate agents."
132-
),
133-
handoffs=[
134-
faq_agent,
135-
handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff),
136-
],
137-
)
138-
139-
faq_agent.handoffs.append(triage_agent)
140-
seat_booking_agent.handoffs.append(triage_agent)
141-
return triage_agent
142-
143-
144-
class ProcessUserMessageInput(BaseModel):
145-
user_input: str
146-
chat_length: int
147-
14825

14926
@workflow.defn
15027
class CustomerServiceWorkflow:
28+
@workflow.init
15129
def __init__(self, input_items: list[TResponseInputItem] | None = None):
15230
self.run_config = RunConfig()
15331
self.chat_history: list[str] = []

0 commit comments

Comments
 (0)