人工介入机制#
工具也可以被设计为需要人工介入的模式。这对于需要人工输入的任务非常有用,例如确认工具调用或提供反馈。
正如我们将在工作流教程中看到的,AgentWorkflow底层的工作原理是通过运行既能发出又能接收事件的步骤。下图展示了构成AgentWorkflow的步骤(蓝色部分)以及在它们之间传递数据的事件(绿色部分)。您会认出这些事件,它们与我们之前在输出流中处理的事件相同。
要实现人工介入机制,我们将让工具发出一个不被工作流中其他任何步骤接收的事件。然后指示工具等待,直到收到特定的"回复"事件。
我们内置了InputRequiredEvent
和HumanResponseEvent
事件来实现这一功能。如果您需要捕获不同形式的人工输入,可以继承这些事件来匹配您的需求。让我们导入它们:
from llama_index.core.workflow import (
InputRequiredEvent,
HumanResponseEvent,
)
接下来我们将创建一个执行假设危险任务的工具。这里有几个新概念:
- 使用
wait_for_event
等待HumanResponseEvent waiter_event
是被写入事件流的事件,用于通知调用者我们正在等待响应waiter_id
是这个特定等待调用的唯一标识符,确保每个waiter_id
只发送一个waiter_event
requirements
参数用于指定我们要等待具有特定user_name
的HumanResponseEvent
from llama_index.core.workflow import Context
async def dangerous_task(ctx: Context) -> str:
"""需要人工确认的危险任务"""
# 发出等待事件(此处为InputRequiredEvent)
# 并等待直到收到HumanResponseEvent
question = "您确定要继续吗?"
response = await ctx.wait_for_event(
HumanResponseEvent,
waiter_id=question,
waiter_event=InputRequiredEvent(
prefix=question,
user_name="Laurie",
),
requirements={"user_name": "Laurie"},
)
# 根据事件输入采取行动
if response.response.strip().lower() == "yes":
return "危险任务已成功完成。"
else:
return "危险任务已中止。"
像往常一样创建代理,传入我们刚定义的工具:
workflow = FunctionAgent(
tools=[dangerous_task],
llm=llm,
system_prompt="您是一个可以执行危险任务的助手。",
)
现在我们可以运行工作流,像处理其他流事件一样处理InputRequiredEvent
,并使用send_event
方法传入HumanResponseEvent
进行响应:
handler = workflow.run(user_msg="我想继续执行危险任务。")
async for event in handler.stream_events():
if isinstance(event, InputRequiredEvent):
# 捕获键盘输入
response = input(event.prefix)
# 发送我们的响应
handler.ctx.send_event(
HumanResponseEvent(
response=response,
user_name=event.user_name,
)
)
response = await handler
print(str(response))
和往常一样,您可以查看此示例的完整代码。
您可以采用任何方式捕获输入:使用图形界面、语音输入,甚至引入另一个独立的代理。如果您的输入需要较长时间处理,或在另一个进程中发生,您可能需要序列化上下文并将其保存到数据库或文件中,以便稍后恢复工作流。
说到引入其他代理,这就引出了我们的下一节内容,详细介绍构建多代理系统的几种方法。