Pause workflow execution for user confirmation, input, or decisions at the step level.
Human-in-the-Loop (HITL) in Workflows enables you to pause execution at any step to collect user confirmation, input, or decisions. The workflow state is persisted, allowing you to resume execution after the user responds.
User input is currently supported for Step (to collect parameters) and Router (to select routes). Other primitives (Condition, Loop, Steps) support confirmation only.
Agent tool-level HITL (e.g., @tool(requires_confirmation=True)) is not propagated to the workflow. If an agent inside a step has tool-level HITL, the workflow will continue but the paused tool may not execute. Use workflow-level HITL (Step.requires_confirmation) instead.
from agno.workflow import Workflow, OnRejectfrom agno.workflow.step import Stepfrom agno.db.sqlite import SqliteDbworkflow = Workflow( name="data_pipeline", db=SqliteDb(db_file="workflow.db"), # Required for HITL steps=[ Step(name="fetch_data", agent=fetch_agent), Step( name="process_data", agent=process_agent, requires_confirmation=True, confirmation_message="Process sensitive data?", on_reject=OnReject.skip, ), Step(name="save_results", agent=save_agent), ],)run_output = workflow.run("Process user data")if run_output.is_paused: for req in run_output.steps_requiring_confirmation: req.confirm() # or req.reject() run_output = workflow.continue_run(run_output)
for req in run_output.steps_requiring_user_input: print(req.user_input_message) for field in req.user_input_schema: value = get_user_value(field.name, field.field_type) req.set_user_input(**{field.name: value})
User input is available in the custom function step via step_input.additional_data["user_input"].
for req in run_output.steps_requiring_route: print(req.available_choices) # ["quick_analysis", "deep_analysis"] req.select("deep_analysis") # or req.select_multiple(["quick_analysis", "deep_analysis"])
Mark custom function steps with HITL configuration using the @pause decorator:
from agno.workflow.decorators import pausefrom agno.workflow.types import UserInputField@pause( requires_user_input=True, user_input_message="Enter parameters:", user_input_schema=[ UserInputField(name="threshold", field_type="float", required=True), ],)def process_data(step_input: StepInput) -> StepOutput: threshold = step_input.additional_data["user_input"]["threshold"] return StepOutput(content=f"Processed with threshold {threshold}")# The decorator config is auto-detected when used in a custom function stepStep(name="process", executor=process_data)