rony-llm-agent/pkg/persona/persona.go

114 lines
2.8 KiB
Go
Raw Normal View History

package persona
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/VictorVargas/rony-llm-agent/pkg/llm"
)
// Persona defines the personality and behavior of the agent.
type Persona struct {
ID string
Name string
Tone string
Style string
Language string
Constraints []string
FewShot []llm.Message
}
// Loader loads personas from files.
type Loader interface {
Load(ctx context.Context, configPath string) (Persona, error)
Discover(ctx context.Context, workdir string) (Persona, error)
}
// ErrPersonaNotFound is returned when a persona file is not found.
var ErrPersonaNotFound = fmt.Errorf("persona not found")
// DefaultPersona returns a basic persona with sensible defaults.
func DefaultPersona() Persona {
return Persona{
ID: "default",
Name: "Rony",
Tone: "professional and helpful",
Style: "clear and concise",
Language: "the user's language",
}
}
// AssembleSystemPrompt builds the final system prompt from persona, base prompt, and AGENTS.md.
func AssembleSystemPrompt(p Persona, agentsMD string) string {
var parts []string
// Base system prompt
parts = append(parts, "You are an AI agent. Be helpful, accurate, and safe.")
// Persona instructions
if p.Name != "" {
parts = append(parts, fmt.Sprintf("Your name is %s.", p.Name))
}
if p.Tone != "" {
parts = append(parts, fmt.Sprintf("Use a %s tone.", p.Tone))
}
if p.Style != "" {
parts = append(parts, fmt.Sprintf("Write in a %s style.", p.Style))
}
if p.Language != "" {
if strings.EqualFold(p.Language, "the user's language") {
parts = append(parts, "Respond in the same language as the user's messages.")
} else {
parts = append(parts, fmt.Sprintf("Respond in %s.", p.Language))
}
}
for _, c := range p.Constraints {
parts = append(parts, fmt.Sprintf("CONSTRAINT: %s", c))
}
// AGENTS.md content
if agentsMD != "" {
parts = append(parts, "---")
parts = append(parts, "Project instructions:")
parts = append(parts, agentsMD)
}
return strings.Join(parts, "\n\n")
}
// DiscoverAgentsMD walks up the directory tree looking for AGENTS.md files.
func DiscoverAgentsMD(root string) string {
var parts []string
current := root
for {
agentsPath := filepath.Join(current, "AGENTS.md")
if _, err := os.Stat(agentsPath); err == nil {
data, err := os.ReadFile(agentsPath)
if err == nil {
parts = append(parts, string(data))
}
}
parent := filepath.Dir(current)
if parent == current || parent == "." {
break
}
current = parent
}
// Also check ~/.config/rony/AGENTS.md
home, err := os.UserHomeDir()
if err == nil {
globalPath := filepath.Join(home, ".config", "rony", "AGENTS.md")
if data, err := os.ReadFile(globalPath); err == nil {
parts = append(parts, string(data))
}
}
return strings.Join(parts, "\n\n")
}