diff --git a/.flake8 b/.flake8 index b44183ed..b55b82c1 100644 --- a/.flake8 +++ b/.flake8 @@ -2,7 +2,7 @@ exclude = scripts/* src/agentscope/rpc/* -max-line-length = 79 +max-line-length = 120 inline-quotes = " avoid-escape = no ignore = diff --git a/config/demo_config.yaml b/config/demo_config.yaml index 3cca4457..38c49782 100644 --- a/config/demo_config.yaml +++ b/config/demo_config.yaml @@ -6,8 +6,6 @@ memory_chat: class: chat.cli_memory_chat memory_service: memory_chat_service generation_model: dashscope_generation - human_name: 用户 - assistant_name: AI memory_service: memory_chat_service: class: memory.service.chat_memory_service @@ -16,25 +14,24 @@ memory_service: read_memory_key: read_memory memory_operations: read_message: - class: memory.operation.read_memory - workflow: dummy_worker + class: memory.operation.read_message description: "read session messages of the user" read_memory: class: memory.operation.read_memory - workflow: set_query_worker,[extract_time_worker|retrieve_store_worker,semantic_rank_worker],fuse_rerank_worker + workflow: set_query_worker,retrieve_store_worker,[extract_time_worker|semantic_rank_worker],fuse_rerank_worker description: "read related memories of the user" list_memory: class: memory.operation.read_memory - workflow: dummy_worker + workflow: set_query_worker,retrieve_store_worker,print_memory_worker description: "read all memories of the user" write_memory: class: memory.operation.write_memory - workflow: dummy_worker + workflow: info_filter_worker,[get_observation_worker|get_observation_with_time_worker],contra_repeat_worker,store_memory_worker description: "write observation memories of the user" interval_time: 60 summary_memory: class: memory.operation.summary_memory - workflow: dummy_worker + workflow: load_memory_worker,get_reflection_subject_worker,update_insight_worker,long_contra_repeat_worker,summary_collect_worker description: "summary observation memories of the user" interval_time: 300 worker: @@ -45,13 +42,13 @@ worker: rank_model: dashscope_rank set_query_worker: class: memory.worker.read.set_query_worker + retrieve_store_worker: + class: memory.worker.read.retrieve_store_worker + retrieve_obs_top_k: 100 + retrieve_ins_pf_top_k: 100 extract_time_worker: class: memory.worker.read.extract_time_worker generation_model_top_k: 1 - retrieve_store_worker: - class: memory.worker.read.retrieve_store_worker - retrieve_obs_top_k: 5 - retrieve_ins_pf_top_k: 5 semantic_rank_worker: class: memory.worker.read.semantic_rank_worker fuse_rerank_worker: @@ -60,12 +57,12 @@ worker: fuse_ratio_dict: conversation: 0.5 observation: 1 - obs_customized: 1 + obs_customized: 1.2 insight: 2.0 - profile: 2.0 - profile_customized: 2.0 fuse_time_ratio: 2.0 fuse_rerank_top_k: 10 + print_memory_worker: + class: memory.worker.read.print_memory_worker info_filter_worker: class: memory.worker.write.info_filter_worker generation_model: dashscope_generation @@ -85,10 +82,20 @@ worker: generation_model_top_k: 1 retrieve_top_k: 30 contra_repeat_max_count: 50 - get_reflection_worker: - class: memory.worker.summary.get_reflection_worker + store_memory_worker: + class: memory.worker.write.store_memory_worker + load_memory_worker: + class: memory.worker.summary.load_memory_worker + get_reflection_subject_worker: + class: memory.worker.summary.get_reflection_subject_worker retrieve_top_k: 100 reflect_obs_cnt_threshold: 32 + update_insight_worker: + class: memory.worker.summary.update_insight_worker + long_contra_repeat_worker: + class: memory.worker.summary.long_contra_repeat_worker + summary_collect_worker: + class: memory.worker.summary.summary_collect_worker models: dashscope_generation: class: models.llama_index_generation_model @@ -102,10 +109,11 @@ models: class: models.llama_index_rank_model module_name: dashscope_rank model_name: gte-rerank -vector_store: - class: storage.llama_index_elastic_search_store +memory_store: + class: storage.llama_index_es_memory_store embedding_model: dashscope_embedding index_name: memory_index es_url: http://localhost:9200 + use_hybrid: false monitor: class: storage.dummy_monitor \ No newline at end of file diff --git a/memory_scope/chat/cli_memory_chat.py b/memory_scope/chat/cli_memory_chat.py index 5550df71..036da933 100644 --- a/memory_scope/chat/cli_memory_chat.py +++ b/memory_scope/chat/cli_memory_chat.py @@ -27,8 +27,8 @@ class CliMemoryChat(BaseMemoryChat): memory_service: str, generation_model: str, stream: bool = True, - human_name: str = "", - assistant_name: str = "", + human_name: str = "用户", + assistant_name: str = "AI", **kwargs): self._memory_service: BaseMemoryService | str = memory_service diff --git a/memory_scope/cli.py b/memory_scope/cli.py index 95e8d372..23519e6f 100644 --- a/memory_scope/cli.py +++ b/memory_scope/cli.py @@ -56,9 +56,9 @@ class CliJob(object): G_CONTEXT.model_dict[name] = init_instance_by_config(conf, name=name) # init vector_store - vector_store_config = self.config["vector_store"] - embedding_model = G_CONTEXT.model_dict[vector_store_config[ModelEnum.EMBEDDING_MODEL.value]] - G_CONTEXT.vector_store = init_instance_by_config(vector_store_config, embedding_model=embedding_model) + memory_store_config = self.config["memory_store"] + embedding_model = G_CONTEXT.model_dict[memory_store_config[ModelEnum.EMBEDDING_MODEL.value]] + G_CONTEXT.memory_store = init_instance_by_config(memory_store_config, embedding_model=embedding_model) # init monitor G_CONTEXT.monitor = init_instance_by_config(self.config["monitor"]) @@ -73,7 +73,7 @@ class CliJob(object): memory_chat = list(G_CONTEXT.memory_chat_dict.values())[0] memory_chat.run() - G_CONTEXT.vector_store.close() + G_CONTEXT.memory_store.close() G_CONTEXT.monitor.close() diff --git a/memory_scope/enumeration/memory_method_enum.py b/memory_scope/enumeration/memory_method_enum.py deleted file mode 100644 index b30adf31..00000000 --- a/memory_scope/enumeration/memory_method_enum.py +++ /dev/null @@ -1,13 +0,0 @@ -from enum import Enum - - -class MemoryMethodEnum(str, Enum): - SUMMARY = "summary" - - RETRIEVE = "retrieve" - - RETRIEVE_ALL = "retrieve_all" - - SUMMARY_SHORT = "summary_short" - - SUMMARY_LONG = "summary_long" diff --git a/memory_scope/enumeration/memory_recall_enum.py b/memory_scope/enumeration/memory_recall_enum.py deleted file mode 100644 index 956ee4f8..00000000 --- a/memory_scope/enumeration/memory_recall_enum.py +++ /dev/null @@ -1,9 +0,0 @@ -from enum import Enum - - -class MemoryRecallType(str, Enum): - SIMILAR = "similar" - - KEYWORD = "keyword" - - PROFILE = "profile" diff --git a/memory_scope/enumeration/memory_status_enum.py b/memory_scope/enumeration/memory_status_enum.py index f8c706b0..60703d95 100644 --- a/memory_scope/enumeration/memory_status_enum.py +++ b/memory_scope/enumeration/memory_status_enum.py @@ -2,6 +2,12 @@ from enum import Enum class MemoryNodeStatus(str, Enum): + NEW = "new" + + MODIFIED = "modified" + + CONTENT_MODIFIED = "content_modified" + ACTIVE = "active" EXPIRED = "expired" diff --git a/memory_scope/enumeration/memory_type_enum.py b/memory_scope/enumeration/memory_type_enum.py index e4e72b7e..42a4e413 100644 --- a/memory_scope/enumeration/memory_type_enum.py +++ b/memory_scope/enumeration/memory_type_enum.py @@ -8,8 +8,4 @@ class MemoryTypeEnum(str, Enum): INSIGHT = "insight" - PROFILE = "profile" - OBS_CUSTOMIZED = "obs_customized" - - PROFILE_CUSTOMIZED = "profile_customized" diff --git a/memory_scope/memory/operation/base_operation.py b/memory_scope/memory/operation/base_operation.py index 72d360ff..a58569f7 100644 --- a/memory_scope/memory/operation/base_operation.py +++ b/memory_scope/memory/operation/base_operation.py @@ -10,6 +10,7 @@ class BaseOperation(metaclass=ABCMeta): def __init__(self, name: str, description: str = "", **kwargs): self.name: str = name self.description: str = description + self.kwargs: dict = kwargs def init_workflow(self): pass diff --git a/memory_scope/memory/operation/read_memory.py b/memory_scope/memory/operation/read_memory.py index 05089392..b51bbd6c 100644 --- a/memory_scope/memory/operation/read_memory.py +++ b/memory_scope/memory/operation/read_memory.py @@ -14,19 +14,17 @@ class ReadMemory(BaseWorkflow, BaseOperation): description: str, chat_messages: List[Message], his_msg_count: int = 0, # supplement to the current query - contextual_msg_count: int = 0, # for the current context dialogue **kwargs): super().__init__(name=name, **kwargs) BaseOperation.__init__(self, name=name, description=description) self.chat_messages: List[Message] = chat_messages self.his_msg_count: int = his_msg_count - self.contextual_msg_count: int = contextual_msg_count def init_workflow(self): self.init_workers() def run_operation(self, **kwargs): - max_count = 1 + max(self.his_msg_count, self.contextual_msg_count) + max_count = 1 + self.his_msg_count self.context[CHAT_MESSAGES] = [x.copy(deep=True) for x in self.chat_messages[-max_count:]] self.context[CHAT_KWARGS] = kwargs self.run_workflow() diff --git a/memory_scope/memory/operation/read_message.py b/memory_scope/memory/operation/read_message.py new file mode 100644 index 00000000..846ab8d9 --- /dev/null +++ b/memory_scope/memory/operation/read_message.py @@ -0,0 +1,21 @@ +from typing import List + +from memory_scope.memory.operation.base_operation import BaseOperation, OPERATION_TYPE +from memory_scope.scheme.message import Message + + +class ReadMessage(BaseOperation): + operation_type: OPERATION_TYPE = "frontend" + + def __init__(self, + name: str, + description: str, + chat_messages: List[Message], + contextual_msg_count: int = 6, # for the current context dialogue + **kwargs): + super().__init__(name=name, description=description, **kwargs) + self.chat_messages: List[Message] = chat_messages + self.contextual_msg_count: int = contextual_msg_count + + def run_operation(self, **kwargs): + return self.chat_messages[-self.contextual_msg_count:] diff --git a/memory_scope/memory/worker/base_worker.py b/memory_scope/memory/worker/base_worker.py index d2da7d26..d4812ada 100644 --- a/memory_scope/memory/worker/base_worker.py +++ b/memory_scope/memory/worker/base_worker.py @@ -28,9 +28,14 @@ class BaseWorker(metaclass=ABCMeta): self.logger: Logger = Logger.get_logger() def submit_async_task(self, fn, *args, **kwargs): + if self.is_multi_thread: + raise RuntimeError(f"async_task is not allowed in multi_thread condition") self.task_list.append((fn, args, kwargs)) def gather_async_result(self): + if self.is_multi_thread: + raise RuntimeError(f"async_task is not allowed in multi_thread condition") + async def async_gather(): return await asyncio.gather(*[fn(*args, **kwargs) for fn, args, kwargs in self.task_list]) @@ -61,12 +66,9 @@ class BaseWorker(metaclass=ABCMeta): def set_context(self, key: str, value: Any): if self.is_multi_thread: with self.context_lock: - self.context_dict[key] = value + self.context[key] = value else: self.context[key] = value def has_content(self, key: str): return key in self.context - - def __getattr__(self, key: str): - return self.kwargs[key] diff --git a/memory_scope/memory/worker/memory_base_worker.py b/memory_scope/memory/worker/memory_base_worker.py index 15656ed8..644e77fa 100644 --- a/memory_scope/memory/worker/memory_base_worker.py +++ b/memory_scope/memory/worker/memory_base_worker.py @@ -4,9 +4,10 @@ from typing import List, Dict from memory_scope.constants.common_constants import CHAT_MESSAGES, CHAT_KWARGS from memory_scope.memory.worker.base_worker import BaseWorker from memory_scope.models.base_model import BaseModel +from memory_scope.scheme.memory_node import MemoryNode from memory_scope.scheme.message import Message +from memory_scope.storage.base_memory_store import BaseMemoryStore from memory_scope.storage.base_monitor import BaseMonitor -from memory_scope.storage.base_vector_store import BaseVectorStore from memory_scope.utils.global_context import G_CONTEXT from memory_scope.utils.prompt_handler import PromptHandler @@ -24,13 +25,15 @@ class MemoryBaseWorker(BaseWorker, metaclass=ABCMeta): self._generation_model: BaseModel | str = generation_model self._rank_model: BaseModel | str = rank_model - self._vector_store: BaseVectorStore | None = None + self._memory_store: BaseMemoryStore | None = None self._monitor: BaseMonitor | None = None self._user_name: str | None = None self._target_name: str | None = None self._prompt_handler: PromptHandler | None = None + self._contex_memory_dict: Dict[str, MemoryNode] = {} + @property def chat_messages(self) -> List[Message]: return self.get_context(CHAT_MESSAGES) @@ -62,10 +65,30 @@ class MemoryBaseWorker(BaseWorker, metaclass=ABCMeta): return self._rank_model @property - def vector_store(self) -> BaseVectorStore: - if self._vector_store is None: - self._vector_store = G_CONTEXT.vector_store - return self._vector_store + def memory_store(self) -> BaseMemoryStore: + if self._memory_store is None: + self._memory_store = G_CONTEXT.memory_store + return self._memory_store + + def get_memories(self, key: str) -> List[MemoryNode]: + memories: List[MemoryNode] = [] + memory_ids: List[str] = self.get_context(key) + if memory_ids: + memories.extend([self._contex_memory_dict[x] for x in memory_ids]) + return memories + + def set_memories(self, key: str, nodes: List[MemoryNode] | MemoryNode): + if not nodes: + return + + if isinstance(nodes, MemoryNode): + nodes = [nodes] + + for node in nodes: + if node.memory_id in self._contex_memory_dict: + continue + self._contex_memory_dict[node.memory_id] = node + self.set_context(key, [n.memory_id for n in nodes]) @property def monitor(self) -> BaseMonitor: diff --git a/memory_scope/memory/worker/read/extract_time_worker.py b/memory_scope/memory/worker/read/extract_time_worker.py index 4416eb93..2026da8b 100644 --- a/memory_scope/memory/worker/read/extract_time_worker.py +++ b/memory_scope/memory/worker/read/extract_time_worker.py @@ -2,9 +2,9 @@ import re from typing import Dict from memory_scope.constants.common_constants import DATATIME_KEY_MAP, QUERY_WITH_TS, EXTRACT_TIME_DICT -from memory_scope.constants.language_constants import DATATIME_WORD_LIST from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker from memory_scope.utils.datetime_handler import DatetimeHandler +from memory_scope.utils.tool_functions import prompt_to_msg class ExtractTimeWorker(MemoryBaseWorker): @@ -14,23 +14,21 @@ class ExtractTimeWorker(MemoryBaseWorker): query, query_timestamp = self.get_context(QUERY_WITH_TS) # find datetime keyword - contain_datetime = False - for datetime_word in self.get_language_value(DATATIME_WORD_LIST): - if datetime_word in query: - contain_datetime = True - break + contain_datetime = DatetimeHandler.has_time_word(query) if not contain_datetime: self.logger.info(f"contain_datetime={contain_datetime}") return # prepare prompt query_time_str = DatetimeHandler(dt=query_timestamp).string_format(self.prompt_handler.time_string_format) - extract_time_prompt: str = self.prompt_handler.extract_time_prompt.format(query=query, - query_time_str=query_time_str) - self.logger.info(f"extract_time_prompt={extract_time_prompt}") + system_prompt = self.prompt_handler.extract_time_system + few_shot = self.prompt_handler.extract_time_few_shot.format(user_name=self.target_name) + user_query = self.prompt_handler.extract_time_user_query.format(query=query, query_time_str=query_time_str) + extract_time_message = prompt_to_msg(system_prompt=system_prompt, few_shot=few_shot, user_query=user_query) + self.logger.info(f"extract_time_message={extract_time_message}") - # call sft model - response = self.generation_model.call(prompt=extract_time_prompt, top_k=self.generation_model_top_k) + # call llm + response = self.generation_model.call(messages=extract_time_message, top_k=self.generation_model_top_k) # if empty, return if not response.status or not response.message.content: diff --git a/memory_scope/memory/worker/read/extract_time_worker.yaml b/memory_scope/memory/worker/read/extract_time_worker.yaml index 017caa8a..b44e8bbf 100644 --- a/memory_scope/memory/worker/read/extract_time_worker.yaml +++ b/memory_scope/memory/worker/read/extract_time_worker.yaml @@ -1,6 +1,9 @@ extract_time_system: cn: | - 任务指令:从语句与语句发生的时间,推断并提取语句内容中指向的时间段。回答尽可能完整的时间段。回答的格式严格遵照示例中的已有格式规范。 + 任务指令:从语句与语句发生的时间,推断并提取语句内容中指向的时间段。回答尽可能完整的时间段。回答的格式严格遵照示例中的已有格式规范。若语句不涉及时间则回答无。 + + en: | + Instructions: From the sentences and the time when they occurred, infer and extract the time periods indicated in the content of the sentences. Answer with the most complete time periods possible. The format of the answers must strictly adhere to the specifications in the examples provided. If the sentence does not involve time, respond with "none." extract_time_few_shot: @@ -47,6 +50,60 @@ extract_time_few_shot: 回答: - 年:1995 - 月:10 - 日:24 + 示例8: + 句子:我的朋友非常喜欢运动,他认为运动有助于增强身体素质。 + 时间:2015年1月23日,2015年第4周,周四,7时38分0秒。 + 回答: + 无 + + en: | + Example 1: + Sentence: I remember you went to the UAE in April the year before last. Which places in the UAE are fun? Which do you prefer, Dubai or Abu Dhabi? Are the desert views spectacular? + Time: August 20, 1992, 34th week of 1992, Monday, 18:46:25. + Answer: + - Year: 1990 - Month: 4 + + Example 2: + Sentence: Remember to attend the meeting at 3 PM the day after tomorrow. I carefully marked this important day on my calendar to remind myself not to miss it. The meeting will be held in the company conference room, and it's an important opportunity to discuss future development directions. + Time: June 19, 2024, 25th week of 2024, Tuesday, 13:30:0. + Answer: + - Year: 2024 - Month: 6 - Day: 21 - Hour: 15 + + Example 3: + Sentence: Next month on the first Saturday, let's go to Hangzhou. + Time: July 15, 2005, 28th week of 2005, Saturday, 0:0:0. + Answer: + - Year: 2005 - Month: 8 - Week: 31 - Day of Week: 6 + + Example 4: + Sentence: The small town we visited last weekend was truly beautiful. + Time: December 2, 1999, 48th week of 1999, Tuesday, 8:40:10. + Answer: + - Year: 1999 - Week: 47 - Day of Week: 6, 7 + + Example 5: + Sentence: The presentation will start in half an hour, remember to prepare the materials. + Time: June 22, 2020, 25th week of 2020, Monday, 9:30:0 + Answer: + - Year: 2020 - Month: 6 - Day: 22 - Hour: 10 - Minute: 0 - Second: 0 + + Example 6: + Sentence: The start time for the 10,000-meter race was 3 minutes and 47 seconds ago. + Time: February 17, 1987, 7th week of 1987, Wednesday, 19:54:43. + Answer: + - Year: 1987 - Month: 2 - Day: 17 - Hour: 19 - Minute: 50 - Second: 56 + + Example 7: + Sentence: At this time last month, we were still preparing for the concert. Every day was busy and fulfilling, and we worked hard for the successful holding of the concert. Rehearsals, set design, and program arrangements - every detail needed careful planning and preparation. + Time: November 24, 1995, 48th week of 1995, Tuesday, 17:45:0. + Answer: + - Year: 1995 - Month: 10 - Day: 24 + + Example 8: + Sentence: My friend loves sports very much and believes that exercise helps improve physical fitness. + Time: January 23, 2015, 4th week of 2015, Thursday, 7:38:0. + Answer: + None extract_time_user_query: @@ -55,6 +112,10 @@ extract_time_user_query: 时间:{query_time_str} 回答: + en: | + Sentence: {query} + Time: {query_time_str} + Answer: time_string_format: cn: | diff --git a/memory_scope/memory/worker/read/print_memory_worker.py b/memory_scope/memory/worker/read/print_memory_worker.py new file mode 100644 index 00000000..3ddce845 --- /dev/null +++ b/memory_scope/memory/worker/read/print_memory_worker.py @@ -0,0 +1,72 @@ +from typing import List + +from memory_scope.constants.common_constants import RETRIEVE_MEMORY_NODES, RESULT +from memory_scope.enumeration.memory_status_enum import MemoryNodeStatus +from memory_scope.enumeration.memory_type_enum import MemoryTypeEnum +from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker +from memory_scope.scheme.memory_node import MemoryNode +from memory_scope.utils.datetime_handler import DatetimeHandler +from memory_scope.utils.timer import timer + + +class PrintMemoryWorker(MemoryBaseWorker): + + @timer + def retrieve_expired_memory(self, query: str): + filter_dict = { + "user_name": self.user_name, + "target_name": self.target_name, + "status": MemoryNodeStatus.EXPIRED.value, + "memory_type": [MemoryTypeEnum.OBSERVATION.value, MemoryTypeEnum.OBS_CUSTOMIZED.value], + } + return self.memory_store.retrieve_memories(query=query, + top_k=self.retrieve_expired_top_k, + filter_dict=filter_dict) + + def _run(self): + expired_memories: List[MemoryNode] = self.retrieve_expired_memory(query="_") + memory_node_list: List[MemoryNode] = self.get_memories(RETRIEVE_MEMORY_NODES) + memory_node_list = sorted(memory_node_list, key=lambda x: x.timestamp, reverse=True) + + obs_content_list: List[str] = [] + insight_content_list: List[str] = [] + expired_content_list: List[str] = [] + i = 0 + j = 0 + for node in memory_node_list: + + if MemoryTypeEnum(node.memory_type) in [MemoryTypeEnum.OBSERVATION, MemoryTypeEnum.OBS_CUSTOMIZED]: + i += 1 + dt_handler = DatetimeHandler(node.timestamp) + dt = dt_handler.datetime_format("%Y%m%d %H:%M:%S") + line = f" {i} {dt} {node.content}" + obs_content_list.append(line) + + elif MemoryTypeEnum(node.memory_type) in [MemoryTypeEnum.INSIGHT, ]: + j += 1 + line = f" {j} {node.content}" + insight_content_list.append(line) + + for i, node in enumerate(expired_memories): + line = f" {j} {node.content}" + expired_content_list.append(line) + + obs_content = "\n".join(obs_content_list) + insight_content = "\n".join(insight_content_list) + expired_content = "\n".join(expired_content_list) + result: str = f""" +The memories of {self.user_name} about {self.target_name}. + +----- observation ----- +{obs_content} +----- observation ----- + +----- insight ----- +{insight_content} +----- insight ----- + +----- expired ----- +{expired_content} +----- expired ----- + """.strip() + self.set_context(RESULT, result) diff --git a/memory_scope/memory/worker/read/retrieve_store_worker.py b/memory_scope/memory/worker/read/retrieve_store_worker.py index d685d826..87b4d50b 100644 --- a/memory_scope/memory/worker/read/retrieve_store_worker.py +++ b/memory_scope/memory/worker/read/retrieve_store_worker.py @@ -16,26 +16,28 @@ class RetrieveStoreWorker(MemoryBaseWorker): "status": MemoryNodeStatus.ACTIVE.value, "memory_type": [MemoryTypeEnum.OBSERVATION.value, MemoryTypeEnum.OBS_CUSTOMIZED.value], } - return await self.vector_store.async_retrieve(query=query, - top_k=self.retrieve_obs_top_k, - filter_dict=filter_dict) + return await self.memory_store.a_retrieve_memories(query=query, + top_k=self.retrieve_obs_top_k, + filter_dict=filter_dict) async def retrieve_from_insight_and_profile(self, query: str) -> List[MemoryNode]: filter_dict = { "user_name": self.user_name, "target_name": self.target_name, "status": MemoryNodeStatus.ACTIVE.value, - "memory_type": [MemoryTypeEnum.INSIGHT.value, MemoryTypeEnum.PROFILE.value], + "memory_type": MemoryTypeEnum.INSIGHT.value, } - return await self.vector_store.async_retrieve(query=query, - top_k=self.retrieve_ins_pf_top_k, - filter_dict=filter_dict) + return await self.memory_store.a_retrieve_memories(query=query, + top_k=self.retrieve_ins_pf_top_k, + filter_dict=filter_dict) def _run(self): query, _ = self.get_context(QUERY_WITH_TS) + self.submit_async_task(self.retrieve_from_observation, query=query) + self.submit_async_task(self.retrieve_from_insight_and_profile, query=query) + memory_node_list: List[MemoryNode] = [] - fn_list = [self.retrieve_from_observation, self.retrieve_from_insight_and_profile] - for result in self.async_run(fn_list=fn_list, query=query): + for result in self.gather_async_result(): if result: memory_node_list.extend(result) self.logger.info(f"memory_node_list.size={len(memory_node_list)}") @@ -43,4 +45,4 @@ class RetrieveStoreWorker(MemoryBaseWorker): memory_node_list = sorted(memory_node_list, key=lambda x: x.score_similar, reverse=True) for node in memory_node_list: self.logger.info(f"recall_stage: content={node.content} score={node.score_similar} type={node.memory_type}") - self.set_context(RETRIEVE_MEMORY_NODES, memory_node_list) + self.set_memories(RETRIEVE_MEMORY_NODES, memory_node_list) diff --git a/memory_scope/memory/worker/read/semantic_rank_worker.py b/memory_scope/memory/worker/read/semantic_rank_worker.py index 161b6e2f..2574b32a 100644 --- a/memory_scope/memory/worker/read/semantic_rank_worker.py +++ b/memory_scope/memory/worker/read/semantic_rank_worker.py @@ -1,7 +1,6 @@ from typing import List, Dict from memory_scope.constants.common_constants import RETRIEVE_MEMORY_NODES, QUERY_WITH_TS, RANKED_MEMORY_NODES -from memory_scope.enumeration.memory_type_enum import MemoryTypeEnum from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker from memory_scope.scheme.memory_node import MemoryNode @@ -11,39 +10,27 @@ class SemanticRankWorker(MemoryBaseWorker): def _run(self): # query query, _ = self.get_context(QUERY_WITH_TS) - memory_node_list: List[MemoryNode] = self.get_context(RETRIEVE_MEMORY_NODES) + memory_node_list: List[MemoryNode] = self.get_memories(RETRIEVE_MEMORY_NODES) if not memory_node_list: self.logger.warning(f"retrieve memory nodes is empty!") return - # solve content repeat, insight & profile has higher priority - memory_node_dict: Dict[str, MemoryNode] = {} - memory_type_selected = [MemoryTypeEnum.INSIGHT.value, - MemoryTypeEnum.PROFILE.value, - MemoryTypeEnum.PROFILE_CUSTOMIZED.value] - for node in memory_node_list: - if node.memory_type in memory_type_selected: - return - memory_node_dict[node.content] = node - for node in memory_node_list: - if node.memory_type not in memory_type_selected: - return - memory_node_dict[node.content] = node + # drop repeated + memory_node_dict: Dict[str, MemoryNode] = {n.content: n for n in memory_node_list} memory_node_list = list(memory_node_dict.values()) response = self.rank_model.call(query=query, documents=[n.content for n in memory_node_list]) - if not response.status or not response.rank_scores: return # set score - rank_memory_nodes: List[MemoryNode] = [] - rank_scores = sorted(response.rank_scores.items(), key=lambda x: x[1], reverse=True) - for idx, score in rank_scores: + for idx, score in response.rank_scores.items(): if idx >= len(memory_node_list): - self.logger.warning(f"idx={idx} exceeds the maximum length of the array") + self.logger.warning(f"idx={idx} exceeds the maximum length of rank_scores!") continue - node = memory_node_list[idx] - node.score_rank = score + memory_node_list[idx].score_rank = score + + memory_node_list = sorted(memory_node_list, key=lambda n: n.score_rank, reverse=True) + for node in memory_node_list: self.logger.info(f"rank_stage: content={node.content} score={node.score_rank}") - self.get_context(RANKED_MEMORY_NODES, rank_memory_nodes) + self.set_memories(RANKED_MEMORY_NODES, memory_node_list) diff --git a/memory_scope/memory/worker/summary/get_reflection_prompt.yaml b/memory_scope/memory/worker/summary/get_reflection_subject_prompt.yaml similarity index 98% rename from memory_scope/memory/worker/summary/get_reflection_prompt.yaml rename to memory_scope/memory/worker/summary/get_reflection_subject_prompt.yaml index 4cde3349..32274f1e 100644 --- a/memory_scope/memory/worker/summary/get_reflection_prompt.yaml +++ b/memory_scope/memory/worker/summary/get_reflection_subject_prompt.yaml @@ -1,4 +1,4 @@ -get_reflect_system: +get_reflection_subject_system: cn: | 任务:从下面的信息中提取出最重要的最多{num_questions}条{user_name}属性,要求不与已有的{user_name}属性语义重复。 要求1:{user_name}属性可以是一般的{user_name}偏好,也可以是运动偏好,旅游偏好,饮食偏好等等,也可以是重要事件性质,比如最近重要的事情,也可以是一些高度概括的人生理想,价值观,人生观,性格, 也可以是和朋友的人际关系等等。 @@ -6,7 +6,7 @@ get_reflect_system: 输出格式:每一行输出一个{user_name}属性,每个{user_name}属性推荐4个字,如果没有信息请回答无,最多输出{num_questions}条。 -get_reflect_few_shot: +get_reflection_subject_few_shot: cn: | 示例1 信息: @@ -85,7 +85,7 @@ get_reflect_few_shot: 新增{user_name}属性: 无 -get_reflect_user_query: +get_reflection_subject_user_query: cn: | 信息: {user_query} diff --git a/memory_scope/memory/worker/summary/get_reflection_worker.py b/memory_scope/memory/worker/summary/get_reflection_subject_worker.py similarity index 83% rename from memory_scope/memory/worker/summary/get_reflection_worker.py rename to memory_scope/memory/worker/summary/get_reflection_subject_worker.py index 62820d27..601fd0da 100644 --- a/memory_scope/memory/worker/summary/get_reflection_worker.py +++ b/memory_scope/memory/worker/summary/get_reflection_subject_worker.py @@ -11,7 +11,7 @@ from memory_scope.utils.response_text_parser import ResponseTextParser from memory_scope.utils.tool_functions import prompt_to_msg -class GetReflectionWorker(MemoryBaseWorker): +class GetReflectionSubjectWorker(MemoryBaseWorker): def new_insight_node(self, insight_key: str) -> MemoryNode: dt_handler = DatetimeHandler() @@ -22,7 +22,7 @@ class GetReflectionWorker(MemoryBaseWorker): meta_data=meta_data, key=insight_key, memory_type=MemoryTypeEnum.INSIGHT.value, - status=MemoryNodeStatus.ACTIVE.value) + status=MemoryNodeStatus.NEW.value) def _run(self): not_reflected_nodes: List[MemoryNode] = self.get_context(NOT_REFLECTED_NODES) @@ -40,10 +40,11 @@ class GetReflectionWorker(MemoryBaseWorker): # gen reflect prompt user_query_list = [n.content for n in not_reflected_nodes] - system_prompt = self.prompt_handler.get_reflect_system.format(user_name=self.target_name, - num_questions=self.reflect_num_questions) - few_shot = self.prompt_handler.get_reflect_few_shot.format(user_name=self.target_name) - user_query = self.prompt_handler.get_reflect_user_query.format( + system_prompt = self.prompt_handler.get_reflection_subject_system.format( + user_name=self.target_name, + num_questions=self.reflect_num_questions) + few_shot = self.prompt_handler.get_reflection_subject_few_shot.format(user_name=self.target_name) + user_query = self.prompt_handler.get_reflection_subject_user_query.format( user_name=self.target_name, exist_keys=self.get_language_value(COMMA_WORD).join(exist_keys), user_query="\n".join(user_query_list)) @@ -59,7 +60,7 @@ class GetReflectionWorker(MemoryBaseWorker): return # parse text & save - new_insight_keys = ResponseTextParser(response.message.content).parse_v2("get_reflection") + new_insight_keys = ResponseTextParser(response.message.content).parse_v2(self.__class__.__name__) if new_insight_keys: for insight_key in new_insight_keys: insight_nodes.append(self.new_insight_node(insight_key)) diff --git a/memory_scope/memory/worker/summary/load_memory_worker.py b/memory_scope/memory/worker/summary/load_memory_worker.py index 00258f15..e99224aa 100644 --- a/memory_scope/memory/worker/summary/load_memory_worker.py +++ b/memory_scope/memory/worker/summary/load_memory_worker.py @@ -19,9 +19,9 @@ class LoadMemoryWorker(MemoryBaseWorker): "memory_type": [MemoryTypeEnum.OBSERVATION.value, MemoryTypeEnum.OBS_CUSTOMIZED.value], "obs_reflected": False, } - nodes: List[MemoryNode] = await self.vector_store.async_retrieve(query=query, - top_k=self.retrieve_not_reflected_top_k, - filter_dict=filter_dict) + nodes: List[MemoryNode] = await self.memory_store.a_retrieve_memories(query=query, + top_k=self.retrieve_not_reflected_top_k, + filter_dict=filter_dict) self.set_context(NOT_REFLECTED_NODES, nodes) @timer @@ -33,9 +33,9 @@ class LoadMemoryWorker(MemoryBaseWorker): "memory_type": [MemoryTypeEnum.OBSERVATION.value, MemoryTypeEnum.OBS_CUSTOMIZED.value], "obs_updated": False, } - nodes: List[MemoryNode] = await self.vector_store.async_retrieve(query=query, - top_k=self.retrieve_not_updated_top_k, - filter_dict=filter_dict) + nodes: List[MemoryNode] = await self.memory_store.a_retrieve_memories(query=query, + top_k=self.retrieve_not_updated_top_k, + filter_dict=filter_dict) self.set_context(NOT_UPDATED_NODES, nodes) @timer @@ -46,9 +46,9 @@ class LoadMemoryWorker(MemoryBaseWorker): "status": MemoryNodeStatus.ACTIVE.value, "memory_type": MemoryTypeEnum.INSIGHT.value, } - nodes: List[MemoryNode] = await self.vector_store.async_retrieve(query=query, - top_k=self.retrieve_insight_top_k, - filter_dict=filter_dict) + nodes: List[MemoryNode] = await self.memory_store.a_retrieve_memories(query=query, + top_k=self.retrieve_insight_top_k, + filter_dict=filter_dict) self.set_context(INSIGHT_NODES, nodes) async def _run(self): diff --git a/memory_scope/memory/worker/summary/long_contra_repeat_prompt.yaml b/memory_scope/memory/worker/summary/long_contra_repeat_prompt.yaml index d344eb6f..0fc7e640 100644 --- a/memory_scope/memory/worker/summary/long_contra_repeat_prompt.yaml +++ b/memory_scope/memory/worker/summary/long_contra_repeat_prompt.yaml @@ -1,11 +1,18 @@ long_contra_repeat_system: cn: | 对下面的{num_obs}句句子,逐一判断是否与“前面序号”的任意句子存在信息的矛盾,或者句子的主要信息被“前面序号”的任意句子中的信息包含。只判断与“前面序号”的句子的关系。 - 如果句子与前面序号的句子存在矛盾,则以前面序号的句子中的信息为准,修改句子中矛盾的部分。 + 对每个句子都做一个判断,最后一共输出{num_obs}条判断。如果句子与前面序号的句子存在矛盾,则以前面序号的句子中的信息为准,修改句子中矛盾的部分。 请一步步思考,并按如下格式输出: 思考:思考的依据和过程,30字以内。 判断:<句子序号> <矛盾,被包含,无> <修改后的内容>,一定加<> + en: | + For the following {num_obs} sentences, determine one by one if there is any contradiction with the information in any "previously numbered" sentences, or if the main information of the sentence is contained within the information of any "previously numbered" sentences. Only evaluate the relation to "previously numbered" sentences. + Make an evaluation for each sentence, resulting in a total of {num_obs} evaluations. If the sentence conflicts with a "previously numbered" sentence, the information in the earlier sentence takes precedence, and the conflicting part of the current sentence should be modified accordingly. + Please think step by step and output in the following format: + Thought: The basis and process of the thought, within 30 words. + Evaluation: , , enclosed in <>. + long_contra_repeat_few_shot: cn: | @@ -50,6 +57,47 @@ long_contra_repeat_few_shot: 思考:第6句中所有信息都被前面序号中第5句的信息完全包含。 判断:<2> <被包含> <> + en: | + Example 1 + Sentences: + 1 {user_name} suffers from insomnia frequently and is interested in the effects of sleeping pills, suggesting a possible consideration of their use. + 2 {user_name} suffers from insomnia frequently and seeks remedies. + 3 Charles is {user_name}'s supervisor. + 4 Charles is {user_name}'s supervisor. + 5 Charles is {user_name}'s supervisor and the branch manager of a bank. + Thought: The first sentence does not have any contradictions or complete repetitions with the previously numbered sentences. + Evaluation: <1> <> + Thought: All information in the second sentence is completely contained within the information of the first sentence. + Evaluation: <2> <> + Thought: The information in the third sentence does not appear in the previously numbered sentences. + Evaluation: <3> <> + Thought: The fourth sentence is completely repetitive of the information in the third sentence, i.e., it is completely contained. + Evaluation: <4> <> + Thought: The information that Charles is {user_name}'s supervisor in the fifth sentence is contained within the information of the third sentence, but the new information that Charles is the branch manager of a bank is not, so it is not contained. + Evaluation: <5> <> + + Example 2 + Sentences: + 1 {user_name}'s child does not perform well academically. + 2 {user_name}'s child often skips school. + 3 {user_name}'s father's birthday is on June 2, 2024, and {user_name} plans to prepare a gift. + 4 {user_name}'s father's birthday is on May 1, 2024. + 5 {user_name} loves playing basketball with classmates. + 6 {user_name} likes playing basketball. + + Thought: The first sentence does not have any contradictions or complete repetitions with the previously numbered sentences. + Evaluation: <1> <> + Thought: The second sentence neither contradicts nor repeats any of the previously numbered sentences. + Evaluation: <2> <> + Thought: The third sentence neither contradicts nor repeats any of the previously numbered sentences. + Evaluation: <3> <> + Thought: The date of {user_name}'s father's birthday in the fourth sentence contradicts the information in the third sentence. + Evaluation: <4> <{user_name}'s father's birthday is on June 2, 2024.> + Thought: The fifth sentence neither contradicts nor repeats any of the previously numbered sentences. + Evaluation: <5> <> + Thought: All information in the sixth sentence is completely contained within the information of the fifth sentence. + Evaluation: <6> <> + long_contra_repeat_user_query: cn: | 句子: diff --git a/memory_scope/memory/worker/summary/long_contra_repeat_worker.py b/memory_scope/memory/worker/summary/long_contra_repeat_worker.py index a11d700d..1193646c 100644 --- a/memory_scope/memory/worker/summary/long_contra_repeat_worker.py +++ b/memory_scope/memory/worker/summary/long_contra_repeat_worker.py @@ -21,9 +21,9 @@ class LongContraRepeatWorker(MemoryBaseWorker): "status": MemoryNodeStatus.ACTIVE.value, "memory_type": [MemoryTypeEnum.OBSERVATION.value, MemoryTypeEnum.OBS_CUSTOMIZED.value] } - retrieve_nodes = await self.vector_store.async_retrieve(query=node.content, - top_k=self.long_contra_repeat_top_k, - filter_dict=filter_dict) + retrieve_nodes = await self.memory_store.a_retrieve_memories(query=node.content, + top_k=self.long_contra_repeat_top_k, + filter_dict=filter_dict) return node, [n for n in retrieve_nodes if n.score_similar >= self.long_contra_repeat_threshold] def _run(self): diff --git a/memory_scope/memory/worker/summary/summary_collect_worker.py b/memory_scope/memory/worker/summary/summary_collect_worker.py index a77644ea..2b757d59 100644 --- a/memory_scope/memory/worker/summary/summary_collect_worker.py +++ b/memory_scope/memory/worker/summary/summary_collect_worker.py @@ -1,46 +1,40 @@ from typing import List, Dict -from memory_scope.constants.common_constants import ( - NEW_INSIGHT_NODES, - MODIFIED_MEMORIES, - INSIGHT_NODES, - NEW_OBS_NODES, - NOT_REFLECTED_OBS_NODES, - NEW, - NOT_REFLECTED_MERGE_NODES, -) -from memory_scope.scheme.memory_node import MemoryNode +from memory_scope.constants.common_constants import NOT_REFLECTED_NODES, INSIGHT_NODES, MERGE_OBS_NODES, \ + NOT_UPDATED_NODES from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker +from memory_scope.scheme.memory_node import MemoryNode class SummaryCollectWorker(MemoryBaseWorker): def _run(self): + update_memories: Dict[str, MemoryNode] = {} insight_nodes: List[MemoryNode] = self.get_context(INSIGHT_NODES) - new_insight_nodes: List[MemoryNode] = self.get_context(NEW_INSIGHT_NODES) - new_obs_nodes: List[MemoryNode] = self.get_context(NEW_OBS_NODES) - not_reflected_nodes: List[MemoryNode] = self.get_context( - NOT_REFLECTED_OBS_NODES - ) - not_reflected_merge_nodes: List[MemoryNode] = self.get_context( - NOT_REFLECTED_MERGE_NODES - ) - - # 合并逻辑,复杂,务必check - all_node_dict: Dict[str, MemoryNode] = {} if insight_nodes: - all_node_dict.update( - {n.id: n for n in insight_nodes if n.obs_updated} - ) - if new_insight_nodes: - all_node_dict.update({n.content: n for n in new_insight_nodes}) - if new_obs_nodes: - # 设置为非新 - for n in new_obs_nodes: - n.obs_updated = "0" - all_node_dict.update({n.content: n for n in new_obs_nodes}) - if not_reflected_merge_nodes and not_reflected_nodes: - # 进入reflect阶段 - all_node_dict.update({n.id: n for n in not_reflected_nodes}) + update_memories.update({n.memory_id: n for n in insight_nodes}) - self.set_context(MODIFIED_MEMORIES, list(all_node_dict.values())) + not_reflected_nodes: List[MemoryNode] = self.get_context(NOT_REFLECTED_NODES) + if not_reflected_nodes: + update_memories.update({n.memory_id: n for n in not_reflected_nodes}) + + not_updated_nodes: List[MemoryNode] = self.get_context(NOT_UPDATED_NODES) + if not_updated_nodes: + for node in not_updated_nodes: + if node.memory_id in update_memories: + + + + + keys = [ + INSIGHT_NODES, + MERGE_OBS_NODES, + NOT_UPDATED_NODES, + NOT_REFLECTED_NODES, + ] + + memory_nodes: List[MemoryNode] = [] + for key in keys: + memory_nodes.extend(self.get_context(key)) + + self.memory_store.update_memories(update_memories) diff --git a/memory_scope/memory/worker/summary/update_insight_worker.py b/memory_scope/memory/worker/summary/update_insight_worker.py index 75348207..b235cd25 100644 --- a/memory_scope/memory/worker/summary/update_insight_worker.py +++ b/memory_scope/memory/worker/summary/update_insight_worker.py @@ -2,6 +2,7 @@ from typing import List from memory_scope.constants.common_constants import INSIGHT_NODES, NOT_UPDATED_NODES, NOT_REFLECTED_NODES from memory_scope.constants.language_constants import COLON_WORD, NONE_WORD, REPEATED_WORD +from memory_scope.enumeration.memory_status_enum import MemoryNodeStatus from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker from memory_scope.scheme.memory_node import MemoryNode from memory_scope.utils.datetime_handler import DatetimeHandler @@ -48,6 +49,8 @@ class UpdateInsightWorker(MemoryBaseWorker): insight_node.meta_data.update({k: str(v) for k, v in dt_handler.dt_info_dict.items()}) insight_node.timestamp = dt_handler.timestamp insight_node.dt = dt_handler.datetime_format() + if insight_node.status == MemoryNodeStatus.ACTIVE.value: + insight_node.status = MemoryNodeStatus.CONTENT_MODIFIED.value self.logger.info(f"after_update_{insight_node.key} value={insight_value}") return insight_node @@ -89,6 +92,10 @@ class UpdateInsightWorker(MemoryBaseWorker): self.logger.info(f"update_{insight_node.key} insight_value={insight_value} is invalid.") return insight_node + if insight_node.value == insight_value: + self.logger.info(f"value={insight_value} is same!") + return insight_node + self.update_insight_node(insight_node=insight_node, insight_value=insight_value) return insight_node @@ -102,7 +109,7 @@ class UpdateInsightWorker(MemoryBaseWorker): return for node in insight_nodes: - if node.content: + if node.status == MemoryNodeStatus.ACTIVE.value: self.submit_async_task(fn=self.filter_obs_nodes, insight_node=node, not_updated_nodes=not_updated_nodes) @@ -126,3 +133,6 @@ class UpdateInsightWorker(MemoryBaseWorker): # get result self.gather_async_result() + + for node in not_updated_nodes: + node.obs_updated = True diff --git a/memory_scope/memory/worker/write/contra_repeat_worker.py b/memory_scope/memory/worker/write/contra_repeat_worker.py index c3c9f21d..b436d3da 100644 --- a/memory_scope/memory/worker/write/contra_repeat_worker.py +++ b/memory_scope/memory/worker/write/contra_repeat_worker.py @@ -29,7 +29,7 @@ class ContraRepeatWorker(MemoryBaseWorker): "memory_type": [MemoryTypeEnum.OBSERVATION.value, MemoryTypeEnum.OBS_CUSTOMIZED.value], "dt": dt_handler.datetime_format(), } - return self.vector_store.retrieve(query=message.content, top_k=self.today_obs_top_k, filter_dict=filter_dict) + return self.memory_store.retrieve_memories(query=message.content, top_k=self.today_obs_top_k, filter_dict=filter_dict) def _run(self): all_obs_nodes: List[MemoryNode] = [] diff --git a/memory_scope/memory/worker/write/contra_repeat_worker.yaml b/memory_scope/memory/worker/write/contra_repeat_worker.yaml index cb8422e9..975e03f5 100644 --- a/memory_scope/memory/worker/write/contra_repeat_worker.yaml +++ b/memory_scope/memory/worker/write/contra_repeat_worker.yaml @@ -11,7 +11,7 @@ contra_repeat_system: Make an evaluation for each sentence, resulting in a total of {num_obs} evaluations. Please think step by step and output in the following format: Thought: The basis and process of the thought, within 30 words. - Evaluation: , enclosed in <>. + Evaluation: , enclosed in <>. contra_repeat_few_shot: diff --git a/memory_scope/memory/worker/write/get_observation_with_time_worker.py b/memory_scope/memory/worker/write/get_observation_with_time_worker.py index eb9defea..6afc3eeb 100644 --- a/memory_scope/memory/worker/write/get_observation_with_time_worker.py +++ b/memory_scope/memory/worker/write/get_observation_with_time_worker.py @@ -1,7 +1,7 @@ from typing import List from memory_scope.constants.common_constants import NEW_OBS_WITH_TIME_NODES -from memory_scope.constants.language_constants import DATATIME_WORD_LIST, COLON_WORD +from memory_scope.constants.language_constants import COLON_WORD from memory_scope.memory.worker.write.get_observation_worker import GetObservationWorker from memory_scope.scheme.memory_node import MemoryNode from memory_scope.scheme.message import Message @@ -16,12 +16,7 @@ class GetObservationWithTimeWorker(GetObservationWorker): user_query_list = [] i = 1 for msg in self.chat_messages: - match = False - for time_keyword in self.get_language_value(DATATIME_WORD_LIST): - if time_keyword in msg.content: - match = True - break - if match: + if DatetimeHandler.has_time_word(query=msg.content): dt_handler = DatetimeHandler(dt=msg.time_created) dt = dt_handler.string_format(self.prompt_handler.time_string_format) user_query_list.append(f"{i} {dt} {self.target_name}{self.get_language_value(COLON_WORD)}{msg.content}") diff --git a/memory_scope/memory/worker/write/get_observation_with_time_worker.yaml b/memory_scope/memory/worker/write/get_observation_with_time_worker.yaml index 4f7665d1..be26e305 100644 --- a/memory_scope/memory/worker/write/get_observation_with_time_worker.yaml +++ b/memory_scope/memory/worker/write/get_observation_with_time_worker.yaml @@ -4,7 +4,7 @@ time_string_format: get_observation_with_time_system: cn: | - 任务:从下面的{num_obs}句{user_name}句子中依次提取出关于{user_name}的重要信息,相应的关键词与时间信息。 + 任务指令:从下面的{num_obs}句{user_name}句子中依次提取出关于{user_name}的重要信息,相应的关键词与时间信息。 每一句{user_name}句子的格式是:<序号> <对话时间> {user_name}:<句子> 对每一句句子,只提取非常明确的信息和进行非常确定的推断,不要进行任何猜测。不要提取重复的信息,如果句子中的所有信息与已经提取出的信息重复了则回答“重复“,如果没有重要信息则回答“无”。 如果{user_name}信息涉及时间,则结合对话时间推断{user_name}信息的时间信息,没有则不输出。注意区分,对于句子中包含{user_name}假设的信息或者{user_name}虚构的内容比如{user_name}创作的小说或剧本,不要提取信息。 @@ -13,6 +13,16 @@ get_observation_with_time_system: 思考:思考的依据和过程,50字以内。 信息:<句子序号> <时间信息或“无”> <明确的重要信息或“重复”或”无“> <关键词> + en: | + Instruction: Extract important information about {user_name}, corresponding keywords, and time information from the following {num_obs} sentences by {user_name}, one by one. + Each sentence from {user_name} is formatted as: {user_name}: + For each sentence, only extract very clear information and make very certain inferences without any speculation. Do not extract repeated information. If all information in the sentence repeats what has already been extracted, respond with "repeat." If there is no important information, respond with "none." + If the information about {user_name} involves time, combine it with the conversation time to infer the time information of {user_name}'s information; if not, do not output time information. Be sure to distinguish information; for example, do not extract hypothetical or fictional content from {user_name} such as {user_name}'s novels or scripts. + Perform information extraction for each sentence, resulting in a total of {num_obs} pieces of information. + Please think step by step, and be sure to output in the following format, with the final output enclosed in <>: + Thought: The basis and process of the thought, within 50 words. + Information: