<@ULVA73B9P> At a high level, what are the best pr...
# ask-marvin
c
@Marvin At a high level, what are the best practices for human-in-the-loop steps in a Prefect workflow?
m
thought for 235.1 seconds
Here are high‑level best practices for human‑in‑the‑loop steps in Prefect 3.x: 1) Choose the right interaction pattern - Quick approvals or gating: use
pause_flow_run(...)
- Long waits (hours/days) and you want to tear down infrastructure: use
suspend_flow_run(...)
with
persist_result=True
on the flow so state/outputs are durable - Ongoing, low‑latency exchanges (chat-like): use streaming inputs via `receive_input`/`send_input` - Only pause/suspend at the flow level (not inside tasks) 2) Use structured forms for great UX and safety - Define forms with
RunInput
(Pydantic) for type-checked inputs, defaults, descriptions, and dropdowns (via Literals/Enums) - Include clear, concise context (e.g., summaries, links, previews) in the
description
- Never include secrets or sensitive payloads in form fields/descriptions 3) Always set explicit timeouts and define the fallback - Set
timeout
on `pause_flow_run`/`suspend_flow_run` and handle timeouts (e.g., fail fast, auto-rollback, or skip) - Use the
key
parameter to make pauses idempotent and prevent re-prompting on retries 4) Automate notifications and resumption - Use Automations to notify the right channel (Slack/email) when a run enters Paused state - Prefer Automations over client-side hooks for reliability - For external approvals, emit custom events and have an Automation resume the run when your event arrives 5) Make approvals auditable and observable - Log what is being approved, by whom, and why; persist decisions as artifacts/variables if needed - Keep the human step a clear gate with explicit branching (approved vs. rejected paths) - Use states and events to trace the full decision path 6) Operate at scale cleanly - Cap how many runs can be paused concurrently (via work pool sizing or concurrency limits elsewhere in your system) - Favor
suspend_flow_run
for long queues of approvals to avoid idle infra costs - Avoid pausing inside retry loops; add
key=
to dedupe pauses if retries occur 7) Secure programmatic control - If resuming programmatically (
resume_flow_run
), scope credentials tightly and use service accounts - Validate and sanitize any input received from external systems before resuming Minimal reference pattern
Copy code
from prefect import flow
from prefect.flow_runs import pause_flow_run
from prefect.input import RunInput
from pydantic import Field

class Approval(RunInput):
    approved: bool = Field(description="Approve to proceed?")
    notes: str = Field(default="")

@flow(persist_result=True)  # recommended if you might suspend later
def pipeline():
    decision = pause_flow_run(
        wait_for_input=Approval.with_initial_data(
            description="Review sample: <s3://bucket/key> (100 rows previewed)"
        ),
        timeout=3600,
        key="gate-approve-v1",   # idempotent pause
    )
    if not decision.approved:
        return "stopped_by_human"
    # continue...
Suspend when waits are long to free infra:
Copy code
from prefect.flow_runs import suspend_flow_run
# use when waits are hours+ and you want to free infra
suspend_flow_run(timeout=86400, key="long-wait-approval")
Event-driven resumption (via Automations) - External system or service:
Copy code
from prefect.events import emit_event
emit_event(event="approval.granted", resource={"prefect.resource.id": "your-resource-or-run"})
- Automation: trigger on custom event “approval.granted” and action “Resume flow run” Useful references - Interactive workflows: Interactive workflows - Form building with RunInput: Form building - Automations: Automations - Custom events: Custom events - States overview: States
- Flow run control (pause/suspend/resume) API: Flow runs API If you share your scenario (approval SLA, who approves, on-prem vs. Cloud, expected volume), I can tailor these patterns to a concrete design.