Support Custom Prompt for Memory Action Decision (#2371)

This commit is contained in:
Wonbin Kim
2025-03-18 14:13:01 +09:00
committed by GitHub
parent b8f40f728f
commit 66d3f9b93c
9 changed files with 498 additions and 165 deletions

View File

@@ -72,7 +72,8 @@
"icon": "wrench",
"pages": [
"features/openai_compatibility",
"features/custom-prompts",
"features/custom-fact-extraction-prompt",
"features/custom-update-memory-prompt",
"open-source/multimodal-support",
"open-source/features/rest-api"
]

View File

@@ -1,25 +1,25 @@
---
title: Custom Prompts
description: 'Enhance your product experience by adding custom prompts tailored to your needs'
title: Custom Fact Extraction Prompt
description: 'Enhance your product experience by adding custom fact extraction prompt tailored to your needs'
icon: "pencil"
iconType: "solid"
---
## Introduction to Custom Prompts
## Introduction to Custom Fact Extraction Prompt
Custom prompts allow you to tailor the behavior of your Mem0 instance to specific use cases or domains.
By defining a custom prompt, you can control how information is extracted, processed, and stored in your memory system.
Custom fact extraction prompt allow you to tailor the behavior of your Mem0 instance to specific use cases or domains.
By defining it, you can control how information is extracted from the user's message.
To create an effective custom prompt:
To create an effective custom fact extraction prompt:
1. Be specific about the information to extract.
2. Provide few-shot examples to guide the LLM.
3. Ensure examples follow the format shown below.
Example of a custom prompt:
Example of a custom fact extraction prompt:
<CodeGroup>
```python Python
custom_prompt = """
custom_fact_extraction_prompt = """
Please only extract entities containing customer support information, order details, and user information.
Here are some few shot examples:
@@ -67,7 +67,7 @@ Return the facts and customer information in a json format as shown above.
```
</CodeGroup>
Here we initialize the custom prompt in the config:
Here we initialize the custom fact extraction prompt in the config:
<CodeGroup>
```python Python
@@ -82,7 +82,7 @@ config = {
"max_tokens": 2000,
}
},
"custom_prompt": custom_prompt,
"custom_fact_extraction_prompt": custom_fact_extraction_prompt,
"version": "v1.1"
}
@@ -166,4 +166,4 @@ await memory.add('I like going to hikes', { userId: "user123" });
```
</CodeGroup>
The custom prompt will process both the user and assistant messages to extract relevant information according to the defined format.
The custom fact extraction prompt will process both the user and assistant messages to extract relevant information according to the defined format.

View File

@@ -0,0 +1,239 @@
---
title: Custom Update Memory Prompt
icon: "pencil"
iconType: "solid"
---
Update memory prompt is a prompt used to determine the action to be performed on the memory.
By customizing this prompt, you can control how the memory is updated.
## Introduction
Mem0 memory system compares the newly retrieved facts with the existing memory and determines the action to be performed on the memory.
The kinds of actions are:
- Add
- Add the newly retrieved facts to the memory.
- Update
- Update the existing memory with the newly retrieved facts.
- Delete
- Delete the existing memory.
- No Change
- Do not make any changes to the memory.
### Example
Example of a custom update memory prompt:
<CodeGroup>
```python Python
UPDATE_MEMORY_PROMPT = """You are a smart memory manager which controls the memory of a system.
You can perform four operations: (1) add into the memory, (2) update the memory, (3) delete from the memory, and (4) no change.
Based on the above four operations, the memory will change.
Compare newly retrieved facts with the existing memory. For each new fact, decide whether to:
- ADD: Add it to the memory as a new element
- UPDATE: Update an existing memory element
- DELETE: Delete an existing memory element
- NONE: Make no change (if the fact is already present or irrelevant)
There are specific guidelines to select which operation to perform:
1. **Add**: If the retrieved facts contain new information not present in the memory, then you have to add it by generating a new ID in the id field.
- **Example**:
- Old Memory:
[
{
"id" : "0",
"text" : "User is a software engineer"
}
]
- Retrieved facts: ["Name is John"]
- New Memory:
{
"memory" : [
{
"id" : "0",
"text" : "User is a software engineer",
"event" : "NONE"
},
{
"id" : "1",
"text" : "Name is John",
"event" : "ADD"
}
]
}
2. **Update**: If the retrieved facts contain information that is already present in the memory but the information is totally different, then you have to update it.
If the retrieved fact contains information that conveys the same thing as the elements present in the memory, then you have to keep the fact which has the most information.
Example (a) -- if the memory contains "User likes to play cricket" and the retrieved fact is "Loves to play cricket with friends", then update the memory with the retrieved facts.
Example (b) -- if the memory contains "Likes cheese pizza" and the retrieved fact is "Loves cheese pizza", then you do not need to update it because they convey the same information.
If the direction is to update the memory, then you have to update it.
Please keep in mind while updating you have to keep the same ID.
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
- **Example**:
- Old Memory:
[
{
"id" : "0",
"text" : "I really like cheese pizza"
},
{
"id" : "1",
"text" : "User is a software engineer"
},
{
"id" : "2",
"text" : "User likes to play cricket"
}
]
- Retrieved facts: ["Loves chicken pizza", "Loves to play cricket with friends"]
- New Memory:
{
"memory" : [
{
"id" : "0",
"text" : "Loves cheese and chicken pizza",
"event" : "UPDATE",
"old_memory" : "I really like cheese pizza"
},
{
"id" : "1",
"text" : "User is a software engineer",
"event" : "NONE"
},
{
"id" : "2",
"text" : "Loves to play cricket with friends",
"event" : "UPDATE",
"old_memory" : "User likes to play cricket"
}
]
}
3. **Delete**: If the retrieved facts contain information that contradicts the information present in the memory, then you have to delete it. Or if the direction is to delete the memory, then you have to delete it.
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
- **Example**:
- Old Memory:
[
{
"id" : "0",
"text" : "Name is John"
},
{
"id" : "1",
"text" : "Loves cheese pizza"
}
]
- Retrieved facts: ["Dislikes cheese pizza"]
- New Memory:
{
"memory" : [
{
"id" : "0",
"text" : "Name is John",
"event" : "NONE"
},
{
"id" : "1",
"text" : "Loves cheese pizza",
"event" : "DELETE"
}
]
}
4. **No Change**: If the retrieved facts contain information that is already present in the memory, then you do not need to make any changes.
- **Example**:
- Old Memory:
[
{
"id" : "0",
"text" : "Name is John"
},
{
"id" : "1",
"text" : "Loves cheese pizza"
}
]
- Retrieved facts: ["Name is John"]
- New Memory:
{
"memory" : [
{
"id" : "0",
"text" : "Name is John",
"event" : "NONE"
},
{
"id" : "1",
"text" : "Loves cheese pizza",
"event" : "NONE"
}
]
}
"""
```
</CodeGroup>
## Output format
The prompt needs to guide the output to follow the structure as shown below:
<CodeGroup>
```json Add
{
"memory": [
{
"id" : "0",
"text" : "This information is new",
"event" : "ADD"
}
]
}
```
```json Update
{
"memory": [
{
"id" : "0",
"text" : "This information replaces the old information",
"event" : "UPDATE",
"old_memory" : "Old information"
}
]
}
```
```json Delete
{
"memory": [
{
"id" : "0",
"text" : "This information will be deleted",
"event" : "DELETE"
}
]
}
```
```json No Change
{
"memory": [
{
"id" : "0",
"text" : "No changes for this information",
"event" : "NONE"
}
]
}
```
</CodeGroup>
## custom update memory prompt vs custom prompt
| Feature | `custom_update_memory_prompt` | `custom_prompt` |
|---------|-------------------------------|-----------------|
| Use case | Determine the action to be performed on the memory | Extract the facts from messages |
| Reference | Retrieved facts from messages and old memory | Messages |
| Output | Action to be performed on the memory | Extracted facts |

View File

@@ -372,7 +372,8 @@ Mem0 offers extensive configuration options to customize its behavior according
|------------------|--------------------------------------|----------------------------|
| `history_db_path` | Path to the history database | "{mem0_dir}/history.db" |
| `version` | API version | "v1.1" |
| `custom_prompt` | Custom prompt for memory processing | None |
| `custom_fact_extraction_prompt` | Custom prompt for memory processing | None |
| `custom_update_memory_prompt` | Custom prompt for update memory | None |
</Accordion>
<Accordion title="Complete Configuration Example">
@@ -409,7 +410,8 @@ config = {
},
"history_db_path": "/path/to/history.db",
"version": "v1.1",
"custom_prompt": "Optional custom prompt for memory processing"
"custom_fact_extraction_prompt": "Optional custom prompt for fact extraction for memory",
"custom_update_memory_prompt": "Optional custom prompt for update memory"
}
```
</Accordion>

View File

@@ -48,8 +48,12 @@ class MemoryConfig(BaseModel):
description="The version of the API",
default="v1.1",
)
custom_prompt: Optional[str] = Field(
description="Custom prompt for the memory",
custom_fact_extraction_prompt: Optional[str] = Field(
description="Custom prompt for the fact extraction",
default=None,
)
custom_update_memory_prompt: Optional[str] = Field(
description="Custom prompt for the update memory",
default=None,
)

View File

@@ -58,162 +58,168 @@ Following is a conversation between the user and the assistant. You have to extr
You should detect the language of the user input and record the facts in the same language.
"""
DEFAULT_UPDATE_MEMORY_PROMPT = """You are a smart memory manager which controls the memory of a system.
You can perform four operations: (1) add into the memory, (2) update the memory, (3) delete from the memory, and (4) no change.
def get_update_memory_messages(retrieved_old_memory_dict, response_content):
return f"""You are a smart memory manager which controls the memory of a system.
You can perform four operations: (1) add into the memory, (2) update the memory, (3) delete from the memory, and (4) no change.
Based on the above four operations, the memory will change.
Based on the above four operations, the memory will change.
Compare newly retrieved facts with the existing memory. For each new fact, decide whether to:
- ADD: Add it to the memory as a new element
- UPDATE: Update an existing memory element
- DELETE: Delete an existing memory element
- NONE: Make no change (if the fact is already present or irrelevant)
Compare newly retrieved facts with the existing memory. For each new fact, decide whether to:
- ADD: Add it to the memory as a new element
- UPDATE: Update an existing memory element
- DELETE: Delete an existing memory element
- NONE: Make no change (if the fact is already present or irrelevant)
There are specific guidelines to select which operation to perform:
There are specific guidelines to select which operation to perform:
1. **Add**: If the retrieved facts contain new information not present in the memory, then you have to add it by generating a new ID in the id field.
- **Example**:
1. **Add**: If the retrieved facts contain new information not present in the memory, then you have to add it by generating a new ID in the id field.
- **Example**:
- Old Memory:
[
{{
{
"id" : "0",
"text" : "User is a software engineer"
}}
}
]
- Retrieved facts: ["Name is John"]
- New Memory:
{{
{
"memory" : [
{{
{
"id" : "0",
"text" : "User is a software engineer",
"event" : "NONE"
}},
{{
},
{
"id" : "1",
"text" : "Name is John",
"event" : "ADD"
}}
}
]
}}
}
2. **Update**: If the retrieved facts contain information that is already present in the memory but the information is totally different, then you have to update it.
If the retrieved fact contains information that conveys the same thing as the elements present in the memory, then you have to keep the fact which has the most information.
Example (a) -- if the memory contains "User likes to play cricket" and the retrieved fact is "Loves to play cricket with friends", then update the memory with the retrieved facts.
Example (b) -- if the memory contains "Likes cheese pizza" and the retrieved fact is "Loves cheese pizza", then you do not need to update it because they convey the same information.
If the direction is to update the memory, then you have to update it.
Please keep in mind while updating you have to keep the same ID.
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
- **Example**:
2. **Update**: If the retrieved facts contain information that is already present in the memory but the information is totally different, then you have to update it.
If the retrieved fact contains information that conveys the same thing as the elements present in the memory, then you have to keep the fact which has the most information.
Example (a) -- if the memory contains "User likes to play cricket" and the retrieved fact is "Loves to play cricket with friends", then update the memory with the retrieved facts.
Example (b) -- if the memory contains "Likes cheese pizza" and the retrieved fact is "Loves cheese pizza", then you do not need to update it because they convey the same information.
If the direction is to update the memory, then you have to update it.
Please keep in mind while updating you have to keep the same ID.
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
- **Example**:
- Old Memory:
[
{{
{
"id" : "0",
"text" : "I really like cheese pizza"
}},
{{
},
{
"id" : "1",
"text" : "User is a software engineer"
}},
{{
},
{
"id" : "2",
"text" : "User likes to play cricket"
}}
}
]
- Retrieved facts: ["Loves chicken pizza", "Loves to play cricket with friends"]
- New Memory:
{{
{
"memory" : [
{{
{
"id" : "0",
"text" : "Loves cheese and chicken pizza",
"event" : "UPDATE",
"old_memory" : "I really like cheese pizza"
}},
{{
},
{
"id" : "1",
"text" : "User is a software engineer",
"event" : "NONE"
}},
{{
},
{
"id" : "2",
"text" : "Loves to play cricket with friends",
"event" : "UPDATE",
"old_memory" : "User likes to play cricket"
}}
}
]
}}
}
3. **Delete**: If the retrieved facts contain information that contradicts the information present in the memory, then you have to delete it. Or if the direction is to delete the memory, then you have to delete it.
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
- **Example**:
3. **Delete**: If the retrieved facts contain information that contradicts the information present in the memory, then you have to delete it. Or if the direction is to delete the memory, then you have to delete it.
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
- **Example**:
- Old Memory:
[
{{
{
"id" : "0",
"text" : "Name is John"
}},
{{
},
{
"id" : "1",
"text" : "Loves cheese pizza"
}}
}
]
- Retrieved facts: ["Dislikes cheese pizza"]
- New Memory:
{{
{
"memory" : [
{{
{
"id" : "0",
"text" : "Name is John",
"event" : "NONE"
}},
{{
},
{
"id" : "1",
"text" : "Loves cheese pizza",
"event" : "DELETE"
}}
}
]
}}
}
4. **No Change**: If the retrieved facts contain information that is already present in the memory, then you do not need to make any changes.
- **Example**:
4. **No Change**: If the retrieved facts contain information that is already present in the memory, then you do not need to make any changes.
- **Example**:
- Old Memory:
[
{{
{
"id" : "0",
"text" : "Name is John"
}},
{{
},
{
"id" : "1",
"text" : "Loves cheese pizza"
}}
}
]
- Retrieved facts: ["Name is John"]
- New Memory:
{{
{
"memory" : [
{{
{
"id" : "0",
"text" : "Name is John",
"event" : "NONE"
}},
{{
},
{
"id" : "1",
"text" : "Loves cheese pizza",
"event" : "NONE"
}}
}
]
}}
}
"""
def get_update_memory_messages(retrieved_old_memory_dict, response_content, custom_update_memory_prompt=None):
if custom_update_memory_prompt is None:
global DEFAULT_UPDATE_MEMORY_PROMPT
custom_update_memory_prompt = DEFAULT_UPDATE_MEMORY_PROMPT
return f"""{custom_update_memory_prompt}
Below is the current content of my memory which I have collected till now. You have to update it in the following format only:
``
```
{retrieved_old_memory_dict}
``
```
The new retrieved facts are mentioned in the triple backticks. You have to analyze the new retrieved facts and determine whether these facts should be added, updated, or deleted in the memory.
@@ -221,6 +227,20 @@ def get_update_memory_messages(retrieved_old_memory_dict, response_content):
{response_content}
```
You must return your response in the following JSON structure only:
{{
"memory" : [
{{
"id" : "<ID of the memory>", # Use existing ID for updates/deletes, or new ID for additions
"text" : "<Content of the memory>", # Content of the memory
"event" : "<Operation to be performed>", # Must be "ADD", "UPDATE", "DELETE", or "NONE"
"old_memory" : "<Old memory content>" # Required only if the event is "UPDATE"
}},
...
]
}}
Follow the instruction mentioned below:
- Do not return anything from the custom few shot prompts provided above.
- If the current memory is empty, then you have to add the new retrieved facts to the memory.

