1
- from __future__ import annotations as _annotations
1
+ from __future__ import annotations
2
2
3
3
import asyncio
4
4
import random
5
5
import uuid
6
6
7
7
from pydantic import BaseModel
8
8
9
- from agents import (
9
+ from src . agents import (
10
10
Agent ,
11
11
HandoffOutputItem ,
12
12
ItemHelpers ,
13
13
MessageOutputItem ,
14
+ ModelSettings ,
14
15
RunContextWrapper ,
16
+ RunConfig ,
15
17
Runner ,
16
18
ToolCallItem ,
17
19
ToolCallOutputItem ,
20
22
handoff ,
21
23
trace ,
22
24
)
23
- from agents .extensions .handoff_prompt import RECOMMENDED_PROMPT_PREFIX
25
+ from src . agents .extensions .handoff_prompt import RECOMMENDED_PROMPT_PREFIX
24
26
25
27
### CONTEXT
26
28
@@ -74,9 +76,13 @@ async def update_seat(
74
76
assert context .context .flight_number is not None , "Flight number is required"
75
77
return f"Updated seat to { new_seat } for confirmation number { confirmation_number } "
76
78
77
-
78
- ### HOOKS
79
-
79
+ @function_tool
80
+ async def respond_to_user (response : str ) -> str :
81
+ """
82
+ Use this function to send a message back to the end user. The agent should call this whenever
83
+ you want to produce a user-facing response.
84
+ """
85
+ return response
80
86
81
87
async def on_seat_booking_handoff (context : RunContextWrapper [AirlineAgentContext ]) -> None :
82
88
flight_number = f"FLT-{ random .randint (100 , 999 )} "
@@ -95,7 +101,7 @@ async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext
95
101
1. Identify the last question asked by the customer.
96
102
2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge.
97
103
3. If you cannot answer the question, transfer back to the triage agent.""" ,
98
- tools = [faq_lookup_tool ],
104
+ tools = [faq_lookup_tool , respond_to_user ],
99
105
)
100
106
101
107
seat_booking_agent = Agent [AirlineAgentContext ](
@@ -109,7 +115,7 @@ async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext
109
115
2. Ask the customer what their desired seat number is.
110
116
3. Use the update seat tool to update the seat on the flight.
111
117
If the customer asks a question that is not related to the routine, transfer back to the triage agent. """ ,
112
- tools = [update_seat ],
118
+ tools = [update_seat , respond_to_user ],
113
119
)
114
120
115
121
triage_agent = Agent [AirlineAgentContext ](
@@ -122,7 +128,12 @@ async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext
122
128
handoffs = [
123
129
faq_agent ,
124
130
handoff (agent = seat_booking_agent , on_handoff = on_seat_booking_handoff ),
131
+ respond_to_user
125
132
],
133
+ tools = [respond_to_user ],
134
+ model_settings = ModelSettings (tool_choice = "required" ),
135
+ tool_use_behavior = {"stop_at_tool_names" : ["respond_to_user" ]},
136
+
126
137
)
127
138
128
139
faq_agent .handoffs .append (triage_agent )
@@ -145,20 +156,30 @@ async def main():
145
156
user_input = input ("Enter your message: " )
146
157
with trace ("Customer service" , group_id = conversation_id ):
147
158
input_items .append ({"content" : user_input , "role" : "user" })
148
- result = await Runner .run (current_agent , input_items , context = context )
159
+ result = await Runner .run (current_agent , input_items , context = context , run_config = RunConfig ( tracing_disabled = True ) )
149
160
161
+ last_tool_name : str | None = None
150
162
for new_item in result .new_items :
151
163
agent_name = new_item .agent .name
152
164
if isinstance (new_item , MessageOutputItem ):
165
+ # In tool_choice="required" scenarios, the agent won't produce bare messages;
166
+ # instead it will call `respond_to_user`. But if the example is run without
167
+ # requiring tool_choice, this branch will handle direct messages.
153
168
print (f"{ agent_name } : { ItemHelpers .text_message_output (new_item )} " )
154
169
elif isinstance (new_item , HandoffOutputItem ):
155
170
print (
156
171
f"Handed off from { new_item .source_agent .name } to { new_item .target_agent .name } "
157
172
)
158
173
elif isinstance (new_item , ToolCallItem ):
159
- print (f"{ agent_name } : Calling a tool" )
174
+ # Stash the name of the tool call so we can treat respond_to_user specially
175
+ last_tool_name = getattr (new_item .raw_item , "name" , None )
176
+ print (f"{ agent_name } called tool:{ f' { last_tool_name } ' if last_tool_name else '' } " )
160
177
elif isinstance (new_item , ToolCallOutputItem ):
161
- print (f"{ agent_name } : Tool call output: { new_item .output } " )
178
+ # If the tool call was respond_to_user, treat its output as the message to display.
179
+ if last_tool_name == "respond_to_user" :
180
+ print (f"{ agent_name } : { new_item .output } " )
181
+ else :
182
+ print (f"{ agent_name } : Tool call output: { new_item .output } " )
162
183
else :
163
184
print (f"{ agent_name } : Skipping item: { new_item .__class__ .__name__ } " )
164
185
input_items = result .to_input_list ()
0 commit comments