answer/pkg/agents/knowledge_processor.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
}