Sessions
Manage conversation state and multi-turn interactions
Sessions manage conversation state between you and agents, enabling multi-turn conversations with context and memory. They handle message history, agent handoffs, and conversation continuity.
Quick Start
// Create a session for conversation state
session := session.New(agent)
// Have a multi-turn conversation
response1, _ := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Hi, my name is Alice")}, nil)
fmt.Println(response1.FinalOutput) // "Hello Alice! Nice to meet you."
response2, _ := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("What's my name?")}, nil)
fmt.Println(response2.FinalOutput) // "Your name is Alice."
Basic Usage
Single Agent Conversations
Maintain context across multiple interactions:
// Create agent and session
agent := agent.New().
SetModel(model).
SetSystemPrompt("You are a helpful coding assistant.")
session := session.New(agent)
// First question
response1, _ := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("How do I create a slice in Go?")}, nil)
// Follow-up question with context
response2, _ := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Can you show me how to append to it?")}, nil)
// Agent remembers we were talking about Go slices
response3, _ := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("What about removing elements?")}, nil)
Accessing Conversation History
Retrieve and analyze conversation messages:
session := session.New(agent)
// Have some conversations
session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Tell me about Go")}, nil)
session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("What are its main features?")}, nil)
// Get the conversation history
messages := session.Messages()
fmt.Printf("Conversation has %d messages\n", len(messages))
for _, msg := range messages {
fmt.Printf("%s: %s\n", msg.Role, msg.Content)
}
Agent Handoffs
Transfer conversations between specialized agents while maintaining context:
// Create specialized agents
codeReviewer := agent.New().
SetModel(model).
SetSystemPrompt("You are a code reviewer. Analyze code for bugs and improvements.")
tester := agent.New().
SetModel(model).
SetSystemPrompt("You generate comprehensive unit tests for code.")
documenter := agent.New().
SetModel(model).
SetSystemPrompt("You create clear documentation for code.")
// Start conversation with code reviewer
session := session.New(codeReviewer)
reviewResponse, _ := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Review this function: "+codeSnippet)}, nil)
// Hand off to test generator (keeps conversation context)
session.SetAgent(tester)
testResponse, _ := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Generate tests based on the review feedback")}, nil)
// Hand off to documenter (knows about code and tests)
session.SetAgent(documenter)
docResponse, _ := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Create documentation covering the reviewed code and tests")}, nil)
Session Configuration
Custom Session Options
Configure session behavior:
// Session with custom configuration
session := session.New(agent).
WithMaxMessages(100). // Limit conversation history
WithSystemContext("Project: E-commerce API") // Add persistent context
Message Filtering
Control which messages are kept in history:
// Create session with message filtering
session := session.New(agent).
WithMessageFilter(func(msg session.Message) bool {
// Only keep user messages and final responses
return msg.Role == "user" || (msg.Role == "assistant" && !msg.IsIntermediate)
})
Streaming with Sessions
Handle real-time conversations with persistent state:
session := session.New(agent)
// Stream first response
stream1, _ := session.RunStreamed(ctx, []agent.ChatMessage{agent.NewUserMessage("Tell me a story about a robot")}, nil)
for event, err := range stream1.StreamEvents() {
if err != nil {
log.Fatal(err)
}
if event.IsTextDelta() {
fmt.Print(event.GetContent())
}
}
fmt.Println()
// Stream follow-up (remembers the story context)
stream2, _ := session.RunStreamed(ctx, []agent.ChatMessage{agent.NewUserMessage("What happens next in the story?")}, nil)
for event, err := range stream2.StreamEvents() {
if err != nil {
log.Fatal(err)
}
if event.IsTextDelta() {
fmt.Print(event.GetContent())
}
}
Advanced Patterns
Session Branching
Create conversation branches for different scenarios:
// Main conversation
mainSession := session.New(agent)
mainSession.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("I'm building a web application")}, nil)
// Branch for different technical approaches
frontendBranch := mainSession.Branch()
frontendBranch.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Focus on React frontend architecture")}, nil)
backendBranch := mainSession.Branch()
backendBranch.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Focus on Go backend architecture")}, nil)
// Both branches remember the original context about building a web app
Session Persistence
Save and restore conversation state:
// Save session state
session := session.New(agent)
session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("Help me plan a vacation to Japan")}, nil)
sessionData, err := session.Export()
if err != nil {
log.Fatal(err)
}
// Store sessionData (JSON) to database/file
err = saveToDatabase(sessionData)
// Later: restore session
sessionData, err = loadFromDatabase()
if err != nil {
log.Fatal(err)
}
restoredSession, err := session.Import(sessionData)
if err != nil {
log.Fatal(err)
}
// Continue conversation where you left off
restoredSession.Run(ctx, []agent.ChatMessage{agent.NewUserMessage("What's the weather like in Tokyo in spring?")}, nil)
Multi-User Sessions
Handle conversations for different users:
type ConversationManager struct {
sessions map[string]*session.Session
agent *agent.Agent
}
func NewConversationManager(agent *agent.Agent) *ConversationManager {
return &ConversationManager{
sessions: make(map[string]*session.Session),
agent: agent,
}
}
func (cm *ConversationManager) HandleUserMessage(userID, message string) (string, error) {
// Get or create session for user
sess, exists := cm.sessions[userID]
if !exists {
sess = session.New(cm.agent)
cm.sessions[userID] = sess
}
// Process message with user's conversation history
response, err := sess.Run(ctx, []agent.ChatMessage{agent.NewUserMessage(message)}, nil)
if err != nil {
return "", err
}
return response.FinalOutput, nil
}
Session Cleanup
Manage memory usage for long-running applications:
type SessionManager struct {
sessions map[string]*session.Session
lastUsed map[string]time.Time
}
func (sm *SessionManager) CleanupOldSessions() {
now := time.Now()
for sessionID, lastUsed := range sm.lastUsed {
// Remove sessions inactive for more than 1 hour
if now.Sub(lastUsed) > time.Hour {
delete(sm.sessions, sessionID)
delete(sm.lastUsed, sessionID)
}
}
}
func (sm *SessionManager) GetSession(id string, agent *agent.Agent) *session.Session {
sm.lastUsed[id] = time.Now()
if sess, exists := sm.sessions[id]; exists {
return sess
}
// Create new session
sess := session.New(agent)
sm.sessions[id] = sess
return sess
}
Best Practices
Memory Management
Control conversation length to manage costs and performance:
// Limit conversation history
session := session.New(agent).
WithMaxMessages(50) // Keep last 50 messages
// Or use sliding window
session := session.New(agent).
WithSlidingWindow(20) // Keep last 20 exchanges
Context Preservation
Keep important context while trimming history:
session := session.New(agent).
WithContextPreservation(func(messages []session.Message) []session.Message {
// Always keep system message and last 10 exchanges
if len(messages) <= 21 { // 1 system + 20 user/assistant
return messages
}
preserved := []session.Message{messages[0]} // Keep system message
preserved = append(preserved, messages[len(messages)-20:]...) // Keep last 20
return preserved
})
Error Handling
Handle session-related errors gracefully:
response, err := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage(userMessage)}, nil)
if err != nil {
switch {
case errors.Is(err, session.ErrMaxMessagesExceeded):
// Handle conversation limit
session.Reset()
return "Conversation history cleared. Please repeat your question."
case errors.Is(err, session.ErrInvalidState):
// Handle corrupted session
session = session.New(agent)
return "Session reset due to error. How can I help you?"
default:
return fmt.Sprintf("Error: %v", err)
}
}
Performance Optimization
Optimize for high-throughput scenarios:
// Use session pools for high concurrency
type SessionPool struct {
sessions chan *session.Session
}
func NewSessionPool(size int, agent *agent.Agent) *SessionPool {
pool := &SessionPool{
sessions: make(chan *session.Session, size),
}
// Pre-populate pool
for i := 0; i < size; i++ {
pool.sessions <- session.New(agent)
}
return pool
}
func (sp *SessionPool) Get(agent *agent.Agent) *session.Session {
select {
case sess := <-sp.sessions:
return sess
default:
// Pool empty, create new session
return session.New(agent)
}
}
func (sp *SessionPool) Put(sess *session.Session) {
select {
case sp.sessions <- sess:
// Session returned to pool
default:
// Pool full, let session be garbage collected
}
}
Error Handling
Common session errors and how to handle them:
response, err := session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage(message)}, nil)
if err != nil {
switch {
case errors.Is(err, session.ErrContextTooLong):
// Trim conversation history
session.TrimToTokenLimit(4000)
response, err = session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage(message)}, nil)
case errors.Is(err, session.ErrInvalidMessage):
// Handle malformed message
log.Printf("Invalid message format: %v", err)
case errors.Is(err, session.ErrSessionExpired):
// Create new session
session = session.New(agent)
response, err = session.Run(ctx, []agent.ChatMessage{agent.NewUserMessage(message)}, nil)
}
}