Update go-llm module version in go.mod and go.sum
This commit is contained in:
parent
24f248d900
commit
7834943cb6
@ -37,7 +37,8 @@ type Agent struct {
|
|||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Knowledge agents.Knowledge
|
Knowledge agents.Knowledge
|
||||||
Directory string
|
DataDir string
|
||||||
|
OutputDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Answer will give the model access to an ubuntu console with python and pip installed, and then ask the model to
|
// Answer will give the model access to an ubuntu console with python and pip installed, and then ask the model to
|
||||||
@ -48,7 +49,7 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
a.Agent = agents.NewAgent(a.Model, gollm.NewToolBox()).WithMaxCalls(200)
|
a.Agent = agents.NewAgent(a.Model, gollm.NewToolBox()).WithMaxCalls(200)
|
||||||
|
|
||||||
if a.MaxCommands <= 0 {
|
if a.MaxCommands <= 0 {
|
||||||
a.MaxCommands = 10000
|
a.MaxCommands = 20 // Default to 20 commands as per requirements
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Knowledge = agents.Knowledge{
|
res.Knowledge = agents.Knowledge{
|
||||||
@ -56,16 +57,25 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
RemainingQuestions: questions,
|
RemainingQuestions: questions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a temporary scratch directory
|
// create temporary directories for data and output
|
||||||
dir, err := os.MkdirTemp("", "console-")
|
dataDir, err := os.MkdirTemp("", "console-data-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Directory = dir
|
outputDir, err := os.MkdirTemp("", "console-output-")
|
||||||
|
if err != nil {
|
||||||
|
os.RemoveAll(dataDir)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res.DataDir = dataDir
|
||||||
|
res.OutputDir = outputDir
|
||||||
|
|
||||||
cl, err := client.NewClientWithOpts(client.FromEnv)
|
cl, err := client.NewClientWithOpts(client.FromEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
os.RemoveAll(dataDir)
|
||||||
|
os.RemoveAll(outputDir)
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
defer cl.Close()
|
defer cl.Close()
|
||||||
@ -73,8 +83,13 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
mounts := []mount.Mount{
|
mounts := []mount.Mount{
|
||||||
{
|
{
|
||||||
Type: mount.TypeBind,
|
Type: mount.TypeBind,
|
||||||
Source: dir,
|
Source: dataDir,
|
||||||
Target: "/home/user",
|
Target: "/data",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: mount.TypeBind,
|
||||||
|
Source: outputDir,
|
||||||
|
Target: "/output",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,25 +98,29 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
Image: "ubuntu:latest",
|
Image: "ubuntu:latest",
|
||||||
Cmd: []string{"tail", "-f", "/dev/null"},
|
Cmd: []string{"tail", "-f", "/dev/null"},
|
||||||
Tty: true,
|
Tty: true,
|
||||||
WorkingDir: "/home/user",
|
WorkingDir: "/data",
|
||||||
},
|
},
|
||||||
HostConfig: &container.HostConfig{
|
HostConfig: &container.HostConfig{
|
||||||
AutoRemove: true,
|
AutoRemove: true,
|
||||||
Mounts: mounts,
|
Mounts: mounts,
|
||||||
},
|
},
|
||||||
Name: filepath.Base(dir),
|
Name: filepath.Base(dataDir),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
os.RemoveAll(dataDir)
|
||||||
|
os.RemoveAll(outputDir)
|
||||||
return res, fmt.Errorf("failed to create container: %w", err)
|
return res, fmt.Errorf("failed to create container: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = c.Close(ctx)
|
_ = c.Close(ctx)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
slog.Info("starting container", "dir", dir, "container", fmt.Sprintf("%+v", c))
|
slog.Info("starting container", "dataDir", dataDir, "outputDir", outputDir, "container", fmt.Sprintf("%+v", c))
|
||||||
err = c.Start(ctx)
|
err = c.Start(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
os.RemoveAll(dataDir)
|
||||||
|
os.RemoveAll(outputDir)
|
||||||
return res, fmt.Errorf("failed to start container: %w", err)
|
return res, fmt.Errorf("failed to start container: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,10 +129,15 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
var history executions
|
var history executions
|
||||||
var keepGoing = true
|
var keepGoing = true
|
||||||
|
|
||||||
opwd, epwd := c.Execute(ctx, "ls -al /home")
|
// Initial setup - install basic tools
|
||||||
|
setupCmd, setupErr := c.Sudo(ctx, "apt-get update && apt-get install -y curl wget git python3 python3-pip")
|
||||||
fmt.Println(opwd)
|
if setupErr == nil {
|
||||||
slog.Info("pwd", "pwd", opwd, "epwd", epwd)
|
history = append(history, execution{
|
||||||
|
Command: "sudo apt-get update && apt-get install -y curl wget git python3 python3-pip",
|
||||||
|
Output: setupCmd,
|
||||||
|
WhatILearned: []string{"Basic tools installed: curl, wget, git, python3, pip"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
tools := map[string]gollm.Function{
|
tools := map[string]gollm.Function{
|
||||||
"exit": gollm.NewFunction(
|
"exit": gollm.NewFunction(
|
||||||
@ -126,18 +150,24 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
return "exiting", nil
|
return "exiting", nil
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"write": gollm.NewFunction(
|
"write_data": gollm.NewFunction(
|
||||||
"write",
|
"write_data",
|
||||||
"write a file in the /root directory",
|
"write a file in the /data directory",
|
||||||
func(ctx *gollm.Context, args struct {
|
func(ctx *gollm.Context, args struct {
|
||||||
Filename string `description:"The name of the file to write"`
|
Filename string `description:"The name of the file to write"`
|
||||||
Content string `description:"The content of the file to write"`
|
Content string `description:"The content of the file to write"`
|
||||||
}) (any, error) {
|
}) (any, error) {
|
||||||
target, err := SafeJoinPath(dir, args.Filename)
|
target, err := SafeJoinPath(dataDir, args.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
dir := filepath.Dir(target)
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
f, err := os.Create(target)
|
f, err := os.Create(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -149,16 +179,67 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return "wrote file", nil
|
return "wrote file to /data/" + args.Filename, nil
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"read": gollm.NewFunction(
|
"write_output": gollm.NewFunction(
|
||||||
"read",
|
"write_output",
|
||||||
"read a file in the /root directory",
|
"write a file in the /output directory (files here will be available after the agent completes)",
|
||||||
|
func(ctx *gollm.Context, args struct {
|
||||||
|
Filename string `description:"The name of the file to write"`
|
||||||
|
Content string `description:"The content of the file to write"`
|
||||||
|
}) (any, error) {
|
||||||
|
target, err := SafeJoinPath(outputDir, args.Filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
dir := filepath.Dir(target)
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(target)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = f.WriteString(args.Content)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "wrote file to /output/" + args.Filename, nil
|
||||||
|
}),
|
||||||
|
|
||||||
|
"read_data": gollm.NewFunction(
|
||||||
|
"read_data",
|
||||||
|
"read a file from the /data directory",
|
||||||
func(ctx *gollm.Context, args struct {
|
func(ctx *gollm.Context, args struct {
|
||||||
Filename string `description:"The name of the file to read"`
|
Filename string `description:"The name of the file to read"`
|
||||||
}) (any, error) {
|
}) (any, error) {
|
||||||
target, err := SafeJoinPath(dir, args.Filename)
|
target, err := SafeJoinPath(dataDir, args.Filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := os.ReadFile(target)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b), nil
|
||||||
|
}),
|
||||||
|
|
||||||
|
"read_output": gollm.NewFunction(
|
||||||
|
"read_output",
|
||||||
|
"read a file from the /output directory",
|
||||||
|
func(ctx *gollm.Context, args struct {
|
||||||
|
Filename string `description:"The name of the file to read"`
|
||||||
|
}) (any, error) {
|
||||||
|
target, err := SafeJoinPath(outputDir, args.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -176,9 +257,11 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
"execute a command in the container",
|
"execute a command in the container",
|
||||||
func(ctx *gollm.Context, args struct {
|
func(ctx *gollm.Context, args struct {
|
||||||
Command string `description:"The command to execute"`
|
Command string `description:"The command to execute"`
|
||||||
|
Learn []string `description:"What you learned from this command (optional)"`
|
||||||
|
ToLearn []string `description:"What you still need to learn (optional)"`
|
||||||
}) (any, error) {
|
}) (any, error) {
|
||||||
if len(history) >= a.MaxCommands {
|
if len(history) >= a.MaxCommands {
|
||||||
return "too many commands", nil
|
return "Command limit reached. You've used all " + fmt.Sprintf("%d", a.MaxCommands) + " allowed commands.", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.OnCommandStart != nil {
|
if a.OnCommandStart != nil {
|
||||||
@ -206,69 +289,77 @@ func (a Agent) Answer(ctx context.Context, questions []string) (Response, error)
|
|||||||
history = append(history, execution{
|
history = append(history, execution{
|
||||||
Command: args.Command,
|
Command: args.Command,
|
||||||
Output: res,
|
Output: res,
|
||||||
|
WhatILearned: args.Learn,
|
||||||
|
WhatIStillNeedToLearn: args.ToLearn,
|
||||||
})
|
})
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"sudo": gollm.NewFunction(
|
"summarize_knowledge": gollm.NewFunction(
|
||||||
"sudo",
|
"summarize_knowledge",
|
||||||
"execute a command in the container",
|
"summarize what you've learned so far",
|
||||||
func(ctx *gollm.Context, args struct {
|
func(ctx *gollm.Context, args struct{}) (any, error) {
|
||||||
Command string `description:"The command to execute"`
|
var learned []string
|
||||||
}) (any, error) {
|
var toLearn []string
|
||||||
if len(history) >= a.MaxCommands {
|
|
||||||
return "too many commands", nil
|
for _, exec := range history {
|
||||||
|
learned = append(learned, exec.WhatILearned...)
|
||||||
|
toLearn = append(toLearn, exec.WhatIStillNeedToLearn...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.OnCommandStart != nil {
|
summary := "Knowledge Summary:\n"
|
||||||
err := a.OnCommandStart(ctx, args.Command)
|
if len(learned) > 0 {
|
||||||
if err != nil {
|
summary += "What I've learned:\n- " + strings.Join(learned, "\n- ") + "\n\n"
|
||||||
return "", err
|
} else {
|
||||||
}
|
summary += "I haven't learned anything specific yet.\n\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.Sudo(ctx, args.Command)
|
if len(toLearn) > 0 {
|
||||||
|
summary += "What I still need to learn:\n- " + strings.Join(toLearn, "\n- ")
|
||||||
if a.OnCommandDone != nil {
|
} else {
|
||||||
err = a.OnCommandDone(ctx, args.Command, res, nil)
|
summary += "I don't have any specific learning goals at the moment."
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
return summary, nil
|
||||||
res = "error executing: " + err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
history = append(history, execution{
|
|
||||||
Command: "sudo " + args.Command,
|
|
||||||
Output: res,
|
|
||||||
})
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < a.MaxCommands && len(history) < a.MaxCommands && keepGoing; i++ {
|
for i := 0; i < a.MaxCommands && len(history) < a.MaxCommands && keepGoing; i++ {
|
||||||
|
|
||||||
systemPrompt := `You are now in a shell in a container of the ubuntu:latest image to answer a question asked by the user, it is very basic install of ubuntu, simple things (like python) are not preinstalled but can be installed via apt. You will be run multiple times and gain knowledge throughout the process.`
|
systemPrompt := `You are now in a shell in a container of the ubuntu:latest image to answer a question asked by the user.
|
||||||
|
You have full control over a bash shell inside this Docker container.
|
||||||
|
|
||||||
if len(history) < a.MaxCommands {
|
Important directories:
|
||||||
systemPrompt += `You can run any command you like to get to the needed results.`
|
- /data: A temporary directory with the lifespan of your processing. Use this for working files.
|
||||||
}
|
- /output: Files placed here will be returned to the caller after you're done. Use this for final results.
|
||||||
|
|
||||||
systemPrompt += `Alternatively, you can use the tool "write" to write a file in the home directory, and also the tool "read" to read a file in the home directory.
|
You can execute up to ` + fmt.Sprintf("%d", a.MaxCommands) + ` commands total. You're currently on command ` + fmt.Sprintf("%d", len(history)+1) + ` of ` + fmt.Sprintf("%d", a.MaxCommands) + `.
|
||||||
When you are done, please use "exit" to exit the container.
|
|
||||||
Respond with any number of commands to answer the question, they will be executed in order.`
|
For each command, you should:
|
||||||
|
1. Think about what you need to learn
|
||||||
|
2. Execute the command using the "execute" function
|
||||||
|
3. Analyze the output and record what you learned
|
||||||
|
4. Plan your next command based on this knowledge
|
||||||
|
|
||||||
|
You can write files directly to /data or /output using the write_data and write_output functions.
|
||||||
|
When you are done, use "exit" to exit the container.`
|
||||||
|
|
||||||
var toolbox []gollm.Function
|
var toolbox []gollm.Function
|
||||||
|
|
||||||
// add unrestricted tools
|
// Add all tools
|
||||||
toolbox = append(toolbox, tools["exit"], tools["write"], tools["read"])
|
toolbox = append(toolbox,
|
||||||
|
tools["exit"],
|
||||||
|
tools["write_data"],
|
||||||
|
tools["write_output"],
|
||||||
|
tools["read_data"],
|
||||||
|
tools["read_output"],
|
||||||
|
tools["summarize_knowledge"],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Only add execute if we haven't reached the command limit
|
||||||
if len(history) < a.MaxCommands {
|
if len(history) < a.MaxCommands {
|
||||||
toolbox = append(toolbox, tools["execute"], tools["sudo"])
|
toolbox = append(toolbox, tools["execute"])
|
||||||
}
|
}
|
||||||
|
|
||||||
kw := shared.KnowledgeWorker{
|
kw := shared.KnowledgeWorker{
|
||||||
|
25
pkg/agents/console.go
Normal file
25
pkg/agents/console.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package agents
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gitea.stevedudenhoeffer.com/steve/answer/pkg/agents/console"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Console will construct a console.Agent, execute the agent, and return the knowledge gained, the output directory,
|
||||||
|
// and any possible error.
|
||||||
|
func (a Agent) Console(ctx context.Context, questions []string) (Knowledge, string, error) {
|
||||||
|
con := console.Agent{
|
||||||
|
Agent: a,
|
||||||
|
Model: a.model,
|
||||||
|
ContextualInformation: a.contextualInformation,
|
||||||
|
MaxCommands: 50,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := con.Answer(ctx, questions)
|
||||||
|
if err != nil {
|
||||||
|
return Knowledge{}, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Knowledge, res.Directory, nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user