Basic Memory Operations in AgenticGoKit
Overview
This tutorial covers the fundamentals of AgenticGoKit's Memory interface, including personal memory operations, chat history management, and session handling. You'll learn how to store and retrieve information, manage conversations, and build memory-enabled agents using the unified Memory interface.
Basic memory operations provide the foundation for all memory-enabled agents and are essential building blocks for advanced features like RAG and knowledge bases.
Prerequisites
- Understanding of Core Concepts
- Basic knowledge of Go programming and interfaces
- Familiarity with context management in Go
- Understanding of key-value storage concepts
Memory Interface Overview
AgenticGoKit provides a unified Memory interface that handles all memory operations:
type Memory interface {
// Personal memory operations
Store(ctx context.Context, content string, tags ...string) error
Query(ctx context.Context, query string, limit ...int) ([]Result, error)
Remember(ctx context.Context, key string, value any) error
Recall(ctx context.Context, key string) (any, error)
// Chat history management
AddMessage(ctx context.Context, role, content string) error
GetHistory(ctx context.Context, limit ...int) ([]Message, error)
// Session management
NewSession() string
SetSession(ctx context.Context, sessionID string) context.Context
ClearSession(ctx context.Context) error
Close() error
// RAG-Enhanced Knowledge Base Operations
IngestDocument(ctx context.Context, doc Document) error
IngestDocuments(ctx context.Context, docs []Document) error
SearchKnowledge(ctx context.Context, query string, options ...SearchOption) ([]KnowledgeResult, error)
// Hybrid Search (Personal Memory + Knowledge Base)
SearchAll(ctx context.Context, query string, options ...SearchOption) (*HybridResult, error)
// RAG Context Assembly for LLM Prompts
BuildContext(ctx context.Context, query string, options ...ContextOption) (*RAGContext, error)
}Setting Up Basic Memory
1. Creating a Memory Instance
The simplest way to get started is with the in-memory provider:
package main
import (
"context"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/kunalkushwaha/agenticgokit/core"
)
func main() {
// Create in-memory storage
memory, err := core.NewMemory(core.AgentMemoryConfig{
Provider: "memory",
Connection: "memory",
MaxResults: 10,
Dimensions: 1536,
AutoEmbed: true,
})
if err != nil {
log.Fatalf("Failed to create memory: %v", err)
}
defer memory.Close()
ctx := context.Background()
// Test basic operations
err = demonstrateBasicOperations(ctx, memory)
if err != nil {
log.Fatalf("Demo failed: %v", err)
}
}
func demonstrateBasicOperations(ctx context.Context, memory core.Memory) error {
// Store some facts with tags
err := memory.Store(ctx, "Paris is the capital of France", "fact", "geography")
if err != nil {
return fmt.Errorf("failed to store fact: %w", err)
}
// Query for information
results, err := memory.Query(ctx, "capital France", 5)
if err != nil {
return fmt.Errorf("failed to query: %w", err)
}
fmt.Printf("Found %d results:\n", len(results))
for _, result := range results {
fmt.Printf("- %s (Score: %.3f, Tags: %v)\n",
result.Content, result.Score, result.Tags)
}
return nil
}2. Memory Configuration Options
// Development configuration - fast iteration
devConfig := core.AgentMemoryConfig{
Provider: "memory",
Connection: "memory",
MaxResults: 10,
Dimensions: 1536,
AutoEmbed: true,
// Enable RAG features
EnableRAG: true,
EnableKnowledgeBase: true,
// Use dummy embeddings for development
Embedding: core.EmbeddingConfig{
Provider: "dummy",
Model: "dummy-model",
},
}
memory, err := core.NewMemory(devConfig)
if err != nil {
log.Fatalf("Failed to create memory: %v", err)
}
defer memory.Close()// Production configuration - optimized for scale
prodConfig := core.AgentMemoryConfig{
Provider: "pgvector",
Connection: "postgres://user:pass@localhost:5432/agentdb",
MaxResults: 10,
Dimensions: 1536,
AutoEmbed: true,
// RAG-enhanced settings
EnableRAG: true,
EnableKnowledgeBase: true,
KnowledgeMaxResults: 20,
KnowledgeScoreThreshold: 0.7,
ChunkSize: 1000,
ChunkOverlap: 200,
// RAG context assembly settings
RAGMaxContextTokens: 4000,
RAGPersonalWeight: 0.3,
RAGKnowledgeWeight: 0.7,
RAGIncludeSources: true,
Embedding: core.EmbeddingConfig{
Provider: "openai",
Model: "text-embedding-3-small",
APIKey: os.Getenv("OPENAI_API_KEY"),
CacheEmbeddings: true,
MaxBatchSize: 100,
TimeoutSeconds: 30,
},
Documents: core.DocumentConfig{
AutoChunk: true,
SupportedTypes: []string{"pdf", "txt", "md", "web", "code"},
MaxFileSize: "10MB",
EnableMetadataExtraction: true,
EnableURLScraping: true,
},
Search: core.SearchConfigToml{
HybridSearch: true,
KeywordWeight: 0.3,
SemanticWeight: 0.7,
EnableReranking: false,
EnableQueryExpansion: false,
},
}
memory, err := core.NewMemory(prodConfig)
if err != nil {
log.Fatalf("Failed to create memory: %v", err)
}
defer memory.Close()# config.toml - External configuration
[memory]
provider = "pgvector"
connection = "postgres://user:pass@localhost:5432/agentdb"
max_results = 10
dimensions = 1536
auto_embed = true
# RAG settings
enable_rag = true
enable_knowledge_base = true
knowledge_max_results = 20
knowledge_score_threshold = 0.7
chunk_size = 1000
chunk_overlap = 200
[memory.embedding]
provider = "openai"
model = "text-embedding-3-small"
cache_embeddings = true
max_batch_size = 100
timeout_seconds = 30
[memory.documents]
auto_chunk = true
supported_types = ["pdf", "txt", "md", "web", "code"]
max_file_size = "10MB"
enable_metadata_extraction = true
[memory.search]
hybrid_search = true
keyword_weight = 0.3
semantic_weight = 0.7Configuration Strategy
- Development: Use in-memory storage with dummy embeddings for fast iteration
- Testing: Use pgvector with dummy embeddings to test database integration
- Production: Use pgvector/Weaviate with real embedding services for full functionality
Personal Memory Operations
The Memory interface provides two types of personal memory operations: content storage with tags and key-value storage.
1. Content Storage with Store and Query
Use Store and Query for content that you want to search semantically:
func demonstrateContentStorage(ctx context.Context, memory core.Memory) error {
// Store facts with tags
facts := []struct {
content string
tags []string
}{
{"The Earth orbits the Sun", []string{"science", "astronomy", "fact"}},
{"Water boils at 100°C at sea level", []string{"science", "physics", "fact"}},
{"Shakespeare wrote Hamlet", []string{"literature", "history", "fact"}},
{"Go is a programming language developed by Google", []string{"programming", "go", "technology"}},
}
for _, fact := range facts {
err := memory.Store(ctx, fact.content, fact.tags...)
if err != nil {
return fmt.Errorf("failed to store fact: %w", err)
}
}
fmt.Printf("Stored %d facts\n", len(facts))
// Query for information
results, err := memory.Query(ctx, "programming language", 3)
if err != nil {
return fmt.Errorf("failed to query: %w", err)
}
fmt.Printf("Found %d results for 'programming language':\n", len(results))
for i, result := range results {
fmt.Printf("%d. %s (Score: %.3f, Tags: %v)\n",
i+1, result.Content, result.Score, result.Tags)
}
return nil
}2. Key-Value Storage with Remember and Recall
Use Remember and Recall for structured data and preferences:
func demonstrateKeyValueStorage(ctx context.Context, memory core.Memory) error {
// Store user preferences and settings
preferences := map[string]any{
"user_name": "Alice Johnson",
"communication_style": "technical",
"preferred_language": "Go",
"experience_level": "intermediate",
"notification_settings": map[string]bool{
"email": true,
"push": false,
"sms": false,
},
}
// Store each preference
for key, value := range preferences {
err := memory.Remember(ctx, key, value)
if err != nil {
return fmt.Errorf("failed to remember %s: %w", key, err)
}
}
fmt.Println("Stored user preferences")
// Retrieve specific preferences
userName, err := memory.Recall(ctx, "user_name")
if err != nil {
return fmt.Errorf("failed to recall user_name: %w", err)
}
commStyle, err := memory.Recall(ctx, "communication_style")
if err != nil {
return fmt.Errorf("failed to recall communication_style: %w", err)
}
fmt.Printf("User: %v, Communication Style: %v\n", userName, commStyle)
// Handle missing keys
theme, err := memory.Recall(ctx, "theme_preference")
if err != nil {
fmt.Printf("Theme preference not set, using default\n")
} else {
fmt.Printf("Theme: %v\n", theme)
}
return nil
}3. Combining Both Approaches
func demonstrateCombinedUsage(ctx context.Context, memory core.Memory) error {
// Store user profile using Remember
err := memory.Remember(ctx, "user_id", "user-123")
if err != nil {
return err
}
err = memory.Remember(ctx, "user_preferences", map[string]string{
"response_style": "detailed",
"expertise_level": "intermediate",
})
if err != nil {
return err
}
// Store user's questions and interests using Store
interests := []string{
"How do I implement concurrency in Go?",
"What are the best practices for error handling?",
"How do I optimize Go applications for performance?",
}
for _, interest := range interests {
err := memory.Store(ctx, interest, "user-interest", "question")
if err != nil {
return err
}
}
// Query for related interests
results, err := memory.Query(ctx, "Go performance optimization", 2)
if err != nil {
return err
}
// Retrieve user preferences for personalized response
prefs, err := memory.Recall(ctx, "user_preferences")
if err != nil {
return err
}
fmt.Printf("Found %d related interests for user with preferences: %v\n",
len(results), prefs)
for _, result := range results {
fmt.Printf("- %s (Score: %.3f)\n", result.Content, result.Score)
}
return nil
}Query Operations and Search Patterns
1. Basic Query Operations
func demonstrateQueryOperations(ctx context.Context, memory core.Memory) error {
// Store some test data first
testData := []struct {
content string
tags []string
}{
{"Go is excellent for concurrent programming", []string{"go", "programming", "concurrency"}},
{"Python is great for data science and AI", []string{"python", "programming", "ai", "data-science"}},
{"JavaScript runs in browsers and servers", []string{"javascript", "programming", "web"}},
{"Rust provides memory safety without garbage collection", []string{"rust", "programming", "memory-safety"}},
}
for _, item := range testData {
err := memory.Store(ctx, item.content, item.tags...)
if err != nil {
return err
}
}
// Basic query - finds semantically similar content
results, err := memory.Query(ctx, "concurrent programming languages")
if err != nil {
return fmt.Errorf("query failed: %w", err)
}
fmt.Printf("Query: 'concurrent programming languages'\n")
fmt.Printf("Found %d results:\n", len(results))
for i, result := range results {
fmt.Printf("%d. %s (Score: %.3f)\n",
i+1, result.Content, result.Score)
fmt.Printf(" Tags: %v\n", result.Tags)
}
return nil
}2. Query with Limits
func demonstrateQueryLimits(ctx context.Context, memory core.Memory) error {
// Query with different limits
queries := []struct {
query string
limit int
}{
{"programming", 2},
{"memory", 1},
{"web development", 3},
}
for _, q := range queries {
results, err := memory.Query(ctx, q.query, q.limit)
if err != nil {
return fmt.Errorf("query failed for '%s': %w", q.query, err)
}
fmt.Printf("Query: '%s' (limit: %d)\n", q.query, q.limit)
for i, result := range results {
fmt.Printf(" %d. %s (%.3f)\n", i+1, result.Content, result.Score)
}
fmt.Println()
}
return nil
}3. Understanding Query Results
func analyzeQueryResults(ctx context.Context, memory core.Memory) error {
// Store content with different relevance levels
content := []string{
"Go programming language features",
"Go is used for backend development",
"Golang has excellent concurrency support",
"Python programming basics",
"Java enterprise applications",
}
for _, item := range content {
memory.Store(ctx, item, "programming")
}
// Query and analyze results
results, err := memory.Query(ctx, "Go language features", 5)
if err != nil {
return err
}
fmt.Println("Query: 'Go language features'")
fmt.Println("Results ordered by relevance:")
for i, result := range results {
relevance := "Low"
if result.Score > 0.8 {
relevance = "High"
} else if result.Score > 0.5 {
relevance = "Medium"
}
fmt.Printf("%d. %s\n", i+1, result.Content)
fmt.Printf(" Score: %.3f (%s relevance)\n", result.Score, relevance)
fmt.Printf(" Created: %s\n", result.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Println()
}
return nil
}Chat History Management
AgenticGoKit provides dedicated methods for managing conversation history with proper session handling.
1. Session Management
func demonstrateSessionManagement(ctx context.Context, memory core.Memory) error {
// Create a new session
sessionID := memory.NewSession()
fmt.Printf("Created new session: %s\n", sessionID)
// Set session context
ctx = memory.SetSession(ctx, sessionID)
// All subsequent operations will use this session
err := memory.AddMessage(ctx, "user", "Hello, I'm starting a new conversation")
if err != nil {
return fmt.Errorf("failed to add message: %w", err)
}
err = memory.AddMessage(ctx, "assistant", "Hello! I'm here to help. What can I do for you?")
if err != nil {
return fmt.Errorf("failed to add message: %w", err)
}
// Get history for this session
messages, err := memory.GetHistory(ctx, 10)
if err != nil {
return fmt.Errorf("failed to get history: %w", err)
}
fmt.Printf("Session %s has %d messages\n", sessionID, len(messages))
return nil
}2. Adding and Retrieving Messages
func demonstrateChatHistory(ctx context.Context, memory core.Memory) error {
// Create and set session
sessionID := memory.NewSession()
ctx = memory.SetSession(ctx, sessionID)
// Simulate a conversation about Go programming
conversation := []struct {
role string
content string
}{
{"user", "What is Go programming language?"},
{"assistant", "Go is a statically typed, compiled programming language developed by Google. It's known for its simplicity, efficiency, and excellent concurrency support."},
{"user", "What makes Go good for concurrent programming?"},
{"assistant", "Go has built-in concurrency primitives like goroutines and channels. Goroutines are lightweight threads, and channels provide a way to communicate between them safely."},
{"user", "Can you show me a simple example?"},
{"assistant", "Sure! Here's a basic example:\n\ngo func() {\n fmt.Println(\"Hello from goroutine\")\n}()\n\nThis creates a goroutine that runs concurrently."},
}
// Add messages to the conversation
for _, msg := range conversation {
err := memory.AddMessage(ctx, msg.role, msg.content)
if err != nil {
return fmt.Errorf("failed to add %s message: %w", msg.role, err)
}
// Small delay to simulate real conversation timing
time.Sleep(100 * time.Millisecond)
}
// Retrieve conversation history
messages, err := memory.GetHistory(ctx, 10)
if err != nil {
return fmt.Errorf("failed to get history: %w", err)
}
fmt.Printf("Conversation History (%d messages):\n", len(messages))
fmt.Println(strings.Repeat("-", 50))
for _, msg := range messages {
timestamp := msg.CreatedAt.Format("15:04:05")
fmt.Printf("[%s] %s: %s\n", timestamp, strings.Title(msg.Role), msg.Content)
fmt.Println()
}
return nil
}3. Managing Multiple Sessions
func demonstrateMultipleSessions(ctx context.Context, memory core.Memory) error {
// Create multiple sessions for different users/conversations
sessions := make(map[string]string)
// User 1 session
sessions["user1"] = memory.NewSession()
ctx1 := memory.SetSession(ctx, sessions["user1"])
memory.AddMessage(ctx1, "user", "I'm interested in learning Go")
memory.AddMessage(ctx1, "assistant", "Great! Go is an excellent language to learn.")
// User 2 session
sessions["user2"] = memory.NewSession()
ctx2 := memory.SetSession(ctx, sessions["user2"])
memory.AddMessage(ctx2, "user", "I need help with Python")
memory.AddMessage(ctx2, "assistant", "I'd be happy to help with Python!")
// Retrieve history for each session
for user, sessionID := range sessions {
sessionCtx := memory.SetSession(ctx, sessionID)
messages, err := memory.GetHistory(sessionCtx, 10)
if err != nil {
return fmt.Errorf("failed to get history for %s: %w", user, err)
}
fmt.Printf("%s's conversation (%s):\n", user, sessionID)
for _, msg := range messages {
fmt.Printf(" %s: %s\n", msg.Role, msg.Content)
}
fmt.Println()
}
return nil
}4. Session Cleanup
func demonstrateSessionCleanup(ctx context.Context, memory core.Memory) error {
// Create a session and add some messages
sessionID := memory.NewSession()
ctx = memory.SetSession(ctx, sessionID)
memory.AddMessage(ctx, "user", "This is a test message")
memory.AddMessage(ctx, "assistant", "This is a test response")
// Check history before cleanup
messages, err := memory.GetHistory(ctx, 10)
if err != nil {
return err
}
fmt.Printf("Messages before cleanup: %d\n", len(messages))
// Clear the session
err = memory.ClearSession(ctx)
if err != nil {
return fmt.Errorf("failed to clear session: %w", err)
}
// Check history after cleanup
messages, err = memory.GetHistory(ctx, 10)
if err != nil {
return err
}
fmt.Printf("Messages after cleanup: %d\n", len(messages))
return nil
}Building Memory-Enabled Agents
1. Simple Memory Agent
type BasicMemoryAgent struct {
name string
memory core.Memory
llm core.LLMProvider
}
func NewBasicMemoryAgent(name string, memory core.Memory, llm core.LLMProvider) *BasicMemoryAgent {
return &BasicMemoryAgent{
name: name,
memory: memory,
llm: llm,
}
}
func (m *BasicMemoryAgent) Run(ctx context.Context, event core.Event, state core.State) (core.AgentResult, error) {
// Extract message from state
message, ok := state.Get("message")
if !ok {
return core.AgentResult{}, fmt.Errorf("no message in state")
}
messageStr := message.(string)
// Set session context
sessionID := event.GetSessionID()
ctx = m.memory.SetSession(ctx, sessionID)
// Store the user message in chat history
err := m.memory.AddMessage(ctx, "user", messageStr)
if err != nil {
return core.AgentResult{}, fmt.Errorf("failed to store user message: %w", err)
}
// Get conversation context
context, err := m.buildContext(ctx, messageStr)
if err != nil {
return core.AgentResult{}, fmt.Errorf("failed to build context: %w", err)
}
// Generate response with context
response, err := m.llm.Generate(ctx, context)
if err != nil {
return core.AgentResult{}, fmt.Errorf("failed to generate response: %w", err)
}
// Store the assistant response
err = m.memory.AddMessage(ctx, "assistant", response)
if err != nil {
return core.AgentResult{}, fmt.Errorf("failed to store assistant response: %w", err)
}
// Return result
outputState := state.Clone()
outputState.Set("response", response)
return core.AgentResult{OutputState: outputState}, nil
}
func (m *BasicMemoryAgent) buildContext(ctx context.Context, currentMessage string) (string, error) {
// Get recent conversation history
history, err := m.memory.GetHistory(ctx, 5)
if err != nil {
return "", fmt.Errorf("failed to get history: %w", err)
}
// Search for relevant stored information
relevant, err := m.memory.Query(ctx, currentMessage, 3)
if err != nil {
return "", fmt.Errorf("failed to query memory: %w", err)
}
// Build context
var contextBuilder strings.Builder
contextBuilder.WriteString("You are a helpful assistant with access to conversation history and stored information.\n\n")
// Add relevant stored information
if len(relevant) > 0 {
contextBuilder.WriteString("Relevant stored information:\n")
for _, item := range relevant {
contextBuilder.WriteString(fmt.Sprintf("- %s\n", item.Content))
}
contextBuilder.WriteString("\n")
}
// Add conversation history (excluding the current message)
if len(history) > 1 { // More than just the current message
contextBuilder.WriteString("Recent conversation:\n")
for _, msg := range history[:len(history)-1] { // Exclude the last (current) message
contextBuilder.WriteString(fmt.Sprintf("%s: %s\n",
strings.Title(msg.Role), msg.Content))
}
contextBuilder.WriteString("\n")
}
contextBuilder.WriteString(fmt.Sprintf("Current message: %s\n\n", currentMessage))
contextBuilder.WriteString("Please provide a helpful response based on the context above.")
return contextBuilder.String(), nil
}2. Personalized Memory Agent
type PersonalizedAgent struct {
name string
memory core.Memory
llm core.LLMProvider
}
func NewPersonalizedAgent(name string, memory core.Memory, llm core.LLMProvider) *PersonalizedAgent {
return &PersonalizedAgent{
name: name,
memory: memory,
llm: llm,
}
}
func (p *PersonalizedAgent) Run(ctx context.Context, event core.Event, state core.State) (core.AgentResult, error) {
message, _ := state.Get("message")
messageStr := message.(string)
// Set session context
sessionID := event.GetSessionID()
ctx = p.memory.SetSession(ctx, sessionID)
// Check for user preferences
userPrefs, err := p.getUserPreferences(ctx)
if err != nil {
log.Printf("Failed to get user preferences: %v", err)
userPrefs = map[string]any{} // Use empty preferences
}
// Store user message
p.memory.AddMessage(ctx, "user", messageStr)
// Build personalized context
context := p.buildPersonalizedContext(ctx, messageStr, userPrefs)
// Generate response
response, err := p.llm.Generate(ctx, context)
if err != nil {
return core.AgentResult{}, err
}
// Store response
p.memory.AddMessage(ctx, "assistant", response)
// Learn from interaction
p.learnFromInteraction(ctx, messageStr, response)
outputState := state.Clone()
outputState.Set("response", response)
outputState.Set("personalized", true)
return core.AgentResult{OutputState: outputState}, nil
}
func (p *PersonalizedAgent) getUserPreferences(ctx context.Context) (map[string]any, error) {
prefs := make(map[string]any)
// Try to recall various preferences
prefKeys := []string{
"communication_style",
"expertise_level",
"preferred_format",
"language_preference",
}
for _, key := range prefKeys {
if value, err := p.memory.Recall(ctx, key); err == nil {
prefs[key] = value
}
}
return prefs, nil
}
func (p *PersonalizedAgent) buildPersonalizedContext(ctx context.Context, message string, prefs map[string]any) string {
var builder strings.Builder
builder.WriteString("You are a personalized assistant. Adapt your response based on user preferences.\n\n")
// Add user preferences
if len(prefs) > 0 {
builder.WriteString("User Preferences:\n")
for key, value := range prefs {
builder.WriteString(fmt.Sprintf("- %s: %v\n", key, value))
}
builder.WriteString("\n")
}
// Get relevant stored information
relevant, err := p.memory.Query(ctx, message, 3)
if err == nil && len(relevant) > 0 {
builder.WriteString("Relevant Information:\n")
for _, item := range relevant {
builder.WriteString(fmt.Sprintf("- %s\n", item.Content))
}
builder.WriteString("\n")
}
// Get conversation history
history, err := p.memory.GetHistory(ctx, 5)
if err == nil && len(history) > 1 {
builder.WriteString("Recent Conversation:\n")
for _, msg := range history[:len(history)-1] {
builder.WriteString(fmt.Sprintf("%s: %s\n", strings.Title(msg.Role), msg.Content))
}
builder.WriteString("\n")
}
builder.WriteString(fmt.Sprintf("Current Message: %s\n\n", message))
builder.WriteString("Provide a helpful, personalized response based on the user's preferences and context.")
return builder.String()
}
func (p *PersonalizedAgent) learnFromInteraction(ctx context.Context, userMessage, response string) {
// Store interaction patterns for learning
interaction := fmt.Sprintf("User: %s | Assistant: %s", userMessage, response)
p.memory.Store(ctx, interaction, "interaction", "learning")
// Simple learning heuristics
if strings.Contains(strings.ToLower(userMessage), "explain") {
p.memory.Remember(ctx, "prefers_explanations", true)
}
if strings.Contains(strings.ToLower(userMessage), "example") {
p.memory.Remember(ctx, "prefers_examples", true)
}
if strings.Contains(strings.ToLower(userMessage), "simple") {
p.memory.Remember(ctx, "communication_style", "simple")
}
}3. Complete Example with Agent Integration
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/kunalkushwaha/agenticgokit/core"
)
func main() {
// Create memory system
memory, err := core.NewMemory(core.AgentMemoryConfig{
Provider: "memory",
Connection: "memory",
MaxResults: 10,
Dimensions: 1536,
EnableRAG: true,
Embedding: core.EmbeddingConfig{
Provider: "dummy", // Use "openai" for production
Model: "dummy-model",
},
})
if err != nil {
log.Fatalf("Failed to create memory: %v", err)
}
defer memory.Close()
// Create a mock LLM for demonstration
llm := &MockLLM{}
// Create memory-enabled agent
agent := NewPersonalizedAgent("memory-assistant", memory, llm)
// Store some initial knowledge
ctx := context.Background()
facts := []string{
"Go is a programming language developed by Google",
"AgenticGoKit is a framework for building multi-agent systems in Go",
"Vector databases are used for similarity search and RAG implementations",
"Memory systems enable agents to learn and remember information",
}
for _, fact := range facts {
memory.Store(ctx, fact, "fact", "knowledge")
}
// Simulate a conversation
sessionID := memory.NewSession()
messages := []string{
"What is Go programming language?",
"I prefer simple explanations",
"How is Go related to AgenticGoKit?",
"Can you give me an example of memory systems?",
}
fmt.Println("=== Memory-Enabled Agent Conversation ===\n")
for i, msg := range messages {
fmt.Printf("User: %s\n", msg)
// Create event and state
event := core.NewEvent(
"memory-assistant",
core.EventData{"message": msg},
map[string]string{"session_id": sessionID},
)
state := core.NewState()
state.Set("message", msg)
// Run agent
result, err := agent.Run(ctx, event, state)
if err != nil {
log.Printf("Agent error: %v", err)
continue
}
response, _ := result.OutputState.Get("response")
fmt.Printf("Assistant: %s\n", response)
fmt.Println(strings.Repeat("-", 50))
// Small delay between messages
time.Sleep(500 * time.Millisecond)
}
// Show what the agent learned
fmt.Println("\n=== Agent Learning Summary ===")
showAgentLearning(ctx, memory, sessionID)
}
// MockLLM for demonstration purposes
type MockLLM struct{}
func (m *MockLLM) Generate(ctx context.Context, prompt string) (string, error) {
// Simple mock responses based on prompt content
if strings.Contains(prompt, "Go programming") {
return "Go is a statically typed, compiled programming language known for its simplicity and efficiency.", nil
}
if strings.Contains(prompt, "AgenticGoKit") {
return "AgenticGoKit is a Go framework that makes it easy to build multi-agent systems with memory and LLM integration.", nil
}
if strings.Contains(prompt, "memory systems") {
return "Memory systems allow agents to store, retrieve, and learn from information across conversations.", nil
}
if strings.Contains(prompt, "simple") {
return "I'll keep my explanations simple and clear as you prefer.", nil
}
return "I understand your question and I'm here to help based on our conversation history.", nil
}
func showAgentLearning(ctx context.Context, memory core.Memory, sessionID string) {
ctx = memory.SetSession(ctx, sessionID)
// Show conversation history
history, err := memory.GetHistory(ctx, 10)
if err != nil {
log.Printf("Failed to get history: %v", err)
return
}
fmt.Printf("Conversation History (%d messages):\n", len(history))
for _, msg := range history {
fmt.Printf("- %s: %s\n", strings.Title(msg.Role),
truncateString(msg.Content, 60))
}
// Show learned preferences
fmt.Println("\nLearned Preferences:")
prefKeys := []string{"communication_style", "prefers_explanations", "prefers_examples"}
for _, key := range prefKeys {
if value, err := memory.Recall(ctx, key); err == nil {
fmt.Printf("- %s: %v\n", key, value)
}
}
// Show stored interactions
interactions, err := memory.Query(ctx, "interaction", 5)
if err == nil && len(interactions) > 0 {
fmt.Printf("\nStored Interactions: %d\n", len(interactions))
}
}
func truncateString(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}Memory Management and Cleanup
1. Session Management Best Practices
func demonstrateSessionBestPractices(ctx context.Context, memory core.Memory) error {
// Create session with meaningful ID
userID := "user-123"
timestamp := time.Now().Unix()
sessionID := fmt.Sprintf("%s-%d", userID, timestamp)
// Alternative: use the built-in session generator
// sessionID := memory.NewSession()
ctx = memory.SetSession(ctx, sessionID)
// Store session metadata
memory.Remember(ctx, "session_start", time.Now())
memory.Remember(ctx, "user_id", userID)
// Use session for conversation
memory.AddMessage(ctx, "user", "Hello, I'm starting a new session")
memory.AddMessage(ctx, "assistant", "Welcome! I'm ready to help.")
// Later, when session ends
memory.Remember(ctx, "session_end", time.Now())
fmt.Printf("Session %s managed successfully\n", sessionID)
return nil
}2. Memory Cleanup Strategies
func demonstrateMemoryCleanup(ctx context.Context, memory core.Memory) error {
// Store some temporary data
tempData := []string{
"Temporary calculation result: 42",
"Cache entry for user session",
"Intermediate processing data",
}
for _, data := range tempData {
memory.Store(ctx, data, "temporary", "cache")
}
fmt.Printf("Stored %d temporary items\n", len(tempData))
// Query all temporary items
tempItems, err := memory.Query(ctx, "temporary", 10)
if err != nil {
return err
}
fmt.Printf("Found %d temporary items before cleanup\n", len(tempItems))
// Clear session to clean up session-specific data
err = memory.ClearSession(ctx)
if err != nil {
return fmt.Errorf("failed to clear session: %w", err)
}
fmt.Println("Session cleared successfully")
return nil
}3. Memory Usage Monitoring
type MemoryMonitor struct {
memory core.Memory
}
func NewMemoryMonitor(memory core.Memory) *MemoryMonitor {
return &MemoryMonitor{memory: memory}
}
func (m *MemoryMonitor) MonitorUsage(ctx context.Context) error {
// Monitor different types of stored data
categories := []string{"fact", "preference", "temporary", "interaction"}
fmt.Println("Memory Usage Report:")
fmt.Println(strings.Repeat("=", 40))
totalItems := 0
for _, category := range categories {
items, err := m.memory.Query(ctx, category, 100) // Get up to 100 items
if err != nil {
log.Printf("Failed to query %s: %v", category, err)
continue
}
fmt.Printf("%-12s: %d items\n", strings.Title(category), len(items))
totalItems += len(items)
}
fmt.Println(strings.Repeat("-", 40))
fmt.Printf("%-12s: %d items\n", "Total", totalItems)
return nil
}
func (m *MemoryMonitor) ShowRecentActivity(ctx context.Context, sessionID string) error {
ctx = m.memory.SetSession(ctx, sessionID)
// Get recent messages
messages, err := m.memory.GetHistory(ctx, 10)
if err != nil {
return err
}
fmt.Printf("\nRecent Activity (Session: %s):\n", sessionID)
fmt.Println(strings.Repeat("=", 50))
if len(messages) == 0 {
fmt.Println("No recent activity")
return nil
}
for _, msg := range messages {
timestamp := msg.CreatedAt.Format("15:04:05")
fmt.Printf("[%s] %s: %s\n",
timestamp,
strings.Title(msg.Role),
truncateString(msg.Content, 50))
}
return nil
}4. Resource Management
func demonstrateResourceManagement() {
// Create memory with proper resource management
memory, err := core.NewMemory(core.AgentMemoryConfig{
Provider: "memory",
Connection: "memory",
MaxResults: 10,
})
if err != nil {
log.Fatalf("Failed to create memory: %v", err)
}
// Always close memory when done
defer func() {
if err := memory.Close(); err != nil {
log.Printf("Error closing memory: %v", err)
} else {
fmt.Println("Memory closed successfully")
}
}()
ctx := context.Background()
// Use memory operations
memory.Store(ctx, "Test data", "test")
// Memory will be properly closed when function exits
fmt.Println("Memory operations completed")
}
// Example of graceful shutdown with context
func demonstrateGracefulShutdown() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
memory, err := core.NewMemory(core.AgentMemoryConfig{
Provider: "memory",
Connection: "memory",
})
if err != nil {
log.Fatalf("Failed to create memory: %v", err)
}
// Use context for all operations
sessionID := memory.NewSession()
ctx = memory.SetSession(ctx, sessionID)
// Perform operations with context
err = memory.AddMessage(ctx, "user", "Hello")
if err != nil {
log.Printf("Failed to add message: %v", err)
}
// Clean shutdown
err = memory.ClearSession(ctx)
if err != nil {
log.Printf("Failed to clear session: %v", err)
}
err = memory.Close()
if err != nil {
log.Printf("Failed to close memory: %v", err)
}
fmt.Println("Graceful shutdown completed")
}Best Practices
1. Content Organization
// Use consistent tagging strategies
const (
TagFact = "fact"
TagPreference = "preference"
TagLearning = "learning"
TagTemporary = "temporary"
TagInteraction = "interaction"
)
func storeWithConsistentTags(ctx context.Context, memory core.Memory) error {
// Store facts with consistent tags
err := memory.Store(ctx, "Go supports concurrent programming", TagFact, "programming", "go")
if err != nil {
return err
}
// Store preferences with clear categorization
err = memory.Store(ctx, "User prefers detailed code examples", TagPreference, "communication")
if err != nil {
return err
}
return nil
}
// Use meaningful key names for Remember/Recall
func useConsistentKeys(ctx context.Context, memory core.Memory) error {
// Use namespaced keys for organization
keys := map[string]any{
"user.name": "Alice",
"user.expertise_level": "intermediate",
"preferences.style": "detailed",
"preferences.format": "code_examples",
"session.start_time": time.Now(),
"session.interaction_count": 0,
}
for key, value := range keys {
err := memory.Remember(ctx, key, value)
if err != nil {
return fmt.Errorf("failed to remember %s: %w", key, err)
}
}
return nil
}2. Session Management Best Practices
func implementSessionBestPractices(memory core.Memory) error {
// Create meaningful session IDs
userID := "user-123"
timestamp := time.Now().Unix()
sessionID := fmt.Sprintf("%s-%d", userID, timestamp)
ctx := context.Background()
ctx = memory.SetSession(ctx, sessionID)
// Store session metadata
sessionData := map[string]any{
"session.id": sessionID,
"session.user_id": userID,
"session.start_time": time.Now(),
"session.platform": "web",
}
for key, value := range sessionData {
memory.Remember(ctx, key, value)
}
// Use session consistently throughout conversation
memory.AddMessage(ctx, "system", "Session started")
return nil
}
func cleanupExpiredSessions(memory core.Memory, maxAge time.Duration) error {
// This would typically be implemented by the memory provider
// For demonstration, we show the pattern
ctx := context.Background()
// Get session start time
startTime, err := memory.Recall(ctx, "session.start_time")
if err != nil {
return nil // No session data
}
if start, ok := startTime.(time.Time); ok {
if time.Since(start) > maxAge {
// Clear expired session
err = memory.ClearSession(ctx)
if err != nil {
return fmt.Errorf("failed to clear expired session: %w", err)
}
fmt.Println("Cleared expired session")
}
}
return nil
}3. Error Handling and Resilience
func robustMemoryOperations(ctx context.Context, memory core.Memory) error {
// Implement retry logic for critical operations
maxRetries := 3
backoff := time.Second
for attempt := 0; attempt < maxRetries; attempt++ {
err := memory.Store(ctx, "critical data", "important")
if err == nil {
return nil // Success
}
if attempt == maxRetries-1 {
return fmt.Errorf("failed to store after %d attempts: %w", maxRetries, err)
}
log.Printf("Store attempt %d failed, retrying in %v: %v",
attempt+1, backoff, err)
time.Sleep(backoff)
backoff *= 2 // Exponential backoff
}
return nil
}
func safeMemoryQuery(ctx context.Context, memory core.Memory, query string) ([]core.Result, error) {
// Query with timeout
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
results, err := memory.Query(ctx, query, 10)
if err != nil {
// Log error but don't fail completely
log.Printf("Memory query failed: %v", err)
return []core.Result{}, nil // Return empty results instead of error
}
return results, nil
}
func gracefulMemoryShutdown(memory core.Memory) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Clear any temporary data
memory.ClearSession(ctx)
// Close memory connection
if err := memory.Close(); err != nil {
log.Printf("Error closing memory: %v", err)
} else {
log.Println("Memory closed gracefully")
}
}4. Performance Optimization
func optimizeMemoryUsage(ctx context.Context, memory core.Memory) error {
// Batch operations when possible
facts := []string{
"Go was created at Google",
"Go compiles to native code",
"Go has garbage collection",
}
// Instead of individual Store calls, batch if possible
for _, fact := range facts {
// Use consistent tags for better organization
err := memory.Store(ctx, fact, "fact", "go", "programming")
if err != nil {
log.Printf("Failed to store fact: %v", err)
continue // Continue with other facts
}
}
// Use appropriate limits for queries
results, err := memory.Query(ctx, "Go programming", 5) // Limit to 5 results
if err != nil {
return err
}
// Process results efficiently
for _, result := range results {
if result.Score > 0.7 { // Only process high-confidence results
fmt.Printf("High confidence result: %s\n", result.Content)
}
}
return nil
}Common Patterns
1. Context-Aware Responses
func generateContextAwareResponse(memory core.Memory, sessionID, message string) (string, error) {
ctx := context.Background()
// Set session context
ctx = memory.SetSession(ctx, sessionID)
// Get user preferences using key-value storage
commStyle, err := memory.Recall(ctx, "communication_style")
if err != nil {
// Default if not found
commStyle = "standard"
}
expertiseLevel, err := memory.Recall(ctx, "expertise_level")
if err != nil {
expertiseLevel = "beginner"
}
// Get relevant facts using semantic search
facts, err := memory.Query(ctx, message, 3)
if err != nil {
return "", fmt.Errorf("failed to query facts: %w", err)
}
// Build context-aware prompt
prompt := buildPromptWithContext(message, commStyle, expertiseLevel, facts)
// Generate response (would use LLM here)
return generateResponse(prompt), nil
}
func buildPromptWithContext(message string, commStyle, expertiseLevel any, facts []core.Result) string {
var builder strings.Builder
builder.WriteString(fmt.Sprintf("Communication style: %v\n", commStyle))
builder.WriteString(fmt.Sprintf("User expertise: %v\n", expertiseLevel))
if len(facts) > 0 {
builder.WriteString("\nRelevant information:\n")
for _, fact := range facts {
builder.WriteString(fmt.Sprintf("- %s\n", fact.Content))
}
}
builder.WriteString(fmt.Sprintf("\nUser message: %s\n", message))
builder.WriteString("Please provide a helpful response based on the context above.")
return builder.String()
}
func generateResponse(prompt string) string {
// Mock response generation - in real implementation, use LLM
return "Generated response based on context"
}2. Learning from Interactions
func learnFromInteraction(memory core.Memory, sessionID, userMessage, response string, feedback string) error {
ctx := context.Background()
// Set session context
ctx = memory.SetSession(ctx, sessionID)
// Store the interaction in chat history (already done by agent)
// But we can store additional learning data
interaction := fmt.Sprintf("User asked: %s | Response quality: %s",
userMessage, feedback)
err := memory.Store(ctx, interaction, "interaction", "learning", "feedback")
if err != nil {
return fmt.Errorf("failed to store interaction: %w", err)
}
// Extract learnings if feedback is positive
if feedback == "helpful" || feedback == "correct" {
// Store successful patterns
pattern := extractPattern(userMessage, response)
err = memory.Store(ctx, pattern, "successful-pattern", "learning")
if err != nil {
return fmt.Errorf("failed to store pattern: %w", err)
}
// Update user preferences based on successful interactions
if strings.Contains(strings.ToLower(userMessage), "explain") {
memory.Remember(ctx, "prefers_explanations", true)
}
if strings.Contains(strings.ToLower(userMessage), "example") {
memory.Remember(ctx, "prefers_examples", true)
}
}
return nil
}
func extractPattern(userMessage, response string) string {
// Simple pattern extraction - in real implementation, use more sophisticated analysis
return fmt.Sprintf("Pattern: %s -> %s",
strings.ToLower(userMessage[:min(50, len(userMessage))]),
strings.ToLower(response[:min(50, len(response))]))
}
func min(a, b int) int {
if a < b {
return a
}
return b
}Conclusion
Basic memory operations provide the foundation for building intelligent agents that can remember, learn, and improve over time. The key concepts covered include:
- Setting up in-memory storage
- Storing and retrieving information
- Managing conversation history
- Building context-aware responses
- Memory management and cleanup
These fundamentals prepare you for more advanced memory systems including vector databases and RAG implementations.
Next Steps
Learning Path
Follow the progressive tutorial series to master memory systems:
🗄️ Next: Vector Databases
Set up production-ready storage with pgvector or Weaviate
📄 Then: Document Ingestion
Learn document processing and knowledge base creation
🧠 Advanced: RAG Implementation
Build retrieval-augmented generation systems
🏗️ Scale: Knowledge Bases
Create enterprise-scale knowledge systems
⚡ Optimize: Memory Optimization
Performance tuning and scaling strategies
Related Resources
Additional Learning
- Memory Systems Overview - Complete architecture guide
- Core Concepts - AgenticGoKit fundamentals
- API Reference - Complete Memory interface documentation
- Examples - Working code examples