View File

@@ -34,7 +34,8 @@ class Memory(MemoryBase):
def __init__(self, config: MemoryConfig = MemoryConfig()):
self.config = config
self.custom_prompt = self.config.custom_prompt
self.custom_fact_extraction_prompt = self.config.custom_fact_extraction_prompt
self.custom_update_memory_prompt = self.config.custom_update_memory_prompt
self.embedding_model = EmbedderFactory.create(self.config.embedder.provider, self.config.embedder.config)
self.vector_store = VectorStoreFactory.create(
self.config.vector_store.provider, self.config.vector_store.config
@@ -176,8 +177,8 @@ class Memory(MemoryBase):
parsed_messages = parse_messages(messages)
if self.custom_prompt:
system_prompt = self.custom_prompt
if self.custom_fact_extraction_prompt:
system_prompt = self.custom_fact_extraction_prompt
user_prompt = f"Input:\n{parsed_messages}"
else:
system_prompt, user_prompt = get_fact_retrieval_messages(parsed_messages)
@@ -221,7 +222,7 @@ class Memory(MemoryBase):
temp_uuid_mapping[str(idx)] = item["id"]
retrieved_old_memory[idx]["id"] = str(idx)
function_calling_prompt = get_update_memory_messages(retrieved_old_memory, new_retrieved_facts)
function_calling_prompt = get_update_memory_messages(retrieved_old_memory, new_retrieved_facts, self.custom_update_memory_prompt)
try:
new_memories_with_actions = self.llm.generate_response(

View File

@@ -0,0 +1,17 @@
from mem0.configs import prompts
def test_get_update_memory_messages():
retrieved_old_memory_dict = [{"id": "1", "text": "old memory 1"}]
response_content = ["new fact"]
custom_update_memory_prompt = "custom prompt determining memory update"
## When custom update memory prompt is provided
##
result = prompts.get_update_memory_messages(retrieved_old_memory_dict, response_content, custom_update_memory_prompt)
assert result.startswith(custom_update_memory_prompt)
## When custom update memory prompt is not provided
##
result = prompts.get_update_memory_messages(retrieved_old_memory_dict, response_content, None)
assert result.startswith(prompts.DEFAULT_UPDATE_MEMORY_PROMPT)

View File

@@ -31,6 +31,25 @@ def memory_instance():
config.graph_store.config = {"some_config": "value"}
return Memory(config)
@pytest.fixture
def memory_custom_instance():
with patch("mem0.utils.factory.EmbedderFactory") as mock_embedder, patch(
"mem0.utils.factory.VectorStoreFactory"
) as mock_vector_store, patch("mem0.utils.factory.LlmFactory") as mock_llm, patch(
"mem0.memory.telemetry.capture_event"
), patch("mem0.memory.graph_memory.MemoryGraph"):
mock_embedder.create.return_value = Mock()
mock_vector_store.create.return_value = Mock()
mock_llm.create.return_value = Mock()
config = MemoryConfig(
version="v1.1",
custom_fact_extraction_prompt="custom prompt extracting memory",
custom_update_memory_prompt="custom prompt determining memory update"
)
config.graph_store.config = {"some_config": "value"}
return Memory(config)
@pytest.mark.parametrize("version, enable_graph", [("v1.0", False), ("v1.1", True)])
def test_add(memory_instance, version, enable_graph):
@@ -239,3 +258,33 @@ def test_get_all(memory_instance, version, enable_graph, expected_result):
memory_instance.graph.get_all.assert_called_once_with({"user_id": "test_user"}, 100)
else:
memory_instance.graph.get_all.assert_not_called()
def test_custom_prompts(memory_custom_instance):
messages = [{"role": "user", "content": "Test message"}]
memory_custom_instance.llm.generate_response = Mock()
with patch("mem0.memory.main.parse_messages", return_value="Test message") as mock_parse_messages:
with patch("mem0.memory.main.get_update_memory_messages", return_value="custom update memory prompt") as mock_get_update_memory_messages:
memory_custom_instance.add(messages=messages, user_id="test_user")
## custom prompt
##
mock_parse_messages.assert_called_once_with(messages)
memory_custom_instance.llm.generate_response.assert_any_call(
messages=[
{"role": "system", "content": memory_custom_instance.config.custom_fact_extraction_prompt},
{"role": "user", "content": f"Input:\n{mock_parse_messages.return_value}"},
],
response_format={"type": "json_object"},
)
## custom update memory prompt
##
mock_get_update_memory_messages.assert_called_once_with([],[],memory_custom_instance.config.custom_update_memory_prompt)
memory_custom_instance.llm.generate_response.assert_any_call(
messages=[{"role": "user", "content": mock_get_update_memory_messages.return_value}],
response_format={"type": "json_object"},
)