Notebooks
M
Microsoft
14 Human Loop

14 Human Loop

agentic-aiai-agents-frameworkcode-samplesagentic-RAGagentic-frameworksemantic-kernelmicrosoft-ai-agents-for-beginnersgenerative-aiai-agents14-microsoft-agent-frameworkautogen

Human-in-the-Loop Workflow with Microsoft Agent Framework

🎯 Learning Objectives

In this notebook, you'll learn how to implement human-in-the-loop workflows using Microsoft Agent Framework's RequestInfoExecutor. This powerful pattern allows you to pause AI workflows to gather human input, making your agents interactive and giving humans control over critical decisions.

πŸ”„ What is Human-in-the-Loop?

Human-in-the-loop (HITL) is a design pattern where AI agents pause execution to request human input before continuing. This is essential for:

  • βœ… Critical decisions - Get human approval before taking important actions
  • βœ… Ambiguous situations - Let humans clarify when AI is uncertain
  • βœ… User preferences - Ask users to choose between multiple options
  • βœ… Compliance & safety - Ensure human oversight for regulated operations
  • βœ… Interactive experiences - Build conversational agents that respond to user input

πŸ—οΈ How It Works in Microsoft Agent Framework

The framework provides three key components for HITL:

  1. RequestInfoExecutor - A special executor that pauses the workflow and emits a RequestInfoEvent
  2. RequestInfoMessage - Base class for typed request payloads sent to humans
  3. RequestResponse - Correlates human responses with original requests using request_id

Workflow Pattern:

Agent detects need for input
    ↓
Sends message to RequestInfoExecutor
    ↓
Workflow pauses & emits RequestInfoEvent
    ↓
Application collects human input (console, UI, etc.)
    ↓
Application sends RequestResponse via send_responses_streaming()
    ↓
Workflow resumes with human input

🏨 Our Example: Hotel Booking with User Confirmation

We'll build upon the conditional workflow by adding human confirmation before suggesting alternative destinations:

  1. User requests a destination (e.g., "Paris")
  2. availability_agent checks if rooms are available
  3. If no rooms β†’ confirmation_agent asks "Would you like to see alternatives?"
  4. Workflow pauses using RequestInfoExecutor
  5. Human responds "yes" or "no" via console input
  6. decision_manager routes based on response:
    • Yes β†’ Show alternative destinations
    • No β†’ Cancel booking request
  7. Display final result

This demonstrates how to give users control over the agent's suggestions!


Let's get started! πŸš€

Step 1: Import Required Libraries

We import the standard Agent Framework components plus human-in-the-loop specific classes:

  • RequestInfoExecutor - Executor that pauses workflow for human input
  • RequestInfoEvent - Event emitted when human input is requested
  • RequestInfoMessage - Base class for typed request payloads
  • RequestResponse - Correlates human responses with requests
  • WorkflowOutputEvent - Event for detecting workflow outputs
[21]
βœ… All imports successful!
πŸ”„ Human-in-the-loop components loaded: RequestInfoExecutor, RequestInfoEvent, RequestResponse

Step 2: Define Pydantic Models for Structured Outputs

These models define the schema that agents will return. We keep all models from the conditional workflow and add:

New for Human-in-the-Loop:

  • HumanFeedbackRequest - Subclass of RequestInfoMessage that defines the request payload sent to humans
    • Contains prompt (question to ask) and destination (context about the unavailable city)
[22]
βœ… Pydantic models defined:
   - BookingCheckResult (availability check)
   - AlternativeResult (alternative suggestion)
   - BookingConfirmation (booking confirmation)
   - ConfirmationQuestion (agent response format) πŸ†•
   - HumanFeedbackRequest (RequestInfoMessage for HITL) πŸ†•

Step 3: Create the Hotel Booking Tool

Same tool from the conditional workflow - checks if rooms are available in the destination.

[23]
βœ… hotel_booking tool created with @ai_function decorator

Step 4: Define Condition Functions for Routing

We need four condition functions for our human-in-the-loop workflow:

From conditional workflow:

  1. has_availability_condition - Routes when hotels ARE available
  2. no_availability_condition - Routes when hotels are NOT available

