From ac8f6e94ff07d01c404fec8fe15a7a7d22079cbb Mon Sep 17 00:00:00 2001 From: mudler Date: Sun, 14 Apr 2024 16:38:45 +0200 Subject: [PATCH] wip: noaction for deciding to stop --- action/noreply.go | 22 +++++++++++++++++ agent/actions.go | 31 ++++++++++++++++++------ agent/agent.go | 16 +++++++++--- agent/options.go | 8 +++++- agent/state.go | 6 ++--- agent/templates.go | 4 +++ example/webui/agentpool.go | 4 +++ example/webui/views/create.html | 3 +++ example/webui/views/partials/header.html | 3 +++ 9 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 action/noreply.go diff --git a/action/noreply.go b/action/noreply.go new file mode 100644 index 0000000..a8ebb50 --- /dev/null +++ b/action/noreply.go @@ -0,0 +1,22 @@ +package action + +// StopActionName is the name of the action +// used by the LLM to stop any further action +const StopActionName = "stop" + +func NewStop() *StopAction { + return &StopAction{} +} + +type StopAction struct{} + +func (a *StopAction) Run(ActionParams) (string, error) { + return "no-op", nil +} + +func (a *StopAction) Definition() ActionDefinition { + return ActionDefinition{ + Name: StopActionName, + Description: "Use this tool to stop any further action and stop the conversation.", + } +} diff --git a/agent/actions.go b/agent/actions.go index 2d20a3c..7d90af0 100644 --- a/agent/actions.go +++ b/agent/actions.go @@ -136,18 +136,33 @@ func (a *Agent) generateParameters(ctx context.Context, pickTemplate string, act } func (a *Agent) systemInternalActions() Actions { - if a.options.enableHUD { - return append(a.options.userActions, - action.NewState(), action.NewReply()) - } + defaultActions := append(a.options.userActions, action.NewReply()) if a.options.initiateConversations && a.selfEvaluationInProgress { // && self-evaluation.. - return append(a.options.userActions, - action.NewState(), action.NewReply(), action.NewConversation()) + acts := append(defaultActions, action.NewConversation()) + if a.options.enableHUD { + acts = append(acts, action.NewState()) + } + if a.options.canStopItself { + acts = append(acts, action.NewStop()) + } + + return acts } - return append(a.options.userActions, - action.NewReply()) + if a.options.canStopItself { + acts := append(defaultActions, action.NewStop()) + if a.options.enableHUD { + acts = append(acts, action.NewState()) + } + return acts + } + + if a.options.enableHUD { + return append(defaultActions, action.NewState()) + } + + return defaultActions } func (a *Agent) prepareHUD() PromptHUD { diff --git a/agent/agent.go b/agent/agent.go index 8d977c1..3b54c06 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -340,6 +340,14 @@ func (a *Agent) consumeJob(job *Job, role string) { } } + if chosenAction.Definition().Name.Is(action.StopActionName) { + if a.options.debugMode { + fmt.Println("LLM decided to stop") + } + job.Result.Finish(nil) + return + } + if chosenAction == nil { // If no action was picked up, the reasoning is the message returned by the assistant // so we can consume it as if it was a reply. @@ -410,8 +418,10 @@ func (a *Agent) consumeJob(job *Job, role string) { if !chosenAction.Definition().Name.Is(action.ReplyActionName) { result, err := a.runAction(chosenAction, params) if err != nil { - job.Result.Finish(fmt.Errorf("error running action: %w", err)) - return + //job.Result.Finish(fmt.Errorf("error running action: %w", err)) + //return + // make the LLM aware of the error of running the action instead of stopping the job here + result = fmt.Sprintf("Error running tool: %v", err) } stateResult := ActionState{ActionCurrentState{chosenAction, params.actionParams, reasoning}, result} @@ -585,7 +595,7 @@ func (a *Agent) periodicallyRun() { // whatNext := NewJob(WithText("Decide what to do based on the state")) whatNext := NewJob( - WithText("Decide what to based on the goal and the persistent goal."), + WithText(innerMonologueTemplate), WithReasoningCallback(a.options.reasoningCallback), WithResultCallback(a.options.resultCallback), ) diff --git a/agent/options.go b/agent/options.go index d606bd9..59cc80c 100644 --- a/agent/options.go +++ b/agent/options.go @@ -21,6 +21,7 @@ type options struct { userActions Actions enableHUD, standaloneJob, showCharacter, enableKB bool debugMode bool + canStopItself bool initiateConversations bool characterfile string statefile string @@ -53,7 +54,7 @@ func defaultOptions() *options { }, character: Character{ Name: "John Doe", - Age: 0, + Age: "", Occupation: "Unemployed", Hobbies: []string{}, MusicTaste: []string{}, @@ -82,6 +83,11 @@ var EnableKnowledgeBase = func(o *options) error { return nil } +var CanStopItself = func(o *options) error { + o.canStopItself = true + return nil +} + func EnableKnowledgeBaseWithResults(results int) Option { return func(o *options) error { o.enableKB = true diff --git a/agent/state.go b/agent/state.go index 7d72af2..6cb976a 100644 --- a/agent/state.go +++ b/agent/state.go @@ -22,7 +22,7 @@ type PromptHUD struct { type Character struct { Name string `json:"name"` - Age int `json:"age"` + Age string `json:"age"` Occupation string `json:"job_occupation"` Hobbies []string `json:"hobbies"` MusicTaste []string `json:"music_taste"` @@ -99,7 +99,7 @@ func (a *Agent) generateIdentity(guidance string) error { func (a *Agent) validCharacter() bool { return a.Character.Name != "" && - a.Character.Age != 0 && + a.Character.Age != "" && a.Character.Occupation != "" && len(a.Character.Hobbies) != 0 && len(a.Character.MusicTaste) != 0 @@ -107,7 +107,7 @@ func (a *Agent) validCharacter() bool { const fmtT = `===================== Name: %s -Age: %d +Age: %s Occupation: %s Hobbies: %v Music taste: %v diff --git a/agent/templates.go b/agent/templates.go index 4e37c87..224abae 100644 --- a/agent/templates.go +++ b/agent/templates.go @@ -40,6 +40,10 @@ func renderTemplate(templ string, hud *PromptHUD, actions Actions, reasoning str return prompt.String(), nil } +const innerMonologueTemplate = `"This is not a typical conversation between an assistant and an user. +You are thinking out loud by yourself now, and you are evaluating the current situation. +Considering the goal and the persistent goal, if you have one, and do something, or decide to plan something for later on. If you have the action available, you might also decide to engage a conversation with the user by notifying him."` + const hudTemplate = `{{with .HUD }}{{if .ShowCharacter}}The assistant acts like an human, has a character and the replies and actions might be influenced by it. {{if .Character.Name}}This is the assistant name: {{.Character.Name}} {{end}}{{if .Character.Age}}This is the assistant age: {{.Character.Age}} diff --git a/example/webui/agentpool.go b/example/webui/agentpool.go index 36d3e79..b37f36f 100644 --- a/example/webui/agentpool.go +++ b/example/webui/agentpool.go @@ -41,6 +41,7 @@ type AgentConfig struct { PermanentGoal string `json:"permanent_goal" form:"permanent_goal"` EnableKnowledgeBase bool `json:"enable_kb" form:"enable_kb"` KnowledgeBaseResults int `json:"kb_results" form:"kb_results"` + CanStopItself bool `json:"can_stop_itself" form:"can_stop_itself"` SystemPrompt string `json:"system_prompt" form:"system_prompt"` } @@ -342,6 +343,9 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error if config.StandaloneJob { opts = append(opts, EnableStandaloneJob) } + if config.CanStopItself { + opts = append(opts, CanStopItself) + } if config.InitiateConversations { opts = append(opts, EnableInitiateConversations) } diff --git a/example/webui/views/create.html b/example/webui/views/create.html index 1377cda..b989da0 100644 --- a/example/webui/views/create.html +++ b/example/webui/views/create.html @@ -99,6 +99,9 @@ + + + diff --git a/example/webui/views/partials/header.html b/example/webui/views/partials/header.html index 3ca1a78..f4b1b55 100644 --- a/example/webui/views/partials/header.html +++ b/example/webui/views/partials/header.html @@ -6,6 +6,9 @@ + + +