Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions multi-agent/agent-supervisor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### Multi-Agent

Multi-agent systems consist of multiple decision-making agents that interact in a shared environment to achieve common or conflicting goals.

- [agent-supervisor](./main.go): An example of a multi-agent system with an agent supervisor to help delegate tasks. In the example, the Leader delegates tasks to RD (Research and Development) and QA (Quality Assurance), if the code doesn’t pass the test, it is sent back to RD for rewriting and then tested again, and the Leader makes corresponding decisions based on feedback, finally returning the tested code.

<img src="./agent-supervisor.png" width="400" height="400">
Binary file added multi-agent/agent-supervisor/agent-supervisor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 76 additions & 0 deletions multi-agent/agent-supervisor/leader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"fmt"
"strings"

"github.com/Rovanta/rmodel/community/processor/go_code_tester"
"github.com/Rovanta/rmodel/processor"
)

const (
memKeyDemand = "demand"
memKeyResponse = "response"
memKeyTask = "task"
memKeyFeedback = "feedback"
memKeyDecision = "decision"
)

func LeaderProcess(b processor.BrainContext) error {
// if it has no task, disassemble task from demand
if !b.ExistMemory(memKeyTask) {
task := rephraseTaskFromDemand(b.GetMemory(memKeyDemand).(string))
_ = b.SetMemory(memKeyTask, task)
_ = b.SetMemory(memKeyDecision, DecisionRD)

return nil
}
switch b.GetMemory(memKeyFeedback).(string) {
case FeedBackRD: // feedback from RD
_ = b.SetMemory(memKeyDecision, DecisionQA) // pass to QA
case FeedBackQA: // feedback from QA
ok := readTestReport(b.GetMemory(memKeyGoTestResult).(string))
if !ok {
// test result not ok, resend to RD
_ = b.SetMemory(memKeyDecision, DecisionRD)
} else {
// pretty response from codes
resp := genResponse(b)
_ = b.SetMemory(memKeyResponse, resp)
_ = b.SetMemory(memKeyDecision, DecisionResponse)
}
default:
return fmt.Errorf("unknown feedback: %v\n", b.GetMemory(memKeyFeedback))
}

return nil
}

func rephraseTaskFromDemand(demand string) string {
// TODO maybe use completion LLM to rephrase demand to task
task := demand

return task
}

func readTestReport(testResult string) bool {
return !strings.Contains(testResult, "FAIL")
}

func genResponse(b processor.BrainContextReader) string {
codes := b.GetMemory(memKeyCodes).(*go_code_tester.Codes).String()
testReport := b.GetMemory(memKeyGoTestResult).(string)

var builder strings.Builder
builder.WriteString("Dear Boss: \n")
builder.WriteString("After the efforts of our RD team and QA team, the final codes and test report are produced as follows:\n\n")
builder.WriteString("==========\n\nCodes:\n\n")
builder.WriteString(codes)
builder.WriteString("==========\n\nTest Report:\n\n")
builder.WriteString("```shell\n")
builder.WriteString(testReport)
builder.WriteString("```")
builder.WriteString("\n")

return builder.String()
}
106 changes: 106 additions & 0 deletions multi-agent/agent-supervisor/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package main

import (
"fmt"

"github.com/Rovanta/rmodel"
"github.com/Rovanta/rmodel/brainlocal"
"github.com/Rovanta/rmodel/processor"
)

var (
FeedBackRD = "RD"
FeedBackQA = "QA"
DecisionRD = "RD"
DecisionQA = "QA"
DecisionResponse = "Response"
)

