Human-in-the-Loop (HITL)
Enable procedures to collaborate with human operators through approval, input, review, and notifications.
Core Principles
- Blocking by default: Approval, input, and review operations block workflow execution
- Non-blocking notifications: Progress updates and alerts don't block execution
- Timeout support: All blocking operations support timeouts with fallback behavior
- Context-rich: Provide structured data to help humans make informed decisions
- Stage integration: Procedure status reflects when waiting for human interaction
HITL Primitives
Human.approve()
Request yes/no approval (blocking)
local approved = Human.approve({
message = "Deploy to production?",
context = {
version = "2.1.0",
environment = "production",
changes = change_summary
},
timeout = 3600, -- 1 hour
default = false
})
if approved then
deploy()
else
Log.info("Deployment cancelled")
endHuman.input()
Request text input (blocking)
local topic = Human.input({
message = "What topic should I research?",
placeholder = "Enter a topic...",
timeout = nil -- Wait forever
})
if topic then
Procedure.run("researcher", {topic = topic})
endHuman.review()
Request work product review (blocking)
local review = Human.review({
message = "Please review this report",
artifact = report_content,
artifact_type = "document",
options = {"approve", "edit", "reject"},
timeout = 86400 -- 24 hours
})
if review.decision == "approve" then
publish(report_content)
elseif review.decision == "edit" then
publish(review.edited_artifact)
else
Log.warn("Report rejected")
endHuman.notify()
Send notification (non-blocking)
Human.notify({
message = "Processing phase 2 of 5",
level = "info", -- info, warning, error
context = {
progress = "40%",
items_processed = 200
}
})
-- Execution continues immediatelySystem.alert()
Send system alert (non-blocking)
System.alert({
message = "Memory usage exceeded threshold",
level = "warning", -- info, warning, error, critical
source = "batch_processor",
context = {
memory_mb = 8500,
threshold_mb = 8000
}
})Declarative HITL Points
For predictable workflows, declare HITL points in YAML:
hitl: review_content: type: review message: "Review the generated content" timeout: 86400 options: [approve, edit, reject] confirm_publish: type: approval message: "Publish to production?" timeout: 3600 default: false workflow: | -- Generate content Worker.turn() -- Use declared review point local review = Human.review("review_content", { artifact = State.get("content") }) -- Use declared approval point if review.decision == "approve" then local approved = Human.approve("confirm_publish") if approved then publish() end end
Common Patterns
Pre-Action Approval
Request approval before critical operations:
workflow: | -- Analyze impact analyze_impact() -- Request approval local approved = Human.approve({ message = "Execute database migration?", context = { affected_tables = State.get("affected_tables"), estimated_duration = "15 minutes", rollback_plan = "Available" }, timeout = 1800 }) if approved then migrate_database() end
Progress with Alerts
Monitor operations and alert on issues:
workflow: | Human.notify({message = "Starting batch job"}) local processed = 0 local failed = 0 for i, item in ipairs(params.items) do local ok = process(item) if ok then processed = processed + 1 else failed = failed + 1 end -- Alert if failure rate too high local failure_rate = failed / i if failure_rate > 0.1 and i > 10 then System.alert({ message = "High failure rate detected", level = "warning", source = "batch_processor", context = {failure_rate = failure_rate} }) local continue = Human.approve({ message = "Continue processing?", default = false }) if not continue then break end end end
Complete Example
name: content_pipeline version: 1.0.0 params: topic: type: string required: true outputs: published: type: boolean required: true stages: - drafting - review - publishing hitl: review_content: type: review message: "Review generated content" timeout: 86400 confirm_publish: type: approval message: "Publish?" timeout: 3600 default: false agents: writer: system_prompt: "Write about: {params.topic}" tools: [research, write, done] workflow: | -- Generate Stage.set("drafting") Human.notify({message = "Generating content"}) repeat Writer.turn() until Tool.called("done") -- Review Stage.set("review") local review = Human.review("review_content", { artifact = State.get("draft") }) if review.decision == "reject" then return {published = false} end -- Publish Stage.set("publishing") local approved = Human.approve("confirm_publish") if approved then publish(review.edited_artifact or State.get("draft")) Human.notify({message = "Published successfully"}) return {published = true} else return {published = false} end