AutoGen for Conversational Multi-Agent

8 min read Module 2 of 10 Topic 5 of 30

What you'll learn

  • Explain the AutoGen 0.4+ agent model and how ConversableAgent, AssistantAgent, and UserProxyAgent relate to each other
  • Orchestrate multi-agent GroupChat sessions with a GroupChatManager that enforces speaker selection policies
  • Register custom functions as agent tools and configure sandboxed code execution for security-compliant deployments
  • Integrate AutoGen with Azure OpenAI and apply cost caps to prevent runaway spend in production workflows
Building this at your company? For enterprise and company teams taking this to production: book a 30-minute session with our AI engineers for architecture guidance, code review, and a rollout plan for your use case.
Book a Team Session

AutoGen takes a fundamentally different approach to multi-agent orchestration than LangGraph: instead of a directed graph of nodes and edges, AutoGen models coordination as a conversation between agents. Each agent has a persona, a set of capabilities, and the ability to send messages to other agents or a group. This conversational framing maps naturally onto knowledge-worker workflows, code review, research synthesis, compliance checking, where the right answer emerges through structured dialogue rather than a predetermined execution path.

AutoGen 0.4+ Architecture

AutoGen 0.4 reorganized the library around cleaner abstractions. The base class is ConversableAgent, which provides the messaging protocol. AssistantAgent and UserProxyAgent are specializations: the assistant reasons and generates responses using an LLM, while the user proxy executes code and relays results back to the conversation.

import autogen
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager

# WHY azure_deployment instead of model: Azure OpenAI uses deployment
# names that may differ from canonical model names. Specifying both
# ensures the client routes to the correct Azure endpoint, which is
# required for enterprise tenants that isolate deployments by team.
llm_config = {
    "config_list": [
        {
            "model": "gpt-5.6-sol",
            "azure_deployment": "gpt-5.6-sol-enterprise",
            "api_type": "azure",
            "api_key": AZURE_OPENAI_API_KEY,
            "base_url": f"https://{AZURE_RESOURCE}.openai.azure.com/",
            "api_version": "2024-05-01-preview",
        }
    ],
    # WHY cache_seed=42: deterministic seed enables response caching.
    # Identical prompts return cached responses, reducing cost by 60-80%
    # in workflows with repetitive patterns (e.g., per-document analysis).
    "cache_seed": 42,
    "max_tokens": 2048,
}

analyst = AssistantAgent(
    name="FinancialAnalyst",
    system_message=(
        "You are a senior financial analyst. You analyze data, identify trends, "
        "and produce structured findings. Always cite the specific data points "
        "supporting your conclusions. Never fabricate numbers."
    ),
    llm_config=llm_config,
)

# WHY human_input_mode='NEVER' in production: NEVER means the proxy
# executes code automatically without prompting a human. ALWAYS would
# block waiting for terminal input, which is impossible in async pipelines.
executor = UserProxyAgent(
    name="CodeExecutor",
    human_input_mode="NEVER",
    max_consecutive_auto_reply=5,
    code_execution_config={
        "executor": autogen.coding.DockerCommandLineCodeExecutor(
            image="python:3.12-slim",
            timeout=60,
            # WHY work_dir in a temp location: prevents generated code from
            # modifying the host filesystem. Combined with Docker isolation,
            # this gives defense-in-depth against runaway code execution.
            work_dir="/tmp/autogen_sandbox",
        )
    },
)

GroupChat Orchestration and Speaker Selection

GroupChat manages turn-taking across multiple agents. The default speaker selection uses an LLM call, adequate for demos but too unpredictable for enterprise workflows where the wrong agent answering a compliance question could introduce liability.

flowchart TD
    A["GroupChatManager\n(orchestrator)"] --> B{"speaker_selection_func"}
    B -->|"'legal' in message"| C["LegalReviewAgent"]
    B -->|"'code' in message"| D["SoftwareEngineerAgent"]
    B -->|"'finance' in message"| E["FinancialAnalystAgent"]
    B -->|"TERMINATE signal"| F["End Conversation"]
    C -->|"response"| A
    D -->|"response"| A
    E -->|"response"| A
    style A fill:#EEF0F7,stroke:#6366F1,color:#0F172A
    style B fill:#fff7ed,stroke:#f59e0b,color:#0F172A
    style C fill:#f0fdf9,stroke:#0D9488,color:#0F172A
    style D fill:#f0fdf9,stroke:#0D9488,color:#0F172A
    style E fill:#f0fdf9,stroke:#0D9488,color:#0F172A