New for human-in-the-loop: 3. user_wants_alternatives_condition - Routes when user says "yes" to alternatives 4. user_declines_alternatives_condition - Routes when user says "no" to alternatives

[24]
βœ… Condition functions defined:
   - has_availability_condition (routes when rooms exist)
   - no_availability_condition (routes when no rooms)
   - user_wants_alternatives_condition (routes when user says yes) πŸ†•
   - user_declines_alternatives_condition (routes when user says no) πŸ†•

Step 5: Create the Decision Manager Executor

This is the core of the human-in-the-loop pattern! The DecisionManager is a custom Executor that:

  1. Receives human feedback via RequestResponse objects
  2. Processes the user's decision (yes/no)
  3. Routes the workflow by sending messages to appropriate agents

Key features:

  • Uses @handler decorator to expose methods as workflow steps
  • Receives RequestResponse[HumanFeedbackRequest, str] containing both the original request and user's answer
  • Yields simple "yes" or "no" messages that trigger our condition functions
[25]
βœ… DecisionManager executor created with @handler method for human feedback

Step 6: Create Custom Display Executor

Same display executor from conditional workflow - yields final results as workflow output.

[26]
βœ… prepare_human_request executor created with @executor decorator
βœ… display_result executor created with @executor decorator

Step 7: Load Environment Variables

Configure the LLM client (GitHub Models, Azure OpenAI, or OpenAI).

[27]
βœ… Chat client configured with GitHub Models

Step 8: Create AI Agents and Executors

We create six workflow components:

Agents (wrapped in AgentExecutor):

  1. availability_agent - Checks hotel availability using the tool
  2. confirmation_agent - πŸ†• Prepares the human confirmation request
  3. alternative_agent - Suggests alternative cities (when user says yes)
  4. booking_agent - Encourages booking (when rooms available)
  5. cancellation_agent - πŸ†• Handles cancellation message (when user says no)

Special Executors: 6. request_info_executor - πŸ†• RequestInfoExecutor that pauses workflow for human input 7. decision_manager - πŸ†• Custom executor that routes based on human response (already defined above)

[28]

Step 9: Build the Workflow with Human-in-the-Loop

Now we construct the workflow graph with conditional routing including the human-in-the-loop path:

Workflow Structure:

availability_agent (START)
        ↓
   Evaluate conditions
        ↙                    β†˜
[no_availability]        [has_availability]
        ↓                        ↓
confirmation_agent          booking_agent
        ↓                        ↓
prepare_human_request      display_result
        ↓
request_info_executor (PAUSE)
        ↓
decision_manager
   ↙         β†˜
[yes]        [no]
   ↓           ↓
alternative  cancellation
   ↓           ↓
display_result

Key Edges:

  • availability_agent β†’ confirmation_agent (when no rooms)
  • confirmation_agent β†’ prepare_human_request (transform type)
  • prepare_human_request β†’ request_info_executor (pause for human)
  • request_info_executor β†’ decision_manager (always - provides RequestResponse)
  • decision_manager β†’ alternative_agent (when user says "yes")
  • decision_manager β†’ cancellation_agent (when user says "no")
  • availability_agent β†’ booking_agent (when rooms available)
  • All paths end at display_result
[29]

Step 10: Run Test Case 1 - City WITHOUT Availability (Paris with Human Confirmation)

This test demonstrates the full human-in-the-loop cycle:

  1. Request hotel in Paris
  2. availability_agent checks β†’ No rooms
  3. confirmation_agent creates human-facing question
  4. request_info_executor pauses workflow and emits RequestInfoEvent
  5. Application detects event and prompts user in console
  6. User types "yes" or "no"
  7. Application sends response via send_responses_streaming()
  8. decision_manager routes based on response
  9. Final result displayed

Key Pattern:

  • Use workflow.run_stream() for first iteration
  • Use workflow.send_responses_streaming(pending_responses) for subsequent iterations
  • Listen for RequestInfoEvent to detect when human input is needed
  • Listen for WorkflowOutputEvent to capture final results
[ ]

πŸ”„ Starting human-in-the-loop workflow...
============================================================

