Course Content
AutoGen Essentials
7 sections · 28 lessons
What’s the difference between an AssistantAgent, UserProxyAgent, and tool-enabled agent in AutoGen?
What you need to know
The three roles in 0.4+
| Agent | Calls an LLM? | Runs things? | Typical job |
|---|---|---|---|
AssistantAgent | Yes | Runs its own Python tools | Plan, write, answer, call APIs |
UserProxyAgent | No | No | Ask a person; returns what they type |
CodeExecutorAgent | Optional | Runs code blocks in an executor | Execute model-written code in a sandbox |
How a tool call runs in AssistantAgent
By default max_tool_iterations=1: the model may request one round of tool calls, the agent runs them, and then the turn ends. With reflect_on_tool_use=True the agent makes one more model call to turn the raw result into a sentence; otherwise it returns a ToolCallSummaryMessage formatted by tool_call_summary_format. Raise max_tool_iterations when the agent must chain calls.
A data-analysis agent that runs code in a sandbox
1from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent, UserProxyAgent2from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination3from autogen_agentchat.teams import RoundRobinGroupChat4from autogen_ext.code_executors.docker import DockerCommandLineCodeExecutor56async def analyse(client, question: str):7 async with DockerCommandLineCodeExecutor(work_dir="work", timeout=60) as sandbox:8 analyst = AssistantAgent("analyst", model_client=client,9 system_message="Write Python in one code block to answer. "10 "After it runs, explain the result and say DONE.")11 runner = CodeExecutorAgent("runner", code_executor=sandbox)12 team = RoundRobinGroupChat([analyst, runner],13 termination_condition=TextMentionTermination("DONE") | MaxMessageTermination(12))14 return await team.run(task=question)1516human = UserProxyAgent("human", input_func=input) # add to a team when a person must answerThe analyst decides; the runner executes; neither can do the other's job. CodeExecutorAgent warns if you give it no approval_func, because then any code it receives runs automatically.
The legacy 0.2 version
1import autogen # legacy 0.2 API2user_proxy = autogen.UserProxyAgent(3 "user_proxy",4 human_input_mode="TERMINATE", # ALWAYS | TERMINATE | NEVER5 code_execution_config={"work_dir": "work", "use_docker": True},6)7autogen.register_function(get_sales, caller=assistant, executor=user_proxy,8 description="Monthly sales for a city.")In 0.2 the assistant only requested the tool (it had the schema); the proxy ran it (it had the function). human_input_mode="ALWAYS" asks a person every turn, "TERMINATE" only at the end, "NEVER" runs fully automatic.
A real-life example
An e-commerce analytics team asks, "Which 5 cities had the biggest drop in Diwali sales versus last year?" The analyst writes a pandas script over sales_2024.csv and sales_2025.csv. The runner executes it in Docker and returns a KeyError: 'city_name'. The analyst reads the traceback, sees the column is called city, fixes it, and the second run prints the table. The whole exchange is four messages.
Without a separate executor, the model would have described a table it never computed. The team adds a UserProxyAgent only for questions that trigger a data export, so a human approves before any file leaves the sandbox.
Follow-up questions to expect
- "Why keep decide and execute in different agents if 0.4 lets one agent do both?" — For safe, read-only tools one agent is fine. For destructive tools or code, a separate executor lets you sandbox it, add an approval step, and audit exactly what ran.
- "How does a human join a 0.4 team without blocking?" —
UserProxyAgentblocks the team while waiting. For slow humans, end the run withHandoffTerminationorSourceMatchTermination, save state, and callrunagain when the person replies. - "Can
CodeExecutorAgentwrite code too?" — In recent versions, yes: give it amodel_clientand it generates and runs code, withmax_retries_on_errorfor fix attempts.