How to Fix 'duplicate tool calls during development' in AutoGen (Python)

By Cyprian AaronsUpdated 2026-04-21
duplicate-tool-calls-during-developmentautogenpython

What the error means

duplicate tool calls during development usually means AutoGen saw the same tool invocation more than once in a single run. In practice, this shows up when you re-register tools, replay messages, restart a conversation without clearing state, or let both the agent and your app call the same function.

The common pattern is: it works once, then breaks as soon as you rerun the notebook, hot-reload the app, or reuse the same AssistantAgent / ConversableAgent instance.

The Most Common Cause

The #1 cause is registering the same tool multiple times while reusing the same agent object.

This happens a lot in notebooks and dev servers. You create an agent at module scope, then rerun the cell or reload the process, and AutoGen ends up with duplicate function metadata.

Broken vs fixed pattern

Broken patternFixed pattern
Re-registering tools on an existing agentCreate a fresh agent per run or guard registration
Reusing conversation state across runsReset chat history before starting a new session
# BROKEN
from autogen import AssistantAgent

def get_weather(city: str) -> str:
    return f"Weather in {city}: sunny"

llm_config = {
    "model": "gpt-4o-mini",
    "api_key": os.environ["OPENAI_API_KEY"],
}

agent = AssistantAgent(
    name="assistant",
    llm_config=llm_config,
)

# This gets executed again on notebook reruns / hot reloads
agent.register_for_llm(name="get_weather", description="Get weather")(get_weather)
agent.register_for_execution(name="get_weather")(get_weather)

# Later...
reply = agent.generate_reply(messages=[{"role": "user", "content": "Weather in Paris?"}])
# FIXED
from autogen import AssistantAgent

def build_agent():
    def get_weather(city: str) -> str:
        return f"Weather in {city}: sunny"

    llm_config = {
        "model": "gpt-4o-mini",
        "api_key": os.environ["OPENAI_API_KEY"],
    }

    agent = AssistantAgent(
        name="assistant",
        llm_config=llm_config,
    )

    # Register once per fresh agent instance
    agent.register_for_llm(name="get_weather", description="Get weather")(get_weather)
    agent.register_for_execution(name="get_weather")(get_weather)

    return agent

agent = build_agent()
reply = agent.generate_reply(messages=[{"role": "user", "content": "Weather in Paris?"}])

If you are using a notebook, this is usually enough:

  • restart kernel before re-running registration code
  • avoid defining agents at global/module scope during development
  • create tool registration inside a factory function

Other Possible Causes

1) You are calling both manual execution and AutoGen execution

If you manually run the tool after AutoGen already decided to call it, you’ll see duplicate behavior.

# BROKEN
reply = assistant.generate_reply(messages=messages)

# Then you also do this yourself:
result = get_weather("Paris")
messages.append({"role": "tool", "content": result})

Fix by letting one side own tool execution.

# FIXED
reply = assistant.generate_reply(messages=messages)
# Do not manually execute the same tool unless you're fully managing tool routing yourself.

2) You reused chat history from a previous run

AutoGen agents keep conversational state. If you append old messages into a new run, the model may see prior tool calls and repeat them.

# BROKEN
messages = previous_messages + [
    {"role": "user", "content": "Run it again"}
]

Use a clean message list for each session.

# FIXED
messages = [
    {"role": "user", "content": "Run it again"}
]

If you need persistence, store it explicitly and prune old tool messages before replaying.

3) Your tool name collides with another registered function

Two different Python functions with the same AutoGen tool name will confuse registration.

# BROKEN
agent.register_for_llm(name="lookup_customer")(lookup_customer_v1)
agent.register_for_llm(name="lookup_customer")(lookup_customer_v2)

Give each tool a unique name.

# FIXED
agent.register_for_llm(name="lookup_customer_v1")(lookup_customer_v1)
agent.register_for_llm(name="lookup_customer_v2")(lookup_customer_v2)

This matters more in multi-agent setups where each AssistantAgent shares similar helper names.

4) You are registering tools inside a request handler

In FastAPI, Flask, Streamlit, or Celery workers, request-level code can run repeatedly. If registration happens on every request against a shared singleton agent, duplicates accumulate.

# BROKEN
@app.post("/chat")
def chat():
    assistant.register_for_llm(name="search_docs")(search_docs)
    assistant.register_for_execution(name="search_docs")(search_docs)
    return assistant.generate_reply(messages=[...])

Register once at startup or create a new agent per request.

# FIXED
@app.on_event("startup")
def startup():
    global assistant
    assistant = build_agent()

@app.post("/chat")
def chat():
    return assistant.generate_reply(messages=[...])

How to Debug It

  1. Print registered tools before running

    • Inspect what AutoGen thinks is registered.
    • Look for duplicate names or repeated wrappers.
    • If your version exposes internal maps, dump them before generate_reply().
  2. Check whether your code runs more than once

    • Notebook cells rerun.
    • Uvicorn reloads modules.
    • Streamlit reruns top-to-bottom.
    • Add a log line right before registration:
      print("registering tools")
      
  3. Separate tool registration from conversation execution

    • Registration should happen once.
    • Execution should happen many times.
    • If both are inside the same function that runs per request, that’s your bug.
  4. Search for duplicate message replay

    • Inspect your message list for repeated tool_calls, function_call, or identical assistant messages.
    • If you persist chat history, trim old tool-related messages before resuming.

A useful sanity check is to restart with this minimal shape:

assistant = build_agent()
messages = [{"role": "user", "content": "Call the weather tool for London"}]
print(assistant.generate_reply(messages=messages))

If that works cleanly but your app fails later, the problem is almost always lifecycle management around stateful agents.

Prevention

  • Build agents through factory functions instead of module-level singletons during development.
  • Register each tool exactly once per agent instance.
  • Reset or recreate chat state between sessions; do not replay old tool_calls blindly.
  • In web apps and notebooks, assume code will rerun and make registration idempotent by design.

If you want one rule to remember: AutoGen agents are stateful objects. Treat them like database connections, not stateless helper functions.


Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides