mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
740cdcaeaf
* fix: remove redundant tools definitions from system prompt Tools are already provided to the LLM via JSON schema through ToProviderDefs(), so the text-based tools section in the system prompt is redundant. This removes the buildToolsSection() logic and the tools field from ContextBuilder, reducing system prompt length while maintaining the "ALWAYS use tools" rule reminder. Fixes #731 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: correct spelling 'initialized' (was 'initialised') --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
159 lines
4.6 KiB
Go
159 lines
4.6 KiB
Go
package agent
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/config"
|
|
"github.com/sipeed/picoclaw/pkg/providers"
|
|
"github.com/sipeed/picoclaw/pkg/routing"
|
|
"github.com/sipeed/picoclaw/pkg/session"
|
|
"github.com/sipeed/picoclaw/pkg/tools"
|
|
)
|
|
|
|
// AgentInstance represents a fully configured agent with its own workspace,
|
|
// session manager, context builder, and tool registry.
|
|
type AgentInstance struct {
|
|
ID string
|
|
Name string
|
|
Model string
|
|
Fallbacks []string
|
|
Workspace string
|
|
MaxIterations int
|
|
MaxTokens int
|
|
Temperature float64
|
|
ContextWindow int
|
|
Provider providers.LLMProvider
|
|
Sessions *session.SessionManager
|
|
ContextBuilder *ContextBuilder
|
|
Tools *tools.ToolRegistry
|
|
Subagents *config.SubagentsConfig
|
|
SkillsFilter []string
|
|
Candidates []providers.FallbackCandidate
|
|
}
|
|
|
|
// NewAgentInstance creates an agent instance from config.
|
|
func NewAgentInstance(
|
|
agentCfg *config.AgentConfig,
|
|
defaults *config.AgentDefaults,
|
|
cfg *config.Config,
|
|
provider providers.LLMProvider,
|
|
) *AgentInstance {
|
|
workspace := resolveAgentWorkspace(agentCfg, defaults)
|
|
os.MkdirAll(workspace, 0o755)
|
|
|
|
model := resolveAgentModel(agentCfg, defaults)
|
|
fallbacks := resolveAgentFallbacks(agentCfg, defaults)
|
|
|
|
restrict := defaults.RestrictToWorkspace
|
|
toolsRegistry := tools.NewToolRegistry()
|
|
toolsRegistry.Register(tools.NewReadFileTool(workspace, restrict))
|
|
toolsRegistry.Register(tools.NewWriteFileTool(workspace, restrict))
|
|
toolsRegistry.Register(tools.NewListDirTool(workspace, restrict))
|
|
toolsRegistry.Register(tools.NewExecToolWithConfig(workspace, restrict, cfg))
|
|
toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict))
|
|
toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict))
|
|
|
|
sessionsDir := filepath.Join(workspace, "sessions")
|
|
sessionsManager := session.NewSessionManager(sessionsDir)
|
|
|
|
contextBuilder := NewContextBuilder(workspace)
|
|
|
|
agentID := routing.DefaultAgentID
|
|
agentName := ""
|
|
var subagents *config.SubagentsConfig
|
|
var skillsFilter []string
|
|
|
|
if agentCfg != nil {
|
|
agentID = routing.NormalizeAgentID(agentCfg.ID)
|
|
agentName = agentCfg.Name
|
|
subagents = agentCfg.Subagents
|
|
skillsFilter = agentCfg.Skills
|
|
}
|
|
|
|
maxIter := defaults.MaxToolIterations
|
|
if maxIter == 0 {
|
|
maxIter = 20
|
|
}
|
|
|
|
maxTokens := defaults.MaxTokens
|
|
if maxTokens == 0 {
|
|
maxTokens = 8192
|
|
}
|
|
|
|
temperature := 0.7
|
|
if defaults.Temperature != nil {
|
|
temperature = *defaults.Temperature
|
|
}
|
|
|
|
// Resolve fallback candidates
|
|
modelCfg := providers.ModelConfig{
|
|
Primary: model,
|
|
Fallbacks: fallbacks,
|
|
}
|
|
candidates := providers.ResolveCandidates(modelCfg, defaults.Provider)
|
|
|
|
return &AgentInstance{
|
|
ID: agentID,
|
|
Name: agentName,
|
|
Model: model,
|
|
Fallbacks: fallbacks,
|
|
Workspace: workspace,
|
|
MaxIterations: maxIter,
|
|
MaxTokens: maxTokens,
|
|
Temperature: temperature,
|
|
ContextWindow: maxTokens,
|
|
Provider: provider,
|
|
Sessions: sessionsManager,
|
|
ContextBuilder: contextBuilder,
|
|
Tools: toolsRegistry,
|
|
Subagents: subagents,
|
|
SkillsFilter: skillsFilter,
|
|
Candidates: candidates,
|
|
}
|
|
}
|
|
|
|
// resolveAgentWorkspace determines the workspace directory for an agent.
|
|
func resolveAgentWorkspace(agentCfg *config.AgentConfig, defaults *config.AgentDefaults) string {
|
|
if agentCfg != nil && strings.TrimSpace(agentCfg.Workspace) != "" {
|
|
return expandHome(strings.TrimSpace(agentCfg.Workspace))
|
|
}
|
|
if agentCfg == nil || agentCfg.Default || agentCfg.ID == "" || routing.NormalizeAgentID(agentCfg.ID) == "main" {
|
|
return expandHome(defaults.Workspace)
|
|
}
|
|
home, _ := os.UserHomeDir()
|
|
id := routing.NormalizeAgentID(agentCfg.ID)
|
|
return filepath.Join(home, ".picoclaw", "workspace-"+id)
|
|
}
|
|
|
|
// resolveAgentModel resolves the primary model for an agent.
|
|
func resolveAgentModel(agentCfg *config.AgentConfig, defaults *config.AgentDefaults) string {
|
|
if agentCfg != nil && agentCfg.Model != nil && strings.TrimSpace(agentCfg.Model.Primary) != "" {
|
|
return strings.TrimSpace(agentCfg.Model.Primary)
|
|
}
|
|
return defaults.GetModelName()
|
|
}
|
|
|
|
// resolveAgentFallbacks resolves the fallback models for an agent.
|
|
func resolveAgentFallbacks(agentCfg *config.AgentConfig, defaults *config.AgentDefaults) []string {
|
|
if agentCfg != nil && agentCfg.Model != nil && agentCfg.Model.Fallbacks != nil {
|
|
return agentCfg.Model.Fallbacks
|
|
}
|
|
return defaults.ModelFallbacks
|
|
}
|
|
|
|
func expandHome(path string) string {
|
|
if path == "" {
|
|
return path
|
|
}
|
|
if path[0] == '~' {
|
|
home, _ := os.UserHomeDir()
|
|
if len(path) > 1 && path[1] == '/' {
|
|
return home + path[1:]
|
|
}
|
|
return home
|
|
}
|
|
return path
|
|
}
|