AutoGen conversable agents are stateful objects in Microsoft’s AutoGen library that exchange messages to accomplish tasks through LLM inference, tool calls, or human input. They form the foundational orchestration primitive in the AutoGen & Microsoft Agent Framework ecosystem, letting you compose autonomous and interactive workflows without hardcoding control flow.
What a Conversable Agent Actually Is
The ConversableAgent class is the base abstraction. It is not an LLM wrapper alone; it is a message handler with an optional LLM client, an optional human interface, and an optional code executor. Subclasses like AssistantAgent (LLM-driven) and UserProxyAgent (human or tool proxy) inherit the same message protocol, which means they are interchangeable in a conversation graph.
from autogen import ConversableAgent, AssistantAgent, UserProxyAgent
The key property: every agent implements generate_reply and receive. When agent A sends a message to agent B, B’s receive appends to its message history and triggers a reply computation. The reply can be a string, a function call, a code block, or a signal to terminate. This uniformity is why AutoGen conversable agents can be wired into group chats, nested chats, and human-in-the-loop pipelines without custom adapters.
Message history is local
Each agent keeps its own chat_messages dictionary keyed by sender. This is not a shared global state. If you want shared context, you propagate messages explicitly or use a group chat manager that broadcasts. Engineers often assume the framework maintains a single transcript; it does not. That design lets an agent hold private reasoning traces that are never sent to a weaker model.
How the Conversation Loop Works
A conversation starts with initiate_chat. The initiating agent sends the first message and then control alternates based on registered reply functions and max_consecutive_auto_reply.
user_proxy.initiate_chat(assistant, message="Ping")
Under the hood, initiate_chat calls send on the recipient, which calls receive, which calls generate_reply. The reply is sent back, and the loop continues until a termination condition. AutoGen conversable agents do not implicitly yield to a scheduler; they block on the reply chain unless you run them in async mode.
Reply registration
You can register custom reply functions with register_reply. This lets you short-circuit LLM calls for deterministic logic, caching, or guardrails.
def echo(recipient, messages, sender, config):
return True, "echo: " + messages[-1]["content"]
agent.register_reply(Agent, reply=echo, position=0)
Position controls ordering. A reply function returning (True, content) stops further processing. This is the extension point that makes the abstraction useful beyond chat: you can inject a retriever, a validator, or a cost limiter as a reply function.
Human input modes
UserProxyAgent supports human_input_mode: ALWAYS, TERMINATE, or NEVER. In NEVER mode it acts as an autonomous tool runner. In ALWAYS it blocks for stdin. This is critical for safety in code execution scenarios where you want a person to approve before a generated script runs.
Termination
Agents stop when is_termination_msg matches, when max_consecutive_auto_reply is exceeded, or when a human interrupts. Without explicit termination, two LLM agents will burn tokens until context limit. In production, set max_consecutive_auto_reply to a small integer and use a custom is_termination_msg that checks for a structured done flag.
Why Engineers Use Them
AutoGen conversable agents solve a real problem: LLM control flow is messy. You need branching, retries, tool use, and human checkpoints. Encoding that in a single prompt is fragile.
Separation of concerns
One agent can own the planner role, another the executor. Each has its own system message and LLM config. You can swap a cheap model for the planner and a strong model for the coder. Because the message contract is identical, swapping agents is a one-line change.
Human-in-the-loop without refactoring
Because UserProxyAgent is just another conversable agent, inserting a human is a config change, not an architecture change. You flip human_input_mode and the same pipeline now pauses for approval.
Group chats and nested delegation
GroupChat extends the model: a manager selects the next speaker. This is still built on the same ConversableAgent message passing, proving the abstraction scales. register_nested_chats lets an agent spawn a sub-conversation (e.g., a critic loop) and return a synthesized result to the parent chat.
Concrete Example: Two-Agent Math Solver
Below is a minimal runnable pattern (using AutoGen 0.2.x). The user proxy executes Python; the assistant reasons.
from autogen import AssistantAgent, UserProxyAgent
llm_config = {
"model": "gpt-4o-mini",
"api_key": "YOUR_KEY",
}
assistant = AssistantAgent(
name="assistant",
llm_config=llm_config,
system_message="You are a math tutor. Write Python to solve."
)
user_proxy = UserProxyAgent(
name="user_proxy",
human_input_mode="NEVER",
code_execution_config={"work_dir": "tmp"},
max_consecutive_auto_reply=2,
)
user_proxy.initiate_chat(
assistant,
message="Solve for x: 3*x + 7 = 22. Show code."
)
The assistant returns a code block. The user proxy executes it, posts the stdout, and the assistant explains. After two auto replies, the chat terminates. This pattern is the canonical demonstration of how AutoGen conversable agents delegate tool use to a non-LLM actor.
Configuring the LLM Backend
AutoGen expects an OpenAI-style client. You can point llm_config["base_url"] at any compatible gateway. For example, an OpenAI-compatible endpoint such as n4n.ai addresses 240+ models behind one URL and will automatically fall back when a provider is rate-limited, which simplifies multi-model routing inside conversable agents.
llm_config = {
"model": "openai/gpt-4o",
"base_url": "https://api.n4n.ai/v1",
"api_key": "YOUR_N4N_KEY",
"extra_body": {"provider_hint": "openai"} # forwarded cache-control hints
}
This keeps your agent code unchanged while the gateway handles degradation and honors client routing directives. If you run many agents in parallel, centralizing model selection at the gateway reduces config drift.
Message Structure and Serialization
A message is a dict: {"role": "user", "content": "...", "name": "sender"}. Agents store these in chat_messages. You can serialize the dict to JSON for audit or replay. Note that function call messages include function_call or tool_calls fields depending on the model. When debugging, print agent.chat_messages[other_agent.name] to see exactly what the agent believes the conversation was.
Common Misconceptions
“They’re just chatbots”
No. A conversable agent can be a silent tool runner, a deterministic router, or a group chat manager. The “conversable” label describes the message protocol, not a UI.
“They always need a human”
UserProxyAgent with human_input_mode="NEVER" and code_execution_config enabled runs fully autonomous. Many production pipelines use zero human touches.
“They manage long-term memory”
Wrong. Each agent’s history is the conversation transcript. There is no vector store, no summarization by default. If you need persistence across sessions, you implement it via register_reply or external storage. AutoGen conversable agents are stateless across process restarts unless you persist chat_messages.
“Conversable means conversational UI”
The term refers to agents that can hold a conversation with other agents programmatically. There is no requirement of a chat frontend. You can call initiate_chat from a cron job.
“They are the same as LangGraph or Semantic Kernel”
Different abstraction. LangGraph is a state machine; AutoGen conversable agents are message-driven objects with reply registration. You can build a graph on top, but the primitive is the agent, not the edge.
Operational Gotchas
Token usage accumulates per agent. Set max_consecutive_auto_reply conservatively. Use silent flag in initiate_chat to reduce logging noise in production. And always sandbox code execution—UserProxyAgent will run generated Python on your host if you let it.
If you need to trace costs, meter at the gateway level. Since each LLM call goes through your llm_config, a per-token usage metering layer gives you exact attribution without instrumenting every agent. AutoGen conversable agents do not emit billing events; they emit HTTP requests.
When to Avoid Them
If your workflow is a strict DAG with no dynamic speaker selection, a simple function chain is lighter. The agent abstraction shines when the next step is not known upfront, or when you need pluggable human checkpoints. Do not reach for group chat because it looks cool; it multiplies token spend quadratically with participants.
That is the core mental model. Treat AutoGen conversable agents as actors that pass typed messages, not as prompts with a chat history.