skip to content

Why does an AutoGen team's second run() see the first task's history, and what does team.reset() do?

level: seniorimportance: should knowfreq 45%

answer

  1. the object outlives the call
  2. run two appends, not replaces
  3. context persists, budget does not
  4. clears state, keeps configuration
  5. never between concurrent callers

basics

~20 s

AutoGen teams are stateful: the group-chat manager and every participant keep the message thread between run() calls, so a second task is appended to the first conversation. await team.reset() clears that accumulated state on the team and its participants, returning them to their initial condition.

solid answer

~50 s

An AgentChat team is a long-lived object, not a one-shot function. Calling `await team.run(task=...)` a second time resumes the same conversation — which is exactly what you want for a follow-up turn ("now translate it to French"), and exactly what you do not want when the second task is unrelated. Two symptoms follow: cost per run climbs because every agent re-sends the whole accumulated thread, and answers bleed across tasks because prior context is still in the prompt. `await team.reset()` clears the team's and each participant's conversation state, so the next run starts clean; it does not change participants, tools, model clients or the termination condition you configured. Reset only between runs — calling it while a run is in flight fails. The termination condition itself is reset by the team at the start of each run, so budgets do not leak across runs even without an explicit reset.

go deeper

for a junior

Know that a team keeps its conversation between run() calls and that await team.reset() clears it so the next task starts fresh.

for a middle

Explain what carries over — the shared thread and each participant's context — what reset preserves (participants, tools, model clients, termination condition), and that the condition is reset per run anyway.

for a senior

Diagnose the symptoms in production: climbing cost per run, cross-task answer bleed, and the concurrency hazard of a shared team; then pick per-request, per-session, or pooled-with-reset deliberately.

for a principal

Treat it as a lifecycle and isolation decision: what a team instance's scope means for tenant isolation, cost attribution and reproducibility, and how you make the safe pattern the default rather than a rule people must remember.

## Teams are objects, not calls In AutoGen AgentChat, `RoundRobinGroupChat`, `SelectorGroupChat`, `Swarm` and the other team types hold state across invocations. The group-chat manager keeps the message thread, and each participant keeps its own model context. `await team.run(task="...")` appends the new task to that thread and continues. You can even call `run()` with no task at all to let the team carry on from where it stopped. This is a deliberate design: the common interactive case is a conversation that pauses (for a human, for a termination cap) and then continues. It becomes a defect only when engineers treat the team like a stateless function and reuse one instance across unrelated requests. ## The two symptoms of unwanted carry-over **Cost.** Every participant re-sends its accumulated context on its turn. A team reused across ten unrelated tasks is, by the tenth, prompting each model call with nine irrelevant conversations. Latency and spend rise in a way that looks like model regression but is really an accounting problem. **Bleed.** Agents answer using facts from a previous task, or a critic approves a draft because a *different* draft was approved earlier. This is subtle, intermittent and easy to misdiagnose as prompt drift. In a service where one team object is shared across users, it is also a data-isolation bug: one user's content appears in another user's prompt. ## What reset() does and does not do `await team.reset()` puts the team and every participant back to their initial state: the shared thread is cleared and each agent's model context is cleared. It is a state operation, not a reconfiguration — the participant list, their tools, their system messages, the model clients and the termination condition you constructed the team with all survive. After a reset, the next `run()` behaves like the first. Two constraints matter operationally. Reset is asynchronous and must be awaited. And it is only valid between runs — you cannot reset a team while a run is in progress; stop the run first, for example with an external termination or by cancelling the run's cancellation token, then reset. ## Termination conditions are handled for you A related question interviewers like: do budgets leak? Termination conditions are stateful — a message counter, an accumulated token total — but the team resets its condition at the start of each run, so a second run gets a fresh budget rather than inheriting an exhausted one. What does *not* reset by itself is the conversation. Keeping the two straight is the point: budget is per run, context is per team until you clear it. ## Designing for it Three workable patterns: 1. **One team per task.** Construct the team inside the request handler, run it, discard it. Simplest, safest, and the right default for stateless services. The cost is object construction, which is trivial next to a model call. 2. **One team per conversation.** Keep the instance for the lifetime of a user's session so follow-ups work naturally, and drop it when the session ends. This is the pattern that pairs with human-in-the-loop pauses, where the whole point is that the thread survives. 3. **Reuse plus explicit reset.** Keep a pooled team and `await team.reset()` between tasks. Cheapest in allocations, but the most dangerous: one missed reset is silent context bleed. If you do this, make the reset unconditional in a `finally` block rather than a step someone can forget. Whatever you choose, never share one team instance across concurrent requests. Turn-taking assumes a single logical conversation; two callers interleaving into one thread produces nonsense that is very hard to reproduce. ## What to say in an interview The strong answer names the trade rather than just the API: statefulness is what makes multi-turn and paused conversations work at all, and the price is that reuse is unsafe by default. Then say which of the three patterns you would ship and why — per-request construction unless the product needs continuity.

  • Does team.reset() also clear the termination condition's counters?
    You do not need it to. The team resets its termination condition at the start of each run, so message counts and token totals do not leak from one run into the next. `reset()` is about the conversation — the shared thread and each participant's model context. Keeping the distinction straight avoids the mistake of assuming a stale budget is why a second run stopped early.
  • Can you call team.reset() while a run is in progress?
    No — reset is only valid between runs, and attempting it during one fails rather than quietly interrupting the conversation. To stop first, either compose an ExternalTermination into the condition and call its set(), or cancel the CancellationToken you passed to run(). Once the run has returned its TaskResult, await team.reset() and start clean.
  • You share one team object across concurrent HTTP requests to save setup cost. What breaks?
    Two conversations interleave into one thread, so agents answer using the other request's messages and turn-taking becomes meaningless. The bug is intermittent and load-dependent, which makes it painful to reproduce. Construct a team per request — object construction is negligible beside model latency — or keep one team per user session and never share an instance across callers.

saying these in an interview costs you the question

  • Thinks each run() call starts a fresh conversation
  • Believes reset() removes participants or tool configuration
  • Shares one team instance across concurrent requests
  • Assumes rising cost across runs means the model got slower
  • Calls reset() mid-run expecting it to abort the conversation

context