Human in the Loop
Add human intervention points to your background processes
Overview
Human in the Loop allows background processes to pause and request human intervention at critical decision points. This is particularly useful for processes that require human approval, input, or decision-making during execution.
A user will see a process requires action in their assistant dashboard, and can approve or deny an action, as well as provide a response to the process.
Requesting Human Action
To request human intervention:
const stepId = await app.processes!.requestHumanAction(processId, {
message: "Need approval for next step",
ui: new CardUIBuilder()
.title("Action Required")
.content("Please approve this action")
.build(),
actions: [
{
id: "approve",
title: "Approve",
requiresResponse: false
},
{
id: "reject",
title: "Reject",
requiresResponse: true // Requires explanation
}
],
timeoutMs: 30 * 1000 // 30 second timeout
});
Waiting for Response
After requesting action, wait for human response:
const response = await app.processes!.waitForHumanAction(
processId,
stepId,
30 * 1000 // Timeout
);
// Handle response
if (response.actionId === "approve") {
// Continue process
} else if (response.actionId === "reject") {
// Handle rejection with response.responseText
}
Example: Phone Call Agent
Here's a real example from a phone call service that uses human intervention when the AI agent needs help:
const askForHelp = {
description: "Ask the person you are calling on behalf of for help",
parameters: z.object({
question: z.string().describe("The question you need help with")
}),
execute: async ({ question }) => {
// Log the help request
callSummary.steps.push({
speaker: "ask_for_help",
text: question,
timestamp: new Date().toISOString()
});
try {
// Request human intervention
const stepId = await processHandler.requestHumanAction(processId, {
message: question,
ui: new CardUIBuilder()
.title("Help requested")
.content(`I need help with: ${question}`)
.build(),
actions: [
{
id: "respond-to-help",
title: "Respond to question",
requiresResponse: true
},
{
id: "end-call",
title: "Just hang up the call",
requiresResponse: false
}
],
timeoutMs: 30 * 1000
});
// Wait for response
const response = await processHandler.waitForHumanAction(
processId,
stepId,
30 * 1000
);
// Handle responses
if (response.actionId === "end-call") {
await hangUp("Call ended by recipient due to human request");
return "Call ended by recipient due to human request";
}
if (response.actionId === "respond-to-help") {
// Log human response
callSummary.steps.push({
speaker: "client",
text: response.responseText || "No response from client",
timestamp: new Date().toISOString()
});
return "The user has responded: " + response.responseText;
}
} catch (error) {
return "The human could not be reached. Please continue or hang up.";
}
}
};
Action Configuration
When requesting human action, you can configure:
interface HumanAction {
id: string; // Unique identifier
title: string; // Button text
requiresResponse: boolean; // Whether text input is required
}
interface HumanActionRequest {
message: string; // Main message
ui?: UIComponent; // Optional UI
actions: HumanAction[]; // Available actions
timeoutMs: number; // Timeout in milliseconds
}
Use Cases
Human in the Loop is ideal for:
- Approval workflows
- Decision points requiring judgment
- Complex situations needing human insight
- Quality control checkpoints
- Exception handling
- Sensitive operations
- Customer service escalations
Error Handling
Always implement timeout handling and fallbacks:
try {
const response = await app.processes!.waitForHumanAction(
processId,
stepId,
timeout
);
// Handle response
} catch (error) {
if (error.code === 'TIMEOUT') {
// Handle timeout
}
// Handle other errors
}