diff --git a/config/config.example.json b/config/config.example.json index 77a8c0683..b3a26f811 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -9,6 +9,11 @@ "max_tool_iterations": 20 } }, + "session": { + "memory_message_limit": 20, + "memory_token_percent": 75, + "memory_notify_user": false + }, "model_list": [ { "model_name": "gpt4", diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index b36f4a0c4..3d9da2946 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -29,6 +29,11 @@ import ( "github.com/sipeed/picoclaw/pkg/utils" ) +const ( + defaultMemoryMessageLimit = 20 + defaultMemoryTokenPercent = 75 +) + type AgentLoop struct { bus *bus.MessageBus cfg *config.Config @@ -747,19 +752,35 @@ func (al *AgentLoop) updateToolContexts(agent *AgentInstance, channel, chatID st func (al *AgentLoop) maybeSummarize(agent *AgentInstance, sessionKey, channel, chatID string) { newHistory := agent.Sessions.GetHistory(sessionKey) tokenEstimate := al.estimateTokens(newHistory) - threshold := agent.ContextWindow * 75 / 100 - if len(newHistory) > 20 || tokenEstimate > threshold { + msgLimit := defaultMemoryMessageLimit + tokenPercent := defaultMemoryTokenPercent + notifyUser := false + if al.cfg != nil { + if al.cfg.Session.MemoryMessageLimit > 0 { + msgLimit = al.cfg.Session.MemoryMessageLimit + } + if al.cfg.Session.MemoryTokenPercent > 0 { + tokenPercent = al.cfg.Session.MemoryTokenPercent + } + notifyUser = al.cfg.Session.MemoryNotifyUser + } + threshold := agent.ContextWindow * tokenPercent / 100 + + if len(newHistory) > msgLimit || tokenEstimate > threshold { summarizeKey := agent.ID + ":" + sessionKey if _, loading := al.summarizing.LoadOrStore(summarizeKey, true); !loading { go func() { defer al.summarizing.Delete(summarizeKey) - if !constants.IsInternalChannel(channel) { + if notifyUser && !constants.IsInternalChannel(channel) { al.bus.PublishOutbound(bus.OutboundMessage{ Channel: channel, ChatID: chatID, Content: "Memory threshold reached. Optimizing conversation history...", }) + } else if !constants.IsInternalChannel(channel) { + logger.InfoCF("agent", "Memory threshold reached, optimizing conversation history", + map[string]interface{}{"session_key": sessionKey}) } al.summarizeSession(agent, sessionKey) }() diff --git a/pkg/config/config.go b/pkg/config/config.go index 20556011a..b0684bb8d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -164,6 +164,10 @@ type AgentBinding struct { type SessionConfig struct { DMScope string `json:"dm_scope,omitempty"` IdentityLinks map[string][]string `json:"identity_links,omitempty"` + // Memory summarization: trigger when history exceeds message count or token percent of context window + MemoryMessageLimit int `json:"memory_message_limit,omitempty"` // default 20 + MemoryTokenPercent int `json:"memory_token_percent,omitempty"` // default 75 + MemoryNotifyUser bool `json:"memory_notify_user,omitempty"` // if true, send "Memory threshold reached..." to user; default false } type AgentDefaults struct {