tRPC-Agent-Go provides powerful session management capabilities to maintain conversation history and context information during Agent-user interactions. Through automatic persistence of conversation records, intelligent summary compression, and flexible storage backends, session management offers complete infrastructure for building stateful intelligent Agents.
π― Key Features
Context Management: Automatically load conversation history for true multi-turn dialogues
Session Summary: Automatically compress long conversation history using LLM while preserving key context and significantly reducing token consumption
Event Limiting: Control maximum number of events stored per session to prevent memory overflow
TTL Management: Support automatic expiration and cleanup of session data
Multiple Storage Backends: Support Memory, Redis, PostgreSQL, MySQL storage
Automatic Management: Automatically handle session creation, loading, and updates after Runner integration
Soft Delete Support: PostgreSQL/MySQL support soft delete with data recovery capability
Quick Start
Integration with Runner
tRPC-Agent-Go's session management integrates with Runner through runner.WithSessionService. Runner automatically handles session creation, loading, updates, and persistence.
Supported Storage Backends: Memory, Redis, PostgreSQL, MySQL
Default Behavior: If runner.WithSessionService is not configured, Runner defaults to using memory storage (Memory), and data will be lost after process restarts.
packagemainimport("context""fmt""time""trpc.group/trpc-go/trpc-agent-go/agent/llmagent""trpc.group/trpc-go/trpc-agent-go/model""trpc.group/trpc-go/trpc-agent-go/model/openai""trpc.group/trpc-go/trpc-agent-go/runner""trpc.group/trpc-go/trpc-agent-go/session/inmemory""trpc.group/trpc-go/trpc-agent-go/session/summary"// Optional: required when enabling summary feature)funcmain(){// 1. Create LLM modelllm:=openai.New("gpt-4",openai.WithAPIKey("your-api-key"))// 2. (Optional) Create summarizer - automatically compress long conversation historysummarizer:=summary.NewSummarizer(llm,// Use same LLM model for summary generationsummary.WithChecksAny(// Trigger when any condition is metsummary.CheckEventThreshold(20),// Trigger when exceeds 20 eventssummary.CheckTokenThreshold(4000),// Trigger when exceeds 4000 tokenssummary.CheckTimeThreshold(5*time.Minute),// Trigger after 5 minutes of inactivity),summary.WithMaxSummaryWords(200),// Limit summary to 200 words)// 3. Create Session Service (optional, defaults to memory storage if not configured)sessionService:=inmemory.NewSessionService(inmemory.WithSummarizer(summarizer),// Optional: inject summarizerinmemory.WithAsyncSummaryNum(2),// Optional: 2 async workersinmemory.WithSummaryQueueSize(100),// Optional: queue size 100)// 4. Create Agentagent:=llmagent.New("my-agent",llmagent.WithModel(llm),llmagent.WithInstruction("You are a helpful assistant"),llmagent.WithAddSessionSummary(true),// Optional: enable summary injection to context// Note: WithAddSessionSummary(true) ignores WithMaxHistoryRuns configuration// Summary includes all history, incremental events fully retained)// 5. Create Runner and inject Session Servicer:=runner.NewRunner("my-agent",agent,runner.WithSessionService(sessionService),)// 6. First conversationctx:=context.Background()userMsg1:=model.NewUserMessage("My name is Alice")eventChan,err:=r.Run(ctx,"user123","session-001",userMsg1)iferr!=nil{fmt.Printf("Error: %v\n",err)return}fmt.Print("AI: ")forevent:=rangeeventChan{ifevent==nil||event.Response==nil{continue}ifevent.Response.Error!=nil{fmt.Printf("\nError: %s (type: %s)\n",event.Response.Error.Message,event.Response.Error.Type)continue}iflen(event.Response.Choices)>0{choice:=event.Response.Choices[0]// Streaming output, prefer Delta.Content, fallback to Message.Contentifchoice.Delta.Content!=""{fmt.Print(choice.Delta.Content)}elseifchoice.Message.Content!=""{fmt.Print(choice.Message.Content)}}ifevent.IsFinalResponse(){break}}fmt.Println()// 7. Second conversation - automatically load history, AI remembers user's nameuserMsg2:=model.NewUserMessage("What's my name?")eventChan,err=r.Run(ctx,"user123","session-001",userMsg2)iferr!=nil{fmt.Printf("Error: %v\n",err)return}fmt.Print("AI: ")forevent:=rangeeventChan{ifevent==nil||event.Response==nil{continue}ifevent.Response.Error!=nil{fmt.Printf("\nError: %s (type: %s)\n",event.Response.Error.Message,event.Response.Error.Type)continue}iflen(event.Response.Choices)>0{choice:=event.Response.Choices[0]// Streaming output, prefer Delta.Content, fallback to Message.Contentifchoice.Delta.Content!=""{fmt.Print(choice.Delta.Content)}elseifchoice.Message.Content!=""{fmt.Print(choice.Message.Content)}}ifevent.IsFinalResponse(){break}}fmt.Println()// Output: Your name is Alice}
Runner Automatic Capabilities
After integrating Session Service, Runner automatically provides the following capabilities without needing to manually call any Session API:
Automatic Session Creation: Automatically create session on first conversation (generates UUID if SessionID is empty)
Automatic Session Loading: Automatically load historical context at the start of each conversation
Automatic Session Updates: Automatically save new events after conversation ends
Context Continuity: Automatically inject conversation history into LLM input for multi-turn dialogues
Automatic Summary Generation (Optional): Generate summaries asynchronously in background when trigger conditions are met, no manual intervention required
Core Capabilities
1οΈβ£ Context Management
The core function of session management is to maintain conversation context, ensuring the Agent can remember historical interactions and provide intelligent responses based on history.
How it Works:
Automatically save user input and AI responses from each conversation round
Automatically load historical events when new conversations begin
Runner automatically injects historical context into LLM input
Default Behavior: After Runner integration, context management is fully automated without manual intervention.
2οΈβ£ Session Summary
As conversations continue to grow, maintaining complete event history can consume significant memory and may exceed LLM context window limits. The session summary feature uses LLM to automatically compress historical conversations into concise summaries, significantly reducing memory usage and token consumption while preserving important context.
Core Features:
Automatic Triggering: Automatically generate summaries based on event count, token count, or time thresholds
Incremental Processing: Only process new events since the last summary, avoiding redundant computation
LLM-Driven: Use configured LLM model to generate high-quality, context-aware summaries
Non-Destructive: Original events are fully preserved, summaries stored separately
Asynchronous Processing: Execute asynchronously in background without blocking conversation flow
Flexible Configuration: Support custom trigger conditions, prompts, and word limits
import("time""trpc.group/trpc-go/trpc-agent-go/session/summary""trpc.group/trpc-go/trpc-agent-go/session/inmemory")// 1. Create summarizersummarizer:=summary.NewSummarizer(summaryModel,summary.WithChecksAny(// Trigger when any condition is metsummary.CheckEventThreshold(20),// Trigger when exceeds 20 eventssummary.CheckTokenThreshold(4000),// Trigger when exceeds 4000 tokenssummary.CheckTimeThreshold(5*time.Minute),// Trigger after 5 minutes of inactivity),summary.WithMaxSummaryWords(200),// Limit summary to 200 words)// 2. Configure session servicesessionService:=inmemory.NewSessionService(inmemory.WithSummarizer(summarizer),inmemory.WithAsyncSummaryNum(2),// 2 async workersinmemory.WithSummaryQueueSize(100),// Queue size 100)// 3. Enable summary injection to AgentllmAgent:=llmagent.New("my-agent",llmagent.WithModel(llm),llmagent.WithAddSessionSummary(true),// Enable summary injection)// 4. Create Runnerr:=runner.NewRunner("my-agent",llmAgent,runner.WithSessionService(sessionService))
Context Injection Mechanism:
After enabling summary, the framework prepends the summary as a system message to the LLM input, while including all incremental events after the summary timestamp to ensure complete context:
βββββββββββββββββββββββββββββββββββββββββββ
β System Prompt β
βββββββββββββββββββββββββββββββββββββββββββ€
β Session Summary (system message) β β Compressed version of historical conversations
β - Updated at: 2024-01-10 14:30 β (events before updated_at)
β - Includes: Event1 ~ Event20 β
βββββββββββββββββββββββββββββββββββββββββββ€
β Event 21 (user message) β β
β Event 22 (assistant response) β β
β Event 23 (user message) β β All new conversations after summary
β Event 24 (assistant response) β β (fully retained, no truncation)
β ... β β
β Event N (current message) β β
βββββββββββββββββββββββββββββββββββββββββββ
Important Note: When WithAddSessionSummary(true) is enabled, the WithMaxHistoryRuns parameter is ignored, and all events after the summary are fully retained.
For detailed configuration and advanced usage, see the Session Summary section.
3οΈβ£ Event Limiting (EventLimit)
Control the maximum number of events stored per session to prevent memory overflow from long conversations.
How it Works:
Automatically evict oldest events (FIFO) when limit is exceeded
sessionService:=inmemory.NewSessionService(inmemory.WithSessionTTL(30*time.Minute),// Session expires after 30 minutes of inactivityinmemory.WithAppStateTTL(24*time.Hour),// App state expires after 24 hoursinmemory.WithUserStateTTL(7*24*time.Hour),// User state expires after 7 days)
Expiration Behavior:
Storage Type
Expiration Mechanism
Auto Cleanup
Memory
Periodic scanning + access-time checking
Yes
Redis
Redis native TTL
Yes
PostgreSQL
Periodic scanning (soft delete or hard delete)
Yes
MySQL
Periodic scanning (soft delete or hard delete)
Yes
Storage Backend Comparison
tRPC-Agent-Go provides four session storage backends to meet different scenario requirements:
Storage Type
Use Case
Advantages
Disadvantages
Memory
Development/testing, small-scale
Simple and fast, no external dependencies
Data not persistent, no distributed support
Redis
Production, distributed
High performance, distributed support, auto-expiration
Suitable for development environments and small-scale applications, no external dependencies required, ready to use out of the box.
Configuration Options
WithSessionEventLimit(limit int): Set maximum number of events stored per session. Default is 1000, evicts old events when exceeded.
WithSessionTTL(ttl time.Duration): Set TTL for session state and event list. Default is 0 (no expiration).
WithAppStateTTL(ttl time.Duration): Set TTL for application-level state. Default is 0 (no expiration).
WithUserStateTTL(ttl time.Duration): Set TTL for user-level state. Default is 0 (no expiration).
WithCleanupInterval(interval time.Duration): Set interval for automatic cleanup of expired data. Default is 0 (auto-determined), if any TTL is configured, default cleanup interval is 5 minutes.
import"trpc.group/trpc-go/trpc-agent-go/session/inmemory"// Default configuration (development environment)sessionService:=inmemory.NewSessionService()// Effect:// - Each session max 1000 events// - All data never expires// - No automatic cleanup// Production environment configurationsessionService:=inmemory.NewSessionService(inmemory.WithSessionEventLimit(500),inmemory.WithSessionTTL(30*time.Minute),inmemory.WithAppStateTTL(24*time.Hour),inmemory.WithUserStateTTL(7*24*time.Hour),inmemory.WithCleanupInterval(10*time.Minute),)// Effect:// - Each session max 500 events// - Session expires after 30 minutes of inactivity// - App state expires after 24 hours// - User state expires after 7 days// - Cleanup every 10 minutes
import("trpc.group/trpc-go/trpc-agent-go/session/inmemory""trpc.group/trpc-go/trpc-agent-go/session/summary")// Create summarizersummarizer:=summary.NewSummarizer(summaryModel,summary.WithEventThreshold(20),summary.WithMaxSummaryWords(200),)// Create session service and inject summarizersessionService:=inmemory.NewSessionService(inmemory.WithSessionEventLimit(1000),inmemory.WithSummarizer(summarizer),inmemory.WithAsyncSummaryNum(2),inmemory.WithSummaryQueueSize(100),inmemory.WithSummaryJobTimeout(30*time.Second),)
Redis Storage
Suitable for production environments and distributed applications, provides high performance and auto-expiration capabilities.
Configuration Options
WithRedisClientURL(url string): Create Redis client via URL. Format: redis://[username:password@]host:port[/database].
WithRedisInstance(instanceName string): Use pre-configured Redis instance. Note: WithRedisClientURL has higher priority than WithRedisInstance.
WithSessionEventLimit(limit int): Set maximum number of events stored per session. Default is 1000.
WithSessionTTL(ttl time.Duration): Set TTL for session state and events. Default is 0 (no expiration).
WithAppStateTTL(ttl time.Duration): Set TTL for application-level state. Default is 0 (no expiration).
WithUserStateTTL(ttl time.Duration): Set TTL for user-level state. Default is 0 (no expiration).
import"trpc.group/trpc-go/trpc-agent-go/session/redis"// Create using URL (recommended)sessionService,err:=redis.NewService(redis.WithRedisClientURL("redis://username:password@127.0.0.1:6379/0"),redis.WithSessionEventLimit(500),)// Complete production environment configurationsessionService,err:=redis.NewService(redis.WithRedisClientURL("redis://localhost:6379/0"),redis.WithSessionEventLimit(1000),redis.WithSessionTTL(30*time.Minute),redis.WithAppStateTTL(24*time.Hour),redis.WithUserStateTTL(7*24*time.Hour),)// Effect:// - Connect to local Redis database 0// - Each session max 1000 events// - Session auto-expires after 30 minutes of inactivity (Redis TTL)// - App state expires after 24 hours// - User state expires after 7 days// - Uses Redis native TTL mechanism, no manual cleanup needed
Configuration Reuse
If multiple components need to use the same Redis instance, you can register and reuse:
import("trpc.group/trpc-go/trpc-agent-go/storage""trpc.group/trpc-go/trpc-agent-go/session/redis")// Register Redis instanceredisURL:="redis://127.0.0.1:6379"storage.RegisterRedisInstance("my-redis-instance",storage.WithClientBuilderURL(redisURL))// Use in session servicesessionService,err:=redis.NewService(redis.WithRedisInstance("my-redis-instance"),redis.WithSessionEventLimit(500),)
import("trpc.group/trpc-go/trpc-agent-go/storage""trpc.group/trpc-go/trpc-agent-go/session/postgres")// Register PostgreSQL instancestorage.RegisterPostgresInstance("my-postgres-instance",storage.WithPostgresHost("localhost"),storage.WithPostgresPort(5432),storage.WithPostgresUser("postgres"),storage.WithPostgresPassword("your-password"),storage.WithPostgresDatabase("trpc_sessions"),)// Use in session servicesessionService,err:=postgres.NewService(postgres.WithInstanceName("my-postgres-instance"),postgres.WithSessionEventLimit(500),)
Schema and Table Prefix
PostgreSQL supports schema and table prefix configuration for multi-tenant and multi-environment scenarios:
sessionService,err:=postgres.NewService(postgres.WithHost("localhost"),postgres.WithSessionTTL(30*time.Minute),// Session expires after 30 minutespostgres.WithAppStateTTL(24*time.Hour),// App state expires after 24 hourspostgres.WithUserStateTTL(7*24*time.Hour),// User state expires after 7 dayspostgres.WithCleanupInterval(10*time.Minute),// Cleanup every 10 minutespostgres.WithSoftDelete(true),// Soft delete mode)// Cleanup behavior:// - softDelete=true: Expired data marked as deleted_at = NOW()// - softDelete=false: Expired data physically deleted// - Queries always filter deleted_at IS NULL
-- Session states tableCREATETABLEsession_states(idBIGSERIALPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,session_idVARCHAR(255)NOTNULL,stateJSONB,created_atTIMESTAMPNOTNULL,updated_atTIMESTAMPNOTNULL,expires_atTIMESTAMP,deleted_atTIMESTAMP);-- Partial unique index (only applies to non-deleted records)CREATEUNIQUEINDEXidx_session_states_unique_activeONsession_states(app_name,user_id,session_id)WHEREdeleted_atISNULL;-- Session events tableCREATETABLEsession_events(idBIGSERIALPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,session_idVARCHAR(255)NOTNULL,eventJSONBNOTNULL,created_atTIMESTAMPNOTNULL,updated_atTIMESTAMPNOTNULL,expires_atTIMESTAMP,deleted_atTIMESTAMP);-- Track events table.CREATETABLEsession_track_events(idBIGSERIALPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,session_idVARCHAR(255)NOTNULL,trackVARCHAR(255)NOTNULL,eventJSONBNOTNULL,created_atTIMESTAMPNOTNULL,updated_atTIMESTAMPNOTNULL,expires_atTIMESTAMP,deleted_atTIMESTAMP);-- Session summaries tableCREATETABLEsession_summaries(idBIGSERIALPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,session_idVARCHAR(255)NOTNULL,filter_keyVARCHAR(255)NOTNULL,summaryJSONBNOTNULL,updated_atTIMESTAMPNOTNULL,expires_atTIMESTAMP,deleted_atTIMESTAMP,UNIQUE(app_name,user_id,session_id,filter_key));-- Application states tableCREATETABLEapp_states(idBIGSERIALPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,keyVARCHAR(255)NOTNULL,valueTEXTDEFAULTNULL,created_atTIMESTAMPNOTNULL,updated_atTIMESTAMPNOTNULL,expires_atTIMESTAMP,deleted_atTIMESTAMP,UNIQUE(app_name,key));-- User states tableCREATETABLEuser_states(idBIGSERIALPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,keyVARCHAR(255)NOTNULL,valueTEXTDEFAULTNULL,created_atTIMESTAMPNOTNULL,updated_atTIMESTAMPNOTNULL,expires_atTIMESTAMP,deleted_atTIMESTAMP,UNIQUE(app_name,user_id,key));
MySQL Storage
Suitable for production environments and applications requiring complex queries, MySQL is a widely used relational database.
Configuration Options
Connection Configuration:
WithMySQLClientDSN(dsn string)οΌMySQL config
WithInstanceName(name string): Use pre-configured MySQL instance.
Session Configuration:
WithSessionEventLimit(limit int): Maximum events per session. Default is 1000.
WithSessionTTL(ttl time.Duration): Session TTL. Default is 0 (no expiration).
WithAppStateTTL(ttl time.Duration): App state TTL. Default is 0 (no expiration).
WithUserStateTTL(ttl time.Duration): User state TTL. Default is 0 (no expiration).
WithCleanupInterval(interval time.Duration): TTL cleanup interval. Default is 5 minutes.
WithSoftDelete(enable bool): Enable or disable soft delete. Default is true.
Async Persistence Configuration:
WithEnableAsyncPersist(enable bool): Enable async persistence. Default is false.
WithAsyncPersisterNum(num int): Number of async persistence workers. Default is 10.
import("trpc.group/trpc-go/trpc-agent-go/storage""trpc.group/trpc-go/trpc-agent-go/session/mysql")// Register MySQL instancestorage.RegisterMySQLInstance("my-mysql-instance",storage.WithMySQLHost("localhost"),storage.WithMySQLPort(3306),storage.WithMySQLUser("root"),storage.WithMySQLPassword("your-password"),storage.WithMySQLDatabase("trpc_sessions"),)// Use in session servicesessionService,err:=mysql.NewService(mysql.WithInstanceName("my-mysql-instance"),mysql.WithSessionEventLimit(500),)
Table Prefix
MySQL supports table prefix configuration for multi-application shared database scenarios:
sessionService,err:=mysql.NewService(mysql.WithMySQLClientDSN("user:password@tcp(localhost:3306)/db?charset=utf8mb4&parseTime=True&loc=Local"),mysql.WithSessionTTL(30*time.Minute),// Session expires after 30 minutesmysql.WithAppStateTTL(24*time.Hour),// App state expires after 24 hoursmysql.WithUserStateTTL(7*24*time.Hour),// User state expires after 7 daysmysql.WithCleanupInterval(10*time.Minute),// Cleanup every 10 minutesmysql.WithSoftDelete(true),// Soft delete mode)// Cleanup behavior:// - softDelete=true: Expired data marked as deleted_at = NOW()// - softDelete=false: Expired data physically deleted// - Queries always filter deleted_at IS NULL
-- Session states tableCREATETABLEsession_states(idBIGINTAUTO_INCREMENTPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,session_idVARCHAR(255)NOTNULL,stateJSON,created_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,expires_atTIMESTAMPNULL,deleted_atTIMESTAMPNULL,UNIQUEKEYidx_session_states_unique(app_name,user_id,session_id,deleted_at))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ci;-- Session events tableCREATETABLEsession_events(idBIGINTAUTO_INCREMENTPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,session_idVARCHAR(255)NOTNULL,eventJSONNOTNULL,created_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,expires_atTIMESTAMPNULL,deleted_atTIMESTAMPNULL,KEYidx_session_events(app_name,user_id,session_id,deleted_at,created_at))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ci;-- Session summaries tableCREATETABLEsession_summaries(idBIGINTAUTO_INCREMENTPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,session_idVARCHAR(255)NOTNULL,filter_keyVARCHAR(255)NOTNULL,summaryJSONNOTNULL,updated_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,expires_atTIMESTAMPNULL,deleted_atTIMESTAMPNULL,UNIQUEKEYidx_session_summaries_unique(app_name,user_id,session_id,filter_key,deleted_at))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ci;-- Application states tableCREATETABLEapp_states(idBIGINTAUTO_INCREMENTPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,`key`VARCHAR(255)NOTNULL,valueTEXTDEFAULTNULL,created_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,expires_atTIMESTAMPNULL,deleted_atTIMESTAMPNULL,UNIQUEKEYidx_app_states_unique(app_name,`key`,deleted_at))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ci;-- User states tableCREATETABLEuser_states(idBIGINTAUTO_INCREMENTPRIMARYKEY,app_nameVARCHAR(255)NOTNULL,user_idVARCHAR(255)NOTNULL,`key`VARCHAR(255)NOTNULL,valueTEXTDEFAULTNULL,created_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,expires_atTIMESTAMPNULL,deleted_atTIMESTAMPNULL,UNIQUEKEYidx_user_states_unique(app_name,user_id,`key`,deleted_at))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ci;
Key Differences Between MySQL and PostgreSQL:
MySQL doesn't support partial index with WHERE deleted_at IS NULL, requires including deleted_at in unique index
MySQL uses JSON type instead of JSONB (similar functionality, different storage format)
MySQL uses ON DUPLICATE KEY UPDATE syntax for UPSERT
Advanced Usage
Direct Use of Session Service API
In most cases, you should use session management through Runner, which automatically handles all details. However, in some special scenarios (such as session management backend, data migration, statistical analysis, etc.), you may need to directly operate the Session Service.
Note: The following APIs are only for special scenarios, daily use of Runner is sufficient.
// List all sessions of a usersessions,err:=sessionService.ListSessions(ctx,session.UserKey{AppName:"my-agent",UserID:"user123",})for_,sess:=rangesessions{fmt.Printf("SessionID: %s, Events: %d\n",sess.ID,len(sess.Events))}
// Get complete sessionsess,err:=sessionService.GetSession(ctx,session.Key{AppName:"my-agent",UserID:"user123",SessionID:"session-id-123",})// Get session with latest 10 events onlysess,err:=sessionService.GetSession(ctx,key,session.WithEventNum(10))// Get events after specified timesess,err:=sessionService.GetSession(ctx,key,session.WithEventTime(time.Now().Add(-1*time.Hour)))
Session Summarization
Overview
As conversations grow longer, maintaining full event history can become memory-intensive and may exceed LLM context windows. The session summarization feature automatically compresses historical conversation content into concise summaries using LLM-based summarization, reducing memory usage while preserving important context for future interactions.
Key Features
Automatic summarization: Automatically trigger summaries based on configurable conditions such as event count, token count, or time threshold.
Incremental summarization: Only new events since the last summary are processed, avoiding redundant computation.
LLM-powered: Uses any configured LLM model to generate high-quality, context-aware summaries.
Non-destructive: Original events remain unchanged; summaries are stored separately.
Asynchronous processing: Summary jobs are processed asynchronously to avoid blocking the main conversation flow.
Customizable prompts: Configure custom summarization prompts and word limits.
Basic Usage
Configure Summarizer
Create a summarizer with an LLM model and configure trigger conditions:
import("time""trpc.group/trpc-go/trpc-agent-go/session/summary""trpc.group/trpc-go/trpc-agent-go/model/openai")// Create LLM model for summarization.summaryModel:=openai.New("gpt-4",openai.WithAPIKey("your-api-key"))// Create summarizer with trigger conditions.summarizer:=summary.NewSummarizer(summaryModel,summary.WithEventThreshold(20),// Trigger when exceeds 20 events.summary.WithTokenThreshold(4000),// Trigger when exceeds 4000 tokens.summary.WithMaxSummaryWords(200),// Limit summary to 200 words.)
Integrate with Session Service
Attach the summarizer to your session service (in-memory or Redis):
import("trpc.group/trpc-go/trpc-agent-go/agent/llmagent""trpc.group/trpc-go/trpc-agent-go/runner")// Create agent with summary injection enabled.llmAgent:=llmagent.New("my-agent",llmagent.WithModel(summaryModel),llmagent.WithAddSessionSummary(true),// Inject summary as system message.llmagent.WithMaxHistoryRuns(10),// Limit history runs when AddSessionSummary=false)// Create runner with session service.runner:=runner.NewRunner("my-agent",llmAgent,runner.WithSessionService(sessionService),)// Summaries are automatically created and injected during conversation.eventChan,err:=runner.Run(ctx,userID,sessionID,userMessage)
How it works:
The framework provides two distinct modes for managing conversation context sent to the LLM:
Mode 1: With Summary (WithAddSessionSummary(true))
The session summary is prepended as a system message.
All incremental events after the summary timestamp are included (no truncation).
This ensures complete context: condensed history (summary) + all new conversations since summarization.
WithMaxHistoryRuns is ignored in this mode.
Mode 2: Without Summary (WithAddSessionSummary(false))
No summary is prepended.
Only the most recent MaxHistoryRuns conversation turns are included.
When MaxHistoryRuns=0 (default), no limit is applied and all history is included.
Use this mode for short sessions or when you want direct control over context window size.
When AddSessionSummary = true:
βββββββββββββββββββββββββββββββββββββββ
β System Prompt β
βββββββββββββββββββββββββββββββββββββββ€
β Session Summary (system message) β β Condensed history
βββββββββββββββββββββββββββββββββββββββ€
β Event 1 (after summary timestamp) β β
β Event 2 β β All incremental
β Event 3 β β events since
β ... β β last summary
β Event N (current) β β
βββββββββββββββββββββββββββββββββββββββ
When AddSessionSummary = false:
βββββββββββββββββββββββββββββββββββββββ
β System Prompt β
βββββββββββββββββββββββββββββββββββββββ€
β Event N-k+1 β β
β Event N-k+2 β β Last k turns
β ... β β (if MaxHistoryRuns=k)
β Event N (current) β β
βββββββββββββββββββββββββββββββββββββββ
Best Practices:
For long-running sessions, use WithAddSessionSummary(true) to maintain full context while managing token usage.
For short sessions or when testing, use WithAddSessionSummary(false) with appropriate MaxHistoryRuns.
The Runner automatically enqueues async summary jobs after appending events to the session.
Configuration Options
Summarizer Options
Configure the summarizer behavior with the following options:
Trigger Conditions:
WithEventThreshold(eventCount int): Trigger summarization when the number of events exceeds the threshold. Example: WithEventThreshold(20) triggers when exceeds 20 events.
WithTokenThreshold(tokenCount int): Trigger summarization when the total token count exceeds the threshold. Example: WithTokenThreshold(4000) triggers when exceeds 4000 tokens.
WithTimeThreshold(interval time.Duration): Trigger summarization when time elapsed since the last event exceeds the interval. Example: WithTimeThreshold(5*time.Minute) triggers after 5 minutes of inactivity.
Composite Conditions:
WithChecksAll(checks ...Checker): Require all conditions to be met (AND logic). Use with Check* functions (not With*). Example:
Note: Use Check* functions (like CheckEventThreshold) inside WithChecksAll and WithChecksAny. Use With* functions (like WithEventThreshold) as direct options to NewSummarizer. The Check* functions create checker instances, while With* functions are option setters.
Summary Generation:
WithMaxSummaryWords(maxWords int): Limit the summary to a maximum word count. The limit is included in the prompt to guide the model's generation. Example: WithMaxSummaryWords(150) requests summaries within 150 words.
WithPrompt(prompt string): Provide a custom summarization prompt. The prompt must include the placeholder {conversation_text}, which will be replaced with the conversation content. Optionally include {max_summary_words} for word limit instructions.
customPrompt:=`Analyze the following conversation and provide a concise summaryfocusing on key decisions, action items, and important context.Keep it within {max_summary_words} words.<conversation>{conversation_text}</conversation>Summary:`summarizer:=summary.NewSummarizer(summaryModel,summary.WithPrompt(customPrompt),summary.WithMaxSummaryWords(100),summary.WithEventThreshold(15),)
Session Service Options
Configure async summary processing in session services:
WithSummarizer(s summary.SessionSummarizer): Inject the summarizer into the session service.
WithAsyncSummaryNum(num int): Set the number of async worker goroutines for summary processing. Default is 2. More workers allow higher concurrency but consume more resources.
WithSummaryQueueSize(size int): Set the size of the summary job queue. Default is 100. Larger queues allow more pending jobs but consume more memory.
WithSummaryJobTimeout(timeout time.Duration): Set the timeout for processing a single summary job. Default is 30 seconds.
Manual Summarization
You can manually trigger summarization using the session service APIs:
Incremental Processing: The summarizer tracks the last summarization time for each session. On subsequent runs, it only processes events that occurred after the last summary.
Delta Summarization: New events are combined with the previous summary (prepended as a system event) to generate an updated summary that incorporates both old context and new information.
Trigger Evaluation: Before generating a summary, the summarizer evaluates configured trigger conditions (event count, token count, time threshold). If conditions aren't met and force=false, summarization is skipped.
Async Workers: Summary jobs are distributed across multiple worker goroutines using hash-based distribution. This ensures jobs for the same session are processed sequentially while different sessions can be processed in parallel.
Fallback Mechanism: If async enqueueing fails (queue full, context cancelled, or workers not initialized), the system automatically falls back to synchronous processing.
Best Practices
Choose appropriate thresholds: Set event/token thresholds based on your LLM's context window and conversation patterns. For GPT-4 (8K context), consider WithTokenThreshold(4000) to leave room for responses.
Use async processing: Always use EnqueueSummaryJob instead of CreateSessionSummary in production to avoid blocking the conversation flow.
Monitor queue sizes: If you see frequent "queue is full" warnings, increase WithSummaryQueueSize or WithAsyncSummaryNum.
Customize prompts: Tailor the summarization prompt to your application's needs. For example, if you're building a customer support agent, focus on key issues and resolutions.
Balance word limits: Set WithMaxSummaryWords to balance between preserving context and reducing token usage. Typical values range from 100-300 words.
Test trigger conditions: Experiment with different combinations of WithChecksAny and WithChecksAll to find the right balance between summary frequency and cost.
Performance Considerations
LLM costs: Each summary generation calls the LLM. Monitor your trigger conditions to balance cost and context preservation.
Memory usage: Summaries are stored in addition to events. Configure appropriate TTLs to manage memory in long-running sessions.
Async workers: More workers increase throughput but consume more resources. Start with 2-4 workers and scale based on load.
Queue capacity: Size the queue based on your expected concurrency and summary generation time.
Complete Example
Here's a complete example demonstrating all components together:
packagemainimport("context""time""trpc.group/trpc-go/trpc-agent-go/agent/llmagent""trpc.group/trpc-go/trpc-agent-go/model""trpc.group/trpc-go/trpc-agent-go/model/openai""trpc.group/trpc-go/trpc-agent-go/runner""trpc.group/trpc-go/trpc-agent-go/session/inmemory""trpc.group/trpc-go/trpc-agent-go/session/summary")funcmain(){ctx:=context.Background()// Create LLM model for both chat and summarization.llm:=openai.New("gpt-4",openai.WithAPIKey("your-api-key"))// Create summarizer with flexible trigger conditions.summarizer:=summary.NewSummarizer(llm,summary.WithMaxSummaryWords(200),summary.WithChecksAny(summary.CheckEventThreshold(20),summary.CheckTokenThreshold(4000),summary.CheckTimeThreshold(5*time.Minute),),)// Create session service with summarizer.sessionService:=inmemory.NewSessionService(inmemory.WithSummarizer(summarizer),inmemory.WithAsyncSummaryNum(2),inmemory.WithSummaryQueueSize(100),inmemory.WithSummaryJobTimeout(30*time.Second),)// Create agent with summary injection enabled.agent:=llmagent.New("my-agent",llmagent.WithModel(llm),llmagent.WithAddSessionSummary(true),llmagent.WithMaxHistoryRuns(10),// Limit history runs when AddSessionSummary=false)// Create runner.r:=runner.NewRunner("my-app",agent,runner.WithSessionService(sessionService))// Run conversation - summaries are automatically managed.userMsg:=model.NewUserMessage("Tell me about AI")eventChan,_:=r.Run(ctx,"user123","session456",userMsg)// Consume events.forevent:=rangeeventChan{// Handle events...}}
By properly using session management, in combination with session summarization mechanisms, you can build stateful intelligent Agents that maintain conversation context while efficiently managing memory, providing users with continuous and personalized interaction experiences while ensuring the long-term sustainability of your system.