Skip to content

Commit deda30d

Browse files
committed
feat: agent support cognitive about ReAct and skills
1 parent 6d2957e commit deda30d

6 files changed

Lines changed: 1190 additions & 9 deletions

File tree

agent.go

Lines changed: 343 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"strings"
78
"sync"
9+
"time"
810

911
"github.com/voocel/mas/llm"
1012
)
@@ -20,7 +22,13 @@ type agent struct {
2022
state map[string]interface{}
2123
provider llm.Provider
2224
eventBus EventBus
23-
mu sync.RWMutex
25+
26+
// Cognitive ability
27+
skillLibrary SkillLibrary
28+
cognitiveState *CognitiveState
29+
cognitiveMode CognitiveMode
30+
31+
mu sync.RWMutex
2432
}
2533

2634
func NewAgent(model, apiKey string) Agent {
@@ -30,13 +38,22 @@ func NewAgent(model, apiKey string) Agent {
3038
}
3139

3240
return &agent{
33-
name: "assistant",
34-
model: model,
35-
temperature: 0.7,
36-
maxTokens: 2000,
37-
tools: make([]Tool, 0),
38-
state: make(map[string]interface{}),
39-
provider: provider,
41+
name: "assistant",
42+
model: model,
43+
temperature: 0.7,
44+
maxTokens: 2000,
45+
tools: make([]Tool, 0),
46+
state: make(map[string]interface{}),
47+
provider: provider,
48+
skillLibrary: NewSkillLibrary(),
49+
cognitiveState: &CognitiveState{
50+
CurrentLayer: CortexLayer,
51+
Mode: AutomaticMode,
52+
LoadedSkills: make([]string, 0),
53+
RecentDecisions: make([]*Decision, 0),
54+
LastUpdate: time.Now(),
55+
},
56+
cognitiveMode: AutomaticMode,
4057
}
4158
}
4259

