Skip to content
LlamaIndex 文档版本 跟随上游 源快照 05da27f1 更新于 2026-10-15

需要人在环路的工作流必须能暂停、告诉调用方需要什么输入,并在调用方给出回应后继续。Workflows 用普通事件就支持了这一点。

最直接的写法是一对步骤:一个返回 InputRequiredEvent,另一个消费 HumanResponseEvent。调用方监听事件流,并把回应送回同一个 handler。

from workflows import Workflow, step
from workflows.events import StartEvent, StopEvent, InputRequiredEvent, HumanResponseEvent

class NumberWorkflow(Workflow):
    @step
    async def ask(self, ev: StartEvent) -> InputRequiredEvent:
        return InputRequiredEvent(prefix="Enter a number: ")

    @step
    async def answer(self, ev: HumanResponseEvent) -> StopEvent:
        return StopEvent(result=ev.response)

workflow = NumberWorkflow()

handler = workflow.run()
async for event in handler.stream_events():
    if isinstance(event, InputRequiredEvent):
        # This could be input(), a websocket reply, a web form submission, etc.
        response = input(event.prefix)
        await handler.send_event(HumanResponseEvent(response=response))

final_result = await handler

在这里,工作流会一直等到 HumanResponseEvent 被发出。如果提示或回应需要更多结构,你可以对这两个事件分别做子类化。

在两次人工回应之间停止/恢复

在 Web 应用里,看到提示的那个进程往往和收到答案的那个请求不是同一个。可以在发出提示后对 context 做快照、把它存起来,等回应到达时再恢复。

import json
from workflows import Context

handler = workflow.run()
async for event in handler.stream_events():
    if isinstance(event, InputRequiredEvent):
        await db.save("run-123", json.dumps(handler.ctx.to_dict()))
        await handler.cancel_run()
        break

# Later, when the human response arrives:
response = form_data["response"]
ctx_dict = json.loads(await db.load("run-123"))
restored_ctx = Context.from_dict(workflow, ctx_dict)
handler = workflow.run(ctx=restored_ctx)

await handler.send_event(HumanResponseEvent(response=response))
async for event in handler.stream_events():
    continue

final_result = await handler

如果你确实要把这次运行交给另一个请求或进程,快照之后请取消原来的 handler。这样就不会留下一个还在内存里等待答案的运行,而那个答案其实会交付给恢复出来的那次运行。

使用 wait_for_event

另一种做法是在单个步骤内用 ctx.wait_for_event() 等待输入:

@step
async def ask_user(self, ctx: Context, ev: StartEvent) -> StopEvent:
    response = await ctx.wait_for_event(
        HumanResponseEvent,
        waiter_event=InputRequiredEvent(prefix="Enter a number: "),
        waiter_id="get_number",
    )
    return StopEvent(result=response.response)

当同一步骤可能多次等待时使用 waiter_id。当多个等待者消费同一类事件、需要把正确的回应路由给正确的等待者时,使用 requirements:

response = await ctx.wait_for_event(
    HumanResponseEvent,
    waiter_event=InputRequiredEvent(prefix="Approve draft? "),
    waiter_id="approve-draft",
    requirements={"request_id": ev.request_id},
)

wait_for_event 在步骤收到其触发事件或匹配的等待事件时,会重放它之前的全部代码。该步骤至少会运行一次直到等待点,随后由等待点抛出一个内部异常来暂停执行。因此 wait_for_event 调用之前的任何代码都必须是可重复执行安全的。

正由于这份复杂性,通常更推荐用「拆分成多个步骤」的事件式做法。

完整细节见 API 参考。

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。