def deterministic_speaker_selector(last_speaker, groupchat):
    """
    WHY deterministic routing over LLM-based selection:
    in regulated industries, the routing decision itself is auditable.
    A rules-based selector produces the same routing for the same input
    every time, which satisfies audit requirements and eliminates a
    hidden LLM call from the critical path.
    """
    last_message = groupchat.messages[-1]["content"].lower()
    agents_by_name = {a.name: a for a in groupchat.agents}

    if "legal" in last_message or "compliance" in last_message:
        return agents_by_name["LegalReviewAgent"]
    if "```python" in last_message or "execute" in last_message:
        return agents_by_name["CodeExecutor"]
    if "forecast" in last_message or "revenue" in last_message:
        return agents_by_name["FinancialAnalyst"]

    # Fall back to LLM-based selection for ambiguous cases
    return "auto"

group_chat = GroupChat(
    agents=[analyst, executor, legal_agent],
    messages=[],
    max_round=20,          # hard cap on conversation turns = cost ceiling
    speaker_selection_func=deterministic_speaker_selector,
    allow_repeat_speaker=False,  # prevents one agent from monopolizing turns
)

manager = GroupChatManager(groupchat=group_chat, llm_config=llm_config)

Function Registration and Nested Chats

AutoGen’s function registration API lets agents call typed, validated Python functions as tools, the same pattern as OpenAI function calling, but integrated into the conversational loop. Nested chats allow one agent pair to spawn an entire sub-conversation to solve a subtask, returning only the final result to the parent chat.

sequenceDiagram
    participant M as GroupChatManager
    participant A as AnalystAgent
    participant E as ExecutorAgent
    participant API as External API

    M->>A: "Analyze Q3 revenue by region"
    A->>E: request: fetch_revenue_data(quarter="Q3", breakdown="region")
    E->>API: HTTP GET /api/revenue?quarter=Q3&breakdown=region
    API-->>E: JSON response
    E-->>A: structured DataFrame summary
    A->>M: "Q3 revenue analysis: APAC +18%, EMEA -3%, Americas +7%"
    M->>M: evaluate: TERMINATE condition met?
from autogen import register_function

def fetch_revenue_data(quarter: str, breakdown: str) -> dict:
    """
    WHY a typed wrapper instead of letting the agent write raw requests:
    the wrapper validates inputs before hitting the API, masks credentials
    (the agent never sees the API key), and normalizes the response format.
    This is the security boundary between the LLM and your data layer.
    """
    validated_quarter = quarter.upper()
    if validated_quarter not in ["Q1", "Q2", "Q3", "Q4"]:
        raise ValueError(f"Invalid quarter: {quarter}")
    response = revenue_client.get(quarter=validated_quarter, breakdown=breakdown)
    return response.to_dict()

# WHY register on both caller and executor: the caller (analyst) decides
# WHEN to invoke the function; the executor runs it. Registering on both
# ensures the analyst's LLM knows the function exists (via its tools list)
# while the executor has the actual implementation to run.
register_function(
    fetch_revenue_data,
    caller=analyst,
    executor=executor,
    description="Fetch revenue data for a given fiscal quarter, broken down by a dimension such as region or product.",
)

Enterprise Integration: Logging and Cost Controls

Production AutoGen deployments need structured logging of every message exchanged, both for debugging and for compliance audit trails. Hook into AutoGen’s message flow by subclassing ConversableAgent and overriding _process_received_message.

Cost caps are enforced through two levers: max_round on GroupChat (caps LLM calls) and max_tokens on llm_config (caps tokens per call). For hard budget enforcement, instrument your Azure OpenAI deployment with a spending limit, and use AutoGen’s token_count utility after each run to track cumulative cost against a per-workflow budget.

The conversational multi-agent model AutoGen provides excels at knowledge-intensive workflows where the right orchestration path isn’t known in advance. In contrast, when you need guaranteed message delivery and replay between agents running in separate processes, you need an event streaming layer, which is exactly what the next lesson covers with Kafka and Redis Streams.

Knowledge Check

3 questions to test your understanding

1 In an AutoGen GroupChat, the software engineer agent keeps getting selected to speak even when a legal review question is posed. What is the correct fix?

2 An AutoGen agent is generating and executing Python code to query a production database. Security requires that this code cannot make outbound network calls or access the filesystem. What is the correct approach?

3 A GroupChat workflow for financial report generation is spending $4.20 per run due to verbose inter-agent messages. What AutoGen mechanism directly controls this?

Discussion

Questions and notes from learners on this topic

Loading discussion…

Go further with expert guidance

Ready to build production AI?
Talk to our R&D team.

These courses give you the foundation. Our embedded AI teams take you from prototype to production in 30–90 days, with your team, your codebase, your goals. Book a free strategy call to see how we can accelerate your AI initiative.

30 minutes · No obligation · Expert AI engineers, not sales reps

AI Architecture Review

Audit your current stack and identify high-impact improvements

Project Review

Get expert feedback on your AI implementation and codebase

Team Mentoring

Upskill your engineers with hands-on AI coaching sessions

AI Strategy

Define your AI roadmap, prioritization, and implementation plan