From 82fa368a5a002336b1455481e4317bdf38818402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B8=AD=E9=98=B3=E9=98=B3?= Date: Tue, 15 Jul 2025 21:02:28 +0800 Subject: [PATCH 1/8] feat: add multi language --- src/memos/templates/mem_reader_prompts.py | 19 +++++++++++++++++++ .../templates/tree_reorganize_prompts.py | 11 ++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/memos/templates/mem_reader_prompts.py b/src/memos/templates/mem_reader_prompts.py index 8e6bf3c46..bcca4623c 100644 --- a/src/memos/templates/mem_reader_prompts.py +++ b/src/memos/templates/mem_reader_prompts.py @@ -1,4 +1,5 @@ SIMPLE_STRUCT_MEM_READER_PROMPT = """You are a memory extraction expert. +Always respond in the same language as the conversation. If the conversation is in Chinese, respond in Chinese. Your task is to extract memories from the perspective of user, based on a conversation between user and assistant. This means identifying what user would plausibly remember — including their own experiences, thoughts, plans, or relevant statements and actions made by others (such as assistant) that impacted or were acknowledged by user. @@ -66,6 +67,20 @@ "summary": "Tom is currently focused on managing a new project with a tight schedule. After a team meeting on June 25, 2025, he realized the original deadline of December 15 might not be feasible due to backend delays. Concerned about insufficient testing time, he welcomed Jerry’s suggestion of proposing an extension. Tom plans to raise the idea of shifting the deadline to January 5, 2026 in the next morning’s meeting. His actions reflect both stress about timelines and a proactive, team-oriented problem-solving approach." } +Another Example in Chinese (注意: 你的输出必须和输入的user语言一致): +{ + "memory list": [ + { + "key": "项目会议", + "memory_type": "LongTermMemory", + "value": "在2025年6月25日下午3点,Tom与团队开会讨论了新项目,涉及时间表,并提出了对12月15日截止日期可行性的担忧。", + "tags": ["项目", "时间表", "会议", "截止日期"] + }, + ... + ], + "summary": "Tom 目前专注于管理一个进度紧张的新项目..." +} + Conversation: ${conversation} @@ -73,6 +88,10 @@ SIMPLE_STRUCT_DOC_READER_PROMPT = """ You are an expert text analyst for a search and retrieval system. Your task is to process a document chunk and generate a single, structured JSON object. +Written in English if the input conversation is in English, or in Chinese if +the conversation is in Chinese, or any language which align with the +conversation language. 如果输入语言是中文,请务必输出中文。 + The input is a single piece of text: `[DOCUMENT_CHUNK]`. You must generate a single JSON object with two top-level keys: `summary` and `tags`. 1. `summary`: diff --git a/src/memos/templates/tree_reorganize_prompts.py b/src/memos/templates/tree_reorganize_prompts.py index 6d3cc1f5f..5173e0f90 100644 --- a/src/memos/templates/tree_reorganize_prompts.py +++ b/src/memos/templates/tree_reorganize_prompts.py @@ -17,6 +17,9 @@ - Provide a list of 5–10 relevant English `tags`. - Write a short `background` note (50–100 words) covering any extra context, sources, or traceability info. +Language rules: +- The `key`, `value`, `tags`, `background` fields must match the language of the input conversation. + Return valid JSON: {{ "key": "", @@ -41,6 +44,9 @@ Example: If you have items about a project across multiple phases, group them by milestone, team, or event. +Language rules: +- The `theme` fields must match the language of the input conversation. + Return valid JSON: {{ "clusters": [ @@ -84,7 +90,7 @@ - Node 2: "The venue was booked for a wedding in August." Answer: CONFLICT -Always respond with ONE word: [CAUSE | CONDITION | RELATE | CONFLICT | NONE] +Always respond with ONE word, no matter what language is for the input nodes: [CAUSE | CONDITION | RELATE_TO | CONFLICT | NONE] """ INFER_FACT_PROMPT = """ @@ -125,6 +131,9 @@ - "Mary organized the 2023 sustainability summit in Berlin." - "Mary presented a keynote on renewable energy at the same summit." +Language rules: +- The `key`, `value`, `tags`, `background` fields must match the language of the input. + Good Aggregate: {{ "key": "Mary's Sustainability Summit Role", From 3f1bc22d87662d191bfecb6f3e2ea1ac453c8940 Mon Sep 17 00:00:00 2001 From: Hao <120852460@qq.com> Date: Wed, 16 Jul 2025 11:17:16 +0800 Subject: [PATCH 2/8] feat: update template --- src/memos/templates/mem_reader_prompts.py | 16 +++++ .../templates/tree_reorganize_prompts.py | 60 ++++++++++++------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/memos/templates/mem_reader_prompts.py b/src/memos/templates/mem_reader_prompts.py index bcca4623c..89b9973e3 100644 --- a/src/memos/templates/mem_reader_prompts.py +++ b/src/memos/templates/mem_reader_prompts.py @@ -87,6 +87,8 @@ Your Output:""" SIMPLE_STRUCT_DOC_READER_PROMPT = """ +**ABSOLUTE, NON-NEGOTIABLE, CRITICAL RULE: The language of your entire JSON output's string values (specifically `summary` and `tags`) MUST be identical to the language of the input `[DOCUMENT_CHUNK]`. There are absolutely no exceptions. Do not translate. If the input is Chinese, the output must be Chinese. If English, the output must be English. Any deviation from this rule constitutes a failure to follow instructions.** + You are an expert text analyst for a search and retrieval system. Your task is to process a document chunk and generate a single, structured JSON object. Written in English if the input conversation is in English, or in Chinese if the conversation is in Chinese, or any language which align with the @@ -141,4 +143,18 @@ "summary": "Tom is currently focused on managing a new project with a tight schedule. After a team meeting on June 25, 2025, he realized the original deadline of December 15 might not be feasible due to backend delays. Concerned about insufficient testing time, he welcomed Jerry’s suggestion of proposing an extension. Tom plans to raise the idea of shifting the deadline to January 5, 2026 in the next morning’s meeting. His actions reflect both stress about timelines and a proactive, team-oriented problem-solving approach." } +Another Example in Chinese (注意: 你的输出必须和输入的user语言一致): +{ + "memory list": [ + { + "key": "项目会议", + "memory_type": "LongTermMemory", + "value": "在2025年6月25日下午3点,Tom与团队开会讨论了新项目,涉及时间表,并提出了对12月15日截止日期可行性的担忧。", + "tags": ["项目", "时间表", "会议", "截止日期"] + }, + ... + ], + "summary": "Tom 目前专注于管理一个进度紧张的新项目..." +} + """ diff --git a/src/memos/templates/tree_reorganize_prompts.py b/src/memos/templates/tree_reorganize_prompts.py index 5173e0f90..79298f62f 100644 --- a/src/memos/templates/tree_reorganize_prompts.py +++ b/src/memos/templates/tree_reorganize_prompts.py @@ -8,29 +8,42 @@ Values: {joined_values} -Backgrounds: +Summaries: {joined_backgrounds} -Your task: -- Generate a single clear English `key` (5–10 words max). -- Write a detailed `value` that merges the key points into a single, complete, well-structured text. This must stand alone and convey what the user should remember. -- Provide a list of 5–10 relevant English `tags`. -- Write a short `background` note (50–100 words) covering any extra context, sources, or traceability info. +Please perform: +1. Identify information that reflects user's experiences, beliefs, concerns, decisions, plans, or reactions — including meaningful input from assistant that user acknowledged or responded to. +2. Resolve all time, person, and event references clearly: + - Convert relative time expressions (e.g., “yesterday,” “next Friday”) into absolute dates using the message timestamp if possible. + - Clearly distinguish between event time and message time. + - If uncertainty exists, state it explicitly (e.g., “around June 2025,” “exact date unclear”). + - Include specific locations if mentioned. + - Resolve all pronouns, aliases, and ambiguous references into full names or identities. + - Disambiguate people with the same name if applicable. +3. Always write from a third-person perspective, referring to user as +"The user" or by name if name mentioned, rather than using first-person ("I", "me", "my"). +For example, write "The user felt exhausted..." instead of "I felt exhausted...". +4. Do not omit any information that user is likely to remember. + - Include all key experiences, thoughts, emotional responses, and plans — even if they seem minor. + - Prioritize completeness and fidelity over conciseness. + - Do not generalize or skip details that could be personally meaningful to user. +5. Summarize all child memory items into one memory item. Language rules: -- The `key`, `value`, `tags`, `background` fields must match the language of the input conversation. +- The `key`, `value`, `tags`, `summary` fields must match the mostly used language of the input memory items. +- Keep `memory_type` in English. Return valid JSON: -{{ - "key": "", - "value": "", - "tags": ["tag1", "tag2", ...], - "background": "" -}} +{ + "key": , + "memory_type": , + "value": , + "tags": , + "summary": +} """ -LOCAL_SUBCLUSTER_PROMPT = """ -You are a memory organization expert. +LOCAL_SUBCLUSTER_PROMPT = """You are a memory organization expert. You are given a cluster of memory items, each with an ID and content. Your task is to divide these into smaller, semantically meaningful sub-clusters. @@ -39,24 +52,25 @@ - Identify natural topics by analyzing common time, place, people, and event elements. - Each sub-cluster must reflect a coherent theme that helps retrieval. - Each sub-cluster should have 2–10 items. Discard singletons. -- Each item ID must appear in exactly one sub-cluster. +- Each item ID must appear in exactly one sub-cluster or be discarded. No duplicates are allowed. +- All IDs in the output must be from the provided Memory items. - Return strictly valid JSON only. Example: If you have items about a project across multiple phases, group them by milestone, team, or event. Language rules: -- The `theme` fields must match the language of the input conversation. +- The `theme` fields must match the mostly used language of the clustered memories. Return valid JSON: -{{ +{ "clusters": [ - {{ - "ids": ["id1", "id2", ...], - "theme": "" - }}, + { + "ids": ["", "", ...], + "key": "" + }, ... ] -}} +} Memory items: {joined_scene} From bbd24ddeacea8bd89fba3adfb570b5a827d8148f Mon Sep 17 00:00:00 2001 From: Hao <120852460@qq.com> Date: Wed, 16 Jul 2025 11:30:17 +0800 Subject: [PATCH 3/8] feat: update template --- src/memos/templates/tree_reorganize_prompts.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/memos/templates/tree_reorganize_prompts.py b/src/memos/templates/tree_reorganize_prompts.py index 79298f62f..96cd51457 100644 --- a/src/memos/templates/tree_reorganize_prompts.py +++ b/src/memos/templates/tree_reorganize_prompts.py @@ -30,14 +30,14 @@ 5. Summarize all child memory items into one memory item. Language rules: -- The `key`, `value`, `tags`, `summary` fields must match the mostly used language of the input memory items. +- The `key`, `value`, `tags`, `summary` fields must match the mostly used language of the input memory items. **如果输入是中文,请输出中文** - Keep `memory_type` in English. Return valid JSON: { "key": , "memory_type": , - "value": , + "value": , "tags": , "summary": } @@ -59,7 +59,7 @@ Example: If you have items about a project across multiple phases, group them by milestone, team, or event. Language rules: -- The `theme` fields must match the mostly used language of the clustered memories. +- The `theme` fields must match the mostly used language of the clustered memories. **如果输入是中文,请输出中文** Return valid JSON: { From c13358da70683505f5cd0694ff18e65781491a80 Mon Sep 17 00:00:00 2001 From: Hao <120852460@qq.com> Date: Wed, 16 Jul 2025 11:32:50 +0800 Subject: [PATCH 4/8] feat: update template --- src/memos/templates/tree_reorganize_prompts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memos/templates/tree_reorganize_prompts.py b/src/memos/templates/tree_reorganize_prompts.py index 96cd51457..618d457d8 100644 --- a/src/memos/templates/tree_reorganize_prompts.py +++ b/src/memos/templates/tree_reorganize_prompts.py @@ -59,7 +59,7 @@ Example: If you have items about a project across multiple phases, group them by milestone, team, or event. Language rules: -- The `theme` fields must match the mostly used language of the clustered memories. **如果输入是中文,请输出中文** +- The `key` fields must match the mostly used language of the clustered memories. **如果输入是中文,请输出中文** Return valid JSON: { From 34af54db243aa2d97ce8d7282bd1291a61e8d909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B8=AD=E9=98=B3=E9=98=B3?= Date: Wed, 16 Jul 2025 11:37:10 +0800 Subject: [PATCH 5/8] feat: adjust json parser for tree-text-memory reorganizer --- .../memories/textual/tree_text_memory/organize/reorganizer.py | 2 +- src/memos/templates/tree_reorganize_prompts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py b/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py index 3487195dc..15595f01d 100644 --- a/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py +++ b/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py @@ -505,7 +505,7 @@ def _summarize_cluster(self, cluster_nodes: list[GraphDBNode], scope: str) -> Gr parent_key = response_json.get("key", "").strip() parent_value = response_json.get("value", "").strip() parent_tags = response_json.get("tags", []) - parent_background = response_json.get("background", "").strip() + parent_background = response_json.get("summary", "").strip() embedding = self.embedder.embed([parent_value])[0] diff --git a/src/memos/templates/tree_reorganize_prompts.py b/src/memos/templates/tree_reorganize_prompts.py index 79298f62f..68548b424 100644 --- a/src/memos/templates/tree_reorganize_prompts.py +++ b/src/memos/templates/tree_reorganize_prompts.py @@ -104,7 +104,7 @@ - Node 2: "The venue was booked for a wedding in August." Answer: CONFLICT -Always respond with ONE word, no matter what language is for the input nodes: [CAUSE | CONDITION | RELATE_TO | CONFLICT | NONE] +Always respond with ONE word, no matter what language is for the input nodes: [CAUSE | CONDITION | RELATE | CONFLICT | NONE] """ INFER_FACT_PROMPT = """ From 17e555546a54924abfb4199c170f88c335d7b6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B8=AD=E9=98=B3=E9=98=B3?= Date: Wed, 16 Jul 2025 12:05:09 +0800 Subject: [PATCH 6/8] feat: adjust json parser for tree-text-memory reorganizer --- src/memos/templates/tree_reorganize_prompts.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/memos/templates/tree_reorganize_prompts.py b/src/memos/templates/tree_reorganize_prompts.py index 68548b424..dc27fd477 100644 --- a/src/memos/templates/tree_reorganize_prompts.py +++ b/src/memos/templates/tree_reorganize_prompts.py @@ -34,13 +34,13 @@ - Keep `memory_type` in English. Return valid JSON: -{ +{{ "key": , "memory_type": , "value": , "tags": , "summary": -} +}} """ LOCAL_SUBCLUSTER_PROMPT = """You are a memory organization expert. @@ -62,15 +62,15 @@ - The `theme` fields must match the mostly used language of the clustered memories. Return valid JSON: -{ +{{ "clusters": [ - { + {{ "ids": ["", "", ...], "key": "" - }, + }}, ... ] -} +}} Memory items: {joined_scene} From 41c0b91679348b6d2e7402d74502d83f97e10f07 Mon Sep 17 00:00:00 2001 From: Hao <120852460@qq.com> Date: Wed, 16 Jul 2025 14:14:01 +0800 Subject: [PATCH 7/8] fix: template format --- .../organize/relation_reason_detector.py | 2 +- .../tree_text_memory/organize/reorganizer.py | 9 +++------ src/memos/templates/tree_reorganize_prompts.py | 16 ++++++++-------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py b/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py index 434272cb7..bd4b4d327 100644 --- a/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +++ b/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py @@ -174,7 +174,7 @@ def _detect_aggregate_node_for_group( combined_nodes = [node, *nearest_nodes] joined = "\n".join(f"- {n.memory}" for n in combined_nodes) - prompt = AGGREGATE_PROMPT.format(joined=joined) + prompt = AGGREGATE_PROMPT.replace('{joined}', joined) response_text = self._call_llm(prompt) response_json = self._parse_json_result(response_text) if not response_json: diff --git a/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py b/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py index 15595f01d..de6d247d0 100644 --- a/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py +++ b/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py @@ -358,7 +358,7 @@ def _local_subcluster(self, cluster_nodes: list[GraphDBNode]) -> list[list[Graph scene_lines.append(line) joined_scene = "\n".join(scene_lines) - prompt = LOCAL_SUBCLUSTER_PROMPT.format(joined_scene=joined_scene) + prompt = LOCAL_SUBCLUSTER_PROMPT.replace('{joined_scene}', joined_scene) messages = [{"role": "user", "content": prompt}] response_text = self.llm.generate(messages) @@ -491,11 +491,8 @@ def _summarize_cluster(self, cluster_nodes: list[GraphDBNode], scope: str) -> Gr ) # Build prompt - prompt = REORGANIZE_PROMPT.format( - joined_keys=joined_keys, - joined_values=joined_values, - joined_backgrounds=joined_backgrounds, - ) + prompt = REORGANIZE_PROMPT.replace('{joined_keys}', joined_keys).\ + replace('{joined_values}', joined_values).replace('{joined_backgrounds}', joined_backgrounds) messages = [{"role": "user", "content": prompt}] response_text = self.llm.generate(messages) diff --git a/src/memos/templates/tree_reorganize_prompts.py b/src/memos/templates/tree_reorganize_prompts.py index c2398d81e..6f25887b8 100644 --- a/src/memos/templates/tree_reorganize_prompts.py +++ b/src/memos/templates/tree_reorganize_prompts.py @@ -34,13 +34,13 @@ - Keep `memory_type` in English. Return valid JSON: -{{ +{ "key": , "memory_type": , "value": , "tags": , "summary": -}} +} """ LOCAL_SUBCLUSTER_PROMPT = """You are a memory organization expert. @@ -62,15 +62,15 @@ - The `key` fields must match the mostly used language of the clustered memories. **如果输入是中文,请输出中文** Return valid JSON: -{{ +{ "clusters": [ - {{ + { "ids": ["", "", ...], "key": "" - }}, + }, ... ] -}} +} Memory items: {joined_scene} @@ -149,12 +149,12 @@ - The `key`, `value`, `tags`, `background` fields must match the language of the input. Good Aggregate: -{{ +{ "key": "Mary's Sustainability Summit Role", "value": "Mary organized and spoke at the 2023 sustainability summit in Berlin, highlighting renewable energy initiatives.", "tags": ["Mary", "summit", "Berlin", "2023"], "background": "Combined from multiple memories about Mary's activities at the summit." -}} +} If you find NO useful higher-level concept, reply exactly: "None". """ From 3b26e2b01aea95ccf1e2a76ea9a9dae43a8200b1 Mon Sep 17 00:00:00 2001 From: Hao <120852460@qq.com> Date: Wed, 16 Jul 2025 16:08:11 +0800 Subject: [PATCH 8/8] fix: template format --- .../organize/relation_reason_detector.py | 2 +- .../tree_text_memory/organize/reorganizer.py | 14 +++++++------- src/memos/templates/tree_reorganize_prompts.py | 9 +-------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py b/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py index bd4b4d327..cc755d6dd 100644 --- a/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +++ b/src/memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py @@ -174,7 +174,7 @@ def _detect_aggregate_node_for_group( combined_nodes = [node, *nearest_nodes] joined = "\n".join(f"- {n.memory}" for n in combined_nodes) - prompt = AGGREGATE_PROMPT.replace('{joined}', joined) + prompt = AGGREGATE_PROMPT.replace("{joined}", joined) response_text = self._call_llm(prompt) response_json = self._parse_json_result(response_text) if not response_json: diff --git a/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py b/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py index de6d247d0..c73fd1228 100644 --- a/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py +++ b/src/memos/memories/textual/tree_text_memory/organize/reorganizer.py @@ -358,7 +358,7 @@ def _local_subcluster(self, cluster_nodes: list[GraphDBNode]) -> list[list[Graph scene_lines.append(line) joined_scene = "\n".join(scene_lines) - prompt = LOCAL_SUBCLUSTER_PROMPT.replace('{joined_scene}', joined_scene) + prompt = LOCAL_SUBCLUSTER_PROMPT.replace("{joined_scene}", joined_scene) messages = [{"role": "user", "content": prompt}] response_text = self.llm.generate(messages) @@ -484,15 +484,15 @@ def _summarize_cluster(self, cluster_nodes: list[GraphDBNode], scope: str) -> Gr if not cluster_nodes: raise ValueError("Cluster nodes cannot be empty.") - joined_keys = "\n".join(f"- {n.metadata.key}" for n in cluster_nodes if n.metadata.key) - joined_values = "\n".join(f"- {n.memory}" for n in cluster_nodes) - joined_backgrounds = "\n".join( - f"- {n.metadata.background}" for n in cluster_nodes if n.metadata.background + memories_items_text = "\n\n".join( + [ + f"{i}. key: {n.metadata.key}\nvalue: {n.memory}\nsummary:{n.metadata.background}" + for i, n in enumerate(cluster_nodes) + ] ) # Build prompt - prompt = REORGANIZE_PROMPT.replace('{joined_keys}', joined_keys).\ - replace('{joined_values}', joined_values).replace('{joined_backgrounds}', joined_backgrounds) + prompt = REORGANIZE_PROMPT.replace("{memory_items_text}", memories_items_text) messages = [{"role": "user", "content": prompt}] response_text = self.llm.generate(messages) diff --git a/src/memos/templates/tree_reorganize_prompts.py b/src/memos/templates/tree_reorganize_prompts.py index 6f25887b8..2ca7e571e 100644 --- a/src/memos/templates/tree_reorganize_prompts.py +++ b/src/memos/templates/tree_reorganize_prompts.py @@ -2,14 +2,7 @@ Given the following child memory items: -Keys: -{joined_keys} - -Values: -{joined_values} - -Summaries: -{joined_backgrounds} +{memory_items_text} Please perform: 1. Identify information that reflects user's experiences, beliefs, concerns, decisions, plans, or reactions — including meaningful input from assistant that user acknowledged or responded to.