sub-sequent action support

This commit is contained in:
Ettore Di Giacinto
2024-04-01 00:45:32 +02:00
parent eb4294bdbb
commit f2e09dfe81
4 changed files with 57 additions and 31 deletions

View File

@@ -187,7 +187,7 @@ func (a *Agent) pickAction(ctx context.Context, templ string, messages []openai.
reason = thought.message reason = thought.message
} }
fmt.Println(reason) fmt.Println("---- Thought: " + reason)
// Decode tool call // Decode tool call
intentionsTools := action.NewIntention(actionsID...) intentionsTools := action.NewIntention(actionsID...)

View File

@@ -21,6 +21,8 @@ type Agent struct {
context *action.ActionContext context *action.ActionContext
availableActions []Action availableActions []Action
currentReasoning string
nextAction Action
currentConversation []openai.ChatCompletionMessage currentConversation []openai.ChatCompletionMessage
} }

View File

@@ -11,13 +11,23 @@ import (
) )
const testActionResult = "In Boston it's 30C today, it's sunny, and humidity is at 98%" const testActionResult = "In Boston it's 30C today, it's sunny, and humidity is at 98%"
const testActionResult2 = "In milan it's very hot today"
var _ Action = &TestAction{} var _ Action = &TestAction{}
type TestAction struct{} type TestAction struct {
response []string
responseN int
}
func (a *TestAction) Run(action.ActionParams) (string, error) { func (a *TestAction) Run(action.ActionParams) (string, error) {
return testActionResult, nil res := a.response[a.responseN]
if len(a.response) == a.responseN {
a.responseN = 0
} else {
a.responseN++
}
return res, nil
} }
func (a *TestAction) Definition() action.ActionDefinition { func (a *TestAction) Definition() action.ActionDefinition {
@@ -46,7 +56,21 @@ var _ = Describe("Agent test", func() {
WithLLMAPIURL(apiModel), WithLLMAPIURL(apiModel),
WithModel(testModel), WithModel(testModel),
// WithRandomIdentity(), // WithRandomIdentity(),
WithActions(&TestAction{}), WithActions(&TestAction{response: []string{testActionResult, testActionResult2}}),
)
Expect(err).ToNot(HaveOccurred())
go agent.Run()
defer agent.Stop()
res := agent.Ask("can you get the weather in boston, and afterward of Milano, Italy?", "")
Expect(res).To(ContainElement(testActionResult), fmt.Sprint(res))
Expect(res).To(ContainElement(testActionResult2), fmt.Sprint(res))
})
It("pick the correct action", func() {
agent, err := New(
WithLLMAPIURL(apiModel),
WithModel(testModel),
// WithRandomIdentity(),
WithActions(&TestAction{response: []string{testActionResult}}),
) )
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
go agent.Run() go agent.Run()

View File

@@ -71,7 +71,6 @@ func (j *JobResult) WaitResult() []string {
} }
func (a *Agent) consumeJob(job *Job) { func (a *Agent) consumeJob(job *Job) {
// Consume the job and generate a response // Consume the job and generate a response
a.Lock() a.Lock()
// Set the action context // Set the action context
@@ -83,11 +82,6 @@ func (a *Agent) consumeJob(job *Job) {
// TODO: Use llava to explain the image content // TODO: Use llava to explain the image content
} }
if job.Text == "" {
fmt.Println("no text!")
return
}
messages := a.currentConversation messages := a.currentConversation
if job.Text != "" { if job.Text != "" {
messages = append(messages, openai.ChatCompletionMessage{ messages = append(messages, openai.ChatCompletionMessage{
@@ -97,16 +91,28 @@ func (a *Agent) consumeJob(job *Job) {
} }
// choose an action first // choose an action first
chosenAction, reasoning, err := a.pickAction(ctx, pickActionTemplate, messages) var chosenAction Action
var reasoning string
if a.currentReasoning != "" && a.nextAction != nil {
// if we are being re-evaluated, we already have the action
// and the reasoning. Consume it here and reset it
chosenAction = a.nextAction
reasoning = a.currentReasoning
a.currentReasoning = ""
a.nextAction = nil
} else {
var err error
chosenAction, reasoning, err = a.pickAction(ctx, pickActionTemplate, messages)
if err != nil { if err != nil {
fmt.Printf("error picking action: %v\n", err) fmt.Printf("error picking action: %v\n", err)
return return
} }
}
if chosenAction == nil || chosenAction.Definition().Name.Is(action.ReplyActionName) { if chosenAction == nil || chosenAction.Definition().Name.Is(action.ReplyActionName) {
fmt.Println("No action to do, just reply") fmt.Println("No action to do, just reply")
job.Result.SetResult(reasoning) job.Result.SetResult(reasoning)
job.Result.Finish()
return return
} }
@@ -126,6 +132,7 @@ func (a *Agent) consumeJob(job *Job) {
fmt.Println("Checking action: ", action.Definition().Name, chosenAction.Definition().Name) fmt.Println("Checking action: ", action.Definition().Name, chosenAction.Definition().Name)
if action.Definition().Name == chosenAction.Definition().Name { if action.Definition().Name == chosenAction.Definition().Name {
fmt.Printf("Running action: %v\n", action.Definition().Name) fmt.Printf("Running action: %v\n", action.Definition().Name)
fmt.Printf("With parameters: %v\n", params.actionParams)
if result, err = action.Run(params.actionParams); err != nil { if result, err = action.Run(params.actionParams); err != nil {
fmt.Printf("error running action: %v\n", err) fmt.Printf("error running action: %v\n", err)
return return
@@ -133,6 +140,7 @@ func (a *Agent) consumeJob(job *Job) {
} }
} }
fmt.Printf("Action run result: %v\n", result) fmt.Printf("Action run result: %v\n", result)
job.Result.SetResult(result)
// calling the function // calling the function
messages = append(messages, openai.ChatCompletionMessage{ messages = append(messages, openai.ChatCompletionMessage{
@@ -151,6 +159,8 @@ func (a *Agent) consumeJob(job *Job) {
ToolCallID: chosenAction.Definition().Name.String(), ToolCallID: chosenAction.Definition().Name.String(),
}) })
a.currentConversation = append(a.currentConversation, messages...)
// given the result, we can now ask OpenAI to complete the conversation or // given the result, we can now ask OpenAI to complete the conversation or
// to continue using another tool given the result // to continue using another tool given the result
followingAction, reasoning, err := a.pickAction(ctx, reEvalTemplate, messages) followingAction, reasoning, err := a.pickAction(ctx, reEvalTemplate, messages)
@@ -166,6 +176,11 @@ func (a *Agent) consumeJob(job *Job) {
// The agent decided to do another action // The agent decided to do another action
fmt.Println("Another action to do: ", followingAction.Definition().Name) fmt.Println("Another action to do: ", followingAction.Definition().Name)
fmt.Println("Reasoning: ", reasoning) fmt.Println("Reasoning: ", reasoning)
// call ourselves again
a.currentReasoning = reasoning
a.nextAction = followingAction
job.Text = ""
a.consumeJob(job)
return return
} }
@@ -187,21 +202,6 @@ func (a *Agent) consumeJob(job *Job) {
fmt.Printf("OpenAI answered the original request with: %v\n", fmt.Printf("OpenAI answered the original request with: %v\n",
msg.Content) msg.Content)
messages = append(messages, msg) a.currentConversation = append(a.currentConversation, msg)
a.currentConversation = append(a.currentConversation, messages...)
if len(msg.ToolCalls) != 0 {
fmt.Printf("OpenAI wants to call again functions: %v\n", msg)
// wants to call again an action (?)
job.Text = "" // Call the job with the current conversation
job.Result.SetResult(result)
a.jobQueue <- job
return
}
// perform the action (if any)
// or reply with a result
// if there is an action...
job.Result.SetResult(result)
job.Result.Finish() job.Result.Finish()
} }