Notebooks
M
Microsoft
14 Middleware

14 Middleware

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

Hotel Booking with Priority Member Middleware

This notebook demonstrates function-based middleware using the Microsoft Agent Framework. We build upon the conditional workflow example by adding a middleware layer that gives priority members special privileges.

What You'll Learn:

  1. Function-Based Middleware: Intercept and modify function results
  2. Context Access: Read and modify context.result after execution
  3. Business Logic Implementation: Priority member benefits
  4. Result Override: Change function outcomes based on user status
  5. Same Workflow, Different Outcomes: Middleware-driven behavior changes

Workflow Architecture with Middleware:

User Input: "I want to book a hotel in Paris"
                    โ†“
        [availability_agent]
        - Calls hotel_booking tool
        - ๐ŸŒŸ priority_check middleware intercepts
        - Checks user membership status
        - IF priority + no rooms โ†’ Override to available!
        - Returns BookingCheckResult
                    โ†“
        Conditional Routing
           /                    \
    [has_availability]    [no_availability]
          โ†“                      โ†“
    [booking_agent]        [alternative_agent]
    (Priority override!)   (Regular users)
          โ†“                      โ†“
       [display_result executor]

Key Difference from Conditional Workflow:

Without Middleware (14-conditional-workflow.ipynb):

  • Paris has no rooms โ†’ Route to alternative_agent

With Middleware (this notebook):

  • Regular user + Paris โ†’ No rooms โ†’ Route to alternative_agent
  • Priority user + Paris โ†’ ๐ŸŒŸ Middleware overrides! โ†’ Available โ†’ Route to booking_agent

Prerequisites:

  • Microsoft Agent Framework installed
  • Understanding of conditional workflows (see 14-conditional-workflow.ipynb)
  • GitHub token or OpenAI API key
  • Basic understanding of middleware patterns
[2]
โœ… All imports successful!

Step 1: Define Pydantic Models for Structured Outputs

These models define the schema that agents will return. We've added a priority_override field to track when middleware modifies the availability result.

[3]
โœ… Pydantic models defined:
   - BookingCheckResult (availability check with priority_override)
   - AlternativeResult (alternative suggestion)
   - BookingConfirmation (booking confirmation)

Step 2: Define Priority Members Database

For this demo, we'll simulate a priority membership database. In production, this would query a real database or API.

Priority Members:

  • alice@example.com - VIP member
  • bob@example.com - Premium member
  • priority_user - Test account
[4]
โœ… Priority members database created
   Priority members: 3 users

Step 3: Create the Hotel Booking Tool

Same as the conditional workflow, but now it will be intercepted by middleware!

[5]
โœ… hotel_booking tool created with @ai_function decorator

Step 4: ๐ŸŒŸ Create Priority Check Middleware (THE KEY FEATURE!)

This is the core functionality of this notebook. The middleware:

  1. Intercepts the hotel_booking function call
  2. Executes the function normally by calling next(context)
  3. Inspects the result in context.result
  4. Overrides the result if user is priority and no rooms available
  5. Returns the modified result back to the agent

Key Pattern:

async def my_middleware(context, next):
    await next(context)  # Execute function
    # Now context.result contains the function's output
    if some_condition:
        context.result = new_value  # Override!
[6]
โœ… priority_check_middleware created
   - Intercepts hotel_booking function
   - Overrides availability for priority members

Step 5: Define Condition Functions for Routing

Same condition functions as the conditional workflow - they inspect the structured output to determine routing.

[7]
โœ… Condition functions defined

Step 6: Create Custom Display Executor

Same executor as before - displays the final workflow output.

[8]
โœ… display_result executor created

Step 7: Load Environment Variables

Configure the LLM client (GitHub Models or OpenAI).

[10]

Step 8: Create AI Agents with Middleware

KEY DIFFERENCE: When creating the availability_agent, we pass the middleware parameter!

This is how we inject the priority_check_middleware into the agent's function invocation pipeline.

[ ]

Step 9: Build the Workflow

Same workflow structure as before - conditional routing based on availability.

[12]

Step 10: Test Case 1 - Regular User in Paris (No Override)