@@ -525,3 +542,321 @@ func (a *agent) convertToolsToLLMFormat() []llm.ToolDefinition {
525542

526543
return tools
527544
}
545+
546+
// Cognitive capabilities implementation
547+
548+
func (a *agent) Plan(ctx context.Context, goal string) (*Plan, error) {
549+
a.mu.Lock()
550+
defer a.mu.Unlock()
551+
552+
// Emit planning event
553+
if err := a.PublishEvent(ctx, EventType("agent.plan.start"), EventData(
554+
"goal", goal,
555+
"agent_name", a.name,
556+
)); err != nil {
557+
fmt.Printf("Failed to publish plan start event: %v\n", err)
558+
}
559+
560+
// Use LLM to generate plan
561+
planPrompt := fmt.Sprintf(`Create a step-by-step plan to achieve this goal: %s
562+
563+
Please respond with a structured plan including:
564+
1. Break down into logical steps
565+
2. Identify which cognitive layer each step should use:
566+
- reflex: immediate responses
567+
- cerebellum: learned skills
568+
- cortex: reasoning and analysis
569+
- meta: high-level planning
570+
571+
Goal: %s`, goal, goal)
572+
573+
response, err := a.Chat(ctx, planPrompt)
574+
if err != nil {
575+
return nil, fmt.Errorf("failed to generate plan: %w", err)
576+
}
577+
578+
plan := NewPlan(goal)
579+
plan.Context["llm_response"] = response
580+
581+
// Update cognitive state
582+
a.cognitiveState.ActivePlan = plan
583+
a.cognitiveState.CurrentLayer = MetaLayer
584+
a.cognitiveState.LastUpdate = time.Now()
585+
586+
// Emit planning completion event
587+
if err := a.PublishEvent(ctx, EventType("agent.plan.complete"), EventData(
588+
"plan_id", plan.ID,
589+
"goal", goal,
590+
"agent_name", a.name,
591+
)); err != nil {
592+
fmt.Printf("Failed to publish plan complete event: %v\n", err)
593+
}
594+
595+
return plan, nil
596+
}
597+
598+
func (a *agent) Reason(ctx context.Context, situation *Situation) (*Decision, error) {
599+
a.mu.Lock()
600+
defer a.mu.Unlock()
601+
602+
// Emit reasoning event
603+
if err := a.PublishEvent(ctx, EventType("agent.reason.start"), EventData(
604+
"situation", situation,
605+
"agent_name", a.name,
606+
)); err != nil {
607+
fmt.Printf("Failed to publish reason start event: %v\n", err)
608+
}
609+
610+
// Create reasoning prompt
611+
reasoningPrompt := fmt.Sprintf(`Analyze this situation and make a decision:
612+
613+
Context: %v
614+
Inputs: %v
615+
Constraints: %v
616+
Goals: %v
617+
618+
Please provide:
619+
1. Your analysis of the situation
620+
2. Recommended action
621+
3. Confidence level (0-1)
622+
4. Which cognitive layer to use for execution`,
623+
situation.Context, situation.Inputs, situation.Constraints, situation.Goals)
624+
625+
response, err := a.Chat(ctx, reasoningPrompt)
626+
if err != nil {
627+
return nil, fmt.Errorf("failed to reason about situation: %w", err)
628+
}
629+
630+
// Create decision
631+
decision := &Decision{
632+
Action: extractAction(response),
633+
Layer: CortexLayer,
634+
Confidence: extractConfidence(response),
635+
Reasoning: response,
636+
Parameters: make(map[string]interface{}),
637+
Timestamp: time.Now(),
638+
}
639+
640+
// Update cognitive state
641+
a.cognitiveState.RecentDecisions = append(a.cognitiveState.RecentDecisions, decision)
642+
if len(a.cognitiveState.RecentDecisions) > 10 {
643+
a.cognitiveState.RecentDecisions = a.cognitiveState.RecentDecisions[1:]
644+
}
645+
a.cognitiveState.CurrentLayer = CortexLayer
646+
a.cognitiveState.LastUpdate = time.Now()
647+
648+
// Emit reasoning completion event
649+
if err := a.PublishEvent(ctx, EventType("agent.reason.complete"), EventData(
650+
"decision", decision,
651+
"agent_name", a.name,
652+
)); err != nil {
653+
fmt.Printf("Failed to publish reason complete event: %v\n", err)
654+
}
655+
656+
return decision, nil
657+
}
658+
659+
func (a *agent) ExecuteSkill(ctx context.Context, skillName string, params map[string]interface{}) (interface{}, error) {
660+
a.mu.RLock()
661+
skill, err := a.skillLibrary.GetSkill(skillName)
662+
a.mu.RUnlock()
663+
664+
if err != nil {
665+
return nil, fmt.Errorf("skill not found: %w", err)
666+
}
667+
668+
// Emit skill execution event
669+
if err := a.PublishEvent(ctx, EventType("agent.skill.start"), EventData(
670+
"skill_name", skillName,
671+
"parameters", params,
672+
"agent_name", a.name,
673+
)); err != nil {
674+
fmt.Printf("Failed to publish skill start event: %v\n", err)
675+
}
676+
677+
// Execute skill
678+
result, err := skill.Execute(ctx, params)
679+
680+
// Update cognitive state
681+
a.mu.Lock()
682+
a.cognitiveState.CurrentLayer = skill.Layer()
683+
a.cognitiveState.LastUpdate = time.Now()
684+
a.mu.Unlock()
685+
686+
// Emit skill completion event
687+
eventType := EventType("agent.skill.complete")
688+
eventData := EventData(
689+
"skill_name", skillName,
690+
"parameters", params,
691+
"agent_name", a.name,
692+
"success", err == nil,
693+
)
694+
695+
if err != nil {
696+
eventType = EventType("agent.skill.error")
697+
eventData["error"] = err.Error()
698+
} else {
699+
eventData["result"] = result
700+
}
701+
702+
if publishErr := a.PublishEvent(ctx, eventType, eventData); publishErr != nil {
703+
fmt.Printf("Failed to publish skill event: %v\n", publishErr)
704+
}
705+
706+
return result, err
707+
}
708+
709+
func (a *agent) React(ctx context.Context, stimulus *Stimulus) (*Action, error) {
710+
a.mu.Lock()
711+
defer a.mu.Unlock()
712+
713+
// Emit reaction event
714+
if err := a.PublishEvent(ctx, EventType("agent.react.start"), EventData(
715+
"stimulus", stimulus,
716+
"agent_name", a.name,
717+
)); err != nil {
718+
fmt.Printf("Failed to publish react start event: %v\n", err)
719+
}
720+
721+
// Quick reactive response based on stimulus urgency
722+
var action *Action
723+
724+
if stimulus.Urgency > 0.8 {
725+
// High urgency - immediate reflex response
726+
action = &Action{
727+
Type: "immediate_response",
728+
Layer: ReflexLayer,
729+
Parameters: stimulus.Data,
730+
Priority: int(stimulus.Urgency * 100),
731+
Timestamp: time.Now(),
732+
}
733+
} else {
734+
// Lower urgency - deliberate response
735+
action = &Action{
736+
Type: "deliberate_response",
737+
Layer: CortexLayer,
738+
Parameters: stimulus.Data,
739+
Priority: int(stimulus.Urgency * 100),
740+
Timestamp: time.Now(),
741+
}
742+
}
743+
744+
// Update cognitive state
745+
a.cognitiveState.CurrentLayer = action.Layer
746+
a.cognitiveState.LastUpdate = time.Now()
747+
748+
// Emit reaction completion event
749+
if err := a.PublishEvent(ctx, EventType("agent.react.complete"), EventData(
750+
"action", action,
751+
"agent_name", a.name,
752+
)); err != nil {
753+
fmt.Printf("Failed to publish react complete event: %v\n", err)
754+
}
755+
756+
return action, nil
757+
}
758+
759+
func (a *agent) GetSkillLibrary() SkillLibrary {
760+
a.mu.RLock()
761+
defer a.mu.RUnlock()
762+
return a.skillLibrary
763+
}
764+
765+
func (a *agent) WithSkills(skills ...Skill) Agent {
766+
newAgent := &agent{
767+
name: a.name,
768+
model: a.model,
769+
systemPrompt: a.systemPrompt,
770+
temperature: a.temperature,
771+
maxTokens: a.maxTokens,
772+
tools: a.tools,
773+
memory: a.memory,
774+
state: a.state,
775+
provider: a.provider,
776+
eventBus: a.eventBus,
777+
skillLibrary: NewSkillLibrary(),
778+
cognitiveState: &CognitiveState{
779+
CurrentLayer: a.cognitiveState.CurrentLayer,
780+
Mode: a.cognitiveState.Mode,
781+
ActivePlan: a.cognitiveState.ActivePlan,
782+
LoadedSkills: make([]string, 0),
783+
RecentDecisions: make([]*Decision, 0),
784+
LastUpdate: time.Now(),
785+
},
786+
cognitiveMode: a.cognitiveMode,
787+
}
788+
789+
// Copy existing skills
790+
for _, skill := range a.skillLibrary.ListSkills() {
791+
newAgent.skillLibrary.RegisterSkill(skill)
792+
}
793+
794+
// Add new skills
795+
for _, skill := range skills {
796+
newAgent.skillLibrary.RegisterSkill(skill)
797+
newAgent.cognitiveState.LoadedSkills = append(newAgent.cognitiveState.LoadedSkills, skill.Name())
798+
}
799+
800+
return newAgent
801+
}
802+
803+
func (a *agent) GetCognitiveState() *CognitiveState {
804+
a.mu.RLock()
805+
defer a.mu.RUnlock()
806+
807+
// Return a copy to prevent external modification
808+
state := *a.cognitiveState
809+
return &state
810+
}
811+
812+
func (a *agent) SetCognitiveMode(mode CognitiveMode) Agent {
813+
newAgent := &agent{
814+
name: a.name,
815+
model: a.model,
816+
systemPrompt: a.systemPrompt,
817+
temperature: a.temperature,
818+
maxTokens: a.maxTokens,
819+
tools: a.tools,
820+
memory: a.memory,
821+
state: a.state,
822+
provider: a.provider,
823+
eventBus: a.eventBus,
824+
skillLibrary: a.skillLibrary,
825+
cognitiveState: &CognitiveState{
826+
CurrentLayer: a.cognitiveState.CurrentLayer,
827+
Mode: mode,
828+
ActivePlan: a.cognitiveState.ActivePlan,
829+
LoadedSkills: a.cognitiveState.LoadedSkills,
830+
RecentDecisions: a.cognitiveState.RecentDecisions,
831+
LastUpdate: time.Now(),
832+
},
833+
cognitiveMode: mode,
834+
}
835+
836+
return newAgent
837+
}
838+
839+
// Helper functions for cognitive processing
840+
841+
func extractAction(response string) string {
842+
// Simple extraction logic - in production you might use more sophisticated parsing
843+
lines := strings.Split(response, "\n")
844+
for _, line := range lines {
845+
if strings.Contains(strings.ToLower(line), "action") || strings.Contains(strings.ToLower(line), "recommend") {
846+
return strings.TrimSpace(line)
847+
}
848+
}
849+
return "continue"
850+
}
851+
852+
func extractConfidence(response string) float64 {
853+
// Simple confidence extraction - in production you might use regex or LLM structured output
854+
if strings.Contains(strings.ToLower(response), "high confidence") || strings.Contains(strings.ToLower(response), "very confident") {
855+
return 0.9
856+
} else if strings.Contains(strings.ToLower(response), "medium confidence") || strings.Contains(strings.ToLower(response), "somewhat confident") {
857+
return 0.7
858+
} else if strings.Contains(strings.ToLower(response), "low confidence") || strings.Contains(strings.ToLower(response), "uncertain") {
859+
return 0.4
860+
}
861+
return 0.6 // default
862+
}

0 commit comments

Comments
 (0)