func main() {
bp := rModel.NewBlueprint()

neuronLeader := bp.AddNeuron(LeaderProcess)
neuronQA := bp.AddNeuron(QAProcess)
neuronRD := bp.AddNeuronWithProcessor(NewRDProcessor())

_, _ = bp.AddEntryLinkTo(neuronLeader)
// leader out-link
rdLink, _ := bp.AddLink(neuronLeader, neuronRD)
qaLink, _ := bp.AddLink(neuronLeader, neuronQA)
endLink, _ := bp.AddEndLinkFrom(neuronLeader)

// leader in-link
_, _ = bp.AddLink(neuronRD, neuronLeader)
_, _ = bp.AddLink(neuronQA, neuronLeader)

_ = neuronLeader.AddCastGroup(DecisionRD, rdLink)
_ = neuronLeader.AddCastGroup(DecisionQA, qaLink)
_ = neuronLeader.AddCastGroup(DecisionResponse, endLink)
neuronLeader.BindCastGroupSelectFunc(func(bcr processor.BrainContextReader) string {
return bcr.GetMemory(memKeyDecision).(string)
})

brain := brainlocal.BuildBrain(bp)
_ = brain.EntryWithMemory(memKeyDemand, "Help me write a function `func Add (x, y int) int` with golang to implement addition, and implement unit test in a separate _test .go file, at least 3 test cases are required")
brain.Wait()
fmt.Printf("Response: %s\n", brain.GetMemory(memKeyResponse).(string))

/*
Response: Dear Boss:
After the efforts of our RD team and QA team, the final codes and test report are produced as follows:

==========

Codes:

**add.go**

```go
package main

func Add(x, y int) int {
return x + y
}
```

**add_test.go**

```go
package main

import "testing"

func TestAdd(t *testing.T) {
cases := []struct {
x, y, expected int
}{
{1, 2, 3},
{-1, 1, 0},
{0, 0, 0},
}

for _, c := range cases {
result := Add(c.x, c.y)
if result != c.expected {
t.Errorf("Add(%d, %d) == %d, expected %d", c.x, c.y, result, c.expected)
}
}
}
```

==========

Test Report:

```shell
#go test -v -run .
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok gocodetester 0.411s

```


*/
}
23 changes: 23 additions & 0 deletions multi-agent/agent-supervisor/qa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"github.com/Rovanta/rmodel/community/processor/go_code_tester"
"github.com/Rovanta/rmodel/processor"
)

const (
memKeyGoTestResult = "go_test_result"
)

func QAProcess(b processor.BrainContext) error {
p := go_code_tester.NewProcessor().WithTestCodeKeep(true)
if err := p.Process(b); err != nil {
return err
}

if err := b.SetMemory(memKeyFeedback, b.GetCurrentNeuronID()); err != nil {
return err
}

return nil
}
95 changes: 95 additions & 0 deletions multi-agent/agent-supervisor/rd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

import (
"fmt"
"os"

"github.com/sashabaranov/go-openai"
"github.com/Rovanta/rmodel/community/processor/go_code_tester"
"github.com/Rovanta/rmodel/community/processor/openai_structured_output"
"github.com/Rovanta/rmodel/processor"
)

var (
memKeyCodes = (&go_code_tester.Codes{}).FunctionName()
)

func NewRDProcessor() *CoderProcessor {
return &CoderProcessor{
clientConfig: openai.DefaultConfig(os.Getenv("OPENAI_API_KEY")),
requestConfig: openai_structured_output.RequestConfig{
Model: openai.GPT3Dot5Turbo0125,
Temperature: 0.7,
Stream: false,
},
}
}

type CoderProcessor struct {
clientConfig openai.ClientConfig
client *openai.Client
requestConfig openai_structured_output.RequestConfig
}

func (p *CoderProcessor) Process(b processor.BrainContext) error {
var prompt string
if !b.ExistMemory(memKeyCodes) {
// read task, write code
prompt = fmt.Sprintf(`{{.%s}}`, memKeyTask)
} else {
// read task, old code and test result, write code
prompt = fmt.Sprintf(`{{.%s}}

My code is as follows:

%s

test result is as follows:

%s

Help me correct my code.
`, memKeyTask, b.GetMemory(memKeyCodes).(*go_code_tester.Codes).String(), memKeyGoTestResult)
}

structuredOutput := p.newStructuredOutputProcessor(prompt)
if err := structuredOutput.Process(b); err != nil {
return err
}

if err := b.SetMemory(memKeyFeedback, b.GetCurrentNeuronID()); err != nil {
return err
}

return nil
}

func (p *CoderProcessor) Clone() processor.Processor {
return &CoderProcessor{
requestConfig: p.requestConfig,
clientConfig: p.clientConfig,
client: nil,
}
}

func (p *CoderProcessor) WithClientConfig(clientConfig openai.ClientConfig) *CoderProcessor {
p.clientConfig = clientConfig
return p
}

func (p *CoderProcessor) WithClient(client *openai.Client) *CoderProcessor {
p.client = client
return p
}

func (p *CoderProcessor) WithRequestConfig(requestConfig openai_structured_output.RequestConfig) *CoderProcessor {
p.requestConfig = requestConfig
return p
}

func (p *CoderProcessor) newStructuredOutputProcessor(prompt string) *openai_structured_output.OpenAIStructuredOutputProcessor {
proc := openai_structured_output.NewProcessor()
_ = proc.WithPromptTemplate(prompt)
_ = proc.WithOutputStructDefinition(go_code_tester.Codes{}, (go_code_tester.Codes{}).FunctionName(), (go_code_tester.Codes{}).FunctionDescription())
return proc.WithClientConfig(p.clientConfig).WithRequestConfig(p.requestConfig)
}