1616from json_repair import repair_json
1717from src .person_info .person_info import person_info_manager
1818import json
19+ import asyncio
20+ from src .chat .utils .chat_message_builder import get_raw_msg_by_timestamp_with_chat
1921
2022logger = get_logger ("processor" )
2123
@@ -37,6 +39,7 @@ def init_prompt():
37391. 根据聊天记录的需求,如果需要你和某个人的信息,请输出你和这个人之间精简的信息
38402. 如果没有特别需要提及的信息,就不用输出这个人的信息
39413. 如果有人问你对他的看法或者关系,请输出你和这个人之间的信息
42+ 4. 你可以完全不输出任何信息,或者不输出某个人
4043
4144请从这些信息中提取出你对某人的了解信息,信息提取成一串文本:
4245
@@ -58,6 +61,11 @@ def __init__(self, subheartflow_id: str):
5861 super ().__init__ ()
5962
6063 self .subheartflow_id = subheartflow_id
64+ self .person_cache : Dict [str , Dict [str , any ]] = {} # {person_id: {"info": str, "ttl": int, "start_time": float}}
65+ self .pending_updates : Dict [str , Dict [str , any ]] = (
66+ {}
67+ ) # {person_id: {"start_time": float, "end_time": float, "grace_period_ttl": int, "chat_id": str}}
68+ self .grace_period_rounds = 5
6169
6270 self .llm_model = LLMRequest (
6371 model = global_config .model .relation ,
@@ -91,54 +99,109 @@ async def process_info(
9199 return [relation_info ]
92100
93101 async def relation_identify (
94- self , observations : Optional [List [Observation ]] = None ,
102+ self ,
103+ observations : Optional [List [Observation ]] = None ,
95104 ):
96105 """
97106 在回复前进行思考,生成内心想法并收集工具调用结果
98-
99- 参数:
100- observations: 观察信息
101-
102- 返回:
103- 如果return_prompt为False:
104- tuple: (current_mind, past_mind) 当前想法和过去的想法列表
105- 如果return_prompt为True:
106- tuple: (current_mind, past_mind, prompt) 当前想法、过去的想法列表和使用的prompt
107107 """
108-
109- if observations is None :
110- observations = []
111- for observation in observations :
112- if isinstance (observation , ChattingObservation ):
113- # 获取聊天元信息
114- is_group_chat = observation .is_group_chat
115- chat_target_info = observation .chat_target_info
116- chat_target_name = "对方" # 私聊默认名称
117- if not is_group_chat and chat_target_info :
118- # 优先使用person_name,其次user_nickname,最后回退到默认值
119- chat_target_name = (
120- chat_target_info .get ("person_name" ) or chat_target_info .get ("user_nickname" ) or chat_target_name
108+ # 0. 从观察信息中提取所需数据
109+ person_list = []
110+ chat_observe_info = ""
111+ is_group_chat = False
112+ if observations :
113+ for observation in observations :
114+ if isinstance (observation , ChattingObservation ):
115+ is_group_chat = observation .is_group_chat
116+ chat_observe_info = observation .get_observe_info ()
117+ person_list = observation .person_list
118+ break
119+
120+ # 1. 处理等待更新的条目(仅检查TTL,不检查是否被重提)
121+ persons_to_update_now = [] # 等待期结束,需要立即更新的用户
122+ for person_id , data in list (self .pending_updates .items ()):
123+ data ["grace_period_ttl" ] -= 1
124+ if data ["grace_period_ttl" ] <= 0 :
125+ persons_to_update_now .append (person_id )
126+
127+ # 触发等待期结束的更新任务
128+ for person_id in persons_to_update_now :
129+ if person_id in self .pending_updates :
130+ update_data = self .pending_updates .pop (person_id )
131+ logger .info (f"{ self .log_prefix } 用户 { person_id } 等待期结束,开始印象更新。" )
132+ asyncio .create_task (
133+ self .update_impression_on_cache_expiry (
134+ person_id , update_data ["chat_id" ], update_data ["start_time" ], update_data ["end_time" ]
121135 )
122- # 获取聊天内容
123- chat_observe_info = observation .get_observe_info ()
124- person_list = observation .person_list
125-
126- nickname_str = ""
127- for nicknames in global_config .bot .alias_names :
128- nickname_str += f"{ nicknames } ,"
136+ )
137+
138+ # 2. 维护活动缓存,并将过期条目移至等待区或立即更新
139+ persons_moved_to_pending = []
140+ for person_id , cache_data in self .person_cache .items ():
141+ cache_data ["ttl" ] -= 1
142+ if cache_data ["ttl" ] <= 0 :
143+ persons_moved_to_pending .append (person_id )
144+
145+ for person_id in persons_moved_to_pending :
146+ if person_id in self .person_cache :
147+ cache_item = self .person_cache .pop (person_id )
148+ start_time = cache_item .get ("start_time" )
149+ end_time = time .time ()
150+ time_elapsed = end_time - start_time
151+
152+ impression_messages = get_raw_msg_by_timestamp_with_chat (self .subheartflow_id , start_time , end_time )
153+ message_count = len (impression_messages )
154+
155+ if message_count > 50 or (time_elapsed > 600 and message_count > 20 ):
156+ logger .info (
157+ f"{ self .log_prefix } 用户 { person_id } 缓存过期,满足立即更新条件 (消息数: { message_count } , 持续时间: { time_elapsed :.0f} s),立即更新。"
158+ )
159+ asyncio .create_task (
160+ self .update_impression_on_cache_expiry (person_id , self .subheartflow_id , start_time , end_time )
161+ )
162+ else :
163+ logger .info (f"{ self .log_prefix } 用户 { person_id } 缓存过期,进入更新等待区。" )
164+ self .pending_updates [person_id ] = {
165+ "start_time" : start_time ,
166+ "end_time" : end_time ,
167+ "grace_period_ttl" : self .grace_period_rounds ,
168+ "chat_id" : self .subheartflow_id ,
169+ }
170+
171+ # 3. 准备LLM输入和直接使用缓存
172+ if not person_list :
173+ return ""
174+
175+ cached_person_info_str = ""
176+ persons_to_process = []
177+ person_name_list_for_llm = []
178+
179+ for person_id in person_list :
180+ if person_id in self .person_cache :
181+ logger .info (f"{ self .log_prefix } 关系识别 (缓存): { person_id } " )
182+ person_name = await person_info_manager .get_value (person_id , "person_name" )
183+ info = self .person_cache [person_id ]["info" ]
184+ cached_person_info_str += f"你对 { person_name } 的了解:{ info } \n "
185+ else :
186+ # 所有不在活动缓存中的用户(包括等待区的)都将由LLM处理
187+ persons_to_process .append (person_id )
188+ person_name_list_for_llm .append (await person_info_manager .get_value (person_id , "person_name" ))
189+
190+ # 4. 如果没有需要LLM处理的人员,直接返回缓存信息
191+ if not persons_to_process :
192+ final_result = cached_person_info_str .strip ()
193+ if final_result :
194+ logger .info (f"{ self .log_prefix } 关系识别 (全部缓存): { final_result } " )
195+ return final_result
196+
197+ # 5. 为需要处理的人员准备LLM prompt
198+ nickname_str = "," .join (global_config .bot .alias_names )
129199 name_block = f"你的名字是{ global_config .bot .nickname } ,你的昵称有{ nickname_str } ,有人也会用这些昵称称呼你。"
130-
131- if is_group_chat :
132- relation_prompt_init = "你对群聊里的人的印象是:\n "
133- else :
134- relation_prompt_init = "你对对方的印象是:\n "
135-
200+ relation_prompt_init = "你对群聊里的人的印象是:\n " if is_group_chat else "你对对方的印象是:\n "
136201 relation_prompt = ""
137- person_name_list = []
138- for person in person_list :
139- relation_prompt += f"{ await relationship_manager .build_relationship_info (person , is_id = True )} \n \n "
140- person_name_list .append (await person_info_manager .get_value (person , "person_name" ))
141-
202+ for person_id in persons_to_process :
203+ relation_prompt += f"{ await relationship_manager .build_relationship_info (person_id , is_id = True )} \n \n "
204+
142205 if relation_prompt :
143206 relation_prompt = relation_prompt_init + relation_prompt
144207 else :
@@ -151,45 +214,76 @@ async def relation_identify(
151214 chat_observe_info = chat_observe_info ,
152215 )
153216
154- # print(prompt)
155-
156- content = ""
217+ # 6. 调用LLM并处理结果
218+ newly_processed_info_str = ""
157219 try :
158220 logger .info (f"{ self .log_prefix } 关系识别prompt: \n { prompt } \n " )
159221 content , _ = await self .llm_model .generate_response_async (prompt = prompt )
160- if not content :
222+ if content :
223+ print (f"content: { content } " )
224+ content_json = json .loads (repair_json (content ))
225+
226+ for person_name , person_info in content_json .items ():
227+ if person_name in person_name_list_for_llm :
228+ try :
229+ idx = person_name_list_for_llm .index (person_name )
230+ person_id = persons_to_process [idx ]
231+
232+ # 关键:检查此人是否在等待区,如果是,则为"唤醒"
233+ start_time = time .time () # 新用户的默认start_time
234+ if person_id in self .pending_updates :
235+ logger .info (f"{ self .log_prefix } 用户 { person_id } 在等待期被LLM重提,重新激活缓存。" )
236+ revived_item = self .pending_updates .pop (person_id )
237+ start_time = revived_item ["start_time" ]
238+
239+ self .person_cache [person_id ] = {
240+ "info" : person_info ,
241+ "ttl" : 5 ,
242+ "start_time" : start_time ,
243+ }
244+ newly_processed_info_str += f"你对 { person_name } 的了解:{ person_info } \n "
245+ except (ValueError , IndexError ):
246+ continue
247+ else :
161248 logger .warning (f"{ self .log_prefix } LLM返回空结果,关系识别失败。" )
162-
163- print (f"content: { content } " )
164-
165- content = repair_json (content )
166- content = json .loads (content )
167-
168- person_info_str = ""
169-
170- for person_name , person_info in content .items ():
171- # print(f"person_name: {person_name}, person_info: {person_info}")
172- # print(f"person_list: {person_name_list}")
173- if person_name not in person_name_list :
174- continue
175- person_str = f"你对 { person_name } 的了解:{ person_info } \n "
176- person_info_str += person_str
177-
178-
249+
179250 except Exception as e :
180- # 处理总体异常
181251 logger .error (f"{ self .log_prefix } 执行LLM请求或处理响应时出错: { e } " )
182252 logger .error (traceback .format_exc ())
183- person_info_str = "关系识别过程中出现错误"
253+ newly_processed_info_str = "关系识别过程中出现错误"
254+
255+ # 7. 合并缓存和新处理的信息
256+ person_info_str = (cached_person_info_str + newly_processed_info_str ).strip ()
184257
185258 if person_info_str == "None" :
186259 person_info_str = ""
187-
188- # 记录初步思考结果
189260
190261 logger .info (f"{ self .log_prefix } 关系识别: { person_info_str } " )
191262
192263 return person_info_str
193264
265+ async def update_impression_on_cache_expiry (
266+ self , person_id : str , chat_id : str , start_time : float , end_time : float
267+ ):
268+ """
269+ 在缓存过期时,获取聊天记录并更新用户印象
270+ """
271+ logger .info (f"缓存过期,开始为 { person_id } 更新印象。时间范围:{ start_time } -> { end_time } " )
272+ try :
273+
274+
275+ impression_messages = get_raw_msg_by_timestamp_with_chat (chat_id , start_time , end_time )
276+ if impression_messages :
277+ logger .info (f"为 { person_id } 获取到 { len (impression_messages )} 条消息用于印象更新。" )
278+ await relationship_manager .update_person_impression (
279+ person_id = person_id , timestamp = end_time , bot_engaged_messages = impression_messages
280+ )
281+ else :
282+ logger .info (f"在指定时间范围内没有找到 { person_id } 的消息,不更新印象。" )
283+
284+ except Exception as e :
285+ logger .error (f"为 { person_id } 更新印象时发生错误: { e } " )
286+ logger .error (traceback .format_exc ())
287+
194288
195289init_prompt ()
0 commit comments