Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Microsoft Autogen SequentialRoutedAgent

From Leeroopedia
Revision as of 11:33, 16 February 2026 by Admin (talk | contribs) (Auto-imported from implementations/Microsoft_Autogen_SequentialRoutedAgent.md)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Key Value
id Microsoft_Autogen_SequentialRoutedAgent
source Microsoft_Autogen
category Core Agent

Overview

Description

The SequentialRoutedAgent is a specialized subclass of autogen_core.RoutedAgent that enforces sequential processing of specific message types using a First-In-First-Out (FIFO) lock. This ensures that messages of designated types are processed in the exact order they are received, preventing race conditions in distributed multi-agent systems.

The class implements:

  • FIFOLock: A custom lock ensuring coroutines acquire the lock in request order
  • Selective Sequential Processing: Only specified message types use the FIFO lock
  • Concurrent Processing: Non-sequential message types are processed normally without locking
  • Order Preservation: Critical for maintaining conversation coherence in group chats

This is essential for group chat scenarios where message order matters. For example, when multiple agents respond simultaneously, their responses must be processed in the order they were published to maintain a coherent conversation thread.

Usage

SequentialRoutedAgent is primarily used as a base class for agents that participate in distributed group chat systems, particularly ChatAgentContainer. It ensures that group chat control messages (start, request, response, etc.) are processed sequentially even when multiple messages arrive concurrently.

Key use cases:

  • Agent containers in group chat teams
  • Any agent that needs to maintain strict message ordering
  • Distributed systems where race conditions on message handling would cause issues

The selective locking approach means only critical message types incur synchronization overhead, while other messages can be processed concurrently for better performance.

Code Reference

Source Location

  • Repository: https://github.com/microsoft/autogen
  • File Path: /tmp/kapso_repo_2mr4n2g4/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_sequential_routed_agent.py
  • Lines: 1-73

Signature

class FIFOLock:
    """A lock that ensures coroutines acquire the lock in the order they request it."""

    def __init__(self) -> None:
        self._queue = asyncio.Queue[asyncio.Event]()
        self._locked = False

    async def acquire(self) -> None:
        ...

    def release(self) -> None:
        ...


class SequentialRoutedAgent(RoutedAgent):
    """A subclass of RoutedAgent that ensures messages of certain types
    are processed sequentially using a FIFO lock."""

    def __init__(self, description: str, sequential_message_types: Sequence[type[Any]]) -> None:
        ...

    async def on_message_impl(self, message: Any, ctx: MessageContext) -> Any | None:
        ...

Import

from autogen_agentchat.teams._group_chat import SequentialRoutedAgent

I/O Contract

FIFOLock

Method Parameters Returns Description
__init__ None None Initialize the FIFO lock with empty queue
acquire None None (async) Acquire lock in FIFO order; waits if lock is held
release None None Release lock; grants to next waiting coroutine

SequentialRoutedAgent

Parameter Type Required Description
description str Yes Description of the agent (passed to RoutedAgent)
sequential_message_types Sequence[type[Any]] Yes Message types that require sequential processing with FIFO lock

Message Processing Behavior

Message Type Processing Mode Description
In sequential_message_types Sequential (with FIFO lock) Processed in exact arrival order, one at a time
Not in sequential_message_types Concurrent (no lock) Processed immediately without waiting

Usage Examples

Basic Usage

from autogen_agentchat.teams._group_chat import SequentialRoutedAgent
from autogen_core import MessageContext


class MyGroupChatAgent(SequentialRoutedAgent):
    def __init__(self):
        super().__init__(
            description="My agent",
            sequential_message_types=[
                GroupChatStart,
                GroupChatRequestPublish,
                GroupChatReset
            ]
        )

    async def handle_message(self, message: Any, ctx: MessageContext):
        # If message type is in sequential_message_types,
        # this will be called with FIFO lock held
        # Otherwise, called without lock
        pass

ChatAgentContainer Example

from autogen_agentchat.teams._group_chat import SequentialRoutedAgent
from autogen_agentchat.teams._group_chat._events import (
    GroupChatStart,
    GroupChatRequestPublish,
    GroupChatReset,
    GroupChatAgentResponse,
    GroupChatTeamResponse
)


class ChatAgentContainer(SequentialRoutedAgent):
    def __init__(self, parent_topic_type: str, output_topic_type: str, agent, message_factory):
        # Ensure these message types are processed sequentially
        super().__init__(
            description=agent.description,
            sequential_message_types=[
                GroupChatStart,           # Start messages must be ordered
                GroupChatRequestPublish,  # Request processing must be sequential
                GroupChatReset,           # Reset must happen in order
                GroupChatAgentResponse,   # Agent responses must maintain order
                GroupChatTeamResponse     # Team responses must maintain order
            ]
        )
        self._agent = agent
        # ... rest of initialization ...

FIFO Lock Behavior

import asyncio


