The autogen human_input_mode always terminate never configuration is one of the first knobs you touch when standing up a UserProxyAgent, yet its behavioral implications are rarely spelled out. Pick ALWAYS and your pipeline blocks on every turn; pick NEVER and it runs unattended until it hits a limit; TERMINATE sits in between with a single checkpoint at shutdown. Below is a head-to-head comparison grounded in how AutoGen 0.2.x actually executes the agent loop.
How human_input_mode Works in AutoGen
AutoGen’s UserProxyAgent is the bridge between LLM-driven ConversableAgents and a human (or a scripted input function). The human_input_mode parameter governs when the agent yields control to input() or a custom human_input_function before sending its reply.
from autogen import UserProxyAgent, ConversableAgent
assistant = ConversableAgent(name="assistant", llm_config={"config_list": [{"model": "gpt-4"}]})
user_proxy = UserProxyAgent(
name="user_proxy",
human_input_mode="ALWAYS", # "TERMINATE" or "NEVER"
function_map={"exit": exit},
)
The internal control flow after the agent receives a message is roughly:
- Generate a candidate reply (or execute a function).
- Check
human_input_mode:ALWAYS→ callget_human_input()unconditionally.TERMINATE→ callget_human_input()only ifis_termination_msg(candidate)is True or the sender indicated termination.NEVER→ skip human input entirely; use the candidate orfunction_mapresult.
- Send the (possibly modified) reply.
This sounds simple, but the downstream effects on cost, latency, and reliability are stark. The autogen human_input_mode always terminate never triad is fundamentally a throttle on autonomy.
Capabilities: What Each Mode Allows
ALWAYS gives you a fully interactive session. At each step you can edit the assistant’s proposed message, inject new context, or veto a tool call. This is the only mode where you can catch a mistaken function invocation before it executes—because the human sees the message prior to the agent acting on it (assuming function_map execution is gated by the reply). For example, if the assistant proposes rm -rf /data, you can delete that line.
TERMINATE is a guardrail, not a copilot. You are prompted only when the conversation is about to end. You can still say “no, do another round” by returning a non-empty string, which resets the termination flag. But you cannot inspect intermediate reasoning unless you also log it elsewhere.
NEVER is pure automation. The agent uses max_consecutive_auto_reply and function_map to proceed. If the assistant asks a question, the user proxy will auto-reply with a default (or empty) message, which often leads to the assistant terminating or looping.
Cost Model: Token and Human Spend
LLM token consumption is identical across modes for the same conversation trajectory—the mode doesn’t add extra model calls by itself. The difference is in human cost and error-driven token waste.
- ALWAYS: Zero extra tokens, but maximum human time. A 20-step task means 20 prompts. If your on-call engineer is the input function, that’s 20 interruptions. However, early correction can prevent a long wasted code-generation spiral, saving tokens downstream.
- TERMINATE: Near-zero human time unless the run ends (usually once). Saves human attention but if the agent goes off the rails for 19 steps, you only catch it at the end.
- NEVER: Zero human time. However, a confused agent can burn thousands of tokens in a retry loop. When routed through an inference gateway that provides per-token usage metering—such as n4n.ai—you’ll see the runaway clearly in the bill, but you won’t be there to stop it.
The economic tradeoff: ALWAYS trades token savings (via early correction) for salary; NEVER trades salary for possible token blowups.
Latency & Throughput
In a synchronous AutoGen script, ALWAYS imposes human latency on every turn—seconds to hours per step. Throughput is bounded by your slowest reviewer.
TERMINATE adds latency only at the tail. A batch of 100 conversations can run overnight and prompt once at the end of each; if you’re not watching, they queue.
NEVER has the lowest latency and highest throughput: the agent loop never blocks. In a GroupChat, NEVER on the user proxy means the group can iterate at machine speed until max_consecutive_auto_reply is hit.
Ergonomics & Developer Experience
ALWAYS is the safest for local experimentation. You can print the state, then type continue or paste a fix. But it is miserable for long tasks. A custom input function can ease the pain:
def auto_input(prompt):
print(prompt)
return "continue" # or read from a queue
user_proxy = UserProxyAgent(
name="user_proxy",
human_input_mode="ALWAYS",
human_input_function=auto_input,
)
TERMINATE requires you to define is_termination_msg correctly. If your assistant uses a non-standard sign-off, TERMINATE may never trigger, and the agent will hang waiting for a termination that never comes—or terminate prematurely.
NEVER is set-and-forget, but only if you’ve implemented function_map thoroughly. Missing a function means the agent gets None and may crash or silently stop.
# NEVER mode with a bounded auto-reply
user_proxy = UserProxyAgent(
name="user_proxy",
human_input_mode="NEVER",
max_consecutive_auto_reply=10,
function_map={"python": execute_python},
)
Ecosystem & Integration
In a GroupChat, the user proxy is typically a participant. With ALWAYS, every speaker transition that involves the user proxy blocks. That freezes the whole group. TERMINATE lets the group run autonomously and only checks in when the chat’s terminate condition is met. NEVER is the standard for scheduled jobs.
With AutoGen’s code execution, ALWAYS lets you approve each cell; TERMINATE only asks after the last cell; NEVER executes whatever the assistant returns (dangerous on a host with privileges). For a GroupChat with a user proxy in TERMINATE:
from autogen import GroupChat, GroupChatManager
gc = GroupChat(agents=[assistant, user_proxy], messages=[], max_round=20)
manager = GroupChatManager(groupchat=gc, llm_config=llm_config)
# user_proxy in TERMINATE will only prompt when gc decides to terminate
Limits & Failure Modes
ALWAYS: If your input function raises or returns EOF, the agent dies. There’s no timeout. A forgotten terminal session stalls the pipeline indefinitely.
TERMINATE: If the termination detection is wrong, you either get prompted spuriously or never. Also, if the agent terminates due to max_consecutive_auto_reply rather than a message, TERMINATE may not prompt because no termination message was sent.
NEVER: The agent can spin in a loop where assistant calls a function, gets result, calls again. Set max_consecutive_auto_reply or implement a circuit breaker in function_map. Without a human, a bad function_map entry can cause silent data corruption.
Comparison Table
| Mode | Human Prompt Trigger | Human Effort | LLM Call Overhead | Autonomy | Best For |
|---|---|---|---|---|---|
| ALWAYS | Every agent reply | Very High | None (same trajectory) | None | Debugging, high-risk approvals |
| TERMINATE | Only on termination signal | Low | None | Partial (until end) | Ops with kill-switch, supervised batches |
| NEVER | Never | None | None (risk of loops) | Full | Unattended batch, CI, scalable services |
Which to Choose: Verdict by Use Case
Prototyping and local debugging
Use ALWAYS. You want to see every message, correct prompts, and understand the agent’s decisions. Wrap the input function to default to “continue” after a timeout if you want semi-automatic runs. The autogen human_input_mode always terminate never decision here is clear: keep the human in the loop at full density.
Production pipelines with a human oversight requirement
Use TERMINATE. Configure is_termination_msg to detect a clear “TASK COMPLETE” string. Your service runs autonomously, and a human gets one prompt to approve the final state or reject it. This matches on-call review of generated PRs or config changes.
Unattended background jobs
Use NEVER with strict max_consecutive_auto_reply and a complete function_map. Run it behind a gateway that meters tokens so you can alert on anomalies. For example, a nightly data-cleaning agent that calls Python functions and exits on its own.
The autogen human_input_mode always terminate never choice is not about preference; it’s about where the human sits in the loop. Put them at every step, at the exit, or nowhere—and design the surrounding code accordingly.