Spot the bug in this nested AutoGen chat that never terminates
Click any words you think contain an error. Click again to unmark.
The UserProxyAgent has no termination predicate and no auto-reply cap, so it keeps replying to the assistant until the default ceiling is hit. Add is_termination_msg or set max_consecutive_auto_reply=0.
Picture two people on a walkie-talkie who were each told 'always say something back to the other person.' Nobody told either of them when to put the radio down. So they keep going. The first person here is your script, who started the call. The second is the assistant. Without a rule like 'stop after the first answer' or 'stop when you hear the word TERMINATE', they will trade messages until someone outside the room counts to a big number and yanks the plug. The fix is to give one of them a rule for hanging up.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
AutoGen's UserProxyAgent and AssistantAgent form a turn-taking pair, and the bug in this snippet is a textbook example of misreading what initiate_chat actually does. The call name sounds like 'send one message and get one back', but the method starts a conversation loop that runs until a termination condition is satisfied. The author forgot to supply the condition.
This section walks the reply loop, names the three signals that can stop it, shows why this code stops only when the default counter ceiling fires, and discusses the operational consequences in production where each unnecessary auto-reply is another LLM call against a billed token budget.
How the reply loop actually works
When you call user.initiate_chat(assistant, message=...), AutoGen runs a turn-taking loop. The proxy sends the initial message to the assistant. The assistant replies. The proxy is then asked: 'do you want to respond?' This is the decision point where the loop either continues or stops.
Three mechanisms can answer that question. Human input mode runs first: if set to 'ALWAYS', the proxy blocks for keyboard input and uses that as the reply; if 'TERMINATE', it asks the human only when the message looks like an ending; if 'NEVER', the human is skipped entirely. The termination predicate is_termination_msg(message) runs next: if it returns True, the loop exits. The auto-reply counter is checked last: if max_consecutive_auto_reply has been reached, the loop exits.
In the buggy snippet, human input is disabled, the predicate is not set (defaults to a function that returns False for every message), and the counter defaults to a high value. So the proxy auto-replies to every assistant message, typically with a generic 'continue' or by echoing the assistant's last message back as context, and the loop only stops when the counter ceiling fires.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
from autogen import AssistantAgent, UserProxyAgent
assistant = AssistantAgent(
'assistant',
llm_config={'model': 'gpt-4o-mini'},
system_message='Answer the user. End your final message with TERMINATE.'
)
user = UserProxyAgent(
'user',
human_input_mode='NEVER',
max_consecutive_auto_reply=0, # hard stop after first reply
is_termination_msg=lambda m: 'TERMINATE' in m.get('content', ''),
code_execution_config=False,
)
user.initiate_chat(
assistant,
message='Summarize the GDPR right to be forgotten in 3 bullets.'
)Real products, models, and research that use this idea.
- AutoGen Studio's chat tutorials universally show is_termination_msg patterns precisely because new users hit this bug
- Microsoft's AutoGen v0.4 release notes call out termination-control changes after community feedback on runaway loops
What an interviewer would ask next. Try answering before peeking at the approach.
QWhy is the default max_consecutive_auto_reply set high instead of 1?
Because the framework's target use case is multi-turn negotiation (code-execution feedback loops, planner-executor pairs). A default of 1 would make those workflows fail unless the user opts in. Defaults reflect intended use.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Assuming initiate_chat means 'one round trip'. It means 'start a conversation that runs until a termination condition fires', and no termination condition was supplied.
60 second bullets to scan on the way to the call.
Three signals that can stop a UserProxyAgent reply loop
Default value of max_consecutive_auto_reply and why it is not 1
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.