A regular user tries to book Paris โ†’ No rooms โ†’ Routes to alternative_agent

[13]

Step 11: Test Case 2 - ๐ŸŒŸ Priority User in Paris (WITH Override!)

A priority member tries to book Paris โ†’ No rooms initially โ†’ ๐ŸŒŸ Middleware overrides! โ†’ Routes to booking_agent

This is the key demonstration of middleware power!

[14]

Step 12: Test Case 3 - Priority User in Stockholm (Already Available)

Priority user tries Stockholm โ†’ Rooms available โ†’ No override needed โ†’ Routes to booking_agent

This shows that middleware only acts when needed!

[15]

Key Takeaways and Middleware Concepts

โœ… What You've Learned:

1. Function-Based Middleware Pattern

Middleware intercepts function calls using a simple async function:

async def my_middleware(
    context: FunctionInvocationContext,
    next: Callable,
) -> None:
    # Before function execution
    print("Intercepting...")
    
    # Execute the function
    await next(context)
    
    # After function execution - inspect result
    if context.result:
        # Modify result if needed
        context.result = modified_value

2. Context Access and Result Override

  • context.function - Access the function being called
  • context.arguments - Read function arguments
  • context.kwargs - Access additional parameters
  • await next(context) - Execute the function
  • context.result - Read/modify the function's output

3. Business Logic Implementation

Our middleware implements priority member benefits:

  • Regular users: No modifications, standard workflow
  • Priority users: Override "no availability" โ†’ "available"
  • Conditional logic: Only overrides when needed

4. Same Workflow, Different Outcomes

The power of middleware:

  • โœ… No changes to the workflow structure
  • โœ… No changes to the tool function
  • โœ… No changes to conditional routing logic
  • โœ… Just middleware โ†’ Different behavior!

๐Ÿš€ Real-World Applications:

  1. VIP/Premium Features

    • Override rate limits for premium users
    • Provide priority access to resources
    • Unlock premium features dynamically
  2. A/B Testing

    • Route users to different implementations
    • Test new features with specific users
    • Gradual feature rollouts
  3. Security & Compliance

    • Audit function calls
    • Block sensitive operations
    • Enforce business rules
  4. Performance Optimization

    • Cache results for specific users
    • Skip expensive operations when possible
    • Dynamic resource allocation
  5. Error Handling & Retry

    • Catch and handle errors gracefully
    • Implement retry logic
    • Fallback to alternative implementations
  6. Logging & Monitoring

    • Track function execution times
    • Log parameters and results
    • Monitor usage patterns

๐Ÿ”‘ Key Differences from Decorators:

FeatureDecoratorMiddleware
ScopeSingle functionAll functions in agent
FlexibilityFixed at definitionDynamic at runtime
ContextLimitedFull agent context
CompositionMultiple decoratorsMiddleware pipeline
Agent-AwareNoYes (access to agent state)

๐Ÿ“š When to Use Middleware:

โœ… Use middleware when:

  • You need to modify behavior based on user/session state
  • You want to apply logic to multiple functions
  • You need access to agent-level context
  • You're implementing cross-cutting concerns (logging, auth, etc.)

โŒ Don't use middleware when:

  • Simple input validation (use Pydantic)
  • Function-specific logic (keep in function)
  • One-time modifications (just change the function)

๐ŸŽ“ Advanced Patterns:

# Multiple middleware (execution order matters!)
middleware=[
    logging_middleware,      # Logs first
    auth_middleware,         # Then checks auth
    cache_middleware,        # Then checks cache
    rate_limit_middleware,   # Then rate limits
    priority_check_middleware  # Finally priority check
]

# Conditional middleware execution
async def conditional_middleware(context, next):
    if should_execute(context):
        await next(context)
        # Modify result
    else:
        # Skip execution entirely
        context.result = cached_value

๐Ÿ”— Related Concepts:

  • Agent Middleware: Intercepts agent.run() calls
  • Function Middleware: Intercepts tool function calls (what we used!)
  • Middleware Pipeline: Chain of middleware executing in order
  • Context Propagation: Pass state through middleware chain