From d0e112fd6ead5a1278d10802d85012a0667ef09c Mon Sep 17 00:00:00 2001 From: "jinli.yl" Date: Wed, 3 Jul 2024 14:50:07 +0800 Subject: [PATCH] [dev] del prompt & modify default human_profile_setting --- .../worker/summary/get_insight_prompt.yaml | 11 - .../worker/summary/get_insight_worker.py | 151 ----------- .../worker/summary/load_memory_worker.py | 2 +- .../worker/summary/update_profile_prompt.yaml | 120 --------- .../worker/summary/update_profile_worker.py | 236 ------------------ 5 files changed, 1 insertion(+), 519 deletions(-) delete mode 100644 memory_scope/memory/worker/summary/get_insight_prompt.yaml delete mode 100644 memory_scope/memory/worker/summary/get_insight_worker.py delete mode 100644 memory_scope/memory/worker/summary/update_profile_prompt.yaml delete mode 100644 memory_scope/memory/worker/summary/update_profile_worker.py diff --git a/memory_scope/memory/worker/summary/get_insight_prompt.yaml b/memory_scope/memory/worker/summary/get_insight_prompt.yaml deleted file mode 100644 index 419057c9..00000000 --- a/memory_scope/memory/worker/summary/get_insight_prompt.yaml +++ /dev/null @@ -1,11 +0,0 @@ -system_prompt: - cn: - -few_shot_prompt: - cn: - -user_query_prompt: - cn: - -content_format: - cn: "用户的{insight_key}:{insight_value}" diff --git a/memory_scope/memory/worker/summary/get_insight_worker.py b/memory_scope/memory/worker/summary/get_insight_worker.py deleted file mode 100644 index 08564c64..00000000 --- a/memory_scope/memory/worker/summary/get_insight_worker.py +++ /dev/null @@ -1,151 +0,0 @@ -from datetime import datetime -from typing import List - -from memory_scope.utils.tool_functions import time_to_formatted_str, get_datetime_info_dict, prompt_to_msg -from memory_scope.constants.common_constants import ( - NEW_INSIGHT_NODES, - DT, - NOT_REFLECTED_MERGE_NODES, - NEW_INSIGHT_KEYS -) -from memory_scope.enumeration.memory_status_enum import MemoryNodeStatus -from memory_scope.enumeration.memory_type_enum import MemoryTypeEnum -from memory_scope.scheme.memory_node import MemoryNode -from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker -from memory_scope.constants.language_constants import NONE_WORD - - -class GetInsightWorker(MemoryBaseWorker): - def new_insight_node(self, insight_key: str, insight_value: str) -> MemoryNode: - created_dt = datetime.now() - obs_dt = time_to_formatted_str(time=created_dt) - meta_data = {k: str(v) for k, v in get_datetime_info_dict(created_dt).items()} - content = self.prompt_handler.content_format.format(insight_key=insight_key, insight_value=insight_value) - return MemoryNode( - content=content, - user_name=self.user_name, - target_name=self.target_name, - memory_type=MemoryTypeEnum.INSIGHT.value, - meta_data=meta_data, - status=MemoryNodeStatus.ACTIVE.value, - obs_dt=obs_dt, - obs_updated=True, - insight_key=insight_key, - insight_value=insight_value, - ) - - def reflect_new_insight_key( - self, insight_key: str, not_reflected_merge_nodes: List[MemoryNode] - ) -> MemoryNode | None: - - # 检索历史memory - related_nodes = self.vector_store.similar_search( - text=insight_key, - size=self.es_insight_similar_top_k, - exact_filters={ - "user_name": self.user_name, - "target_name": self.target_name, - "status": MemoryNodeStatus.ACTIVE.value, - "memory_type": [ - MemoryTypeEnum.OBSERVATION.value, - MemoryTypeEnum.OBS_CUSTOMIZED.value, - ], - }, - ) - - # 合并新增nodes - related_nodes.extend(not_reflected_merge_nodes) - - # content去重 - related_node_dict = {n.memory_node.content: n for n in related_nodes} - related_nodes = sorted( - list(related_node_dict.values()), key=lambda x: x.memory_node.memory_id # memory_id or use_id? - ) - documents = [n.memory_node.content for n in related_nodes] - - # 重排所有记忆 - result = self.rank_model.call(query=insight_key, documents=documents) - if not result: - self.add_run_info( - f"reflect insight_key={insight_key} call rerank client failed!" - ) - return - - # 根据打分过滤 - for index, score in result.rank_scores.items(): - related_nodes[index].score_rank = score - - related_nodes_sorted = sorted( - related_nodes, key=lambda x: x.score_rank, reverse=True - )[: self.insight_obs_max_cnt] - - # prepare prompt - user_query_list = [x.memory_node.content for x in related_nodes_sorted] - get_insight_message = prompt_to_msg( - system_prompt=self.prompt_handler.system_prompt, - few_shot=self.prompt_handler.few_shot_prompt, - user_query=self.prompt_handler.user_query_prompt.format( - insight_key=insight_key, user_query="\n".join(user_query_list) - ) - ) - - self.logger.info(f"get_insight_message={get_insight_message}") - - # call LLM, 提取insight - response = self.generation_model.call( - messages=get_insight_message, - model_name=self.get_insight_model, - max_token=self.get_insight_max_token, - temperature=self.get_insight_temperature, - top_k=self.get_insight_top_k, - ) - # return if empty - if not response: - self.add_run_info("reflect_upon_user_attr call llm failed!") - return - response_text = response.message.content.strip() - if response_text in [self.get_language_value(NONE_WORD)]: - return - return self.new_insight_node( - insight_key=insight_key, insight_value=response_text - ) - - def _run(self): - new_insight_keys: List[MemoryNode] = self.get_context(NEW_INSIGHT_KEYS) - if not new_insight_keys: - self.add_run_info("new_insight_keys is empty! stop insight.") - return - - not_reflected_merge_nodes: List[MemoryNode] = self.get_context( - NOT_REFLECTED_MERGE_NODES - ) - if not not_reflected_merge_nodes: - self.add_run_info("not_reflected_merge_nodes is empty! stop get insight.") - return - - # submit insight task - for insight_key in new_insight_keys: - self.submit_thread( - self.reflect_new_insight_key, - sleep_time=1, - insight_key=insight_key, - not_reflected_merge_nodes=not_reflected_merge_nodes, - ) - - # save output - new_insight_nodes: List[MemoryNode] = [] - for result in self.join_threads(): - if result: - new_insight_nodes.append(result) - assert isinstance(result, MemoryNode) - insight_key = result.insight_key - insight_value = result.insight_value - self.logger.info( - f"after_get_insight insight_key={insight_key} insight_value={insight_value}" - ) - - self.set_context(NEW_INSIGHT_NODES, new_insight_nodes) - - # set REFLECTED - for node in not_reflected_merge_nodes: - node.obs_reflected = True diff --git a/memory_scope/memory/worker/summary/load_memory_worker.py b/memory_scope/memory/worker/summary/load_memory_worker.py index 23248b04..b5a0ab70 100644 --- a/memory_scope/memory/worker/summary/load_memory_worker.py +++ b/memory_scope/memory/worker/summary/load_memory_worker.py @@ -47,7 +47,7 @@ class LoadMemoryWorker(MemoryBaseWorker): top_k=self.retrieve_profiles_top_k, filter_dict=filter_dict) nodes: List[MemoryNode] = [] - human_profile_setting = G_CONTEXT.meta_data.get("human_profile_setting", []) + human_profile_setting = G_CONTEXT.meta_data.get("human_profile_setting", {}) for attr_key in human_profile_setting: diff --git a/memory_scope/memory/worker/summary/update_profile_prompt.yaml b/memory_scope/memory/worker/summary/update_profile_prompt.yaml deleted file mode 100644 index 16f28c2e..00000000 --- a/memory_scope/memory/worker/summary/update_profile_prompt.yaml +++ /dev/null @@ -1,120 +0,0 @@ -update_plural_profile_system_prompt: - cn: " -从下面的句子中提取出给定类别的用户资料信息,并判断和已有信息是否重复。只输出无重复的新信息。若无法提取该类别的用户资料的新信息则回答无。 -请一步步思考,并按如下格式输出: -思考: 思考的依据和过程,150字以内。 -用户资料: <信息>或<无>, 一定加<> -" - en: - -update_plural_profile_few_shot_prompt: - cn: " -示例1: -句子:用户上周去了西溪游泳馆游泳,那个游泳馆人非常多。 -句子:用户计划每周六和朋友张三去朝阳体育馆打羽毛球。 -类别:运动(用户喜欢的运动) -已有信息:运动(用户喜欢的运动):游泳 -思考:从第一句句子可以得出游泳是用户喜欢的运动之一,但与已有信息重复。从第二句句子可以得出羽毛球是用户喜欢的运动之一,是新的信息。 -用户资料: <羽毛球> - -示例2: -句子:用户对咖啡因过敏。 -句子:用户不喜欢吃香菇。 -类别:过敏(用户的已知过敏反应) -已有信息:过敏(用户的已知过敏反应): 咖啡因 -思考:从第一句句子可以得出咖啡因是用户的已知过敏反应之一,但与已有信息重复。从第二句句子只能得出用户不喜欢香菇而非对香菇过敏,无法得出新的用户已知过敏信息。 -用户资料: <无> - -示例3: -句子:用户热衷于动作类类游戏如只狼、艾尔登法环。 -句子:用户在休闲时间经常长时间玩策略类游戏如文明6。 -句子:用户是音乐发烧友,关注各个品牌的耳机的音质和性价比。 -类别:爱好(用户的业余爱好) -已有信息:爱好(用户的业余爱好): -思考:从第一句句子可以得出动作类游戏是用户的爱好之一,是新的信息。从第二句句子可以得出策略类游戏是用户的爱好之一,是新的信息。从第三句句子可以得出音乐是用户的爱好之一,是新的信息。 -用户资料: <动作类游戏, 策略类游戏, 音乐> - -示例4: -句子:关于职场沟通你有什么具体的建议吗?最好结合一个实例。我一直听人说要加强沟通,经常和上司沟通,同步项目的进展,但是我总是感觉还有许多事情要做。 -句子:项目并没有达到一个充分的可以汇报的状态,然后准备汇报材料又很费时间,导致有时候我没有及时和上司同步项目状态。针对这个情况你有什么建议? -类别:职业(用户的职业) -已有信息:职业(用户的职业):工程师 -思考:句子中虽然提及了职场沟通等工作相关内容,但是并不能推断出用户的职位是什么,只能推知与宽泛的项目实施与管理相关。 -用户资料: <无> -" - en: - -update_plural_profile_user_query_prompt: - cn: " -{user_query} -类别:{update_profile} -已有信息:{update_profile_value} -" - en: - -update_unique_profile_system_prompt: - cn: " -从下面的句子中提取出给定类别的用户资料信息,并判断与已有信息是否矛盾。若矛盾则输出更新的信息,若不矛盾则保留已有信息,整合已有信息和新信息并输出。 -请一步步思考,并按如下格式输出: -思考: 思考的依据和过程,150字以内。 -用户资料: <信息>, 一定加<> -" - en: - - -update_unique_profile_few_shot_prompt: - cn: " -示例1: -句子:因为昨天成都下大雨,用户全身都被淋湿了。 -句子:用户关心明天成都的天气预报。 -类别:地区(用户所在地区) -已有信息:地区(用户所在地区): 杭州 -思考:从第一句句子可以得出用户在成都。第二句句子没有直接透露用户所在地信息,但与第一句句子用户在成都的信息吻合。这与已有信息(用户在杭州)矛盾,输出更新的信息。 -用户资料:<成都> - -示例2: -句子:用户女朋友下个月过生日。 -句子:用户生日在7月15日。 -类别:生日(用户的生日)。 -已有信息:生日(用户的生日):1987年7月15日。 -思考:第一句句子中提及生日,但并不是用户的生日,无法得出用户生日信息。从第二句句子可以得出用户生日在7月15日,与已有信息不矛盾,整合可以得出用户生日是1987年7月15日。 -用户资料: <1987年7月15日> - -示例3: -句子:用户在招商银行工作。 -句子:用户刚刚毕业,第一份工作是银行前台。 -句子:用户的理想工作是职业游戏选手。 -类别:职业(用户的职业) -已有信息:职业(用户的职业): -思考:整合第一和第二句句子的信息可以得出用户的现在的职业是招商银行前台。第三句句子说明了用户的理想工作但并不是现在的职业。 -用户资料:<招商银行前台> - -示例4: -句子:用户大学期间接触过优化算法的研究。 -类别:学习专业 (用户大学学习的专业) -已有信息:学习专业 (用户大学学习的专业):与人工智能相关 -思考:从句子可以得出用户大学学习的专业与优化算法相关,这与已有信息(用户大学学习的专业与人工智能相关)不矛盾,整合可以得出用户大学学习的专业与人工智能和优化算法相关。 -用户资料:<与人工智能和优化算法相关> - -示例5: -句子:今天和同学去打球了。 -句子:明天和女朋友一起去杭州旅游。 -类别:学习专业 (用户大学学习的专业) -已有信息:学习专业 (用户大学学习的专业): -思考:两个句子和学习专业都没有关联,没有新提取的信息。 -用户资料:<无> -" - en: - -update_unique_profile_user_query_prompt: - cn: " -{user_query} -类别:{update_profile} -已有信息:{update_profile_value} -" - en: - - -user_query: - cn: "句子:{content}" - en: diff --git a/memory_scope/memory/worker/summary/update_profile_worker.py b/memory_scope/memory/worker/summary/update_profile_worker.py deleted file mode 100644 index 3bf7b3c3..00000000 --- a/memory_scope/memory/worker/summary/update_profile_worker.py +++ /dev/null @@ -1,236 +0,0 @@ -from typing import List - -from memory_scope.utils.response_text_parser import ResponseTextParser -from memory_scope.constants.common_constants import NEW_OBS_NODES, NEW_USER_PROFILE -from memory_scope.enumeration.memory_type_enum import MemoryTypeEnum -from memory_scope.scheme.memory_node import MemoryNode -from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker -from memory_scope.utils.global_context import GlobalContext -from memory_scope.utils.tool_functions import prompt_to_msg -from memory_scope.constants.language_constants import COMMA_WORD, COLON_WORD, NONE_WORD, REPEATED_WORD - - -class UpdateProfileWorker(MemoryBaseWorker): - @property - def extra_user_attrs(self): - return GlobalContext.global_configs.get("extra_user_attrs", []) - - def filter_obs_nodes( - self, user_attr: MemoryNode, new_obs_nodes: List[MemoryNode] - ) -> (MemoryNode, List[MemoryNode], float): - max_score: float = 0 - filtered_nodes: List[MemoryNode] = [] - result = self.rank_model.call( - query=user_attr.meta_data.get("description", ""), - documents=[x.content for x in new_obs_nodes], - ) - - if not result: - self.add_run_info( - f"update_user_attr={user_attr.meta_data.get("memory_key", "")} call rerank failed!" - ) - return user_attr, filtered_nodes, max_score - - # 找到大于阈值的obs node - filtered_nodes: List[MemoryNode] = [] - for index, score in result.rank_scores.items(): - node = new_obs_nodes[index] - keep_flag = "filtered" - if score >= self.update_profile_threshold: - filtered_nodes.append(node) - keep_flag = "keep" - max_score = max(max_score, score) - self.logger.info( - f"key={user_attr.meta_data.get("memory_key", "")} desc={user_attr.meta_data.get("description", "")} " - f"content={node.content} score={score} keep_flag={keep_flag}" - ) - - if not filtered_nodes: - self.logger.info(f"update_user_attr={user_attr} filtered_nodes is empty!") - return user_attr, filtered_nodes, max_score - - def update_user_attr( - self, user_attr: MemoryNode, filtered_nodes: List[MemoryNode] - ) -> MemoryNode: - self.logger.info( - f"update_user_attr memory_key={user_attr.meta_data.get("memory_key", "")} desc={user_attr.meta_data.get("description", "")} " - f"value={user_attr.meta_data.get("value", "")} doc.size={len(filtered_nodes)}" - ) - - # 根据不同的参数类型是否多值,分别给出prompt - user_query_list = [] - for node in filtered_nodes: - user_query_list.append(self.prompt_handler.user_query.format(content=node.content)) - update_profile = f"{user_attr.meta_data.get("memory_key", "")}({user_attr.meta_data.get("description", "")})" - update_profile_value = update_profile + self.get_language_prompt(COLON_WORD) + self.get_language_prompt(COMMA_WORD).join(user_attr.meta_data.get("value", "")) - - if user_attr.meta_data.get("is_unique", 0) == 1: - update_profile_message = prompt_to_msg( - system_prompt=self.prompt_handler.update_unique_profile_system_prompt, - few_shot=self.prompt_handler.update_unique_profile_few_shot_prompt, - user_query=self.prompt_handler.update_unique_profile_user_query_prompt.format( - user_query="\n".join(user_query_list), - update_profile=update_profile, - update_profile_value=update_profile_value, - ), - ) - else: - update_profile_message = prompt_to_msg( - system_prompt=self.prompt_handler.update_plural_profile_system_prompt, - few_shot=self.prompt_handler.update_plural_profile_few_shot_prompt, - user_query=self.prompt_handler.update_plural_profile_user_query_prompt.format( - user_query="\n".join(user_query_list), - update_profile=update_profile, - update_profile_value=update_profile_value, - ), - ) - self.logger.info(f"update_profile_message={update_profile_message}") - - # call LLM - response: str = self.generation_model.call( - messages=update_profile_message, - model_name=self.update_profile_model, - max_token=self.update_profile_max_token, - temperature=self.update_profile_temperature, - top_k=self.update_profile_top_k, - ) - - # return if empty - if not response: - self.add_run_info( - f"update_one_user_attr key={user_attr.meta_data.get("memory_key", "")} call llm failed!" - ) - return user_attr - - profile_list = ResponseTextParser(response.message.content).parse_v1( - f"update_attr {user_attr.meta_data.get("memory_key", "")}" - ) - if not profile_list: - self.add_run_info( - f"update_one_user_attr key={user_attr.meta_data.get("memory_key", "")} profile_list empty 1!" - ) - return user_attr - profile_list = profile_list[0] - if not profile_list: - self.add_run_info( - f"update_one_user_attr key={user_attr.meta_data.get("memory_key", "")} profile_list empty 2" - ) - return user_attr - profile = profile_list[0] - - if not profile or profile in self.get_language_value([NONE_WORD, REPEATED_WORD]): - self.logger.info(f"profile={profile}, skip.") - return user_attr - - # check 英文中午逗号 - if user_attr.meta_data.get("is_unique", 0) == 1: - user_attr.meta_data["value"] = [profile.strip()] - else: - attr_value_list = profile.replace(",", ",").split(",") - user_attr.meta_data["value"] = [ - x.strip() for x in sorted(list(set(user_attr.meta_data.get("value", "") + attr_value_list))) - ] - return user_attr - - def add_extra_user_attrs(self): - # 解析为空返回 - extra_user_attr_list = [x.strip() for x in self.extra_user_attrs if x.strip()] - if not extra_user_attr_list: - return - - for user_attr_info in extra_user_attr_list: - user_attr_split = user_attr_info.split(self.get_language_prompt(COLON_WORD)) - - # 格式不对返回 - if len(user_attr_split) < 1: - continue - user_attr_key = user_attr_split[0] - - user_attr_desc = "" - if len(user_attr_split) >= 2: - user_attr_desc = user_attr_split[1] - - user_attr_unique = 0 - if len(user_attr_split) >= 3: - user_attr_unique = int(user_attr_split[2]) - - # 已经包含返回 - if user_attr_key in self.user_profile_dict: - user_attr = self.user_profile_dict[user_attr_key] - # description为空,补充description - if not user_attr.meta_data.get("description", ""): - user_attr.meta_data["description"] = user_attr_desc - continue - - # 增加新属性 - new_attr = MemoryNode( - memory_id=self.memory_id, - meta_data={ - "memory_key": user_attr_key, - "is_unique": int(user_attr_unique), - "is_mutable": 1, - "description": user_attr_desc - }, - memory_type=MemoryTypeEnum.PROFILE, - status=1, - obs_profile_updated=True, - ) - self.user_profile_dict[user_attr_key] = new_attr - - def _run(self): - new_obs_nodes: List[MemoryNode] = self.get_context(NEW_OBS_NODES) - if not new_obs_nodes: - self.logger.info("new_obs_nodes is empty, stop user profile!") - self.set_context(NEW_USER_PROFILE, list(self.user_profile_dict.values())) - return - - # 增加环境变量配置的属性 - if self.extra_user_attrs: - self.add_extra_user_attrs() - - new_user_profile: List[MemoryNode] = [] - self.set_context(NEW_USER_PROFILE, new_user_profile) - - for user_attr_key, user_attr in self.user_profile_dict.items(): - # 不可修改直接跳过 - if user_attr.meta_data.get("is_mutable", 0) != 1: - new_user_profile.append(user_attr) - self.logger.info(f"{user_attr_key} is not mutable! continue") - continue - - self.submit_thread( - self.filter_obs_nodes, - sleep_time=0.1, - user_attr=user_attr, - new_obs_nodes=new_obs_nodes, - ) - - # 选择topN - result_list = [] - for result in self.join_threads(): - user_attr, filtered_nodes, max_score = result - if not filtered_nodes: - continue - result_list.append(result) - result_sorted = sorted(result_list, key=lambda x: x[2], reverse=True) - if len(result_sorted) > self.update_profile_max_thread: - result_sorted = result_sorted[: self.update_profile_max_thread] - - # 提交LLM update任务 - for user_attr, filtered_nodes, _ in result_sorted: - self.submit_thread( - self.update_user_attr, - sleep_time=1, - user_attr=user_attr, - filtered_nodes=filtered_nodes, - ) - - # collect result & save - for result in self.join_threads(): - if result: - user_attribute: MemoryNode = result - self.logger.info( - f"after_update_profile memory_key={user_attribute.meta_data.get("memory_key", "")} " - f"desc={user_attribute.meta_data.get("description", "")} value={user_attribute.meta_data.get("value", "")}" - ) - new_user_profile.append(user_attribute)