使用 Return Direct 工具控制 Agent 推理循环¶
所有工具都有一个 return_direct
选项——如果设置为 True
,并且调用了相关的工具(未调用其他工具),则 Agent 推理循环将结束,并直接返回工具输出。
当您知道工具输出已经足够好时,这对于加快响应时间非常有用,可以避免 Agent 重写响应并结束推理循环。
本笔记本将逐步演示一个场景,其中 Agent 需要从用户那里收集信息以进行餐厅预订。
在 [ ]
已复制!
%pip install llama-index-core llama-index-llms-anthropic
%pip install llama-index-core llama-index-llms-anthropic
在 [ ]
已复制!
import os
os.environ["ANTHROPIC_API_KEY"] = "sk-..."
import os os.environ["ANTHROPIC_API_KEY"] = "sk-..."
工具设置¶
在 [ ]
已复制!
from typing import Optional
from llama_index.core.tools import FunctionTool
from pydantic import BaseModel
# we will store booking under random IDs
bookings = {}
# we will represent and track the state of a booking as a Pydantic model
class Booking(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
phone: Optional[str] = None
date: Optional[str] = None
time: Optional[str] = None
def get_booking_state(user_id: str) -> str:
"""Get the current state of a booking for a given booking ID."""
try:
return str(bookings[user_id].dict())
except:
return f"Booking ID {user_id} not found"
def update_booking(user_id: str, property: str, value: str) -> str:
"""Update a property of a booking for a given booking ID. Only enter details that are explicitly provided."""
booking = bookings[user_id]
setattr(booking, property, value)
return f"Booking ID {user_id} updated with {property} = {value}"
def create_booking(user_id: str) -> str:
"""Create a new booking and return the booking ID."""
bookings[user_id] = Booking()
return "Booking created, but not yet confirmed. Please provide your name, email, phone, date, and time."
def confirm_booking(user_id: str) -> str:
"""Confirm a booking for a given booking ID."""
booking = bookings[user_id]
if booking.name is None:
raise ValueError("Please provide your name.")
if booking.email is None:
raise ValueError("Please provide your email.")
if booking.phone is None:
raise ValueError("Please provide your phone number.")
if booking.date is None:
raise ValueError("Please provide the date of your booking.")
if booking.time is None:
raise ValueError("Please provide the time of your booking.")
return f"Booking ID {user_id} confirmed!"
# create tools for each function
get_booking_state_tool = FunctionTool.from_defaults(fn=get_booking_state)
update_booking_tool = FunctionTool.from_defaults(fn=update_booking)
create_booking_tool = FunctionTool.from_defaults(
fn=create_booking, return_direct=True
)
confirm_booking_tool = FunctionTool.from_defaults(
fn=confirm_booking, return_direct=True
)
from typing import Optional from llama_index.core.tools import FunctionTool from pydantic import BaseModel # 我们将预订存储在随机 ID 下 bookings = {} # 我们将预订状态表示并跟踪为 Pydantic 模型 class Booking(BaseModel): name: Optional[str] = None email: Optional[str] = None phone: Optional[str] = None date: Optional[str] = None time: Optional[str] = None def get_booking_state(user_id: str) -> str: """获取给定预订 ID 的当前预订状态。""" try: return str(bookings[user_id].dict()) except: return f"未找到预订 ID {user_id}" def update_booking(user_id: str, property: str, value: str) -> str: """更新给定预订 ID 的预订属性。仅输入明确提供的详细信息。""" booking = bookings[user_id] setattr(booking, property, value) return f"预订 ID {user_id} 已更新,{property} = {value}" def create_booking(user_id: str) -> str: """创建新预订并返回预订 ID。""" bookings[user_id] = Booking() return "预订已创建,但尚未确认。请提供您的姓名、电子邮件、电话、日期和时间。" def confirm_booking(user_id: str) -> str: """确认给定预订 ID 的预订。""" booking = bookings[user_id] if booking.name is None: raise ValueError("请提供您的姓名。") if booking.email is None: raise ValueError("请提供您的电子邮件。") if booking.phone is None: raise ValueError("请提供您的电话号码。") if booking.date is None: raise ValueError("请提供您的预订日期。") if booking.time is None: raise ValueError("请提供您的预订时间。") return f"预订 ID {user_id} 已确认!" # 为每个函数创建工具 get_booking_state_tool = FunctionTool.from_defaults(fn=get_booking_state) update_booking_tool = FunctionTool.from_defaults(fn=update_booking) create_booking_tool = FunctionTool.from_defaults( fn=create_booking, return_direct=True ) confirm_booking_tool = FunctionTool.from_defaults( fn=confirm_booking, return_direct=True )
一位用户进来了!让我们帮他们预订¶
在 [ ]
已复制!
from llama_index.llms.anthropic import Anthropic
from llama_index.core.llms import ChatMessage
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.workflow import Context
llm = Anthropic(model="claude-3-sonnet-20240229", temperature=0.1)
user = "user123"
system_prompt = f"""You are now connected to the booking system and helping {user} with making a booking.
Only enter details that the user has explicitly provided.
Do not make up any details.
"""
agent = FunctionAgent(
tools=[
get_booking_state_tool,
update_booking_tool,
create_booking_tool,
confirm_booking_tool,
],
llm=llm,
system_prompt=system_prompt,
)
# create a context for the agent to hold the state/history of a session
ctx = Context(agent)
from llama_index.llms.anthropic import Anthropic from llama_index.core.llms import ChatMessage from llama_index.core.agent.workflow import FunctionAgent from llama_index.core.workflow import Context llm = Anthropic(model="claude-3-sonnet-20240229", temperature=0.1) user = "user123" system_prompt = f"""您现在已连接到预订系统,正在帮助 {user} 进行预订。仅输入用户明确提供的详细信息。不要编造任何详细信息。 """ agent = FunctionAgent( tools=[ get_booking_state_tool, update_booking_tool, create_booking_tool, confirm_booking_tool, ], llm=llm, system_prompt=system_prompt, ) # 为 Agent 创建一个上下文以保存会话状态/历史 ctx = Context(agent)
在 [ ]
已复制!
from llama_index.core.agent.workflow import AgentStream, ToolCallResult
handler = agent.run(
"Hello! I would like to make a booking, around 5pm?", ctx=ctx
)
async for ev in handler.stream_events():
if isinstance(ev, AgentStream):
print(f"{ev.delta}", end="", flush=True)
elif isinstance(ev, ToolCallResult):
print(
f"\nCall {ev.tool_name} with {ev.tool_kwargs}\nReturned: {ev.tool_output}"
)
response = await handler
from llama_index.core.agent.workflow import AgentStream, ToolCallResult handler = agent.run( "你好!我想预订,大概下午 5 点?", ctx=ctx ) async for ev in handler.stream_events(): if isinstance(ev, AgentStream): print(f"{ev.delta}", end="", flush=True) elif isinstance(ev, ToolCallResult): print( f"\n调用 {ev.tool_name},参数为 {ev.tool_kwargs}\n返回:{ev.tool_output}" ) response = await handler
Okay, let's create a new booking for you.{"user_id": "user123"} Call create_booking with {'user_id': 'user123'} Returned: Booking created, but not yet confirmed. Please provide your name, email, phone, date, and time.
在 [ ]
已复制!
print(str(response))
print(str(response))
Booking created, but not yet confirmed. Please provide your name, email, phone, date, and time.
完美,我们可以看到函数输出被直接返回,没有修改或最终的 LLM 调用!
在 [ ]
已复制!
handler = agent.run(
"Sure! My name is Logan, and my email is [email protected]?", ctx=ctx
)
async for ev in handler.stream_events():
if isinstance(ev, AgentStream):
print(f"{ev.delta}", end="", flush=True)
elif isinstance(ev, ToolCallResult):
print(
f"\nCall {ev.tool_name} with {ev.tool_kwargs}\nReturned: {ev.tool_output}"
)
response = await handler
handler = agent.run( "好的!我叫 Logan,我的邮箱是 [email protected]?", ctx=ctx ) async for ev in handler.stream_events(): if isinstance(ev, AgentStream): print(f"{ev.delta}", end="", flush=True) elif isinstance(ev, ToolCallResult): print( f"\n调用 {ev.tool_name},参数为 {ev.tool_kwargs}\n返回:{ev.tool_output}" ) response = await handler
Got it, thanks for providing your name and email. I've updated the booking with that information.{"user_id": "user123", "property": "name", "value": "Logan"}{"user_id": "user123", "property": "email", "value": "[email protected]"} Call update_booking with {'user_id': 'user123', 'property': 'name', 'value': 'Logan'} Returned: Booking ID user123 updated with name = Logan Call update_booking with {'user_id': 'user123', 'property': 'email', 'value': '[email protected]'} Returned: Booking ID user123 updated with email = [email protected] Please also provide your phone number, preferred date, and time for the booking.
在 [ ]
已复制!
print(str(response))
print(str(response))
Please also provide your phone number, preferred date, and time for the booking.
在 [ ]
已复制!
handler = agent.run(
"Right! My phone number is 1234567890, the date of the booking is April 5, at 5pm.",
ctx=ctx,
)
async for ev in handler.stream_events():
if isinstance(ev, AgentStream):
print(f"{ev.delta}", end="", flush=True)
elif isinstance(ev, ToolCallResult):
print(
f"\nCall {ev.tool_name} with {ev.tool_kwargs}\nReturned: {ev.tool_output}"
)
response = await handler
handler = agent.run( "对了!我的电话号码是 1234567890,预订日期是 4 月 5 日,时间是下午 5 点。", ctx=ctx, ) async for ev in handler.stream_events(): if isinstance(ev, AgentStream): print(f"{ev.delta}", end="", flush=True) elif isinstance(ev, ToolCallResult): print( f"\n调用 {ev.tool_name},参数为 {ev.tool_kwargs}\n返回:{ev.tool_output}" ) response = await handler
Great, thank you for providing the additional details. I've updated the booking with your phone number, date, and time.{"user_id": "user123", "property": "phone", "value": "1234567890"}{"user_id": "user123", "property": "date", "value": "2023-04-05"}{"user_id": "user123", "property": "time", "value": "17:00"} Call update_booking with {'user_id': 'user123', 'property': 'phone', 'value': '1234567890'} Returned: Booking ID user123 updated with phone = 1234567890 Call update_booking with {'user_id': 'user123', 'property': 'date', 'value': '2023-04-05'} Returned: Booking ID user123 updated with date = 2023-04-05 Call update_booking with {'user_id': 'user123', 'property': 'time', 'value': '17:00'} Returned: Booking ID user123 updated with time = 17:00 Looks like I have all the necessary details. Let me confirm this booking for you.{"user_id": "user123"} Call confirm_booking with {'user_id': 'user123'} Returned: Booking ID user123 confirmed!
在 [ ]
已复制!
print(str(response))
print(str(response))
Booking ID user123 confirmed!
在 [ ]
已复制!
print(bookings["user123"])
print(bookings["user123"])
name='Logan' email='[email protected]' phone='1234567890' date='2023-04-05' time='17:00'