πŸš€ Starting workflow with request: 'I want to book a hotel in Paris'

⏸️  WORKFLOW PAUSED - Human input requested!
   Request ID: 032c8fce-b9d1-400e-ba8d-afd2248e2926
   Destination: Paris

============================================================
πŸ’¬ QUESTION FOR YOU:
   Unfortunately, there are no rooms available in Paris. Would you like to explore nearby alternative destinations?
============================================================

πŸ“ You answered: yes

πŸ“€ Sending human responses: {'032c8fce-b9d1-400e-ba8d-afd2248e2926': 'yes'}

πŸš€ Starting workflow with request: 'I want to book a hotel in Paris'

πŸ“ You answered: yes

πŸ“€ Sending human responses: {'032c8fce-b9d1-400e-ba8d-afd2248e2926': 'yes'}

πŸš€ Starting workflow with request: 'I want to book a hotel in Paris'

⏸️  WORKFLOW PAUSED - Human input requested!
   Request ID: cf48dad0-ee5e-4f60-8806-341a7a292bd4
   Destination: Paris

============================================================
πŸ’¬ QUESTION FOR YOU:
   I'm sorry to inform you that there are no available hotel rooms in Paris. Would you like me to suggest nearby alternative destinations?
============================================================

πŸ“ You answered: 

πŸ“€ Sending human responses: {'cf48dad0-ee5e-4f60-8806-341a7a292bd4': ''}

πŸš€ Starting workflow with request: 'I want to book a hotel in Paris'

πŸ“ You answered: 

πŸ“€ Sending human responses: {'cf48dad0-ee5e-4f60-8806-341a7a292bd4': ''}

πŸš€ Starting workflow with request: 'I want to book a hotel in Paris'

Step 11: Run Test Case 2 - City WITH Availability (Stockholm - No Human Input Needed)

This test demonstrates the direct path when rooms are available:

  1. Request hotel in Stockholm
  2. availability_agent checks β†’ Rooms available βœ…
  3. booking_agent suggests booking
  4. display_result shows confirmation
  5. No human input required!

The workflow bypasses the human-in-the-loop path entirely when rooms are available.

[ ]

Key Takeaways and Human-in-the-Loop Best Practices

βœ… What You've Learned:

1. RequestInfoExecutor Pattern

The human-in-the-loop pattern in Microsoft Agent Framework uses three key components:

  • RequestInfoExecutor - Pauses workflow and emits events
  • RequestInfoMessage - Base class for typed request payloads (subclass this!)
  • RequestResponse - Correlates human responses with original requests

Critical Understanding:

  • RequestInfoExecutor does NOT collect input itself - it only pauses the workflow
  • Your application code must listen for RequestInfoEvent and collect input
  • You must call send_responses_streaming() with a dict mapping request_id to user's answer

2. Streaming Execution Pattern

# First iteration
stream = workflow.run_stream(initial_request)

# Subsequent iterations (after human input)
stream = workflow.send_responses_streaming(pending_responses)

# Always process events
events = [event async for event in stream]

3. Event-Driven Architecture

Listen for specific events to control workflow:

  • RequestInfoEvent - Human input is needed (workflow paused)
  • WorkflowOutputEvent - Final result is available (workflow complete)
  • WorkflowStatusEvent - State changes (IN_PROGRESS, IDLE_WITH_PENDING_REQUESTS, etc.)

4. Custom Executors with @handler

The DecisionManager demonstrates how to create executors that:

  • Use @handler decorator to expose methods as workflow steps
  • Receive typed messages (e.g., RequestResponse[HumanFeedbackRequest, str])
  • Route workflow by sending messages to other executors
  • Access context via WorkflowContext

5. Conditional Routing with Human Decisions

You can create condition functions that evaluate human responses:

def user_wants_alternatives_condition(message: Any) -> bool:
    response_text = message.agent_run_response.text.lower()
    return response_text == "yes"

