feat: add loop detection

Signed-off-by: mudler <mudler@localai.io>
This commit is contained in:
mudler
2025-04-09 19:13:41 +02:00
parent 1c4ab09335
commit 0eb68b6c20
6 changed files with 59 additions and 0 deletions

View File

@@ -489,6 +489,23 @@ func (a *Agent) consumeJob(job *types.Job, role string) {
}
}
// check if the agent is looping over the same action
// if so, we need to stop it
if a.options.loopDetectionSteps > 0 && len(job.GetPastActions()) > 0 {
count := map[string]int{}
for i := len(job.GetPastActions()) - 1; i >= 0; i-- {
pastAction := job.GetPastActions()[i]
if pastAction.Action.Definition().Name == chosenAction.Definition().Name &&
pastAction.Params.String() == actionParams.String() {
count[chosenAction.Definition().Name.String()]++
}
}
if count[chosenAction.Definition().Name.String()] > a.options.loopDetectionSteps {
xlog.Info("Loop detected, stopping agent", "agent", a.Character.Name, "action", chosenAction.Definition().Name)
chosenAction = nil
}
}
//xlog.Debug("Picked action", "agent", a.Character.Name, "action", chosenAction.Definition().Name, "reasoning", reasoning)
if chosenAction == nil {
// If no action was picked up, the reasoning is the message returned by the assistant
@@ -551,6 +568,8 @@ func (a *Agent) consumeJob(job *types.Job, role string) {
return
}
job.AddPastAction(chosenAction, &actionParams)
var err error
conv, err = a.handlePlanning(job.GetContext(), job, chosenAction, actionParams, reasoning, pickTemplate, conv)
if err != nil {

View File

@@ -126,6 +126,7 @@ var _ = Describe("Agent test", func() {
agent, err := New(
WithLLMAPIURL(apiURL),
WithModel(testModel),
WithLoopDetectionSteps(3),
// WithRandomIdentity(),
WithActions(&TestAction{response: map[string]string{
"boston": testActionResult,

View File

@@ -28,6 +28,7 @@ type options struct {
canStopItself bool
initiateConversations bool
loopDetectionSteps int
forceReasoning bool
canPlan bool
characterfile string
@@ -113,6 +114,13 @@ func WithTimeout(timeout string) Option {
}
}
func WithLoopDetectionSteps(steps int) Option {
return func(o *options) error {
o.loopDetectionSteps = steps
return nil
}
}
func WithConversationsPath(path string) Option {
return func(o *options) error {
o.conversationsPath = path