feat(memory): enhance memory management with improved tool parameters and agent coordination

This commit is contained in:
jinli.yl 2026-01-29 00:12:03 +08:00
parent 3684eb25eb
commit f841ccc4b9
10 changed files with 147 additions and 125 deletions

View file

@ -1,5 +1,6 @@
"""Base memory agent for handling memory operations with tool-based reasoning."""
import json
from abc import ABCMeta
from ...core.enumeration import MemoryType
@ -57,7 +58,11 @@ class BaseMemoryAgent(BaseReact, metaclass=ABCMeta):
@property
def meta_memory_info(self) -> str:
"""Get the meta memory info from context."""
lines = ["Format: - memory_target: memory_type memories about memory_target"]
lines = []
for memory_target, memory_type in self.memory_target_type_mapping.items():
lines.append(f"- {memory_target}: {memory_type} memories about {memory_target}")
line = {
"agent": f"Agent managing {memory_type} memories for {memory_target}",
"memory_target": memory_target,
}
lines.append(json.dumps(line, ensure_ascii=False))
return "\n".join(lines)

View file

@ -1,5 +1,5 @@
system_prompt: |
You are a memory retrieval Agent responsible for retrieving {memory_type} memories about {memory_target}.
You are a Memory Retrieval Agent specialized in retrieving {memory_type} memories about {memory_target}.
## User Profile
{user_profile}
@ -7,39 +7,62 @@ system_prompt: |
## User Question
{context}
## Retrieval Strategy
### Phase 1 `retrieve_memory`
- Purpose: Search for relevant memories using semantic similarity
- Try at least 3-5 different queries before moving to next phase:
* Direct question
* Direct question reformulation
* Different phrasings and perspectives
* Entity-focused queries (names, places, events)
* Various keyword combinations
- Time filter (optional):
* Format: single date '20200101' or range '20200101,20200102'
* Example: '20200101,20200102' for 20200101 <= time <= 20200102
* Single-sided: '0,20200102' (before date) or '20200101,99999999' (after date)
- If no results: retry with different time ranges or remove time constraints
## Multi-Phase Retrieval Strategy
Follow these phases sequentially to gather comprehensive information:
### Phase 2 `read_history`
- Purpose: Read full original conversation context
- Use this ONLY after completing multiple retrieve_memory attempts
- Extract history_id from context
- Prioritize most relevant or recent history entries
- Read multiple histories if needed for complete understanding
### Phase 1: Semantic Search (No Time Filter)
**Tool**: `retrieve_memory` (without time constraints)
**Objective**: Cast a wide net to find potentially relevant memories
**Approach**:
- Execute 3-5 diverse search queries using different formulations:
* Original question verbatim
* Rephrased variations (different wording, synonyms)
* Entity-focused queries (extract and search specific names, places, events)
* Keyword-based searches (core concepts, topics)
* Related context queries (broader themes)
- Review all results before proceeding to next phase
## Response Requirements
- Answer ONLY based on retrieved memories / user profile / history - NO hallucination or inference
- Always cite the source: reference specific memories with their timestamps
- If information conflicts, present all versions with their respective times
- Try multiple search angles before concluding no information exists
### Phase 2: Temporal Search (Optional)
**Tool**: `retrieve_memory` (with time filter)
**When to use**: Only if the user question contains temporal references (dates, time periods, "when", "recent", "last year", etc.)
**Time Filter Format**:
- Single date: `20200101`
- Date range: `20200101,20200102` (inclusive: 20200101 ≤ time ≤ 20200102)
- Before date: `0,20200102` (up to and including 20200102)
- After date: `20200101,99999999` (from 20200101 onwards)
**Approach**:
- Identify temporal constraints from the user question
- Refine Phase 1 queries with appropriate time filters
- Try multiple time ranges if initial searches yield no results
### Output Format
1. When answering, structure your response as follows:
- [timestamp][Relevant retrieved memories / user profile / history from context]
2. If no relevant information found after thorough search (5+ queries), state:
"No relevant information found after thorough search using multiple query strategies."
### Phase 3: Deep Dive into History
**Tool**: `read_history`
**When to use**: After exhausting retrieval attempts OR when specific conversation context is needed
**Approach**:
- Extract `history_id` from retrieved memory references
- Prioritize histories that are most relevant or recent
- Read multiple histories if necessary for complete context
- Use this to understand the full conversation surrounding a memory
## Response Guidelines
**Critical Rules**:
- Base your answer EXCLUSIVELY on retrieved memories, user profile, and history data
- Never infer, assume, or hallucinate information
- Always cite sources with timestamps: `[timestamp] Memory content`
- Present conflicting information transparently with respective timestamps
- Exhaust all search strategies before concluding information doesn't exist
**Output Format**:
When information is found:
```
[timestamp] Relevant memory/profile/history content
[timestamp] Additional relevant content
```
When no information is found after thorough search (5+ queries across phases):
```
No relevant information found after exhaustive search using multiple query strategies and retrieval phases.
```
user_message: |
Answer the question following the retrieval strategy and response requirements above.
Retrieve relevant memories following the multi-phase strategy outlined above.

