Skip to content

人工介入机制#

工具也可以被设计为需要人工介入的模式。这对于需要人工输入的任务非常有用,例如确认工具调用或提供反馈。

正如我们将在工作流教程中看到的,AgentWorkflow底层的工作原理是通过运行既能发出又能接收事件的步骤。下图展示了构成AgentWorkflow的步骤(蓝色部分)以及在它们之间传递数据的事件(绿色部分)。您会认出这些事件,它们与我们之前在输出流中处理的事件相同。

工作流示意图

要实现人工介入机制,我们将让工具发出一个不被工作流中其他任何步骤接收的事件。然后指示工具等待,直到收到特定的"回复"事件。

我们内置了InputRequiredEventHumanResponseEvent事件来实现这一功能。如果您需要捕获不同形式的人工输入,可以继承这些事件来匹配您的需求。让我们导入它们:

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))

和往常一样,您可以查看此示例的完整代码

您可以采用任何方式捕获输入:使用图形界面、语音输入,甚至引入另一个独立的代理。如果您的输入需要较长时间处理,或在另一个进程中发生,您可能需要序列化上下文并将其保存到数据库或文件中,以便稍后恢复工作流。

说到引入其他代理,这就引出了我们的下一节内容,详细介绍构建多代理系统的几种方法。