feat(memory): integrate vault into agent system

Register memory_save, memory_search, memory_recall tools in agent instance.
Update GetMemoryContext() to prefer vault _index.md over legacy MEMORY.md
with automatic truncation for large indexes. Update system prompt to
reference vault tools instead of raw MEMORY.md editing.
This commit is contained in:
Paul De Velder 2026-02-23 16:15:15 +01:00
parent ed8725448a
commit 1102a64715
3 changed files with 40 additions and 12 deletions

View file

@ -68,7 +68,8 @@ You are picoclaw, a helpful AI assistant.
## Workspace ## Workspace
Your workspace is at: %s Your workspace is at: %s
- Memory: %s/memory/MEMORY.md - Memory Vault: %s/memory/ (use memory_save, memory_search, memory_recall tools)
- Vault Index: %s/memory/_index.md (auto-generated)
- Daily Notes: %s/memory/YYYYMM/YYYYMMDD.md - Daily Notes: %s/memory/YYYYMM/YYYYMMDD.md
- Skills: %s/skills/{skill-name}/SKILL.md - Skills: %s/skills/{skill-name}/SKILL.md
@ -80,8 +81,8 @@ Your workspace is at: %s
2. **Be helpful and accurate** - When using tools, briefly explain what you're doing. 2. **Be helpful and accurate** - When using tools, briefly explain what you're doing.
3. **Memory** - When interacting with me if something seems memorable, update %s/memory/MEMORY.md`, 3. **Memory** - When something seems memorable, use the memory_save tool to save structured notes with tags. Use memory_recall to retrieve past knowledge.`,
now, runtime, workspacePath, workspacePath, workspacePath, workspacePath, toolsSection, workspacePath) now, runtime, workspacePath, workspacePath, workspacePath, workspacePath, workspacePath, toolsSection)
} }
func (cb *ContextBuilder) buildToolsSection() string { func (cb *ContextBuilder) buildToolsSection() string {

View file

@ -6,6 +6,7 @@ import (
"strings" "strings"
"github.com/sipeed/picoclaw/pkg/config" "github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/memory"
"github.com/sipeed/picoclaw/pkg/providers" "github.com/sipeed/picoclaw/pkg/providers"
"github.com/sipeed/picoclaw/pkg/routing" "github.com/sipeed/picoclaw/pkg/routing"
"github.com/sipeed/picoclaw/pkg/session" "github.com/sipeed/picoclaw/pkg/session"
@ -55,6 +56,12 @@ func NewAgentInstance(
toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict)) toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict))
toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict)) toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict))
// Memory vault tools
memVault := memory.NewVault(filepath.Join(workspace, "memory"))
toolsRegistry.Register(tools.NewMemorySaveTool(memVault))
toolsRegistry.Register(tools.NewMemorySearchTool(memVault))
toolsRegistry.Register(tools.NewMemoryRecallTool(memVault))
// Configure security middleware from config // Configure security middleware from config
if cfg != nil { if cfg != nil {
sec := cfg.Tools.Security sec := cfg.Tools.Security

View file

@ -12,15 +12,18 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"github.com/sipeed/picoclaw/pkg/memory"
) )
// MemoryStore manages persistent memory for the agent. // MemoryStore manages persistent memory for the agent.
// - Long-term memory: memory/MEMORY.md // It supports both the legacy MEMORY.md format and the new vault-based
// - Daily notes: memory/YYYYMM/YYYYMMDD.md // index system. When a vault _index.md exists, it is preferred over MEMORY.md.
type MemoryStore struct { type MemoryStore struct {
workspace string workspace string
memoryDir string memoryDir string
memoryFile string memoryFile string
vault *memory.Vault
} }
// NewMemoryStore creates a new MemoryStore with the given workspace path. // NewMemoryStore creates a new MemoryStore with the given workspace path.
@ -36,6 +39,7 @@ func NewMemoryStore(workspace string) *MemoryStore {
workspace: workspace, workspace: workspace,
memoryDir: memoryDir, memoryDir: memoryDir,
memoryFile: memoryFile, memoryFile: memoryFile,
vault: memory.NewVault(memoryDir),
} }
} }
@ -123,24 +127,40 @@ func (ms *MemoryStore) GetRecentDailyNotes(days int) string {
} }
// GetMemoryContext returns formatted memory context for the agent prompt. // GetMemoryContext returns formatted memory context for the agent prompt.
// Includes long-term memory and recent daily notes. // Prefers the vault index (_index.md) when available, falling back to
// the legacy MEMORY.md. Recent daily notes are always included.
func (ms *MemoryStore) GetMemoryContext() string { func (ms *MemoryStore) GetMemoryContext() string {
longTerm := ms.ReadLongTerm() // Prefer vault index over legacy MEMORY.md
index := ms.vault.ReadIndex()
if index == "" {
index = ms.ReadLongTerm()
}
recentNotes := ms.GetRecentDailyNotes(3) recentNotes := ms.GetRecentDailyNotes(3)
if longTerm == "" && recentNotes == "" { if index == "" && recentNotes == "" {
return "" return ""
} }
var sb strings.Builder var sb strings.Builder
if longTerm != "" { if index != "" {
sb.WriteString("## Long-term Memory\n\n") sb.WriteString("## Memory Vault Index\n\n")
sb.WriteString(longTerm) // Truncate if too large for system prompt
if len(index) > 4000 {
if tableEnd := strings.Index(index, "\n## Tags"); tableEnd > 0 {
sb.WriteString(index[:tableEnd])
} else {
sb.WriteString(index[:4000])
sb.WriteString("\n\n(Index truncated. Use memory_search for full details.)")
}
} else {
sb.WriteString(index)
}
} }
if recentNotes != "" { if recentNotes != "" {
if longTerm != "" { if index != "" {
sb.WriteString("\n\n---\n\n") sb.WriteString("\n\n---\n\n")
} }
sb.WriteString("## Recent Daily Notes\n\n") sb.WriteString("## Recent Daily Notes\n\n")