🎯 Real-World Applications:

  1. Approval Workflows

    • Get manager approval before processing expense reports
    • Require human review before sending automated emails
    • Confirm high-value transactions before execution
  2. Content Moderation

    • Flag questionable content for human review
    • Ask moderators to make final decision on edge cases
    • Escalate to humans when AI confidence is low
  3. Customer Service

    • Let AI handle routine questions automatically
    • Escalate complex issues to human agents
    • Ask customer if they want to speak to a human
  4. Data Processing

    • Ask humans to resolve ambiguous data entries
    • Confirm AI interpretations of unclear documents
    • Let users choose between multiple valid interpretations
  5. Safety-Critical Systems

    • Require human confirmation before irreversible actions
    • Get approval before accessing sensitive data
    • Confirm decisions in regulated industries (healthcare, finance)
  6. Interactive Agents

    • Build conversational bots that ask follow-up questions
    • Create wizards that guide users through complex processes
    • Design agents that collaborate with humans step-by-step

πŸ”„ Comparison: With vs Without Human-in-the-Loop

FeatureConditional WorkflowHuman-in-the-Loop Workflow
ExecutionSingle workflow.run()Loop with run_stream() + send_responses_streaming()
User InputNone (fully automated)Interactive prompts via input() or UI
ComponentsAgents + Executors+ RequestInfoExecutor + DecisionManager
EventsAgentExecutorResponse onlyRequestInfoEvent, WorkflowOutputEvent, etc.
PausingNo pausingWorkflow pauses at RequestInfoExecutor
Human ControlNo human controlHumans make key decisions
Use CaseAutomationCollaboration & oversight

πŸš€ Advanced Patterns:

Multiple Human Decision Points

You can have multiple RequestInfoExecutor nodes in the same workflow:

.add_edge(agent1, request_info_1)  # First human decision
.add_edge(decision_manager_1, agent2)
.add_edge(agent2, request_info_2)  # Second human decision
.add_edge(decision_manager_2, final_agent)

Timeout Handling

Implement timeouts for human responses:

import asyncio

try:
    answer = await asyncio.wait_for(
        asyncio.to_thread(input, "Enter yes/no: "),
        timeout=60.0
    )
except asyncio.TimeoutError:
    answer = "no"  # Default to safe option

Rich UI Integration

Instead of input(), integrate with web UI, Slack, Teams, etc.:

if isinstance(event, RequestInfoEvent):
    # Send notification to user's preferred channel
    await slack_client.send_message(
        user_id=current_user,
        text=event.data.prompt,
        request_id=event.request_id
    )

Conditional Human-in-the-Loop

Only ask for human input in specific situations:

def needs_human_approval_condition(message: Any) -> bool:
    # Only route to human if confidence is low or value is high
    if result.confidence < 0.7 or result.value > 10000:
        return True
    return False

⚠️ Best Practices:

  1. Always Subclass RequestInfoMessage

    • Provides type safety and validation
    • Enables rich context for UI rendering
    • Clarifies intent of each request type
  2. Use Descriptive Prompts

    • Include context about what you're asking
    • Explain consequences of each choice
    • Keep questions simple and clear
  3. Handle Unexpected Input

    • Validate user responses
    • Provide defaults for invalid input
    • Give clear error messages
  4. Track Request IDs

    • Use the correlation between request_id and responses
    • Don't try to manage state manually
  5. Design for Non-Blocking

    • Don't block threads waiting for input
    • Use async patterns throughout
    • Support concurrent workflow instances

πŸ“š Related Concepts:

  • Agent Middleware - Intercept agent calls (previous notebook)
  • Workflow State Management - Persist workflow state between runs
  • Multi-Agent Collaboration - Combine human-in-the-loop with agent teams
  • Event-Driven Architectures - Build reactive systems with events

πŸŽ“ Congratulations!

You've mastered human-in-the-loop workflows with Microsoft Agent Framework! You now know how to:

  • βœ… Pause workflows to gather human input
  • βœ… Use RequestInfoExecutor and RequestInfoMessage
  • βœ… Handle streaming execution with events
  • βœ… Create custom executors with @handler
  • βœ… Route workflows based on human decisions
  • βœ… Build interactive AI agents that collaborate with humans

This is a critical pattern for building trustworthy, controllable AI systems! πŸš€