Files
agents_and_robots/tools/memorytools/memory.go
T
egutierrez 8d89a762fb refactor: mover tools a subpackages individuales
Cada tool ahora vive en su propio subpackage dentro de tools/ (clock, file,
http, knowledgetools, matrix, memorytools, ssh, weather) en lugar de archivos
planos en el paquete raíz tools/. Esto mejora la organización, permite imports
selectivos y reduce acoplamiento entre tools. El paquete tools/ raíz conserva
los tipos base (Def, Param, Result, ToolFunc, Tool, Registry).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:16:45 +00:00

201 lines
6.9 KiB
Go

package memorytools
import (
"context"
"fmt"
"strings"
"sync"
"time"
"github.com/enmanuel/agents/pkg/memory"
"github.com/enmanuel/agents/tools"
)
// MemoryStore is the subset of memory.Store needed by memory tools.
type MemoryStore interface {
SaveFact(ctx context.Context, fact memory.Fact) error
RecallFacts(ctx context.Context, agentID, subject string, key *string) ([]memory.Fact, error)
DeleteFacts(ctx context.Context, agentID, subject string, key *string) error
}
// WindowClearer allows tools to clear the conversation window for a room.
type WindowClearer interface {
ClearWindow(roomID string)
}
// RoomContext is a thread-safe holder for the current room ID.
// Set by the runtime before each event handling; read by memory_clear_context.
type RoomContext struct {
mu sync.RWMutex
roomID string
}
// Set updates the current room ID.
func (rc *RoomContext) Set(roomID string) {
rc.mu.Lock()
rc.roomID = roomID
rc.mu.Unlock()
}
// Get returns the current room ID.
func (rc *RoomContext) Get() string {
rc.mu.RLock()
defer rc.mu.RUnlock()
return rc.roomID
}
// NewMemorySave creates a tool that saves a fact to long-term memory.
func NewMemorySave(agentID string, store MemoryStore) tools.Tool {
return tools.Tool{
Def: tools.Def{
Name: "memory_save",
Description: "Save a fact to long-term memory. Use this to remember important information about users, topics, or preferences.",
Parameters: []tools.Param{
{Name: "subject", Type: "string", Description: "The subject this fact is about (e.g. a username, a topic)", Required: true},
{Name: "key", Type: "string", Description: "The fact key (e.g. 'favorite_language', 'timezone')", Required: true},
{Name: "value", Type: "string", Description: "The fact value to store", Required: true},
},
},
Exec: func(ctx context.Context, args map[string]any) tools.Result {
subject := tools.GetString(args, "subject")
key := tools.GetString(args, "key")
value := tools.GetString(args, "value")
if subject == "" || key == "" || value == "" {
return tools.Result{Err: fmt.Errorf("memory_save: subject, key, and value are required")}
}
err := store.SaveFact(ctx, memory.Fact{
AgentID: agentID,
Subject: subject,
Key: key,
Value: value,
})
if err != nil {
return tools.Result{Err: fmt.Errorf("memory_save: %w", err)}
}
return tools.Result{Output: fmt.Sprintf("saved: %s.%s = %s", subject, key, value)}
},
}
}
// NewMemoryRecall creates a tool that retrieves facts from long-term memory.
func NewMemoryRecall(agentID string, store MemoryStore) tools.Tool {
return tools.Tool{
Def: tools.Def{
Name: "memory_recall",
Description: "Recall facts from long-term memory about a subject. Omit key to get all facts for the subject.",
Parameters: []tools.Param{
{Name: "subject", Type: "string", Description: "The subject to recall facts about", Required: true},
{Name: "key", Type: "string", Description: "Optional specific fact key to recall", Required: false},
},
},
Exec: func(ctx context.Context, args map[string]any) tools.Result {
subject := tools.GetString(args, "subject")
if subject == "" {
return tools.Result{Err: fmt.Errorf("memory_recall: subject is required")}
}
var keyPtr *string
if k := tools.GetString(args, "key"); k != "" {
keyPtr = &k
}
facts, err := store.RecallFacts(ctx, agentID, subject, keyPtr)
if err != nil {
return tools.Result{Err: fmt.Errorf("memory_recall: %w", err)}
}
if len(facts) == 0 {
return tools.Result{Output: fmt.Sprintf("no facts found for subject %q", subject)}
}
var sb strings.Builder
for _, f := range facts {
fmt.Fprintf(&sb, "%s.%s = %s\n", f.Subject, f.Key, f.Value)
}
return tools.Result{Output: sb.String()}
},
}
}
// NewMemoryForget creates a tool that deletes facts from long-term memory.
func NewMemoryForget(agentID string, store MemoryStore) tools.Tool {
return tools.Tool{
Def: tools.Def{
Name: "memory_forget",
Description: "Delete facts from long-term memory. Omit key to delete all facts for the subject.",
Parameters: []tools.Param{
{Name: "subject", Type: "string", Description: "The subject whose facts to delete", Required: true},
{Name: "key", Type: "string", Description: "Optional specific fact key to delete; omit to delete all", Required: false},
},
},
Exec: func(ctx context.Context, args map[string]any) tools.Result {
subject := tools.GetString(args, "subject")
if subject == "" {
return tools.Result{Err: fmt.Errorf("memory_forget: subject is required")}
}
var keyPtr *string
if k := tools.GetString(args, "key"); k != "" {
keyPtr = &k
}
err := store.DeleteFacts(ctx, agentID, subject, keyPtr)
if err != nil {
return tools.Result{Err: fmt.Errorf("memory_forget: %w", err)}
}
if keyPtr != nil {
return tools.Result{Output: fmt.Sprintf("forgot %s.%s", subject, *keyPtr)}
}
return tools.Result{Output: fmt.Sprintf("forgot all facts about %s", subject)}
},
}
}
// NewMemoryClearContext creates a tool that clears the conversation window.
func NewMemoryClearContext(clearer WindowClearer, roomCtx *RoomContext) tools.Tool {
return tools.Tool{
Def: tools.Def{
Name: "memory_clear_context",
Description: "Clear the conversation context window. Useful to start fresh. Omit room_id to clear the current room.",
Parameters: []tools.Param{
{Name: "room_id", Type: "string", Description: "Optional room ID to clear; defaults to current room", Required: false},
},
},
Exec: func(ctx context.Context, args map[string]any) tools.Result {
roomID := tools.GetString(args, "room_id")
if roomID == "" {
roomID = roomCtx.Get()
}
if roomID == "" {
return tools.Result{Err: fmt.Errorf("memory_clear_context: no room_id provided and no current room")}
}
clearer.ClearWindow(roomID)
return tools.Result{Output: fmt.Sprintf("conversation context cleared for room %s", roomID)}
},
}
}
// NewMemorySummary creates a tool that saves an important summary to long-term memory.
func NewMemorySummary(agentID string, store MemoryStore) tools.Tool {
return tools.Tool{
Def: tools.Def{
Name: "memory_summary",
Description: "Save an important summary or takeaway from the current conversation to long-term memory.",
Parameters: []tools.Param{
{Name: "text", Type: "string", Description: "The summary text to save", Required: true},
},
},
Exec: func(ctx context.Context, args map[string]any) tools.Result {
text := tools.GetString(args, "text")
if text == "" {
return tools.Result{Err: fmt.Errorf("memory_summary: text is required")}
}
key := time.Now().UTC().Format("2006-01-02T15:04:05")
err := store.SaveFact(ctx, memory.Fact{
AgentID: agentID,
Subject: "_summary",
Key: key,
Value: text,
})
if err != nil {
return tools.Result{Err: fmt.Errorf("memory_summary: %w", err)}
}
return tools.Result{Output: "summary saved"}
},
}
}