155 lines
4.2 KiB
Go
155 lines
4.2 KiB
Go
package agents
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
gollm "gitea.stevedudenhoeffer.com/steve/go-llm"
|
|
)
|
|
|
|
// AnswerQuestionWithKnowledge will take a knowledge object and use the gained knowledge to answer a question.
|
|
func (a Agent) AnswerQuestionWithKnowledge(ctx context.Context, knowledge Knowledge) (string, error) {
|
|
originalQuestions := strings.Join(knowledge.OriginalQuestions, "\n")
|
|
infoGained := ""
|
|
|
|
// group all the gained knowledge by source
|
|
var m = map[string][]string{}
|
|
for _, k := range knowledge.Knowledge {
|
|
m[k.Source] = append(m[k.Source], k.Info)
|
|
}
|
|
|
|
// now order them in a list so they can be referenced by index
|
|
type source struct {
|
|
source string
|
|
info []string
|
|
}
|
|
|
|
var sources []source
|
|
for k, v := range m {
|
|
sources = append(sources, source{
|
|
source: k,
|
|
info: v,
|
|
})
|
|
|
|
if len(infoGained) > 0 {
|
|
infoGained += "\n"
|
|
}
|
|
|
|
infoGained += strings.Join(v, "\n")
|
|
}
|
|
|
|
systemPrompt := `I am trying to answer a question, and I gathered some knowledge in an attempt to do so. Here is what I am trying to answer:
|
|
` + originalQuestions + `
|
|
|
|
Here is the knowledge I have gathered from ` + fmt.Sprint(len(sources)) + ` sources:
|
|
` + infoGained
|
|
|
|
if len(knowledge.RemainingQuestions) > 0 {
|
|
systemPrompt += "\n\nI still have some questions that I could not find an answer to:\n" + strings.Join(knowledge.RemainingQuestions, "\n")
|
|
}
|
|
|
|
systemPrompt += "\n\nUsing the sources, write an answer to the original question. Note any information that wasn't able to be answered."
|
|
|
|
res, err := a.WithSystemPrompt(systemPrompt).
|
|
WithSystemPromptSuffix(``).
|
|
WithToolbox(nil).
|
|
CallAndExecute(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
systemPrompt = `I am trying to source an analysis of information I have gathered.
|
|
To do this I will provide you with all of the sourced information I have gathered in the format of:
|
|
[Source]
|
|
- Information
|
|
- Information
|
|
- Information
|
|
|
|
Where Source will be a number from 1 to ` + fmt.Sprint(len(sources)) + ` and Information will be the information gathered from that source.
|
|
|
|
You should then read the information provided by the user and tag the information with citations from the sources provided. If a fact is provided by multiple sources, you should tag it with all of the sources that provide that information.
|
|
|
|
For instance, if the sourced data were:
|
|
[1]
|
|
- The diameter of the moon is 3,474.8 km
|
|
- The moon's age is 4.53 billion years
|
|
[2]
|
|
- The moon's age is 4.53 billion years
|
|
[3]
|
|
- The moon is on average 238,855 miles away from the Earth
|
|
|
|
And the user provided the following information:
|
|
The moon is 4.5 billion years old, 238,855 miles away from the Earth, and has a diameter of 3,474.8 km.
|
|
|
|
You would then tag the information with the sources like so:
|
|
The moon is 4.5 billion years old [1,2], 238,855 miles away from the Earth [3], and has a diameter of 3,474.8 km [1].`
|
|
|
|
providedIntel := `Here is the information I have gathered:
|
|
`
|
|
|
|
for i, s := range sources {
|
|
providedIntel += "[" + fmt.Sprint(i+1) + "]\n"
|
|
for _, info := range s.info {
|
|
providedIntel += " - " + info + "\n"
|
|
}
|
|
}
|
|
|
|
summarizedData := `Here is the I need you to source with citations:
|
|
` + res.Text
|
|
|
|
res, err = a.WithSystemPrompt(systemPrompt).
|
|
WithSystemPromptSuffix(``).
|
|
WithToolbox(nil).
|
|
CallAndExecute(ctx, gollm.Message{Role: gollm.RoleSystem, Text: providedIntel}, summarizedData)
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// now go through the response and find all citations
|
|
// use this by looking for \[[\d+,]+\]
|
|
// then use the number to find the source
|
|
|
|
re := regexp.MustCompile(`\[([\d,\s]+)]`)
|
|
|
|
// find all the citations
|
|
citations := re.FindAllString(res.Text, -1)
|
|
|
|
// now we need to find the sources
|
|
lookup := map[int][]string{}
|
|
for _, c := range citations {
|
|
c = strings.Trim(c, "[]")
|
|
a := strings.Split(c, ",")
|
|
|
|
for _, v := range a {
|
|
v = strings.TrimSpace(v)
|
|
i, _ := strconv.Atoi(v)
|
|
|
|
if i < 1 || i > len(sources) {
|
|
continue
|
|
}
|
|
|
|
lookup[i] = append(lookup[i], sources[i-1].source)
|
|
}
|
|
}
|
|
|
|
text := res.Text
|
|
|
|
if len(lookup) > 0 {
|
|
text += "\n\nHere are the sources for the information provided:\n"
|
|
|
|
for i := 1; i <= len(sources); i++ {
|
|
if _, ok := lookup[i]; !ok {
|
|
continue
|
|
}
|
|
|
|
text += "[" + fmt.Sprint(i) + "] <" + lookup[i][0] + ">\n"
|
|
}
|
|
}
|
|
|
|
return text, nil
|
|
}
|