1+ import asyncio
12from collections import deque
3+ from collections .abc import Callable
24from dataclasses import dataclass
35from datetime import datetime
46
@@ -57,63 +59,40 @@ class MemoryRecord:
5759
5860
5961class Memory :
60- """
61- 短时记忆
62- """
63-
6462 def __init__ (
6563 self ,
6664 llm_client : LLMClient ,
6765 compressed_message : str | None = None ,
6866 messages : list [Message ] | None = None ,
6967 length_limit : int = 10 ,
7068 ):
71- """
72- 初始化记忆
73- 参数
74-
75- - length_limit: 记忆消息长度限制
76- """
7769 self .__length_limit = length_limit
78-
79- if compressed_message :
80- self .__compressed_message = compressed_message
81- """
82- 压缩后的旧消息
83- """
84- else :
85- self .__compressed_message = ""
86-
87- if messages :
88- self .__messages = deque (messages , maxlen = length_limit * 5 ) # 有4/5空间不可直接访问,用于放置待压缩消息
89- """
90- 记忆消息列表
91- """
92- else :
93- self .__messages : deque [Message ] = deque (
94- maxlen = length_limit * 5
95- ) # 有4/5空间不可直接访问,用于放置待压缩消息
96-
70+ self .__compressed_message = compressed_message or ""
71+ self .__messages = deque (messages , maxlen = length_limit * 5 ) if messages else deque (maxlen = length_limit * 5 )
9772 self .__llm_client = llm_client
98- """
99- 用于压缩消息的 LLM 客户端
100- """
73+ self .__compress_counter = 0
74+ self .__compress_task : asyncio .Task | None = None # 压缩任务句柄
10175
10276 def related_users (self ) -> list [str ]:
10377 """
10478 获取相关用户列表
10579 """
10680 return list ({message .user_name for message in self .__messages })
10781
108- async def compress_message (self ):
82+ async def clear (self ) -> None :
10983 """
110- 压缩历史消息
84+ 清除所有记忆
11185 """
112- history_messages = [f"{ message .user_name } : { message .content } " for message in self .__messages ][
113- : self .__length_limit
114- ]
86+ self .__messages .clear ()
87+ self .__compressed_message = ""
88+ self .__compress_counter = 0
89+ await self .__cancel_compress_task ()
90+ logger .info ("已清除所有记忆" )
91+
92+ async def __compress_message (self , after_compress : Callable [[], None ] | None = None ):
93+ history_messages = [f"{ msg .user_name } : { msg .content } " for msg in self .__messages ]
11594 prompt = f"""
116- 请将以下消息分参与的话题压缩,保留
95+ 请将以下消息分参与的话题压缩,提取
11796
11897- 话题简要内容
11998- 参与者和它们的发言总结
@@ -130,22 +109,19 @@ async def compress_message(self):
130109"""
131110 try :
132111 response = await self .__llm_client .generate_response (prompt , model = "Qwen/Qwen3-8B" )
112+ if after_compress :
113+ after_compress ()
133114 if response :
134115 self .__compressed_message = response
135116 logger .info (f"压缩消息成功: { response } " )
136117 else :
137118 logger .warning ("压缩消息失败,原因未知" )
119+ except asyncio .CancelledError :
120+ logger .info ("压缩任务被取消" )
121+ raise
138122 except Exception as e :
139123 logger .error (f"压缩消息时发生错误: { e } " )
140124
141- def clear (self ) -> None :
142- """
143- 清除所有记忆
144- """
145- self .__messages .clear ()
146- self .__compressed_message = ""
147- logger .info ("已清除所有记忆" )
148-
149125 def access (self ) -> MemoryRecord :
150126 """
151127 访问记忆
@@ -155,11 +131,28 @@ def access(self) -> MemoryRecord:
155131 compressed_history = self .__compressed_message ,
156132 )
157133
158- def update (self , message_chunk : list [ Message ] ):
134+ async def __cancel_compress_task (self ):
159135 """
160- 更新记忆
161- 参数
162-
163- - message_chunk: 消息块
136+ 取消压缩任务
164137 """
138+ if self .__compress_task and not self .__compress_task .done ():
139+ self .__compress_task .cancel ()
140+ try :
141+ await self .__compress_task
142+ except asyncio .CancelledError :
143+ pass
144+
145+ async def update (self , message_chunk : list [Message ], after_compress : Callable [[], None ] | None = None ):
165146 self .__messages .extend (message_chunk )
147+
148+ # 每self.__length_limit条消息压缩一次
149+ if self .__compress_counter < self .__length_limit :
150+ self .__compress_counter += 1
151+ return
152+
153+ self .__compress_counter = 0
154+ # 如果有正在执行的压缩任务,先取消它
155+ await self .__cancel_compress_task ()
156+
157+ # 开启新的压缩任务
158+ self .__compress_task = asyncio .create_task (self .__compress_message (after_compress = after_compress ))
0 commit comments