async def demonstrate_fifo_lock():
    from autogen_agentchat.teams._group_chat._sequential_routed_agent import FIFOLock

    lock = FIFOLock()
    results = []

    async def worker(id: int, delay: float):
        # Workers request lock in order: 1, 2, 3
        # They will acquire in same order regardless of processing time
        await lock.acquire()
        try:
            await asyncio.sleep(delay)
            results.append(id)
        finally:
            lock.release()

    # Start workers - they request lock in order
    await asyncio.gather(
        worker(1, 0.1),  # Requests first, completes slow
        worker(2, 0.01), # Requests second, would complete fast
        worker(3, 0.01)  # Requests third, would complete fast
    )

    # Results are [1, 2, 3] - FIFO order preserved
    assert results == [1, 2, 3]

Selective Sequential Processing

from autogen_agentchat.teams._group_chat import SequentialRoutedAgent
from autogen_core import event


class MyAgent(SequentialRoutedAgent):
    def __init__(self):
        super().__init__(
            description="Selective agent",
            sequential_message_types=[CriticalMessage]
        )

    @event
    async def handle_critical(self, message: CriticalMessage, ctx):
        # This uses FIFO lock - processed sequentially
        await process_critically(message)

    @event
    async def handle_normal(self, message: NormalMessage, ctx):
        # This does NOT use FIFO lock - processed concurrently
        await process_normally(message)


async def test_selective_processing():
    agent = MyAgent()

    # These are processed concurrently (no lock)
    await asyncio.gather(
        agent.handle_normal(NormalMessage(), ctx),
        agent.handle_normal(NormalMessage(), ctx),
        agent.handle_normal(NormalMessage(), ctx)
    )

    # These are processed sequentially (FIFO lock)
    await asyncio.gather(
        agent.handle_critical(CriticalMessage(), ctx),
        agent.handle_critical(CriticalMessage(), ctx),
        agent.handle_critical(CriticalMessage(), ctx)
    )

Exception Safety

from autogen_agentchat.teams._group_chat import SequentialRoutedAgent


class SafeAgent(SequentialRoutedAgent):
    async def on_message_impl(self, message, ctx):
        if any(isinstance(message, t) for t in self._sequential_message_types):
            # Lock is acquired
            await self._fifo_lock.acquire()
            try:
                # Even if this raises, lock is released
                return await super().on_message_impl(message, ctx)
            finally:
                # CRITICAL: Lock is always released
                self._fifo_lock.release()
        else:
            # No lock needed
            return await super().on_message_impl(message, ctx)


async def test_exception_safety():
    agent = SafeAgent(
        description="test",
        sequential_message_types=[ErrorMessage]
    )

    try:
        # Even if this raises exception...
        await agent.on_message_impl(ErrorMessage(), ctx)
    except Exception:
        pass

    # ...the lock is still released for next message
    await agent.on_message_impl(ErrorMessage(), ctx)  # Won't deadlock

Performance Comparison

import time


async def benchmark_sequential_vs_concurrent():
    class TestAgent(SequentialRoutedAgent):
        def __init__(self, sequential: bool):
            types = [TestMessage] if sequential else []
            super().__init__(description="test", sequential_message_types=types)

        async def handle(self, message, ctx):
            await asyncio.sleep(0.01)  # Simulate work

    # Sequential processing
    agent_seq = TestAgent(sequential=True)
    start = time.time()
    await asyncio.gather(*[
        agent_seq.on_message_impl(TestMessage(), ctx)
        for _ in range(10)
    ])
    sequential_time = time.time() - start
    print(f"Sequential: {sequential_time:.2f}s")

    # Concurrent processing
    agent_conc = TestAgent(sequential=False)
    start = time.time()
    await asyncio.gather(*[
        agent_conc.on_message_impl(TestMessage(), ctx)
        for _ in range(10)
    ])
    concurrent_time = time.time() - start
    print(f"Concurrent: {concurrent_time:.2f}s")

    # Sequential is ~10x slower (processes one at a time)
    # Concurrent is ~10x faster (processes all in parallel)

Custom Sequential Types

from pydantic import BaseModel


class OrderedMessage(BaseModel):
    """Message that must be processed in order."""
    sequence_number: int
    content: str


class UnorderedMessage(BaseModel):
    """Message that can be processed concurrently."""
    content: str


class CustomAgent(SequentialRoutedAgent):
    def __init__(self):
        super().__init__(
            description="Custom agent",
            sequential_message_types=[
                OrderedMessage,  # Must be sequential
                # UnorderedMessage not included - will be concurrent
            ]
        )

    @event
    async def handle_ordered(self, message: OrderedMessage, ctx):
        # Guaranteed to process in arrival order
        print(f"Processing ordered message {message.sequence_number}")

    @event
    async def handle_unordered(self, message: UnorderedMessage, ctx):
        # Can process concurrently
        print(f"Processing unordered message")

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment