Human-in-the-Loop Patterns
Not every agent action should be fully autonomous. HITL patterns let you add human approval gates, collect feedback, handle escalations, and keep humans in control of critical decisions while agents handle the routine work.
Overview
Human-in-the-loop (HITL) is a design pattern where agent workflows pause at specific points to request human input, approval, or review. Rekall supports HITL through execution memory -- pause a task, notify a human, capture their response, and resume.
HITL + Execution Memory
HITL patterns build on top of Execution Memory. Make sure you understand pause/resume/checkpoint before diving into HITL patterns.
When to Pause for Human Input
Common scenarios where you should add a human approval gate:
- •Destructive operations -- Deleting data, dropping tables, force-pushing to main
- •External actions -- Sending emails, posting to Slack, creating PRs
- •Cost-incurring actions -- Cloud provisioning, API calls with usage fees
- •Ambiguous situations -- When the agent is uncertain about the right action
- •Compliance requirements -- Actions that require human sign-off for audit
import Rekall from '@rekall/agent-sdk';const rekall = new Rekall({ apiKey: 'rk_your_key', agentId: 'agent_abc123' });// Define which actions require human approvalconst hitlPolicy = await rekall.execution.setHITLPolicy({rules: [{action: 'delete',resource: '*',requireApproval: true,approvers: ['user:adam'],},{action: 'deploy',resource: 'production',requireApproval: true,approvers: ['user:adam', 'user:alice'],minApprovals: 1,},{action: '*',resource: '*',requireApproval: false, // Everything else is auto-approved},],});
Approval Workflows
Single Approval Gate
The simplest HITL pattern: pause execution, notify a human, and wait for approval.
// During task execution, pause for approvalasync function deployToProduction(service: string, version: string) {const execution = await rekall.execution.create({name: `Deploy ${service}@${version} to production`,config: { onFailure: 'pause' },});await rekall.execution.start({ executionId: execution.id });// Step 1: Run tests (auto)await runTests(service);await rekall.execution.checkpoint({executionId: execution.id,step: 1,message: 'Tests passed',});// Step 2: Build (auto)const image = await buildImage(service, version);await rekall.execution.checkpoint({executionId: execution.id,step: 2,state: { image },message: 'Image built',});// Step 3: HITL - Request approval before deployingconst approval = await rekall.execution.requestApproval({executionId: execution.id,title: 'Production Deployment Approval',description: `Ready to deploy ${service}@${version} to production.\n\nTests: PASSED\nImage: ${image}`,approvers: ['user:adam'],timeout: 3600000, // 1 hour to approvenotifyVia: ['email', 'slack'],});// Execution is now paused. It will resume when approved.// The function returns when the approval is granted.if (approval.status === 'approved') {// Step 4: Deploy (approved by human)await deploy(service, version, 'production');await rekall.execution.complete({executionId: execution.id,result: { deployedBy: approval.approvedBy },});} else {// Approval denied or timed outawait rekall.execution.complete({executionId: execution.id,result: {status: 'rejected',reason: approval.reason,},});}}
Multi-Approval Chains
For critical actions, require multiple approvals from different people or roles.
const approval = await rekall.execution.requestApproval({executionId: execution.id,title: 'Database Schema Migration',description: 'Dropping the legacy_users table and migrating data to users_v2',chain: [{step: 'Technical Review',approvers: ['user:alice', 'user:bob'],minApprovals: 1,timeout: 3600000,},{step: 'Manager Approval',approvers: ['user:carol'],minApprovals: 1,timeout: 7200000,},],notifyVia: ['email', 'slack'],});// The execution pauses until all chain steps are approved// Each step notifies the next group when the previous is approvedconsole.log(`Approval status: ${approval.status}`);console.log(`Chain progress:`);for (const step of approval.chainStatus) {console.log(` ${step.name}: ${step.status} (${step.approvedBy?.join(', ') || 'pending'})`);}
Human Feedback Integration
Beyond simple approve/reject, agents can collect structured feedback from humans and use it to improve their next action.
Feedback Loops
// Request feedback with a structured formconst feedback = await rekall.execution.requestFeedback({executionId: execution.id,title: 'Review Generated Code',description: 'Please review the generated authentication module',artifacts: [{ type: 'code', content: generatedCode, language: 'typescript' },],form: {fields: [{name: 'quality',type: 'rating',label: 'Code Quality',min: 1,max: 5,},{name: 'issues',type: 'multiselect',label: 'Issues Found',options: ['Missing error handling','Security concern','Performance issue','Style mismatch','Logic error','No issues',],},{name: 'comments',type: 'text',label: 'Additional Comments',required: false,},],},timeout: 86400000, // 24 hours});// Use feedback to improveif (feedback.data.quality < 3) {// Re-generate based on feedbackconst issues = feedback.data.issues.join(', ');const comments = feedback.data.comments || '';// Store feedback as a memory for learningawait rekall.memories.create({type: 'episodic',content: `Code generation feedback: quality ${feedback.data.quality}/5. Issues: ${issues}. ${comments}`,metadata: { tags: ['feedback', 'code-generation'] },importance: 0.8,});}
Correction Patterns
When a human corrects an agent's output, store the correction as both an episodic memory and a preference signal.
// Human provides a correctionconst correction = await rekall.execution.requestFeedback({executionId: execution.id,title: 'Correct Agent Output',artifacts: [{ type: 'code', content: agentOutput }],form: {fields: [{ name: 'correctedCode', type: 'code', label: 'Your corrected version' },{ name: 'explanation', type: 'text', label: 'What was wrong?' },],},});// Store the correction as episodic memoryawait rekall.memories.create({type: 'episodic',content: `Agent output was corrected. Issue: ${correction.data.explanation}`,metadata: {tags: ['correction', 'learning'],original: agentOutput,corrected: correction.data.correctedCode,},importance: 0.85,});// This feeds into preference learning automatically// Next time, Rekall will detect the pattern and suggest a preference
Escalation Patterns
Confidence-Based Escalation
Define escalation rules based on agent confidence. Low-confidence actions escalate to humans; high-confidence ones proceed automatically.
// Define escalation policyconst policy = await rekall.execution.setEscalationPolicy({rules: [{// High confidence: proceed automaticallyconfidenceRange: [0.9, 1.0],action: 'auto-approve',},{// Medium confidence: notify but proceedconfidenceRange: [0.7, 0.9],action: 'notify-and-proceed',notifyVia: ['slack'],},{// Low confidence: pause and askconfidenceRange: [0.4, 0.7],action: 'request-approval',approvers: ['user:adam'],timeout: 3600000,},{// Very low confidence: escalate to seniorconfidenceRange: [0.0, 0.4],action: 'escalate',approvers: ['user:carol'], // ManagernotifyVia: ['email', 'slack'],priority: 'high',},],});// During execution, agent declares its confidenceasync function processTask(task: Task) {const confidence = await evaluateConfidence(task);const decision = await rekall.execution.checkEscalation({executionId: execution.id,action: task.action,confidence,context: {reasoning: 'Based on 3 similar past tasks, 2 succeeded with this approach',alternatives: ['Alternative A', 'Alternative B'],},});if (decision.approved) {await executeAction(task);} else {console.log(`Escalated: ${decision.reason}`);// Execution is paused, waiting for human}}
Escalation feeds learning
Every escalation and its resolution is stored as an episodic memory. Over time, Rekall learns which situations the agent handles well and which need human review, automatically adjusting confidence thresholds.
Combining Execution Memory with HITL
Here is a complete example that combines execution checkpoints, HITL approval, human feedback, and escalation in a single workflow.
1async function codeReviewWorkflow(prNumber: number) {2 const execution = await rekall.execution.create({3 name: `Code review for PR #${prNumber}`,4 config: {5 checkpointInterval: 'per-step',6 onFailure: 'pause',7 },8 });910 await rekall.execution.start({ executionId: execution.id });1112 // Step 1: Automated analysis (no HITL)13 const analysis = await analyzeCode(prNumber);14 await rekall.execution.checkpoint({15 executionId: execution.id,16 step: 1,17 state: { analysis },18 message: `Analysis complete: ${analysis.issues.length} issues found`,19 });2021 // Step 2: Check if critical issues need human review22 const criticalIssues = analysis.issues.filter(i => i.severity === 'critical');2324 if (criticalIssues.length > 0) {25 // HITL: Pause for human review of critical issues26 const review = await rekall.execution.requestFeedback({27 executionId: execution.id,28 title: `Critical issues in PR #${prNumber}`,29 description: `Found ${criticalIssues.length} critical issues that need human review.`,30 artifacts: criticalIssues.map(issue => ({31 type: 'code',32 content: issue.codeSnippet,33 title: issue.title,34 })),35 form: {36 fields: [37 {38 name: 'action',39 type: 'select',40 label: 'Action',41 options: ['Block PR', 'Approve with comments', 'False positive'],42 },43 { name: 'comments', type: 'text', label: 'Review comments' },44 ],45 },46 });4748 await rekall.execution.checkpoint({49 executionId: execution.id,50 step: 2,51 state: { analysis, humanReview: review.data },52 message: `Human review: ${review.data.action}`,53 });5455 if (review.data.action === 'Block PR') {56 await postReview(prNumber, 'changes_requested', review.data.comments);57 await rekall.execution.complete({ executionId: execution.id });58 return;59 }60 }6162 // Step 3: Post automated review63 await postReview(prNumber, 'comment', formatReview(analysis));64 await rekall.execution.complete({65 executionId: execution.id,66 result: {67 issues: analysis.issues.length,68 criticalReviewed: criticalIssues.length,69 },70 });71}
Next Steps
- •Execution State -- Foundation for HITL: checkpoints, pause, resume
- •Workflow Automation -- Add HITL gates to procedural workflows
- •Preference Learning -- HITL feedback feeds into preference detection
- •Agent Memory -- Agent identity and memory for HITL-aware agents
