Why does my AutoGen agent keep calling the same tool?
Rius shows an AutoGen tool loop as repeated execute_tool spans inside one invoke_agent span. The Tool Loop alert does not fire on them, because they carry no tool arguments. Open the trace, read the repeated calls and the model calls between them, and you see what the agent was told each time. AutoGen records the tool name but not its arguments, and Tool Loop compares arguments. To be told next time, create a custom alert on LLM calls or cost per trace for this service.
How it works
AutoGen emits its own spans for agent creation, agent runs and tool calls, and the OpenAI integration records each model call as a ChatCompletion generation with tokens and cost. The AutoGen integration page lists what lands in the trace. A looping run looks like this:
handle-ticket AGENT
├─ create_agent support_agent
└─ invoke_agent support_agent
├─ ChatCompletion LLM
├─ execute_tool account_status
├─ ChatCompletion LLM
├─ execute_tool account_status
└─ ...This sketch shows the shape of the pattern, not a recorded run. Each cycle is a model call that decides to use the tool, then a tool span. The repetition is the symptom. The cause is in what the model saw before each decision. The usual causes Rius is built to catch:
- The agent repeats the same search with the same query because no result satisfies its stop condition.
- A broken tool schema makes the model retry the identical call and expect a different outcome.
- A planner and an executor cycle on the same lookup, making no progress while the token bill grows.
The pre-defined Tool Loop alert fires when the same tool is called with identical arguments at least 3 times in a row inside one trace, and calls whose arguments were not captured are never counted. AutoGen’s execute_tool spans record the tool name but not its arguments, so Tool Loop does not fire on an AutoGen loop. The arguments are still in the trace: each ChatCompletion span’s output holds the tool_call the model made.
Set it up
- Make sure the AutoGen run is traced, with one
observewrapper on the entrypoint. Do not wrap the tool functions themselves: AutoGen runs tools in a detached context, so a wrapper splits off as its own single-span trace, and the framework’sexecute_toolspans already cover tools.
pip install "glassflow-rius[openai]" "autogen-agentchat" "autogen-ext[openai]"import rius
from autogen_agentchat.agents import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
from rius import SpanKind
rius.init(api_key="ri_...", service_name="my-agent")
def account_status(email: str) -> str:
"""Fetch a customer's account standing by email."""
return lookup(email)
@rius.observe(name="handle-ticket", kind=SpanKind.AGENT)
async def handle_ticket(ticket: str) -> str:
agent = AssistantAgent(
name="support_agent",
model_client=OpenAIChatCompletionClient(model="gpt-5-mini"),
tools=[account_status],
system_message="Answer support questions; check accounts with the tool.",
)
result = await agent.run(task=ticket)
return str(result.messages[-1].content)- Open Traces in the console and find the run. Use the trace tools in this order. A tool loop does not collapse. The trace page groups repeated calls into one
N attemptsrow only when every attempt but the last failed, so a loop of successful calls shows as separate spans. Read them one by one, or list them on the Spans tab. - Select a
ChatCompletionspan between two repeats. On its Input / Output tab, switch to Since previous llm call. Messages are marked added, removed or changed, so you see whether the tool result reached the prompt and whether the system prompt or earlier turns changed. Compare thetool_callarguments in eachChatCompletionoutput to see whether the model repeated the same call: the tool spans’ Raw tab does not show them. - Select an
execute_toolspan and check its status. A failed tool call is returned to the model as the tool’s result, so an error there can explain why the model asks again. - For a run with several model calls, read the context growth strip above the tree. Input tokens per call climbing steadily is the cost of the loop. To see every call of the tool across runs, use the Spans tab and search for the tool name.
- To be told about the next loop, create a custom alert on LLM calls or cost per trace, filtered to this service.
What you get
- A single place to see the loop: the repeated
execute_toolspans, each model call between them, and the tokens and cost each cycle added. Cost is a list-price estimate in US dollars. - The model’s input before each repeat, so you can tell whether the agent never got a usable result or got one and ignored it.
- Next time, a custom alert on LLM calls or cost per trace for this service. The pre-defined Tool Loop alert compares tool arguments, which AutoGen’s tool spans do not carry, so it does not cover this case.
- A way to route the firing to webhook, email or Slack through alert channels.
For the wider habit of checking agents after deploy, see Monitor AI agents in production.
When something else fits
- If the tool is called with different arguments each time, the agent is searching rather than looping, and the context growth strip is the better place to look.
- If your agent runs on another framework, the integrations overview lists what each one records.