feat(memory): add RAG memory store and self-memory system (promises, strategies)
This commit is contained in:
94
internal/memory/self_memory.go
Normal file
94
internal/memory/self_memory.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package memory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.db123.ir/db123/divacode/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SelfMemory struct {
|
||||||
|
db *storage.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type Promise struct {
|
||||||
|
ID string
|
||||||
|
Promise string
|
||||||
|
Context string
|
||||||
|
Fulfilled bool
|
||||||
|
CreatedAt time.Time
|
||||||
|
Deadline *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Strategy struct {
|
||||||
|
ID string
|
||||||
|
Situation string
|
||||||
|
Strategy string
|
||||||
|
Outcome string
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSelfMemory(db *storage.DB) *SelfMemory {
|
||||||
|
return &SelfMemory{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *SelfMemory) AddPromise(promise, context string, deadline *time.Time) error {
|
||||||
|
id := time.Now().Format("150405.000")
|
||||||
|
return sm.db.Exec(
|
||||||
|
"INSERT INTO agent_promises (id, promise, context, deadline) VALUES (?, ?, ?, ?)",
|
||||||
|
id, promise, context, deadline,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *SelfMemory) FulfillPromise(id string) error {
|
||||||
|
return sm.db.Exec("UPDATE agent_promises SET fulfilled = 1 WHERE id = ?", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *SelfMemory) ActivePromises() ([]Promise, error) {
|
||||||
|
rows, err := sm.db.Query(
|
||||||
|
"SELECT id, promise, context, fulfilled, created_at FROM agent_promises WHERE fulfilled = 0 ORDER BY created_at DESC",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var promises []Promise
|
||||||
|
for rows.Next() {
|
||||||
|
var p Promise
|
||||||
|
var created string
|
||||||
|
if err := rows.Scan(&p.ID, &p.Promise, &p.Context, &p.Fulfilled, &created); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
promises = append(promises, p)
|
||||||
|
}
|
||||||
|
return promises, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *SelfMemory) AddStrategy(situation, strategy, outcome string) error {
|
||||||
|
id := time.Now().Format("150405.000")
|
||||||
|
return sm.db.Exec(
|
||||||
|
"INSERT INTO agent_strategies (id, situation, strategy, outcome) VALUES (?, ?, ?, ?)",
|
||||||
|
id, situation, strategy, outcome,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *SelfMemory) StrategiesByOutcome(outcome string) ([]Strategy, error) {
|
||||||
|
rows, err := sm.db.Query(
|
||||||
|
"SELECT id, situation, strategy, outcome FROM agent_strategies WHERE outcome = ? ORDER BY created_at DESC",
|
||||||
|
outcome,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var strategies []Strategy
|
||||||
|
for rows.Next() {
|
||||||
|
var s Strategy
|
||||||
|
if err := rows.Scan(&s.ID, &s.Situation, &s.Strategy, &s.Outcome); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
strategies = append(strategies, s)
|
||||||
|
}
|
||||||
|
return strategies, nil
|
||||||
|
}
|
||||||
71
internal/memory/store.go
Normal file
71
internal/memory/store.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package memory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.db123.ir/db123/divacode/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Store struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
db *storage.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStore(db *storage.DB) *Store {
|
||||||
|
return &Store{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) Store(ctx context.Context, content string, source string) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
importance := 0.5
|
||||||
|
if len(content) > 100 {
|
||||||
|
importance = 0.7
|
||||||
|
}
|
||||||
|
|
||||||
|
id := time.Now().Format("150405.000000000")
|
||||||
|
return s.db.Exec(
|
||||||
|
"INSERT INTO memories (id, content, importance, source, created_at) VALUES (?, ?, ?, ?, datetime('now'))",
|
||||||
|
id, content, importance, source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) Search(ctx context.Context, query string, topK int) ([]string, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
rows, err := s.db.Query(
|
||||||
|
`SELECT content FROM memories
|
||||||
|
WHERE content LIKE ?
|
||||||
|
ORDER BY importance DESC, created_at DESC
|
||||||
|
LIMIT ?`,
|
||||||
|
"%"+query+"%", topK,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var results []string
|
||||||
|
for rows.Next() {
|
||||||
|
var content string
|
||||||
|
if err := rows.Scan(&content); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("memory search", "query", query, "results", len(results))
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) UpdateImportance(ctx context.Context, id string, importance float64) error {
|
||||||
|
return s.db.Exec(
|
||||||
|
"UPDATE memories SET importance = ?, last_accessed = datetime('now') WHERE id = ?",
|
||||||
|
importance, id,
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user