AssayAssay
05 / 08

Lesson 05 · Control · 13 min

Detecting no progress

A budget stops a loop eventually. It does not tell you whether the loop was getting anywhere, and it wastes every iteration between the point things went wrong and the ceiling. Detecting stalled progress directly is cheap and much more informative.

The simplest useful signal is a repeated tool call. If the agent calls a tool with arguments it has already used, it is re-reading something it has already been told. Once may be reasonable; twice is almost always a stall.

const signature = `${call.name}:${call.arguments}`;
const seen = (counts.get(signature) ?? 0) + 1;
counts.set(signature, seen);

if (seen > REPEAT_TOLERANCE) {
  return { stopReason: "no_progress", steps };
}

The value of stopping this way is diagnostic rather than economic. "Hit the iteration limit" tells you almost nothing. "Stopped because it called read_policy with the same argument three times" points straight at the cause — usually a missing observation, a tool result the model cannot use, or a protocol mistake like the one in lesson 2.

  • Repeated identical calls: the loop is not absorbing what it is told.
  • Alternating between two calls: it is oscillating between hypotheses without deciding.
  • Rising tool-error rate: the arguments are wrong and it is not learning from the errors.
  • Long turns producing no calls and no answer: it is deliberating rather than acting.

Try the task with no matching order. There is nothing to find, so a loop with no stall detection will look up the same missing order until the budget runs out. With detection on, it stops in three iterations and tells you why.

Checkpoint

Produce a run that ends with the no-progress exit, and name the call it was repeating.

“Where is my order 41902? It hasn't arrived yet.”

The shortest useful loop: one tool, one answer. A model that replies without calling anything is inventing the status.

Run the "nothing to find" task. Watch for the repeat markers in the trace and see which exit the loop takes.

Nothing has run yet. Pick a task and press Run — every iteration appears below.