Sessions¶
Sessions enable stateful interactions across multiple runs. They maintain conversation history and custom state data, allowing agents to remember context from previous interactions.
What is a Session?¶
A session is a container for:
- History: Previous messages in the conversation
- State: Custom data persisted across runs
- Identity: A unique session ID
graph TB
subgraph Session
H[History] --> R1[Run 1]
H --> R2[Run 2]
H --> R3[Run 3]
S[State]
end
Creating Sessions¶
Client-Side Sessions¶
client = SimpleAcp::Client::Base.new(base_url: "http://localhost:8000")
# Use a specific session
client.use_session("user-123-conversation")
# All subsequent runs use this session
client.run_sync(agent: "chat", input: [...])
client.run_sync(agent: "chat", input: [...])
# Clear when done
client.clear_session
Server-Side Sessions¶
# Specify session when creating a run
run = server.run_sync(
agent_name: "chat",
input: messages,
session_id: "conversation-456"
)
Session History¶
History automatically accumulates messages across runs:
# First run
client.use_session("my-session")
client.run_sync(agent: "chat", input: [Message.user("Hello")])
# History: [user: "Hello", agent: "Hi there!"]
# Second run
client.run_sync(agent: "chat", input: [Message.user("How are you?")])
# History: [user: "Hello", agent: "Hi there!", user: "How are you?", agent: "I'm good!"]
Accessing History in Agents¶
server.agent("contextual") do |context|
# Get all previous messages
history = context.history
# Build context from history
conversation = history.map do |msg|
"#{msg.role}: #{msg.text_content}"
end.join("\n")
SimpleAcp::Models::Message.agent(
"Based on our conversation:\n#{conversation}\n\nContinuing..."
)
end
Session State¶
Custom state data persists across runs:
server.agent("counter") do |context|
# Read current state (nil if first run)
count = context.state || 0
# Increment
count += 1
# Save new state
context.set_state(count)
SimpleAcp::Models::Message.agent("Count: #{count}")
end
Complex State¶
State can be any JSON-serializable data:
server.agent("quiz") do |context|
state = context.state || { score: 0, questions_asked: 0 }
# Update state
state[:questions_asked] += 1
if correct_answer?(context.input)
state[:score] += 1
end
context.set_state(state)
SimpleAcp::Models::Message.agent(
"Score: #{state[:score]}/#{state[:questions_asked]}"
)
end
Session Properties¶
session = client.session("session-id")
session.id # => "session-id"
session.history # => Array of Messages
session.state # => Custom state data or nil
Session Storage¶
Sessions are persisted by the storage backend:
Common Patterns¶
Multi-Turn Conversation¶
server.agent("assistant") do |context|
# Build prompt with history
messages = context.history + context.input
# Call LLM with full context
response = llm.chat(messages)
SimpleAcp::Models::Message.agent(response)
end
User Preferences¶
server.agent("personalized") do |context|
state = context.state || { preferences: {} }
# Check for preference updates
if context.input.first&.text_content&.start_with?("set preference")
key, value = parse_preference(context.input.first.text_content)
state[:preferences][key] = value
context.set_state(state)
return SimpleAcp::Models::Message.agent("Preference saved!")
end
# Use preferences
prefs = state[:preferences]
# ...
end
Shopping Cart¶
server.agent("cart") do |context|
cart = context.state || { items: [], total: 0 }
command = context.input.first&.text_content
case command
when /^add (.+)/
item = $1
cart[:items] << item
cart[:total] += get_price(item)
when /^remove (.+)/
item = $1
cart[:items].delete(item)
cart[:total] -= get_price(item)
when "checkout"
# Process order...
cart = { items: [], total: 0 }
end
context.set_state(cart)
SimpleAcp::Models::Message.agent(
"Cart: #{cart[:items].join(', ')} - Total: $#{cart[:total]}"
)
end
Best Practices¶
Generate Meaningful Session IDs¶
# Good: Meaningful, traceable
client.use_session("user-#{user_id}-chat-#{timestamp}")
# Avoid: Random, hard to debug
client.use_session(SecureRandom.uuid)
Clean Up Sessions¶
# When conversation ends
client.clear_session
# Or delete server-side
storage.delete_session(session_id)
Limit History Size¶
server.agent("bounded") do |context|
# Only use recent history
recent = context.history.last(10)
# ...
end
Handle Missing State¶
server.agent("safe") do |context|
# Always provide defaults
state = context.state || default_state
# Validate state structure
state[:version] ||= 1
state[:data] ||= {}
# ...
end
Next Steps¶
- Learn about Events for real-time updates
- Explore Multi-Turn Conversations
- See Session Management in the client guide