Repeat-tool reminder detects loops but does not reliably break them #6370
Replies: 3 comments
|
Your diagnosis is right, and the fix is already sanctioned upstream — the guard's own README lists it as a known, unimplemented limitation:
So the detection is working as designed; the enforcement tier was simply deferred. Two things are worth separating, because they have different causes. 1. Why the reminder does not change behaviour. The reminder is delivered as an ordinary 2. The part that is easy to miss, and it explains the tail of your log. The same README adds:
The default thresholds are The enforcement boundary is real, and it is not
That difference matters for your case. A block still executes the search and still costs a full model turn before the model sees a failure, so a determined loop survives it. A pre-execute denial removes the round trip entirely: the repeated action stops producing a result to react to.
I published the enforcement tier as a plugin:
dsh plugin add @argszero/cordis-plugin-repeat-guard-escalationIt counts the same chains the shipped guard counts and, from The reminder is untouched — the shipped guard still runs and still explains itself. Three deliberate limits, since a guard that traps the session is worse than the loop it prevents:
22 tests pass (11 pure-rule + 11 against the real On your point that this is "less about loop detection and more about loop recovery": agreed, and that is how it is built. The guard's detection was never the problem; the missing piece was that nothing happened after the advice was ignored. |
|
I hit a second variant of the same recovery problem, this time with the Edit tool rather than grep. The agent repeatedly submitted an invalid edit where old_string and new_string were identical. Harness returned the deterministic error: old_string and new_string must differ but the agent continued retrying the same failed Edit call many times instead of re-reading the target region, reconstructing the patch, or recognizing that the intended change might already be present. So this looks like the same broader issue from a different angle: loop detection / failure detection exists A stronger recovery path could be: detect repeated identical failed tool calls, This occurred on 0.1.5-rc.2 with the same local Qwen3.8 / TabbyAPI setup as the original report. |
|
This is the same class of failure from a different angle, and you have put your finger on the important half: detection is not the missing piece, recovery is. I checked the shipped guard against your case and your diagnosis holds exactly. That last point matters for your Edit case: the model was already past the point where the guard had anything to say, and the guard would not have said anything different even if it were still speaking. I have shipped this as v0.1.1 of the plugin from the first half of this thread,
The denial quotes your error back verbatim, since that is what the next attempt has to be aimed at: Two honest notes on the design, because both are deliberate:
Enforcement remains bounded ( One correction worth recording, since it affects anyone building on these seams — and it corrected my own first version's documentation. I had written that a denied call never reaches
Deny still skips dispatch, which is why it remains the right lever — the repeated side effect does not happen and no model turn is spent on it. But the call's result is still observable, which is what let me build the failure tier on top of deny, and also means a plugin must be able to recognise its own denials: otherwise it reads its own refusal text as the tool's error and escalates against itself. The two seams share the Of your five suggestions: (1) detect repeated identical failures — done, this is the Verification: 32/32 tests, including a deliberately mutation-checked case that fails if the plugin ever counts its own denial as a tool failure. The published artifact was installed into an empty project and exercised on npm install @argszero/cordis-plugin-repeat-guard-escalationIf you deploy it on your Qwen3.8/TabbyAPI setup, the number worth watching is whether the failing-edit chain now stops at 4 instead of running on — and if it still runs on, that is a bug in my counter and I would want the log. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
I’m testing DeepSeek Harness 0.1.5-rc.2 with a local OpenAI-compatible provider:
Harness: 0.1.5-rc.2
Model: Qwen3.8-27B-exl3-8.00bpw
Backend: TabbyAPI
Tool format: qwen3_coder
Context: 262144
I’ve run into a repeated tool-call loop where Harness correctly detects the repetition and injects a repeat-tool-reminder, but the model continues issuing essentially the same tool call anyway.
Example from one task:
Read BayComposer-private-alpha-stabilization\tests\qt_test_lifecycle.py
Grep def release
Read BayComposer-private-alpha-stabilization\core\playback_service.py
Grep def test_
Grep def test_
Grep def test_
Context injection repeat-tool-reminder grep × 3
Grep def test_
Read BayComposer-private-alpha-stabilization\docs\ROADMAP.md
Grep PSA-001|PSA-002|PSA-003
Grep def test_
Grep def test_
Grep def test_
Context injection repeat-tool-reminder grep × 3
Grep def test_
Grep def test_
Context injection repeat-tool-reminder grep × 5
The loop detector is clearly working, but the recovery behavior appears too weak. The reminder is injected, yet the model still falls back to the same broad search instead of synthesizing the evidence already collected or changing strategy.
Expected behavior
After repeated identical or near-identical tool calls are detected, Harness could do something stronger, such as:
deduplicate or block the same tool call after N repetitions
force a “summarize what you already know” step
require the model to state what unresolved information it is trying to obtain before another repeated tool call
suppress broad searches when the same query has already been exhausted
redirect the agent toward exact-symbol/file inspection instead of repeating a repository-wide grep
track exhausted search paths within the task
Actual behavior
Harness detects the repeated calls and injects reminders, but the model can continue the same loop anyway.
Why this matters
On large coding tasks, especially with long-context local models, this can waste substantial context and time even though the needed information may already be present in the task history.
Manual intervention fixes it easily. For example, telling the model:
Stop repeating searches that have already returned the same evidence. Use the information already collected and only search for a specific unresolved symbol.
usually breaks the loop immediately.
So the issue seems less about loop detection and more about loop recovery / enforcement.
It would be useful if future Harness versions could turn the existing repeat-tool detection into a stronger automatic recovery mechanism.
I found a few related discussions after posting this, especially #5072 and #5540. My case seems slightly narrower: repeat-tool-reminder did detect the exact repeated tool call and injected reminders at ×3 and ×5, but the model still continued issuing the same grep def test_ call.
So this appears to demonstrate the “advisory only” limitation directly: detection worked, but recovery/enforcement did not. A possible improvement would be an optional high-threshold hard block or forced “synthesize existing evidence before another tool call” step after repeated reminders fail.
All reactions