Skip to content

Commit cb77ea3

Browse files
committed
triage agent tool choice requried + respond to user
1 parent ca49991 commit cb77ea3

File tree

2 files changed

+37
-15
lines changed

2 files changed

+37
-15
lines changed

docs/examples.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ Check out a variety of sample implementations of the SDK in the examples section
77

88
- **agent_patterns:**
99
Examples in this category illustrate common agent design patterns, such as
10-
10+
1111
- Deterministic workflows
1212
- Agents as tools
1313
- Parallel agent execution
1414

1515
- **basic:**
1616
These examples showcase foundational capabilities of the SDK, such as
17-
17+
1818
- Dynamic system prompts
1919
- Streaming outputs
2020
- Lifecycle events
@@ -31,6 +31,7 @@ Check out a variety of sample implementations of the SDK in the examples section
3131

3232
- **customer_service** and **research_bot:**
3333
Two more built-out examples that illustrate real-world applications
34-
35-
- **customer_service**: Example customer service system for an airline.
34+
35+
- **customer_service**: Example customer service system for an airline, with
36+
`tool_choice` required for the triage agent.
3637
- **research_bot**: Simple deep research clone.

examples/customer_service/main.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
from __future__ import annotations as _annotations
1+
from __future__ import annotations
22

33
import asyncio
44
import random
55
import uuid
66

77
from pydantic import BaseModel
88

9-
from agents import (
9+
from src.agents import (
1010
Agent,
1111
HandoffOutputItem,
1212
ItemHelpers,
1313
MessageOutputItem,
14+
ModelSettings,
1415
RunContextWrapper,
16+
RunConfig,
1517
Runner,
1618
ToolCallItem,
1719
ToolCallOutputItem,
@@ -20,7 +22,7 @@
2022
handoff,
2123
trace,
2224
)
23-
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
25+
from src.agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
2426

2527
### CONTEXT
2628

@@ -74,9 +76,13 @@ async def update_seat(
7476
assert context.context.flight_number is not None, "Flight number is required"
7577
return f"Updated seat to {new_seat} for confirmation number {confirmation_number}"
7678

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
8086

8187
async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext]) -> None:
8288
flight_number = f"FLT-{random.randint(100, 999)}"
@@ -95,7 +101,7 @@ async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext
95101
1. Identify the last question asked by the customer.
96102
2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge.
97103
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],
99105
)
100106

101107
seat_booking_agent = Agent[AirlineAgentContext](
@@ -109,7 +115,7 @@ async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext
109115
2. Ask the customer what their desired seat number is.
110116
3. Use the update seat tool to update the seat on the flight.
111117
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],
113119
)
114120

115121
triage_agent = Agent[AirlineAgentContext](
@@ -122,7 +128,12 @@ async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext
122128
handoffs=[
123129
faq_agent,
124130
handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff),
131+
respond_to_user
125132
],
133+
tools=[respond_to_user],
134+
model_settings=ModelSettings(tool_choice="required"),
135+
tool_use_behavior={"stop_at_tool_names": ["respond_to_user"]},
136+
126137
)
127138

128139
faq_agent.handoffs.append(triage_agent)
@@ -145,20 +156,30 @@ async def main():
145156
user_input = input("Enter your message: ")
146157
with trace("Customer service", group_id=conversation_id):
147158
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))
149160

161+
last_tool_name: str | None = None
150162
for new_item in result.new_items:
151163
agent_name = new_item.agent.name
152164
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.
153168
print(f"{agent_name}: {ItemHelpers.text_message_output(new_item)}")
154169
elif isinstance(new_item, HandoffOutputItem):
155170
print(
156171
f"Handed off from {new_item.source_agent.name} to {new_item.target_agent.name}"
157172
)
158173
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 ''}")
160177
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}")
162183
else:
163184
print(f"{agent_name}: Skipping item: {new_item.__class__.__name__}")
164185
input_items = result.to_input_list()

0 commit comments

Comments
 (0)