Loop guard: also identical call with identical result 3 times in a row; baseline restarts (run base 20500)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aUaQeLnwbb1zTpN7kHeat
This commit is contained in:
Kral
2026-10-04 07:59:28 +02:00
parent a94c2a3b5d
commit 5c68a1b1b7
4 changed files with 14 additions and 4 deletions

View File

@@ -156,8 +156,11 @@ class LlmAgent:
err, text = proxy.call(fn["name"], args)
messages.append({"role": "tool", "tool_call_id": c.get("id", ""),
"content": ("ERROR: " if err else "") + text[:12000]})
if self.loop_guard and proxy.same_push_streak >= self.loop_guard:
final = f"Stopped: loop (the same source was pushed {proxy.same_push_streak} times in a row)."
streak = max(proxy.same_push_streak, proxy.same_call_streak)
if self.loop_guard and streak >= self.loop_guard:
final = (f"Stopped: loop (the same source was pushed {proxy.same_push_streak} times in a row)."
if proxy.same_push_streak >= self.loop_guard else
f"Stopped: loop (the same call with the same result {proxy.same_call_streak} times in a row).")
self.end_reason = "loop"
break
if self.end_reason == "loop":

View File

@@ -71,6 +71,8 @@ class ToolProxy:
self.activation_errors = [] # unique error messages of failed writes
self.same_push_streak = 0 # identical sap_push_source (object + source hash) in a row
self._last_push = None
self.same_call_streak = 0 # identical call (tool, arguments and result) in a row, any tool
self._last_call = None
self.log = open(log_path, "a")
self.adt = None
self.fallbacks = [] # writes that needed the ADT activation fallback
@@ -171,6 +173,11 @@ class ToolProxy:
for m in activation_messages(text, err):
if m not in self.activation_errors and len(self.activation_errors) < 30:
self.activation_errors.append(m)
if tool in MODEL_TOOLS and not result[1].startswith("Tool "):
ck = (tool, hashlib.md5(json.dumps(args, sort_keys=True).encode()).hexdigest(),
hashlib.md5(result[1].encode()).hexdigest())
self.same_call_streak = self.same_call_streak + 1 if ck == self._last_call else 1
self._last_call = ck
entry["is_error"], entry["result"] = result[0], result[1][:20000]
self.log.write(json.dumps(entry) + "\n")
self.log.flush()