View file

@ -99,6 +99,7 @@ class PersonalSummarizer(BaseMemoryAgent):
else:
tools_s2, messages_s2, success_s2 = [], [], True
answer = (messages_s1[-1].content if success_s1 else "") + (messages_s2[-1].content if success_s2 else "")
success = success_s1 and success_s2
messages = messages_s1 + messages_s2
tools = tools_s1 + tools_s2
@ -108,8 +109,9 @@ class PersonalSummarizer(BaseMemoryAgent):
memory_nodes.extend(tool.memory_nodes)
return {
"answer": memory_nodes,
"answer": answer,
"success": success,
"messages": messages,
"tools": tools,
"memory_nodes": memory_nodes,
}

View file

@ -1,51 +1,5 @@
system_prompt_s1_zh: |
你是一个记忆Agent,负责管理关于 {memory_target} 的 {memory_type} 类型记忆。
## 最新对话
Format: round<index> [<timestamp>] <role/name>: <content>
{context}
## 任务
### 步骤1
根据`最新对话`的内容,在 `add_draft_and_retrieve_similar_memory` 中创建记忆草稿 `memory_draft`。
工具会根据memory_draft的内容行向量检索,返回历史相似记忆,确保在第二步的时候更好的管理记忆库记忆。
### 步骤2
使用`update_memory`更新向量库记忆。
通过`memory_ids_to_delete`删除历史记忆,`memories_to_add`添加新记忆,包括message_time和memory_content。
要求:
- 原样提取最新对话中的内容,不得推断、假设或编造。
- 最后记忆库包含所有的历史记忆和新的记忆,例如记录在同一个主题下用户不同时间的变化。
- 最后记忆库有比较好的组织,同一主题的记忆放到同一条中,不要有重复/多余的记忆。
user_message_s1_zh: |
严格按照步骤1和步骤2完成任务
system_prompt_s2_zh: |
你是一个Profile Agent,负责管理关于 {memory_target} 的 Profile。
## 最新对话
Format: round<index> [<timestamp>] <role/name>: <content>
{context}
## 任务
### 步骤1
根据`最新对话`的内容,在 `add_draft_and_read_all_profiles` 中创建记忆草稿 `profile_draft`。
工具会直接返回所有的Profile,确保在第二步的时候更好的管理Profile。
### 步骤2
使用`update_profile`更新profile库。
通过`profile_ids_to_delete`删除历史Profile,`profiles_to_add`添加新Profile,包括message_time、profile_key和profile_value。
要求:
- 原样提取最新对话中的内容,不得推断、假设或编造。
- 最后Profile库只保留用户最新的状态。例如用户开始喜欢吃苹果,后来只吃喜欢香蕉,可以记录:水果偏好:香蕉
- 最后Profile库有比较好的组织,同一主题的Profile放到同一条中,不要有重复/多余的Profile。
user_message_s2_zh: |
严格按照步骤1和步骤2完成任务
system_prompt_s1: |
You are a Memory Agent responsible for managing {memory_type} type memories about {memory_target}.
You are a Memory Agent responsible for managing {memory_type} memories about {memory_target}.
## Latest Conversation
Format: round<index> [<timestamp>] <role/name>: <content>
@ -53,22 +7,23 @@ system_prompt_s1: |
## Task
### Step 1
Based on the content of `Latest Conversation`, create a memory draft `memory_draft` in `add_draft_and_retrieve_similar_memory`.
The tool will perform vector retrieval based on the content of memory_draft and return historically similar memories to better manage the memory store in Step 2.
Create a memory draft in `add_draft_and_retrieve_similar_memory` based on the latest conversation.
Use actual names from the conversation (e.g., "Bob likes apples") instead of generic references (e.g., "user likes apples"). Always record memories with real names.
The tool will retrieve similar historical memories via vector search to help you consolidate the memory store in Step 2.
### Step 2
Use `update_memory` to update the vector store memories.
Delete historical memories through `memory_ids_to_delete`, add new memories through `memories_to_add`, including message_time and memory_content.
Update the vector store using `update_memory`.
Remove outdated memories via `memory_ids_to_delete` and add new ones via `memories_to_add` with their message_time and memory_content.
Requirements:
- Extract content from the latest conversation as-is, without inference, assumption, or fabrication.
- The final memory store should contain all historical memories and new memories, for example, recording user changes at different times under the same topic.
- The final memory store should be well-organized, with memories on the same topic placed in one entry, without duplicate/redundant memories.
- Extract only what's explicitly stated in the conversation—no inferences, assumptions, or fabrications.
- Preserve all relevant historical and new memories, capturing how things change over time within the same topic.
- Keep the memory store well-organized: group related memories together and eliminate redundancy.
user_message_s1: |
Strictly complete the task following Step 1 and Step 2
Complete the task by following Step 1 and Step 2 in order
system_prompt_s2: |
You are a Profile Agent responsible for managing the Profile about {memory_target}.
You are a Profile Agent responsible for managing profiles about {memory_target}.
## Latest Conversation
Format: round<index> [<timestamp>] <role/name>: <content>
@ -76,16 +31,15 @@ system_prompt_s2: |
## Task
### Step 1
Based on the content of `Latest Conversation`, create a profile draft `profile_draft` in `add_draft_and_read_all_profiles`.
The tool will directly return all Profiles to better manage the Profile store in Step 2.
Create a profile draft in `add_draft_and_read_all_profiles` based on the latest conversation.
The tool will return all existing profiles to help you maintain the profile store in Step 2.
### Step 2
Use `update_profile` to update the profile store.
Delete historical Profiles through `profile_ids_to_delete`, add new Profiles through `profiles_to_add`, including message_time, profile_key, and profile_value.
Update the profile store using `update_profile`.
Remove conflicting or redundant entries profiles via `profile_ids_to_delete` and add new ones via `profiles_to_add` with their message_time, profile_key, and profile_value.
Requirements:
- Extract content from the latest conversation as-is, without inference, assumption, or fabrication.
- The final Profile store should only keep the user's latest state. For example, if the user initially liked apples but later only likes bananas, record: Fruit preference: banana
- The final Profile store should be well-organized, with Profiles on the same topic placed in one entry, without duplicate/redundant Profiles.
- Extract only what's explicitly stated in the conversation—no inferences, assumptions, or fabrications.
- Keep the profile store well-organized: group related profiles together and eliminate duplicates.
user_message_s2: |
Strictly complete the task following Step 1 and Step 2
Complete the task by following Step 1 and Step 2 in order

