From 744af1902583b688f8a6b3c39c6c001599e43d4b Mon Sep 17 00:00:00 2001 From: mudler Date: Thu, 4 Apr 2024 22:44:59 +0200 Subject: [PATCH] WIP? --- agent/actions.go | 84 +++++++++++++++++++++++++++++++++++++-------- agent/agent_test.go | 2 +- agent/templates.go | 2 ++ 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/agent/actions.go b/agent/actions.go index d433423..5fbff1c 100644 --- a/agent/actions.go +++ b/agent/actions.go @@ -169,13 +169,13 @@ func (a *Agent) prepareHUD() PromptHUD { } } -func (a *Agent) prepareConversationParse(templ string, messages []openai.ChatCompletionMessage, canReply bool, reasoning string) ([]openai.ChatCompletionMessage, Actions, []string, error) { +func (a *Agent) prepareConversationParse(templ string, messages []openai.ChatCompletionMessage, canReply bool, reasoning string) ([]openai.ChatCompletionMessage, Actions, error) { // prepare the prompt prompt := bytes.NewBuffer([]byte{}) promptTemplate, err := template.New("pickAction").Parse(templ) if err != nil { - return nil, []Action{}, nil, err + return nil, []Action{}, err } actions := a.systemActions() @@ -207,19 +207,13 @@ func (a *Agent) prepareConversationParse(templ string, messages []openai.ChatCom HUD: promptHUD, }) if err != nil { - return nil, []Action{}, nil, err + return nil, []Action{}, err } if a.options.debugMode { fmt.Println("=== PROMPT START ===", prompt.String(), "=== PROMPT END ===") } - // Get all the available actions IDs - actionsID := []string{} - for _, m := range actions { - actionsID = append(actionsID, m.Definition().Name.String()) - } - conversation := []openai.ChatCompletionMessage{} conversation = append(conversation, openai.ChatCompletionMessage{ @@ -227,18 +221,75 @@ func (a *Agent) prepareConversationParse(templ string, messages []openai.ChatCom Content: prompt.String(), }) - return conversation, actions, actionsID, nil + return conversation, actions, nil } // pickAction picks an action based on the conversation func (a *Agent) pickAction(ctx context.Context, templ string, messages []openai.ChatCompletionMessage, canReply bool) (Action, string, error) { - conversation, actions, actionsID, err := a.prepareConversationParse(templ, messages, canReply, "") + c := messages + + // prepare the prompt + prompt := bytes.NewBuffer([]byte{}) + + promptTemplate, err := template.New("pickAction").Parse(templ) if err != nil { return nil, "", err } + + actions := a.systemActions() + if !canReply { + actions = a.systemInternalActions() + } + + // Get all the actions definitions + definitions := []action.ActionDefinition{} + for _, m := range actions { + definitions = append(definitions, m.Definition()) + } + + var promptHUD *PromptHUD + if a.options.enableHUD { + h := a.prepareHUD() + promptHUD = &h + } + + err = promptTemplate.Execute(prompt, struct { + HUD *PromptHUD + Actions []action.ActionDefinition + Reasoning string + Messages []openai.ChatCompletionMessage + }{ + Actions: definitions, + Messages: messages, + HUD: promptHUD, + }) + if err != nil { + return nil, "", err + } + // Get the LLM to think on what to do + // and have a thought + + found := false + for _, cc := range c { + if cc.Content == prompt.String() { + found = true + break + } + } + if !found { + c = append([]openai.ChatCompletionMessage{ + { + Role: "system", + Content: prompt.String(), + }, + }, c...) + } + + // We also could avoid to use functions here and get just a reply from the LLM + // and then use the reply to get the action thought, err := a.decision(ctx, - conversation, + c, Actions{action.NewReasoning()}.ToTools(), action.NewReasoning().Definition().Name) if err != nil { @@ -256,10 +307,15 @@ func (a *Agent) pickAction(ctx context.Context, templ string, messages []openai. reason = thought.message } - // Decode tool call + // From the thought, get the action call + // Get all the available actions IDs + actionsID := []string{} + for _, m := range actions { + actionsID = append(actionsID, m.Definition().Name.String()) + } intentionsTools := action.NewIntention(actionsID...) params, err := a.decision(ctx, - append(conversation, openai.ChatCompletionMessage{ + append(c, openai.ChatCompletionMessage{ Role: "assistent", Content: reason, }), diff --git a/agent/agent_test.go b/agent/agent_test.go index f86d3d6..c4398fb 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -167,7 +167,7 @@ var _ = Describe("Agent test", func() { Expect(agent.State().Goal).To(ContainSubstring("guitar"), fmt.Sprint(agent.State())) }) - FIt("it automatically performs things in the background", func() { + It("it automatically performs things in the background", func() { agent, err := New( WithLLMAPIURL(apiModel), WithModel(testModel), diff --git a/agent/templates.go b/agent/templates.go index 5cb6c47..301a241 100644 --- a/agent/templates.go +++ b/agent/templates.go @@ -53,9 +53,11 @@ You can take any of the following tools: To answer back to the user, use the "reply" tool. Given the text below, decide which action to take and explain the detailed reasoning behind it. For answering without picking a choice, reply with 'none'. +{{if .Messages}} {{range .Messages -}} {{.Role}}{{if .FunctionCall}}(tool_call){{.FunctionCall}}{{end}}: {{if .FunctionCall}}{{.FunctionCall}}{{else if .ToolCalls -}}{{range .ToolCalls -}}{{.Name}} called with {{.Arguments}}{{end}}{{ else }}{{.Content -}}{{end}} {{end}} +{{end}} {{if .Reasoning}}Reasoning: {{.Reasoning}}{{end}} `