159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/providers"
|
|
"github.com/sipeed/picoclaw/pkg/skills"
|
|
)
|
|
|
|
type ContextBuilder struct {
|
|
workspace string
|
|
skillsLoader *skills.SkillsLoader
|
|
}
|
|
|
|
func NewContextBuilder(workspace string) *ContextBuilder {
|
|
builtinSkillsDir := filepath.Join(filepath.Dir(workspace), "picoclaw", "skills")
|
|
return &ContextBuilder{
|
|
workspace: workspace,
|
|
skillsLoader: skills.NewSkillsLoader(workspace, builtinSkillsDir),
|
|
}
|
|
}
|
|
|
|
func (cb *ContextBuilder) BuildSystemPrompt() string {
|
|
now := time.Now().Format("2006-01-02 15:04 (Monday)")
|
|
workspacePath, _ := filepath.Abs(filepath.Join(cb.workspace))
|
|
|
|
return fmt.Sprintf(`# picoclaw 🦞
|
|
|
|
You are picoclaw, a helpful AI assistant. You have access to tools that allow you to:
|
|
- Read, write, and edit files
|
|
- Execute shell commands
|
|
- Search the web and fetch web pages
|
|
- Send messages to users on chat channels
|
|
- Spawn subagents for complex background tasks
|
|
|
|
## Current Time
|
|
%s
|
|
|
|
## Workspace
|
|
Your workspace is at: %s
|
|
- Memory files: %s/memory/MEMORY.md
|
|
- Daily notes: %s/memory/2006-01-02.md
|
|
- Custom skills: %s/skills/{skill-name}/SKILL.md
|
|
|
|
## Weather Information
|
|
When users ask about weather, use the web_fetch tool with wttr.in URLs:
|
|
- Current weather: https://wttr.in/{city}?format=j1
|
|
- Beijing: https://wttr.in/Beijing?format=j1
|
|
- Shanghai: https://wttr.in/Shanghai?format=j1
|
|
- New York: https://wttr.in/New_York?format=j1
|
|
- London: https://wttr.in/London?format=j1
|
|
- Tokyo: https://wttr.in/Tokyo?format=j1
|
|
|
|
IMPORTANT: When responding to direct questions or conversations, reply directly with your text response.
|
|
Only use the 'message' tool when you need to send a message to a specific chat channel (like WhatsApp).
|
|
For normal conversation, just respond with text - do not call the message tool.
|
|
|
|
Always be helpful, accurate, and concise. When using tools, explain what you're doing.
|
|
When remembering something, write to %s/memory/MEMORY.md`,
|
|
now, workspacePath, workspacePath, workspacePath, workspacePath, workspacePath)
|
|
}
|
|
|
|
func (cb *ContextBuilder) LoadBootstrapFiles() string {
|
|
bootstrapFiles := []string{
|
|
"AGENTS.md",
|
|
"SOUL.md",
|
|
"USER.md",
|
|
"TOOLS.md",
|
|
"IDENTITY.md",
|
|
"MEMORY.md",
|
|
}
|
|
|
|
var result string
|
|
for _, filename := range bootstrapFiles {
|
|
filePath := filepath.Join(cb.workspace, filename)
|
|
if data, err := os.ReadFile(filePath); err == nil {
|
|
result += fmt.Sprintf("## %s\n\n%s\n\n", filename, string(data))
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (cb *ContextBuilder) BuildMessages(history []providers.Message, currentMessage string, media []string) []providers.Message {
|
|
messages := []providers.Message{}
|
|
|
|
systemPrompt := cb.BuildSystemPrompt()
|
|
bootstrapContent := cb.LoadBootstrapFiles()
|
|
if bootstrapContent != "" {
|
|
systemPrompt += "\n\n" + bootstrapContent
|
|
}
|
|
|
|
skillsSummary := cb.skillsLoader.BuildSkillsSummary()
|
|
if skillsSummary != "" {
|
|
systemPrompt += "\n\n## Available Skills\n\n" + skillsSummary
|
|
}
|
|
|
|
skillsContent := cb.loadSkills()
|
|
if skillsContent != "" {
|
|
systemPrompt += "\n\n" + skillsContent
|
|
}
|
|
|
|
messages = append(messages, providers.Message{
|
|
Role: "system",
|
|
Content: systemPrompt,
|
|
})
|
|
|
|
messages = append(messages, history...)
|
|
|
|
messages = append(messages, providers.Message{
|
|
Role: "user",
|
|
Content: currentMessage,
|
|
})
|
|
|
|
return messages
|
|
}
|
|
|
|
func (cb *ContextBuilder) AddToolResult(messages []providers.Message, toolCallID, toolName, result string) []providers.Message {
|
|
messages = append(messages, providers.Message{
|
|
Role: "tool",
|
|
Content: result,
|
|
ToolCallID: toolCallID,
|
|
})
|
|
return messages
|
|
}
|
|
|
|
func (cb *ContextBuilder) AddAssistantMessage(messages []providers.Message, content string, toolCalls []map[string]interface{}) []providers.Message {
|
|
msg := providers.Message{
|
|
Role: "assistant",
|
|
Content: content,
|
|
}
|
|
if len(toolCalls) > 0 {
|
|
messages = append(messages, msg)
|
|
}
|
|
return messages
|
|
}
|
|
|
|
func (cb *ContextBuilder) loadSkills() string {
|
|
allSkills := cb.skillsLoader.ListSkills(true)
|
|
if len(allSkills) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var skillNames []string
|
|
for _, s := range allSkills {
|
|
skillNames = append(skillNames, s.Name)
|
|
}
|
|
|
|
content := cb.skillsLoader.LoadSkillsForContext(skillNames)
|
|
if content == "" {
|
|
return ""
|
|
}
|
|
|
|
return "# Skill Definitions\n\n" + content
|
|
}
|