View file

@ -5,15 +5,21 @@ system_prompt: |
{context}
## Available Memory Agents
Each line indicates a specialized Memory Agent that is an expert for retrieving memories about a specific memory_target.
Each line below is a JSON object representing a specialized Memory Agent:
- `agent`: Description of what this agent specializes in
- `memory_target`: The unique identifier for this agent (THIS IS WHAT YOU MUST USE)
{meta_memory_info}
## Your Task
Analyze the context and delegate retrieval tasks to appropriate specialized agents:
1. Examine the context content and identify which memory_target(s) are relevant for retrieving information
1. Examine the context content and identify which memory_target(s) from the "Available Memory Agents" list above should be queried for relevant information
2. For each relevant memory_target, delegate the retrieval task to its corresponding specialized agent
- The memory_target must **exactly match** existing entries in the "Available Memory Agents" listed above
- Do NOT delegate to agents that don't exist above
- **CRITICAL**: The memory_target must be EXACTLY one of the `memory_target` field values from the JSON objects listed in "Available Memory Agents" above
- **DO NOT** extract or create new memory_target names from the context content
- **DO NOT** use topic names, entity names, descriptions, or any other identifiers from the context as memory_targets
- **DO NOT** use the agent description text as memory_target
- **ONLY** use the exact string values from the `memory_target` fields in the JSON objects above
- Each memory_target should be assigned **only once** - do not duplicate assignments
3. Use the `delegate_task` tool **once** with all relevant memory_target(s) to enable parallel processing by specialized agents

View file

