Memory
Distributed memory with automatic scoping and vector operations for Go agents
Memory
Distributed memory with automatic scoping and vector operations
The Memory type provides hierarchical state management for Go agents. It supports multiple isolation scopes (workflow, session, user, global) with automatic scope ID resolution from execution context, plus vector operations for semantic search.
Memory is automatically shared between agents in the same workflow. When Agent A
calls Agent B via app.Call(), both agents access the same workflow memory.
Basic Operations
app.RegisterReasoner("example", func(ctx context.Context, input map[string]any) (any, error) {
// Set a value (session scope by default)
err := app.Memory.Set(ctx, "user_preference", map[string]any{"theme": "dark"})
if err != nil {
return nil, err
}
// Get a value
pref, err := app.Memory.Get(ctx, "user_preference")
if err != nil {
return nil, err
}
// Get with default value
theme, err := app.Memory.GetWithDefault(ctx, "theme", "light")
if err != nil {
return nil, err
}
// Delete a value
err = app.Memory.Delete(ctx, "user_preference")
if err != nil {
return nil, err
}
// List keys in current scope
keys, err := app.Memory.List(ctx)
if err != nil {
return nil, err
}
return map[string]any{"keys": keys, "theme": theme}, nil
})Core Methods
Prop
Type
Memory Scopes
Memory is organized into four hierarchical scopes. By default, operations use session scope, but you can access specific scopes using scope helpers.
Default scope. Persists across workflow executions within the same session.
// Default operations use session scope
app.Memory.Set(ctx, "preferences", prefs)
val, _ := app.Memory.Get(ctx, "preferences")
// Explicit session scope
sessionMem := app.Memory.SessionScope()
sessionMem.Set(ctx, "key", value)Use for: User preferences, conversation history, shopping carts.
Task coordination. Shared across all agents in the same workflow execution.
workflowMem := app.Memory.WorkflowScope()
// Store intermediate results
workflowMem.Set(ctx, "step_result", data)
// Read in another agent (same workflow)
result, _ := workflowMem.Get(ctx, "step_result")Use for: Multi-step workflows, intermediate results, cross-agent data sharing.
User-specific data. Persists across sessions for the same user/actor.
userMem := app.Memory.UserScope()
// Store user-specific data
userMem.Set(ctx, "learned_preferences", prefs)
// Access in future sessions
prefs, _ := userMem.Get(ctx, "learned_preferences")Use for: Long-term user preferences, learned behaviors, user profiles.
System-wide data. Shared across all agents, sessions, and workflows.
globalMem := app.Memory.GlobalScope()
// Store feature flags
globalMem.Set(ctx, "feature_flags", map[string]any{
"new_ui": true,
"beta": false,
})
// Access from any agent
flags, _ := globalMem.Get(ctx, "feature_flags")Use for: Feature flags, system configuration, shared reference data.
Explicit Scoping
Use Scoped() for explicit scope and ID control:
// Explicit scope with custom scope ID
customMem := app.Memory.Scoped(agent.ScopeSession, "custom_session_123")
customMem.Set(ctx, "key", value)MemoryScope Constants
const (
ScopeWorkflow MemoryScope = "workflow"
ScopeSession MemoryScope = "session"
ScopeUser MemoryScope = "user"
ScopeGlobal MemoryScope = "global"
)Vector Operations
Store and search embeddings for semantic similarity. Vector operations support the same scoping as regular memory.
SetVector
Store a vector embedding with optional metadata.
embedding := []float64{0.1, 0.2, 0.3, 0.4, 0.5}
metadata := map[string]any{
"title": "Introduction to Agentfield",
"content": "Agentfield is a distributed agent framework...",
}
err := app.Memory.SetVector(ctx, "doc_123", embedding, metadata)Prop
Type
GetVector
Retrieve a stored vector and its metadata.
embedding, metadata, err := app.Memory.GetVector(ctx, "doc_123")
if err != nil {
return nil, err
}
if embedding == nil {
// Vector not found
}
fmt.Println("Title:", metadata["title"])Returns: (embedding []float64, metadata map[string]any, err error)
If the vector is not found, returns (nil, nil, nil).
SearchVector
Perform similarity search to find related vectors.
queryEmbedding := []float64{0.15, 0.25, 0.35, 0.45, 0.55}
results, err := app.Memory.SearchVector(ctx, queryEmbedding, agent.SearchOptions{
Limit: 5,
Threshold: 0.7,
})
if err != nil {
return nil, err
}
for _, r := range results {
fmt.Printf("Key: %s, Score: %.2f, Title: %s\n",
r.Key, r.Score, r.Metadata["title"])
}SearchOptions
Prop
Type
VectorSearchResult
Prop
Type
DeleteVector
Remove a vector from storage.
err := app.Memory.DeleteVector(ctx, "doc_123")Scoped Vector Operations
Vector operations work with all scope helpers:
// Workflow-scoped vectors
workflowMem := app.Memory.WorkflowScope()
workflowMem.SetVector(ctx, "doc_1", embedding, metadata)
results, _ := workflowMem.SearchVector(ctx, query, agent.SearchOptions{Limit: 5})
// Global-scoped vectors (shared knowledge base)
globalMem := app.Memory.GlobalScope()
globalMem.SetVector(ctx, "kb_doc_1", embedding, metadata)Examples
Cross-Agent Memory Sharing
// Agent A: Support Agent
app.RegisterReasoner("analyze_ticket", func(ctx context.Context, input map[string]any) (any, error) {
ticket := input["ticket"].(string)
analysis, err := app.AI(ctx, fmt.Sprintf("Analyze: %s", ticket))
if err != nil {
return nil, err
}
// Store in workflow memory (shared with other agents)
workflowMem := app.Memory.WorkflowScope()
workflowMem.Set(ctx, "ticket_analysis", map[string]any{
"priority": "high",
"category": "billing",
})
// Call specialist agent
return app.Call(ctx, "specialist-agent.handle", input)
})
// Agent B: Specialist Agent (different process)
app.RegisterReasoner("handle", func(ctx context.Context, input map[string]any) (any, error) {
// Access same workflow memory
workflowMem := app.Memory.WorkflowScope()
analysis, _ := workflowMem.Get(ctx, "ticket_analysis")
// analysis contains {priority: "high", category: "billing"}
return map[string]any{"handled": true, "analysis": analysis}, nil
})RAG with Vector Memory
app.RegisterReasoner("index_document", func(ctx context.Context, input map[string]any) (any, error) {
docID := input["doc_id"].(string)
content := input["content"].(string)
// Generate embedding (using external embedding service)
embedding, err := generateEmbedding(content)
if err != nil {
return nil, err
}
// Store in global scope for cross-workflow access
globalMem := app.Memory.GlobalScope()
err = globalMem.SetVector(ctx, fmt.Sprintf("doc_%s", docID), embedding, map[string]any{
"title": input["title"],
"content": content[:500], // Store preview
"indexed_at": time.Now().Format(time.RFC3339),
})
return map[string]any{"indexed": true, "doc_id": docID}, nil
})
app.RegisterReasoner("search_docs", func(ctx context.Context, input map[string]any) (any, error) {
query := input["query"].(string)
// Generate query embedding
queryEmbedding, err := generateEmbedding(query)
if err != nil {
return nil, err
}
// Search in global scope
globalMem := app.Memory.GlobalScope()
results, err := globalMem.SearchVector(ctx, queryEmbedding, agent.SearchOptions{
Limit: 5,
Threshold: 0.7,
})
if err != nil {
return nil, err
}
// Format results
docs := make([]map[string]any, len(results))
for i, r := range results {
docs[i] = map[string]any{
"key": r.Key,
"score": r.Score,
"title": r.Metadata["title"],
}
}
return map[string]any{"query": query, "results": docs}, nil
})Session-Based State
app.RegisterReasoner("chat", func(ctx context.Context, input map[string]any) (any, error) {
message := input["message"].(string)
// Get conversation history from session
history, _ := app.Memory.GetWithDefault(ctx, "history", []any{})
// Type assertion for history
historySlice, ok := history.([]any)
if !ok {
historySlice = []any{}
}
// Add user message
historySlice = append(historySlice, map[string]any{
"role": "user",
"content": message,
})
// Generate response
response, err := app.AI(ctx, fmt.Sprintf("History: %v\nUser: %s", historySlice, message))
if err != nil {
return nil, err
}
// Add assistant response
historySlice = append(historySlice, map[string]any{
"role": "assistant",
"content": response.Text(),
})
// Save updated history
app.Memory.Set(ctx, "history", historySlice)
return map[string]any{"response": response.Text()}, nil
})Typed Memory Access
Use GetTyped for automatic JSON unmarshaling into Go structs:
type UserPreferences struct {
Theme string `json:"theme"`
Language string `json:"language"`
Timezone string `json:"timezone"`
}
app.RegisterReasoner("get_preferences", func(ctx context.Context, input map[string]any) (any, error) {
var prefs UserPreferences
sessionMem := app.Memory.SessionScope()
err := sessionMem.GetTyped(ctx, "user_preferences", &prefs)
if err != nil {
return nil, err
}
// prefs is now populated
return prefs, nil
})Storage Backends
The Go SDK supports two storage backends:
In-Memory Backend (Development)
Used by default when no control plane is configured. Data is lost when the process exits.
// Automatic when AgentFieldURL is empty
app, _ := agent.New(agent.Config{
NodeID: "my-agent",
// No AgentFieldURL = in-memory backend
})Control Plane Backend (Production)
Distributed storage via the Agentfield control plane. Supports SQLite (development) or PostgreSQL (production).
app, _ := agent.New(agent.Config{
NodeID: "my-agent",
AgentFieldURL: "http://localhost:8080",
Token: os.Getenv("BRAIN_TOKEN"),
})The control plane backend automatically handles scope resolution via HTTP headers and provides distributed memory across multiple agent instances.
Best Practices
Use Appropriate Scopes
// ✅ Workflow scope for task coordination
workflowMem := app.Memory.WorkflowScope()
workflowMem.Set(ctx, "intermediate_result", data)
// ✅ Session scope for user state
app.Memory.Set(ctx, "user_preferences", prefs)
// ✅ Global scope for shared configuration
globalMem := app.Memory.GlobalScope()
globalMem.Set(ctx, "feature_flags", flags)Handle Errors
// ✅ Always check for errors
val, err := app.Memory.Get(ctx, "key")
if err != nil {
return nil, fmt.Errorf("memory get failed: %w", err)
}
if val == nil {
// Key not found - use default
val = defaultValue
}Use Descriptive Keys
// ✅ Good - hierarchical and descriptive
app.Memory.Set(ctx, "customer_123_preferences", prefs)
app.Memory.Set(ctx, "order_456_status", status)
// ❌ Bad - ambiguous
app.Memory.Set(ctx, "data", prefs)
app.Memory.Set(ctx, "x", status)Related
- Agent Package - Agent configuration and lifecycle
- Calling Reasoners - Cross-agent calls with memory context
- Memory Operations REST API - Direct API access
- Shared Memory Concepts - Understanding distributed memory