-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Description
Problem: Reflection-based memory strategies (à la "Generative Agents" paper) need a hook to trigger periodic consolidation, but there's no standard way to signal reflection points.
Context:
- Reflection is a pattern where agents periodically synthesize high-level insights from observations
- Examples: "User prefers functional programming", "User is learning Go"
- Requires LLM calls to analyze memory and extract patterns
- Should be triggered at natural cycle boundaries (end of session, N observations, time-based)
Current Limitation:
- No standard interface method for reflection
- Custom implementations would need proprietary API
- Agent can't signal "good time to reflect"
Proposed Solution: Add optional Reflect() method that agents can call to trigger memory consolidation.
Required Changes:
- Add Reflect() to Memory interface (
memory.go):
// Memory is core element of agents behaviour...
type Memory interface {
// Purge clears all observations
Purge()
// Commit stores new observation
Commit(*Observation)
// Context builds message sequence for prompting
Context(chatter.Message) []chatter.Message
// Reflect triggers memory consolidation and synthesis.
// Called by agents at natural cycle boundaries to:
// - Synthesize high-level insights from observations
// - Compress old observations into summaries
// - Recalculate importance scores
// - Update indexes or embeddings
//
// Implementations that don't need reflection can no-op.
// Return error if reflection fails (e.g., LLM call fails).
//
// Example triggers:
// - After N observations committed
// - At end of agent session
// - Periodically (time-based)
// - When memory is nearly full
Reflect(context.Context) error
}- Implement no-op for existing memories:
memory/void.go:
func (s *Void) Reflect(ctx context.Context) error {
return nil // No-op for stateless memory
}memory/stream.go:
func (s *Stream) Reflect(ctx context.Context) error {
return nil // Stream doesn't perform reflection by default
}- Add optional reflection trigger in Automata (
agent/automata.go):
// PromptOnce forgets state and prompts within a new session.
// Triggers memory reflection before purging.
func (automata *Automata[A, B]) PromptOnce(ctx context.Context, input A, opt ...chatter.Opt) (B, error) {
automata.mu.Lock()
defer automata.mu.Unlock()
// Trigger reflection before resetting
if err := automata.memory.Reflect(ctx); err != nil {
// Log but don't fail - reflection is best-effort
// Could add logger here when available
}
automata.Purge()
return automata.Prompt(ctx, input, opt...)
}- Add explicit Reflect() public method (
agent/automata.go):
// Reflect triggers memory consolidation and synthesis.
// Call this periodically for reflection-based memory implementations.
// Has no effect for simple memory types (Void, Stream).
//
// Example:
// // After batch of requests
// for _, input := range batch {
// agent.Prompt(ctx, input)
// }
// agent.Reflect(ctx) // Consolidate learnings
func (automata *Automata[A, B]) Reflect(ctx context.Context) error {
return automata.memory.Reflect(ctx)
}- Document in README.md:
### Memory Reflection
Advanced memory implementations can implement reflection - periodic synthesis of insights:
```go
type ReflectiveMemory struct {
llm chatter.Chatter
observations []*Observation
insights []string
}
func (m *ReflectiveMemory) Reflect(ctx context.Context) error {
// Ask LLM to synthesize insights
prompt := buildReflectionPrompt(m.observations)
reply, err := m.llm.Prompt(ctx, prompt)
if err != nil {
return err
}
// Store high-level insights
m.insights = append(m.insights, extractInsights(reply)...)
// Could also: compress observations, recalculate scores, etc.
return nil
}Trigger reflection:
// After session
agent.Prompt(ctx, input)
agent.Reflect(ctx) // Consolidate learnings
// Or periodically
for i, input := range inputs {
agent.Prompt(ctx, input)
if i % 100 == 0 {
agent.Reflect(ctx)
}
}See examples/12_reflective_memory for complete implementation.
- Add tests (
memory/stream_test.go):
func TestStreamReflectNoOp(t *testing.T) {
mem := NewStream(10, "")
err := mem.Reflect(context.Background())
it.Then(t).Should(
it.Nil(err), // Should not error
)
}Estimated Effort: 2 hours
Skills Required: Interface design, understanding reflection patterns
Breaking Changes:
Migration: All existing Memory implementations must add Reflect(context.Context) error method. Simple no-op for most:
func (m *MyMemory) Reflect(ctx context.Context) error {
return nil
}Benefits:
- ✅ Standard interface for reflection-based strategies
- ✅ Agents can trigger reflection at appropriate times
- ✅ No-op for simple memories (no overhead)
- ✅ Enables sophisticated memory patterns