Documentation
¶
Overview ¶
Package repeatguard breaks a run out of a tool-call loop by TELLING the model it is in one.
It never blocks a call — a legitimately repeated poll must still work, and at the call site a real loop is indistinguishable from a legitimate retry. Only the model knows which it is, so the plugin supplies the observation and leaves the judgment alone.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultExclude []string
DefaultExclude is empty, and deliberately so: this plugin names no other capability's tools.
Bookkeeping tools are the case that used to live here as a hardcoded update_plan exclusion. They are now handled generically — the run tells the guard which tools are administrative (agentcore.BookkeepingTool), so a planner from any package is transparent to the chain and this plugin depends on none of them. Exclude remains for domain-specific patterns a composition knows about.
var DefaultThresholds = []int{3, 5, 8}
DefaultThresholds is the escalation ladder: a gentle nudge at 3, then progressively louder ones. Below 3 would fire on an ordinary retry-after-error.
Functions ¶
This section is empty.
Types ¶
type Plugin ¶
type Plugin struct {
// Thresholds are the consecutive-repeat counts that trigger a reminder. The
// FIRST delivers a short generic nudge; every later one delivers the detailed
// form naming the tool, the run length, and the arguments. nil uses
// DefaultThresholds. Validated at composition: empty is fine (nil ⇒
// defaults), but a value below 2 or a duplicate is a configuration error, not
// a silent fallback.
Thresholds []int
// Include limits tracking to matching tool names ('*' wildcards allowed).
// Empty tracks every tool.
Include []string
// Exclude makes matching tools transparent to the chain ('*' wildcards
// allowed) — they neither increment nor reset the counter. This is what makes
// exclusion useful: a bookkeeping call interleaved into a loop must not
// launder it, so `run_sql X → update_plan → run_sql X` still counts as two
// consecutive run_sql X. nil uses DefaultExclude.
Exclude []string
// ArgumentsPreviewChars caps how much of the canonical argument string the
// detailed reminder quotes, so a looping write-shaped payload cannot ride
// unbounded into every subsequent request. The chain key always compares the
// FULL canonical string; this bounds only the reminder text. 0 uses
// defaultArgumentsPreviewChars.
ArgumentsPreviewChars int
}
Plugin tunes the repeated-tool-call reminder: an advisory loop-breaker that watches a run's stream of tool calls, counts runs of consecutive calls to the same tool with identical arguments, and injects an escalating reminder when the count reaches a configured threshold.
It is NOT a model-facing tool and never blocks: a legitimately repeated call is delayed by nothing. The decision — retry differently, gather more evidence, or finish — stays entirely with the model. That matters because a real loop is indistinguishable from a legitimate retry at the call site; only the model knows which it is, so the guard supplies the observation and leaves the judgment alone.
This fills a genuine hole. agentcore's existing loop breakers all fire at the wrong altitude for this failure: the goal gate catches a verbatim-repeated ANSWER, compaction summarizes stale duplicate results away AFTER the fact, and the circuit breaker only counts tool FAILURES. A tool that succeeds every time and is called with identical arguments twenty turns running — a query the model keeps re-issuing because it will not accept the answer — trips none of them, and burns the whole turn budget.
Ported from deepseek-harness's dsh-repeat-tool-reminder (MIT).
func (Plugin) BeginRun ¶
BeginRun starts a fresh chain for this run, carrying the run's bookkeeping predicate so administrative tools stay transparent to the chain.