agent.Memory()
Distributed memory with automatic scoping, vectors, and pluggable backends
The agent.Memory() interface provides distributed state management across agents with automatic scoping to workflow, session, user, or global contexts.
Memory is automatically shared between agents in the same workflow. When Agent A
calls Agent B via agent.Call(), both agents access the same workflow memory.
Basic Operations
package main
import (
"context"
"github.com/anthropics/agentfield/sdk/go/agent"
)
func main() {
app := agent.New(agent.Config{
NodeID: "my-agent",
})
app.Reasoner("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
settings, err := app.Memory().GetWithDefault(ctx, "settings", map[string]any{})
if err != nil {
return nil, err
}
// Delete a value
err = app.Memory().Delete(ctx, "temp_data")
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{
"pref": pref,
"settings": settings,
"keys": keys,
}, nil
})
}Core Methods
Prop
Type
Memory Scopes
The Go SDK provides four memory scopes with automatic scope ID resolution from the execution context.
Task coordination scope. Shared across all agents in the same workflow execution.
// Get workflow-scoped memory
workflowMem := app.Memory().WorkflowScope()
// Store intermediate results
err := workflowMem.Set(ctx, "step_result", data)
// Read from any agent in the workflow
result, err := workflowMem.Get(ctx, "step_result")Use for: Task coordination, intermediate results, cross-agent data sharing.
Default scope. Persists across multiple workflow executions within a session.
// Session scope is the default
err := app.Memory().Set(ctx, "preferences", prefs)
// Or explicitly via SessionScope()
sessionMem := app.Memory().SessionScope()
history, err := sessionMem.Get(ctx, "history")Use for: User preferences, conversation history, shopping carts.
User/actor-private state. Persists across sessions for the same user.
userMem := app.Memory().UserScope()
// Store user-specific data
err := userMem.Set(ctx, "api_cache", cachedData)
// Track rate limits per user
err = userMem.Set(ctx, "rate_limit_remaining", 950)Use for: Caching, rate limit tracking, user configuration.
System-wide data. Shared across all agents, sessions, and workflows.
globalMem := app.Memory().GlobalScope()
// Store feature flags
err := globalMem.Set(ctx, "feature_flags", map[string]any{
"newUI": true,
"betaFeatures": false,
})
// Get global config
config, err := globalMem.Get(ctx, "app_config")Use for: Feature flags, system configuration, shared reference data.
Scoped Memory
Access memory in specific scopes using scope accessors. Each returns a *ScopedMemory with the same core methods.
// Workflow scope - isolated to current execution
workflowMem := app.Memory().WorkflowScope()
// Session scope - persists across workflows
sessionMem := app.Memory().SessionScope()
// User scope - persists across sessions
userMem := app.Memory().UserScope()
// Global scope - shared system-wide
globalMem := app.Memory().GlobalScope()
// Custom scope with explicit ID
customMem := app.Memory().Scoped(agent.ScopeSession, "custom-session-123")ScopedMemory Methods
Prop
Type
Typed Retrieval
Use GetTyped to unmarshal values directly into Go structs:
type UserPreferences struct {
Theme string `json:"theme"`
Notifications bool `json:"notifications"`
}
func handler(ctx context.Context, input map[string]any) (any, error) {
var prefs UserPreferences
err := app.Memory().SessionScope().GetTyped(ctx, "preferences", &prefs)
if err != nil {
return nil, err
}
// prefs is now populated with the stored values
return map[string]any{"theme": prefs.Theme}, nil
}Vector Operations
Store and search embeddings for semantic similarity.
SetVector()
err := app.Memory().SetVector(ctx, "doc_1", embedding, map[string]any{
"title": "Introduction",
"content": "Document content...",
})Prop
Type
GetVector()
embedding, metadata, err := app.Memory().GetVector(ctx, "doc_1")
if err != nil {
return nil, err
}
if embedding == nil {
// Vector not found
}SearchVector()
results, err := app.Memory().SearchVector(ctx, queryEmbedding, agent.SearchOptions{
Limit: 10,
Threshold: 0.7,
})
// results: []VectorSearchResult with Key, Score, Metadata, Scope, ScopeID
for _, r := range results {
fmt.Printf("Key: %s, Score: %.2f\n", r.Key, r.Score)
}Prop
Type
DeleteVector()
err := app.Memory().DeleteVector(ctx, "doc_1")Scoped Vector Operations
All vector operations are also available on ScopedMemory:
globalMem := app.Memory().GlobalScope()
// Store vectors globally for cross-workflow search
err := globalMem.SetVector(ctx, "global_doc", embedding, metadata)
// Search globally
results, err := globalMem.SearchVector(ctx, queryEmbedding, agent.SearchOptions{
Limit: 5,
})Memory Backends
The Go SDK supports pluggable memory backends through the MemoryBackend interface.
InMemoryBackend (Default)
Thread-safe in-memory storage for development and testing:
// Uses InMemoryBackend by default
app := agent.New(agent.Config{
NodeID: "my-agent",
})
// Or explicitly
backend := agent.NewInMemoryBackend()
app := agent.New(agent.Config{
NodeID: "my-agent",
MemoryBackend: backend,
})
// Clear for testing
backend.Clear()
backend.ClearScope(agent.ScopeSession, "test-session")InMemoryBackend data is lost when the process exits. Use ControlPlaneMemoryBackend for persistent, distributed storage in production.
ControlPlaneMemoryBackend
Distributed storage that delegates to the Agentfield control plane:
import "github.com/anthropics/agentfield/sdk/go/agent"
backend := agent.NewControlPlaneMemoryBackend(
"https://your-control-plane.example.com", // Control plane URL
"your-api-token", // Authentication token
"my-agent", // Agent node ID
)
app := agent.New(agent.Config{
NodeID: "my-agent",
MemoryBackend: backend,
})This backend:
- Persists data across agent restarts
- Enables memory sharing between distributed agents
- Supports vector operations with similarity search
- Automatically propagates execution context headers
Custom Backends
Implement the MemoryBackend interface for custom storage:
type MemoryBackend interface {
Set(scope MemoryScope, scopeID, key string, value any) error
Get(scope MemoryScope, scopeID, key string) (any, bool, error)
Delete(scope MemoryScope, scopeID, key string) error
List(scope MemoryScope, scopeID string) ([]string, error)
// Vector operations
SetVector(scope MemoryScope, scopeID, key string, embedding []float64, metadata map[string]any) error
GetVector(scope MemoryScope, scopeID, key string) ([]float64, map[string]any, bool, error)
SearchVector(scope MemoryScope, scopeID string, embedding []float64, opts SearchOptions) ([]VectorSearchResult, error)
DeleteVector(scope MemoryScope, scopeID, key string) error
}Examples
Cross-Agent State Sharing
// Agent A: Support Agent
app.Reasoner("analyze_ticket", func(ctx context.Context, input map[string]any) (any, error) {
ticketID := input["ticket_id"].(string)
analysis, err := analyzeTicket(ctx, ticketID)
if err != nil {
return nil, err
}
// Store in workflow memory for other agents
workflowMem := app.Memory().WorkflowScope()
err = workflowMem.Set(ctx, "ticket_analysis", map[string]any{
"priority": analysis.Priority,
"category": analysis.Category,
})
if err != nil {
return nil, err
}
// Call specialist agent
return app.Call(ctx, "specialist-agent.handle", map[string]any{
"ticketId": ticketID,
})
})
// Agent B: Specialist Agent (on different server)
app.Reasoner("handle", func(ctx context.Context, input map[string]any) (any, error) {
// Access same workflow memory set by Agent A
workflowMem := app.Memory().WorkflowScope()
analysis, err := workflowMem.Get(ctx, "ticket_analysis")
if err != nil {
return nil, err
}
analysisMap := analysis.(map[string]any)
return map[string]any{
"handled": true,
"priority": analysisMap["priority"],
}, nil
})Session-Based Chat History
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
app.Reasoner("chat", func(ctx context.Context, input map[string]any) (any, error) {
userID := input["user_id"].(string)
message := input["message"].(string)
// Use explicit session scope with user ID
sessionMem := app.Memory().Scoped(agent.ScopeSession, userID)
// Get history
var history []Message
_ = sessionMem.GetTyped(ctx, "history", &history)
// Generate response with context
response, err := app.AI(ctx, agent.AIRequest{
System: "You are a helpful assistant.",
User: fmt.Sprintf("History: %v\nUser: %s", history[max(0, len(history)-5):], message),
})
if err != nil {
return nil, err
}
// Update history
history = append(history,
Message{Role: "user", Content: message},
Message{Role: "assistant", Content: response.Text},
)
err = sessionMem.Set(ctx, "history", history)
if err != nil {
return nil, err
}
return map[string]any{"response": response.Text}, nil
})Semantic Search
app.Reasoner("search_docs", func(ctx context.Context, input map[string]any) (any, error) {
query := input["query"].(string)
// Generate embedding for query (using your embedding provider)
queryEmbedding, err := generateEmbedding(query)
if err != nil {
return nil, err
}
// Search for similar documents 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
}
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
})Document Indexing
app.Reasoner("index_document", func(ctx context.Context, input map[string]any) (any, error) {
docID := input["doc_id"].(string)
title := input["title"].(string)
content := input["content"].(string)
// Generate embedding
embedding, err := generateEmbedding(content)
if err != nil {
return nil, err
}
// Store globally for cross-workflow search
globalMem := app.Memory().GlobalScope()
err = globalMem.SetVector(ctx, fmt.Sprintf("doc_%s", docID), embedding, map[string]any{
"title": title,
"docId": docID,
"indexedAt": time.Now().Format(time.RFC3339),
})
if err != nil {
return nil, err
}
return map[string]any{"indexed": true, "docId": docID}, nil
})Best Practices
Key Naming Conventions
Use hierarchical, descriptive key names:
// Good - hierarchical and descriptive
app.Memory().Set(ctx, "customer_123.profile", profileData)
app.Memory().Set(ctx, "order_456.status", "shipped")
// Bad - flat and ambiguous
app.Memory().Set(ctx, "c123p", profileData)
app.Memory().Set(ctx, "status", "shipped")Scope Selection
Choose the appropriate scope based on data lifetime:
// Workflow - task coordination (short-lived, multi-agent)
app.Memory().WorkflowScope().Set(ctx, "current_step", "processing")
// Session - user data (medium-lived, user-specific)
app.Memory().SessionScope().Set(ctx, "preferences", userPrefs)
// User - actor state (long-lived, user-private)
app.Memory().UserScope().Set(ctx, "rate_limit", remaining)
// Global - system config (permanent, system-wide)
app.Memory().GlobalScope().Set(ctx, "api_version", "2.1.0")Error Handling
Always handle memory operation errors:
func safeMemoryOp(ctx context.Context, key string, data any) error {
if err := app.Memory().Set(ctx, key, data); err != nil {
// Log error and potentially use fallback
log.Printf("Memory operation failed: %v", err)
return err
}
return nil
}
func getWithFallback(ctx context.Context, key string, fallback any) any {
val, err := app.Memory().Get(ctx, key)
if err != nil || val == nil {
return fallback
}
return val
}Related
- Agent Package - Agent configuration and setup
- Calling Reasoners - Cross-agent calls with memory context
- Memory Events - Event system deep dive