@ -85,7 +85,7 @@ class ReMeSummarizer(BaseMemoryAgent):
success = success and agent.response.success
messages.extend(agent.response.metadata["messages"])
tools.extend(agent.response.metadata["tools"])
memory_nodes.extend(agent.response.answer)
memory_nodes.extend(agent.response.metadata["memory_nodes"])
return {
"answer": memory_nodes,

View file

@ -5,15 +5,21 @@ system_prompt: |
{context}
## Available Memory Agents
Each line indicates a specialized Memory Agent that is an expert for summarizing memories about a specific memory_target.
Each line below is a JSON object representing a specialized Memory Agent:
- `agent`: Description of what this agent specializes in
- `memory_target`: The unique identifier for this agent (THIS IS WHAT YOU MUST USE)
{meta_memory_info}
## Your Task
Analyze the context and delegate summarization tasks to appropriate specialized agents:
1. Examine the context content and identify which memory_target(s) are relevant for storing information
1. Examine the context content and identify which memory_target(s) from the "Available Memory Agents" list above should receive this information
2. For each relevant memory_target, delegate the summarization task to its corresponding specialized agent
- The memory_target must **exactly match** existing entries in the "Available Memory Agents" listed above
- Do NOT delegate to agents that don't exist above
- **CRITICAL**: The memory_target must be EXACTLY one of the `memory_target` field values from the JSON objects listed in "Available Memory Agents" above
- **DO NOT** extract or create new memory_target names from the context content
- **DO NOT** use topic names, preference categories, descriptions, or any other identifiers from the context as memory_targets
- **DO NOT** use the agent description text as memory_target
- **ONLY** use the exact string values from the `memory_target` fields in the JSON objects above
- Each memory_target should be assigned **only once** - do not duplicate assignments
3. Use the `delegate_task` tool **once** with all relevant memory_target(s) to enable parallel processing by specialized agents

View file

@ -101,7 +101,7 @@ class ReMe(Application):
user_name: str | list[str] = "",
task_name: str | list[str] = "",
tool_name: str | list[str] = "",
enable_thinking_params: bool = False,
enable_thinking_params: bool = True,
version: str = "default",
retrieve_top_k: int = 20,
return_dict: bool = False,
@ -211,7 +211,7 @@ class ReMe(Application):
user_name: str | list[str] = "",
task_name: str | list[str] = "",
tool_name: str | list[str] = "",
enable_thinking_params: bool = False,
enable_thinking_params: bool = True,
version: str = "default",
retrieve_top_k: int = 20,
enable_time_filter: bool = True,

View file

@ -11,25 +11,43 @@ from ...core.utils import deduplicate_memories
class AddDraftAndRetrieveSimilarMemory(BaseMemoryTool):
"""Tool to add draft memory and retrieve similar memories"""
def __init__(self, top_k: int = 20, enable_memory_target: bool = False, **kwargs):
def __init__(
self,
top_k: int = 20,
enable_memory_target: bool = False,
enable_when_to_use: bool = False,
**kwargs,
):
super().__init__(**kwargs)
self.top_k: int = top_k
self.enable_memory_target: bool = enable_memory_target
self.enable_when_to_use: bool = enable_when_to_use
def _build_query_parameters(self) -> dict:
"""Build the query parameters schema"""
properties = {
"memory_draft": {
"message_time": {
"type": "string",
"description": "memory_draft",
"description": "message time, e.g. '2020-01-01 00:00:00'",
},
"memory_content": {
"type": "string",
"description": "content of the memory.",
},
}
required = ["memory_draft"]
required = ["message_time", "memory_content"]
if self.enable_when_to_use:
properties["when_to_use"] = {
"type": "string",
"description": "description of when to use this memory.",
}
required.append("when_to_use")
if self.enable_memory_target:
properties["memory_target"] = {
"type": "string",
"description": "memory_target",
"description": "target memory type for this memory.",
}
required.append("memory_target")
@ -56,7 +74,7 @@ class AddDraftAndRetrieveSimilarMemory(BaseMemoryTool):
"properties": {
"draft_items": {
"type": "array",
"description": "List of draft memory items.",
"description": "draft_items",
"items": self._build_query_parameters(),
},
},
@ -82,7 +100,7 @@ class AddDraftAndRetrieveSimilarMemory(BaseMemoryTool):
queries_by_target[target].append(
{
"query": item["memory_draft"],
"query": item["memory_content"],
"limit": self.top_k,
"filters": {},
},

View file

@ -31,24 +31,32 @@ class DelegateTask(BaseMemoryTool):
"parameters": {
"type": "object",
"properties": {
"memory_target_tasks": {
"tasks": {
"type": "array",
"description": "List of memory_target tasks to delegate to specific memory agents",
"description": "List of tasks to delegate to specific memory agents",
"items": {
"type": "string",
"description": "A memory_target identifier to delegate to the corresponding agent",
"type": "object",
"description": "A task item",
"properties": {
"memory_target": {
"type": "string",
"description": "The memory_target identifier to "
"delegate to the corresponding agent",
},
},
"required": ["memory_target"],
},
},
},
"required": ["memory_target_tasks"],
"required": ["tasks"],
},
},
)
async def execute(self):
# Deduplicate and validate memory_target_tasks
memory_target_tasks = self.context.get("memory_target_tasks", [])
memory_target_tasks = sorted(set(memory_target_tasks))
# Deduplicate and validate tasks
tasks = self.context.get("tasks", [])
memory_target_tasks = sorted(set(task["memory_target"] for task in tasks))
# Submit memory_target_tasks to agents
agent_list: list[BaseMemoryAgent] = []