Repository: camel-ai/camel
Pull request: feat: Link memory to agent with id
## Description

- Adds an agent_id to every memory record.
- Updates ChatHistoryBlock to retrieve only the memories associated with the specified agent.
- Creates this interface for ChatAgent to save, load and clear memories:
```
agent = ChatAgent(`system_message`="you are a helpful assistant", agent_id="001") # The agent is could be generated randomly is not specified.
agent.save_memory(path: str) -> save `AgentMemory` with `agent_id` presisitently
agent.load_memory(memory: AgentMemory)
agent.load_memory_from_path(path: str)
agent.clear_memory()
```
- Adds an option to load agent specific memories from all subclasses of BaseKeyValueStorage and all subclasses of BaseVectorStorage 
- Fixes #1708 
- Fixes some features requested in #1588 

## Checklist

Go over all the following points, and put an `x` in all the boxes that apply.

- [x] I have read the [CONTRIBUTION](https://github.com/camel-ai/camel/blob/master/CONTRIBUTING.md) guide (**required**)
- [x] I have linked this PR to an issue using the Development section on the right sidebar or by adding `Fixes #issue-number` in the PR description (**required**)
- [x] I have checked if any dependencies need to be added or updated in `pyproject.toml` and `poetry.lock`
- [x] I have updated the tests accordingly (*required for a bug fix or a new feature*)
- [ ] I have updated the documentation if needed:
- [ ] I have added examples if this is a new feature

If you are unsure about any of these, don't hesitate to ask. We are here to help!


Problem statement:
Agent ID associated with memories for better tracking and organization.
```
agent = ChatAgent(`system_message`="you are a helpful assistant", agent_id="001") # The agent is could be generated randomly is not specified.
agent.save_memory(path: str) -> save `AgentMemory` with `agent_id` presisitently
agent.load_memory(memory: AgentMemory)
agent.load_memory_from_path(path: str)
agent.clear_memory()
```

Hints from issue discussion:

Commit message: fix: merge conflict error_info

Patch to review:
diff --git a/camel/agents/chat_agent.py b/camel/agents/chat_agent.py
index 9452bb4df6..de453d92a4 100644
--- a/camel/agents/chat_agent.py
+++ b/camel/agents/chat_agent.py
@@ -16,7 +16,10 @@
 import json
 import logging
 import textwrap
+import uuid
 from collections import defaultdict
+from datetime import datetime
+from pathlib import Path
 from typing import (
     TYPE_CHECKING,
     Any,
@@ -59,6 +62,7 @@
 )
 from camel.prompts import TextPrompt
 from camel.responses import ChatAgentResponse
+from camel.storages.key_value_storages.json import JsonStorage
 from camel.toolkits import FunctionTool
 from camel.types import (
     ChatCompletion,
@@ -138,6 +142,8 @@ class ChatAgent(BaseAgent):
             the next model in ModelManager. (default: :str:`round_robin`)
         single_iteration (bool): Whether to let the agent perform only one
             model calling at each step. (default: :obj:`False`)
+        agent_id (str, optional): The ID of the agent. If not provided, a
+            random UUID will be generated. (default: :obj:`None`)
     """
 
     def __init__(
@@ -157,6 +163,7 @@ def __init__(
         response_terminators: Optional[List[ResponseTerminator]] = None,
         scheduling_strategy: str = "round_robin",
         single_iteration: bool = False,
+        agent_id: Optional[str] = None,
     ) -> None:
         # Set up model backend
         self.model_backend = ModelManager(
@@ -171,16 +178,25 @@ def __init__(
             scheduling_strategy=scheduling_strategy,
         )
         self.model_type = self.model_backend.model_type
+        # Assign unique ID
+        self.agent_id = agent_id if agent_id else str(uuid.uuid4())
 
         # Set up memory
         context_creator = ScoreBasedContextCreator(
             self.model_backend.token_counter,
             token_limit or self.model_backend.token_limit,
         )
+
         self.memory: AgentMemory = memory or ChatHistoryMemory(
-            context_creator, window_size=message_window_size
+            context_creator,
+            window_size=message_window_size,
+            agent_id=self.agent_id,
         )
 
+        # So we don't have to pass agent_id when we define memory
+        if memory is not None:
+            memory.agent_id = self.agent_id
+
         # Set up system message and initialize messages
         self._original_system_message = (
             BaseMessage.make_assistant_message(
@@ -321,9 +337,77 @@ def update_memory(
             role (OpenAIBackendRole): The backend role type.
         """
         self.memory.write_record(
-            MemoryRecord(message=message, role_at_backend=role)
+            MemoryRecord(
+                message=message,
+                role_at_backend=role,
+                timestamp=datetime.now().timestamp(),
+                agent_id=self.agent_id,
+            )
         )
 
+    def load_memory(self, memory: AgentMemory) -> None:
+        r"""Load the provided memory into the agent.
+
+        Args:
+            memory (AgentMemory): The memory to load into the agent.
+
+        Returns:
+            None
+        """
+
+        for context_record in memory.retrieve():
+            self.memory.write_record(context_record.memory_record)
+        print(f"Memory loaded from {memory}")
+
+    def load_memory_from_path(self, path: str) -> None:
+        r"""
+        Loads memory records from a JSON file filtered by this agent's ID.
+
+        Args:
+            path (str): The file path to a JSON memory file that uses
+                JsonStorage.
+
+        Raises:
+            ValueError: If no matching records for the agent_id are found
+                (optional check; commented out below).
+        """
+        json_store = JsonStorage(Path(path))
+        all_records = json_store.load()
+
+        if not all_records:
+            raise ValueError(
+                f"No records found for agent_id={self.agent_id} in {path}"
+            )
+
+        for record_dict in all_records:
+            record = MemoryRecord.from_dict(record_dict)
+            self.memory.write_records([record])
+        print(f"Memory loaded from {path}")
+
+    def save_memory(self, path: str) -> None:
+        r"""
+        Retrieves the current conversation data from memory and writes it
+        into a JSON file using JsonStorage.
+
+        Args:
+            path (str): Target file path to store JSON data.
+        """
+        json_store = JsonStorage(Path(path))
+        context_records = self.memory.retrieve()
+        to_save = [cr.memory_record.to_dict() for cr in context_records]
+        json_store.save(to_save)
+        print(f"Memory saved to {path}")
+
+    def clear_memory(self) -> None:
+        r"""Clear the agent's memory and reset to initial state.
+
+        Returns:
+            None
+        """
+        self.memory.clear()
+        if self.system_message is not None:
+            self.update_memory(self.system_message, OpenAIBackendRole.SYSTEM)
+
     def _generate_system_message_for_output_language(
         self,
     ) -> Optional[BaseMessage]:
diff --git a/camel/memories/agent_memories.py b/camel/memories/agent_memories.py
index f6e401a662..8bf35d1a7d 100644
--- a/camel/memories/agent_memories.py
+++ b/camel/memories/agent_memories.py
@@ -33,6 +33,8 @@ class ChatHistoryMemory(AgentMemory):
         window_size (int, optional): The number of recent chat messages to
             retrieve. If not provided, the entire chat history will be
             retrieved.  (default: :obj:`None`)
+        agent_id (str, optional): The ID of the agent associated with the chat
+            history.
     """
 
     def __init__(
@@ -40,6 +42,7 @@ def __init__(
         context_creator: BaseContextCreator,
         storage: Optional[BaseKeyValueStorage] = None,
         window_size: Optional[int] = None,
+        agent_id: Optional[str] = None,
     ) -> None:
         if window_size is not None and not isinstance(window_size, int):
             raise TypeError("`window_size` must be an integer or None.")
@@ -48,6 +51,15 @@ def __init__(
         self._context_creator = context_creator
         self._window_size = window_size
         self._chat_history_block = ChatHistoryBlock(storage=storage)
+        self._agent_id = agent_id
+
+    @property
+    def agent_id(self) -> Optional[str]:
+        return self._agent_id
+
+    @agent_id.setter
+    def agent_id(self, val: Optional[str]) -> None:
+        self._agent_id = val
 
     def retrieve(self) -> List[ContextRecord]:
         records = self._chat_history_block.retrieve(self._window_size)
@@ -63,6 +75,12 @@ def retrieve(self) -> List[ContextRecord]:
         return records
 
     def write_records(self, records: List[MemoryRecord]) -> None:
+        for record in records:
+            # assign the agent_id to the record
+            if record.agent_id == "":
+                # if the agent memory has an agent_id, use it
+                if self.agent_id is not None:
+                    record.agent_id = self.agent_id
         self._chat_history_block.write_records(records)
 
     def get_context_creator(self) -> BaseContextCreator:
@@ -84,6 +102,8 @@ class VectorDBMemory(AgentMemory):
             (default: :obj:`None`)
         retrieve_limit (int, optional): The maximum number of messages
             to be added into the context.  (default: :obj:`3`)
+        agent_id (str, optional): The ID of the agent associated with
+            the messages stored in the vector database.
     """
 
     def __init__(
@@ -91,13 +111,23 @@ def __init__(
         context_creator: BaseContextCreator,
         storage: Optional[BaseVectorStorage] = None,
         retrieve_limit: int = 3,
+        agent_id: Optional[str] = None,
     ) -> None:
         self._context_creator = context_creator
         self._retrieve_limit = retrieve_limit
         self._vectordb_block = VectorDBBlock(storage=storage)
+        self._agent_id = agent_id
 
         self._current_topic: str = ""
 
+    @property
+    def agent_id(self) -> Optional[str]:
+        return self._agent_id
+
+    @agent_id.setter
+    def agent_id(self, val: Optional[str]) -> None:
+        self._agent_id = val
+
     def retrieve(self) -> List[ContextRecord]:
         return self._vectordb_block.retrieve(
             self._current_topic,
@@ -109,6 +139,13 @@ def write_records(self, records: List[MemoryRecord]) -> None:
         for record in records:
             if record.role_at_backend == OpenAIBackendRole.USER:
                 self._current_topic = record.message.content
+
+            # assign the agent_id to the record
+            if record.agent_id == "":
+                # if the agent memory has an agent_id, use it
+                if self.agent_id is not None:
+                    record.agent_id = self.agent_id
+
         self._vectordb_block.write_records(records)
 
     def get_context_creator(self) -> BaseContextCreator:
@@ -133,6 +170,8 @@ class LongtermAgentMemory(AgentMemory):
             (default: :obj:`None`)
         retrieve_limit (int, optional): The maximum number of messages
             to be added into the context.  (default: :obj:`3`)
+        agent_id (str, optional): The ID of the agent associated with the chat
+            history and the messages stored in the vector database.
     """
 
     def __init__(
@@ -141,12 +180,22 @@ def __init__(
         chat_history_block: Optional[ChatHistoryBlock] = None,
         vector_db_block: Optional[VectorDBBlock] = None,
         retrieve_limit: int = 3,
+        agent_id: Optional[str] = None,
     ) -> None:
         self.chat_history_block = chat_history_block or ChatHistoryBlock()
         self.vector_db_block = vector_db_block or VectorDBBlock()
         self.retrieve_limit = retrieve_limit
         self._context_creator = context_creator
         self._current_topic: str = ""
+        self._agent_id = agent_id
+
+    @property
+    def agent_id(self) -> Optional[str]:
+        return self._agent_id
+
+    @agent_id.setter
+    def agent_id(self, val: Optional[str]) -> None:
+        self._agent_id = val
 
     def get_context_creator(self) -> BaseContextCreator:
         r"""Returns the context creator used by the memory.
@@ -166,7 +215,8 @@ def retrieve(self) -> List[ContextRecord]:
         """
         chat_history = self.chat_history_block.retrieve()
         vector_db_retrieve = self.vector_db_block.retrieve(
-            self._current_topic, self.retrieve_limit
+            self._current_topic,
+            self.retrieve_limit,
         )
         return chat_history[:1] + vector_db_retrieve + chat_history[1:]
 
diff --git a/camel/memories/base.py b/camel/memories/base.py
index 57865236c7..140a720097 100644
--- a/camel/memories/base.py
+++ b/camel/memories/base.py
@@ -13,7 +13,7 @@
 # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
 
 from abc import ABC, abstractmethod
-from typing import List, Tuple
+from typing import List, Optional, Tuple
 
 from camel.memories.records import ContextRecord, MemoryRecord
 from camel.messages import OpenAIMessage
@@ -112,6 +112,16 @@ class AgentMemory(MemoryBlock, ABC):
     the memory records stored within the AgentMemory.
     """
 
+    @property
+    @abstractmethod
+    def agent_id(self) -> Optional[str]:
+        pass
+
+    @agent_id.setter
+    @abstractmethod
+    def agent_id(self, val: Optional[str]) -> None:
+        pass
+
     @abstractmethod
     def retrieve(self) -> List[ContextRecord]:
         r"""Get a record list from the memory for creating model context.
@@ -138,3 +148,15 @@ def get_context(self) -> Tuple[List[OpenAIMessage], int]:
                 context in OpenAIMessage format and the total token count.
         """
         return self.get_context_creator().create_context(self.retrieve())
+
+    def __repr__(self) -> str:
+        r"""Returns a string representation of the AgentMemory.
+
+        Returns:
+            str: A string in the format 'ClassName(agent_id=<id>)'
+                if agent_id exists, otherwise just 'ClassName()'.
+        """
+        agent_id = getattr(self, '_agent_id', None)
+        if agent_id:
+            return f"{self.__class__.__name__}(agent_id='{agent_id}')"
+        return f"{self.__class__.__name__}()"
diff --git a/camel/memories/records.py b/camel/memories/records.py
index 2f3be051da..bac344d4a5 100644
--- a/camel/memories/records.py
+++ b/camel/memories/records.py
@@ -39,6 +39,8 @@ class MemoryRecord(BaseModel):
             key-value pairs that provide more information. If not given, it
             will be an empty `Dict`.
         timestamp (float, optional): The timestamp when the record was created.
+        agent_id (str): The identifier of the agent associated with this
+            memory.
     """
 
     model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -50,6 +52,7 @@ class MemoryRecord(BaseModel):
     timestamp: float = Field(
         default_factory=lambda: datetime.now(timezone.utc).timestamp()
     )
+    agent_id: str = Field(default="")
 
     _MESSAGE_TYPES: ClassVar[dict] = {
         "BaseMessage": BaseMessage,
@@ -73,6 +76,7 @@ def from_dict(cls, record_dict: Dict[str, Any]) -> "MemoryRecord":
             role_at_backend=record_dict["role_at_backend"],
             extra_info=record_dict["extra_info"],
             timestamp=record_dict["timestamp"],
+            agent_id=record_dict["agent_id"],
         )
 
     def to_dict(self) -> Dict[str, Any]:
@@ -88,6 +92,7 @@ def to_dict(self) -> Dict[str, Any]:
             "role_at_backend": self.role_at_backend,
             "extra_info": self.extra_info,
             "timestamp": self.timestamp,
+            "agent_id": self.agent_id,
         }
 
     def to_openai_message(self) -> OpenAIMessage:
diff --git a/camel/storages/key_value_storages/__init__.py b/camel/storages/key_value_storages/__init__.py
index 014a692885..5428c836f9 100644
--- a/camel/storages/key_value_storages/__init__.py
+++ b/camel/storages/key_value_storages/__init__.py
@@ -14,7 +14,7 @@
 
 from .base import BaseKeyValueStorage
 from .in_memory import InMemoryKeyValueStorage
-from .json import JsonStorage
+from .json import CamelJSONEncoder, JsonStorage
 from .redis import RedisStorage
 
 __all__ = [
@@ -22,4 +22,5 @@
     'InMemoryKeyValueStorage',
     'JsonStorage',
     'RedisStorage',
+    'CamelJSONEncoder',
 ]
diff --git a/camel/storages/key_value_storages/json.py b/camel/storages/key_value_storages/json.py
index 50f666029c..8dd36d6b4b 100644
--- a/camel/storages/key_value_storages/json.py
+++ b/camel/storages/key_value_storages/json.py
@@ -26,7 +26,7 @@
 )
 
 
-class _CamelJSONEncoder(json.JSONEncoder):
+class CamelJSONEncoder(json.JSONEncoder):
     r"""A custom JSON encoder for serializing specifically enumerated types.
     Ensures enumerated types can be stored in and retrieved from JSON format.
     """
@@ -62,7 +62,7 @@ def __init__(self, path: Optional[Path] = None) -> None:
     def _json_object_hook(self, d) -> Any:
         if "__enum__" in d:
             name, member = d["__enum__"].split(".")
-            return getattr(_CamelJSONEncoder.CAMEL_ENUMS[name], member)
+            return getattr(CamelJSONEncoder.CAMEL_ENUMS[name], member)
         else:
             return d
 
@@ -75,7 +75,7 @@ def save(self, records: List[Dict[str, Any]]) -> None:
         """
         with self.json_path.open("a") as f:
             f.writelines(
-                [json.dumps(r, cls=_CamelJSONEncoder) + "\n" for r in records]
+                [json.dumps(r, cls=CamelJSONEncoder) + "\n" for r in records]
             )
 
     def load(self) -> List[Dict[str, Any]]:
diff --git a/camel/storages/vectordb_storages/base.py b/camel/storages/vectordb_storages/base.py
index 6fb32accad..fe55a42e8f 100644
--- a/camel/storages/vectordb_storages/base.py
+++ b/camel/storages/vectordb_storages/base.py
@@ -87,7 +87,11 @@ def create(
     ) -> "VectorDBQueryResult":
         r"""A class method to construct a `VectorDBQueryResult` instance."""
         return cls(
-            record=VectorRecord(vector=vector, id=id, payload=payload),
+            record=VectorRecord(
+                vector=vector,
+                id=id,
+                payload=payload,
+            ),
             similarity=similarity,
         )
 
diff --git a/camel/toolkits/__init__.py b/camel/toolkits/__init__.py
index c892954d53..e84cdace25 100644
--- a/camel/toolkits/__init__.py
+++ b/camel/toolkits/__init__.py
@@ -50,6 +50,7 @@
 from .zapier_toolkit import ZapierToolkit
 from .sympy_toolkit import SymPyToolkit
 from .mineru_toolkit import MinerUToolkit
+from .memory_toolkit import MemoryToolkit
 from .audio_analysis_toolkit import AudioAnalysisToolkit
 from .excel_toolkit import ExcelToolkit
 from .video_analysis_toolkit import VideoAnalysisToolkit
@@ -60,7 +61,6 @@
 from .terminal_toolkit import TerminalToolkit
 from .pubmed_toolkit import PubMedToolkit
 
-
 __all__ = [
     'BaseToolkit',
     'FunctionTool',
@@ -97,6 +97,7 @@
     'ZapierToolkit',
     'SymPyToolkit',
     'MinerUToolkit',
+    'MemoryToolkit',
     'MCPToolkit',
     'AudioAnalysisToolkit',
     'ExcelToolkit',
diff --git a/camel/toolkits/memory_toolkit.py b/camel/toolkits/memory_toolkit.py
new file mode 100644
index 0000000000..0bfe43c996
--- /dev/null
+++ b/camel/toolkits/memory_toolkit.py
@@ -0,0 +1,136 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# memory_toolkit.py
+import json
+from typing import TYPE_CHECKING, Optional
+
+from camel.memories import (
+    ChatHistoryMemory,
+    MemoryRecord,
+    ScoreBasedContextCreator,
+)
+from camel.toolkits.base import BaseToolkit
+from camel.toolkits.function_tool import FunctionTool
+
+if TYPE_CHECKING:
+    from camel.agents import ChatAgent
+
+
+class MemoryToolkit(BaseToolkit):
+    r"""
+    A toolkit that provides methods for saving, loading, and clearing a
+    ChatAgent's memory.
+    These methods are exposed as FunctionTool objects for
+    function calling. Internally, it calls:
+      - agent.save_memory(path)
+      - agent.load_memory(new_memory_obj)
+      - agent.load_memory_from_path(path)
+      - agent.clear_memory()
+    """
+
+    def __init__(self, agent: 'ChatAgent', timeout: Optional[float] = None):
+        super().__init__(timeout=timeout)
+        self.agent = agent
+
+    def save(self, path: str) -> str:
+        r"""
+        Saves the agent's current memory to a JSON file by calling
+        agent.save_memory(path).
+
+        Args:
+            path (str): The file path to save the memory to.
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.save_memory(path)
+        return f"Memory saved to {path}"
+
+    def load(self, memory_json: str) -> str:
+        r"""
+        Loads memory into the agent from a JSON string:
+          1) Parse `memory_json` as a list of memory records
+          2) Create a fresh ChatHistoryMemory
+          3) Write records into that memory
+          4) Call agent.load_memory(...) with that new memory
+
+        Args:
+            memory_json (str): A JSON string containing memory records.
+
+        Returns:
+            str: Confirmation or error message.
+        """
+        try:
+            data = json.loads(memory_json.strip())
+            if not isinstance(data, list):
+                return "[ERROR] Memory data should be a list of records."
+
+            # Build a fresh ChatHistoryMemory
+            context_creator = ScoreBasedContextCreator(
+                token_counter=self.agent.model_backend.token_counter,
+                token_limit=self.agent.model_backend.token_limit,
+            )
+            new_memory = ChatHistoryMemory(context_creator)
+
+            # Convert each record dict -> MemoryRecord
+            for record_dict in data:
+                record = MemoryRecord.from_dict(record_dict)
+                new_memory.write_record(record)
+
+            # Load into the agent
+            self.agent.load_memory(new_memory)
+            return "Loaded memory from provided JSON string."
+        except json.JSONDecodeError:
+            return "[ERROR] Invalid JSON string provided."
+        except Exception as e:
+            return f"[ERROR] Failed to load memory: {e!s}"
+
+    def load_from_path(self, path: str) -> str:
+        r"""
+        Loads the agent's memory from a JSON file by calling
+        agent.load_memory_from_path(path).
+
+        Args:
+            path (str): The file path to load the memory from.
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.load_memory_from_path(path)
+        return f"Memory loaded from {path}"
+
+    def clear_memory(self) -> str:
+        r"""
+        Clears the agent's memory by calling agent.clear_memory().
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.clear_memory()
+        return "Memory has been cleared."
+
+    def get_tools(self) -> list[FunctionTool]:
+        r"""
+        Expose the memory management methods as function tools
+        for the ChatAgent.
+
+        Returns:
+            list[FunctionTool]: List of FunctionTool objects.
+        """
+        return [
+            FunctionTool(self.save),
+            FunctionTool(self.load),
+            FunctionTool(self.load_from_path),
+            FunctionTool(self.clear_memory),
+        ]
diff --git a/examples/memories/agent_memory_test.py b/examples/memories/agent_memory_test.py
new file mode 100644
index 0000000000..0123e0fd01
--- /dev/null
+++ b/examples/memories/agent_memory_test.py
@@ -0,0 +1,127 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+import os
+from pathlib import Path
+
+from camel.agents import ChatAgent
+from camel.memories import ChatHistoryMemory
+from camel.memories.context_creators.score_based import (
+    ScoreBasedContextCreator,
+)
+from camel.messages import BaseMessage
+from camel.models.model_factory import ModelFactory
+from camel.types import ModelPlatformType, ModelType
+from camel.types.enums import OpenAIBackendRole
+from camel.utils import OpenAITokenCounter
+
+
+def test_chat_agent_memory():
+    context_creator = ScoreBasedContextCreator(
+        token_counter=OpenAITokenCounter(ModelType.GPT_4O_MINI),
+        token_limit=1024,
+    )
+
+    model = ModelFactory.create(
+        model_platform=ModelPlatformType.OPENAI,
+        model_type=ModelType.GPT_4O_MINI,
+    )
+
+    # 1. Instantiate a ChatAgent with system_message & agent_id
+    #    Using ChatHistoryMemory as an example memory store.
+    agent = ChatAgent(
+        system_message="You are a helpful assistant",
+        agent_id="001",
+        model=model,
+    )
+
+    # 2. Perform steps so that some MemoryRecords accumulate
+    #    - We'll just send a message and read the response to populate memory
+    user_input_1 = (
+        "Hello, can you remember these instructions?: Banana is a country."
+    )
+    response_1 = agent.step(user_input_1)
+    print(
+        "Assistant response 1:",
+        response_1.msgs[0].content if response_1.msgs else "No response",
+    )
+
+    user_input_2 = (
+        "Please store and recall this next time: CAMEL lives in Banana."
+    )
+    response_2 = agent.step(user_input_2)
+    print(
+        "Assistant response 2:",
+        response_2.msgs[0].content if response_2.msgs else "No response",
+    )
+
+    # 3. Save the agent's memory to a JSON file
+    save_path = Path("./chat_agent_memory.json")
+    agent.save_memory(save_path)
+    print(f"Agent memory saved to {save_path}.")
+
+    # 4. Create a separate agent that loads memory from the file
+    #    We'll pass no explicit memory, as the loaded data will be used
+    new_agent = ChatAgent(
+        system_message="You are a helpful assistant",
+        agent_id="001",
+        model=model,
+    )
+
+    # Load the memory from our file
+    new_agent.load_memory_from_path(save_path)
+
+    # Test that the memory is loaded by requesting a response
+    user_input_3 = "What were we talking about?"
+    response_3 = new_agent.step(user_input_3)
+    print(
+        "New Agent response (after loading memory):",
+        response_3.msgs[0].content if response_3.msgs else "No response",
+    )
+
+    # 5. Demonstrate loading memory from an existing AgentMemory
+    #    Suppose we had another agent with some different or combined memory:
+    another_agent = ChatAgent(
+        system_message="Another system message",
+        agent_id="002",
+        memory=ChatHistoryMemory(
+            agent_id="002", context_creator=context_creator
+        ),
+    )
+
+    # Add some record to the second agent's memory
+    message = BaseMessage.make_user_message(
+        role_name="User", content="This is memory from a second agent"
+    )
+    another_agent.update_memory(
+        message, role=OpenAIBackendRole.USER
+    )  # role passed for demonstration
+
+    # Extract the memory object from the second agent
+    second_agent_memory = another_agent.memory
+
+    # Now load that memory into new_agent. We override new_agent's memory.
+    new_agent.load_memory(second_agent_memory)
+
+    # Confirm it's loaded by printing some details:
+    print("After loading another agent's memory, new_agent has records:")
+    for record in new_agent.memory.retrieve():
+        print(record.memory_record.message.content)
+
+    # Clean up: remove the saved file if desired
+    if os.path.exists(save_path):
+        os.remove(save_path)
+
+
+if __name__ == "__main__":
+    test_chat_agent_memory()
diff --git a/examples/memories/agent_memory_vector_db_test.py b/examples/memories/agent_memory_vector_db_test.py
new file mode 100644
index 0000000000..e997af0fdc
--- /dev/null
+++ b/examples/memories/agent_memory_vector_db_test.py
@@ -0,0 +1,119 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+import os
+from pathlib import Path
+
+from camel.agents import ChatAgent
+from camel.memories import VectorDBMemory
+from camel.memories.context_creators.score_based import (
+    ScoreBasedContextCreator,
+)
+from camel.models.model_factory import ModelFactory
+from camel.storages.vectordb_storages import QdrantStorage
+from camel.types import ModelPlatformType, ModelType
+from camel.utils import OpenAITokenCounter
+
+
+def test_chat_agent_vectordb_memory_save_and_load():
+    # 1) Create a QdrantStorage in-memory instance
+    #    (in production, set path to a real directory or remote)
+    vector_storage = QdrantStorage(
+        vector_dim=1536,
+        path=":memory:",
+    )
+
+    # 2) Create a ScoreBasedContextCreator for token limiting
+    context_creator = ScoreBasedContextCreator(
+        token_counter=OpenAITokenCounter(ModelType.GPT_4O_MINI),
+        token_limit=1024,
+    )
+
+    # 3) Build a model
+    model = ModelFactory.create(
+        model_platform=ModelPlatformType.OPENAI,
+        model_type=ModelType.GPT_4O_MINI,
+    )
+
+    # 4) Create first ChatAgent with VectorDBMemory
+    agent1 = ChatAgent(
+        system_message="You are assistant #1 with vector DB memory.",
+        agent_id="agent_001",
+        model=model,
+    )
+    agent1_memory = VectorDBMemory(
+        context_creator=context_creator,
+        storage=vector_storage,
+        retrieve_limit=2,
+        agent_id=agent1.agent_id,  # ensure consistent agent_id
+    )
+    agent1.memory = agent1_memory
+
+    # 5) Agent #1 accumulates some conversation
+    user_input_1 = "Remember that dolphins use echolocation."
+    response_1 = agent1.step(user_input_1)
+    print(
+        "Agent #1 response:",
+        response_1.msgs[0].content if response_1.msgs else "No response",
+    )
+
+    user_input_2 = "And whales are the largest mammals."
+    response_2 = agent1.step(user_input_2)
+    print(
+        "Agent #1 response:",
+        response_2.msgs[0].content if response_2.msgs else "No response",
+    )
+
+    # 6) SAVE agent1's memory to a JSON file (storing the conversation)
+    save_path = Path("./agent1_vectordb_memory.json")
+    agent1.save_memory(save_path)
+    print(f"Agent #1 memory saved to {save_path}.")
+
+    # 7) Create a new agent, load that JSON memory to confirm retrieval
+    new_agent1 = ChatAgent(
+        system_message="""You are the resurrected assistant #1 with 
+        vector DB memory.""",
+        agent_id="agent_001",  # same agent_id to match the saved records
+        model=model,
+    )
+    # Use a new VectorDBMemory pointing to the same underlying storage
+    # (or a new vector store if you prefer, but that won't have the
+    # original embeddings)
+    new_agent1_memory = VectorDBMemory(
+        context_creator=context_creator,
+        storage=vector_storage,
+        retrieve_limit=2,
+        agent_id=new_agent1.agent_id,
+    )
+
+    new_agent1.memory = new_agent1_memory
+
+    # Load memory from JSON, which replays the stored MemoryRecords
+    new_agent1.load_memory_from_path(save_path)
+
+    # 8) Check if the new agent can recall previous info from loaded
+    # conversation
+    user_input_3 = "What do you remember about marine mammals?"
+    response_3 = new_agent1.step(user_input_3)
+    print(
+        "New Agent #1 response (after loading memory):",
+        response_3.msgs[0].content if response_3.msgs else "No response",
+    )
+
+    # Optionally, remove the JSON file
+    if os.path.exists(save_path):
+        os.remove(save_path)
+
+
+if __name__ == "__main__":
+    test_chat_agent_vectordb_memory_save_and_load()
diff --git a/examples/memories/vector_db_memory.py b/examples/memories/vector_db_memory.py
new file mode 100644
index 0000000000..f25acf25c8
--- /dev/null
+++ b/examples/memories/vector_db_memory.py
@@ -0,0 +1,113 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+from camel.agents import ChatAgent
+from camel.memories import VectorDBMemory
+from camel.memories.context_creators.score_based import (
+    ScoreBasedContextCreator,
+)
+from camel.models.model_factory import ModelFactory
+from camel.storages.vectordb_storages import QdrantStorage
+from camel.types import ModelPlatformType, ModelType
+from camel.utils import OpenAITokenCounter
+
+
+def test_chat_agent_vectordb_memory():
+    # Shared vector storage
+    vector_storage = QdrantStorage(
+        vector_dim=1536,
+        path=":memory:",
+    )
+
+    context_creator = ScoreBasedContextCreator(
+        token_counter=OpenAITokenCounter(ModelType.GPT_3_5_TURBO),
+        token_limit=2048,
+    )
+
+    # Memory for agent 1
+    vectordb_memory_agent1 = VectorDBMemory(
+        context_creator=context_creator,
+        storage=vector_storage,
+        retrieve_limit=2,
+        agent_id="vector_agent_007",
+    )
+
+    # Memory for agent 2
+    vectordb_memory_agent2 = VectorDBMemory(
+        context_creator=context_creator,
+        storage=vector_storage,
+        retrieve_limit=2,
+        agent_id="vector_agent_008",
+    )
+
+    model = ModelFactory.create(
+        model_platform=ModelPlatformType.OPENAI,
+        model_type=ModelType.GPT_3_5_TURBO,
+    )
+
+    # Agent 1
+    agent1 = ChatAgent(
+        system_message="You are Agent 007.",
+        model=model,
+        memory=vectordb_memory_agent1,
+        agent_id="vector_agent_007",
+    )
+
+    # Agent 2
+    agent2 = ChatAgent(
+        system_message="You are Agent 008.",
+        model=model,
+        memory=vectordb_memory_agent2,
+        agent_id="vector_agent_008",
+    )
+
+    # Populate agent 1 memory
+    agent1.step("Elephants are the best swimmers on Earth.")
+    agent1.step("Whales have eyelashes.")
+
+    # Populate agent 2 with different memory
+    agent2.step("The sun is a star.")
+    agent2.step("The moon orbits the Earth.")
+
+    # Query both agents
+    response_1 = agent1.step("What did I tell you about whales or elephants?")
+    response_2 = agent2.step("What have I told you about stars and moons?")
+
+    print(
+        "Agent 1 response:",
+        response_1.msgs[0].content if response_1.msgs else "No response",
+    )
+    print(
+        "Agent 2 response:",
+        response_2.msgs[0].content if response_2.msgs else "No response",
+    )
+
+    # Retrieve and print agent-specific records
+    print("\nAgent 1's memory records:")
+    for ctx_record in vectordb_memory_agent1.retrieve():
+        print(
+            f"""Score: {ctx_record.score:.2f} | 
+            Content: {ctx_record.memory_record.message.content}"""
+        )
+
+    print("\nAgent 2's memory records:")
+    retrieved_context_agent2 = vectordb_memory_agent2.retrieve()
+    for ctx_record in retrieved_context_agent2:
+        print(
+            f"""Score: {ctx_record.score:.2f} |
+            Content: {ctx_record.memory_record.message.content}"""
+        )
+
+
+if __name__ == "__main__":
+    test_chat_agent_vectordb_memory()
diff --git a/examples/toolkits/memory_toolkit.py b/examples/toolkits/memory_toolkit.py
new file mode 100644
index 0000000000..1560e13119
--- /dev/null
+++ b/examples/toolkits/memory_toolkit.py
@@ -0,0 +1,90 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+
+from camel.agents import ChatAgent
+from camel.configs import ChatGPTConfig
+from camel.models import ModelFactory
+from camel.toolkits.memory_toolkit import MemoryToolkit
+from camel.types import ModelPlatformType, ModelType
+
+
+def run_memory_toolkit_example():
+    """
+    Demonstrates a ChatAgent using the MemoryToolkit for
+    function calling to manage memory.
+    """
+
+    # Create a Model
+    model_config_dict = ChatGPTConfig(temperature=0.0).as_dict()
+    model = ModelFactory.create(
+        model_platform=ModelPlatformType.DEFAULT,
+        model_type=ModelType.DEFAULT,
+        model_config_dict=model_config_dict,
+    )
+
+    # Create a ChatAgent
+    agent = ChatAgent(
+        system_message="""You are an assistant that can manage 
+        conversation memory using tools.""",
+        model=model,
+    )
+
+    # Add MemoryToolkit to the Agent
+    memory_toolkit = MemoryToolkit(agent=agent)
+    for tool in memory_toolkit.get_tools():
+        agent.add_tool(tool)
+
+    # Have a conversation to populate memory
+    print("\n--- Starting a Conversation ---")
+    user_msg_1 = "Tell me about the moon."
+    print(f"[User] {user_msg_1}")
+    response_1 = agent.step(user_msg_1)
+    print(f"[Agent] {response_1.msgs[0].content}")
+
+    # Save the memory to a file via function calling
+    print("\n--- Saving Memory ---")
+    save_msg = "Please save the current memory to 'conversation_memory.json'."
+    response_save = agent.step(save_msg)
+    print(f"[Agent] {response_save.msgs[0].content}")
+    print(f"[Tool Call Info] {response_save.info['tool_calls']}")
+
+    # Clear the memory via function calling
+    print("\n--- Clearing Memory ---")
+    clear_msg = "Please clear the memory."
+    response_clear = agent.step(clear_msg)
+    print(f"[Agent] {response_clear.msgs[0].content}")
+    print(f"[Tool Call Info] {response_clear.info['tool_calls']}")
+
+    # Verify memory is cleared
+    print("\n--- Checking Memory After Clear ---")
+    check_msg = "What do you remember about the moon?"
+    response_check = agent.step(check_msg)
+    print(f"[Agent] {response_check.msgs[0].content}")
+
+    # Load memory from the saved file via function calling
+    print("\n--- Loading Memory from File ---")
+    load_msg = "Please load the memory from 'conversation_memory.json'."
+    response_load = agent.step(load_msg)
+    print(f"[Agent] {response_load.msgs[0].content}")
+    print(f"[Tool Call Info] {response_load.info['tool_calls']}")
+
+    # Verify memory is restored
+    print("\n--- Checking Memory After Load ---")
+    check_msg = "What do you remember about the moon?"
+    response_restored = agent.step(check_msg)
+    print(f"[Agent] {response_restored.msgs[0].content}")
+
+
+if __name__ == "__main__":
+    run_memory_toolkit_example()
diff --git a/test/agents/test_chat_agent.py b/test/agents/test_chat_agent.py
index 870a2ba142..3e4923138c 100644
--- a/test/agents/test_chat_agent.py
+++ b/test/agents/test_chat_agent.py
@@ -398,6 +398,7 @@ def test_chat_agent_messages_window():
             for _ in range(5)
         ]
     )
+
     openai_messages, _ = assistant.memory.get_context()
     assert len(openai_messages) == 2
 
diff --git a/test/memories/test_agent_memories.py b/test/memories/test_agent_memories.py
index 4acd18bb8b..161ddc2711 100644
--- a/test/memories/test_agent_memories.py
+++ b/test/memories/test_agent_memories.py
@@ -11,6 +11,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+from datetime import datetime
 from unittest.mock import MagicMock
 
 import pytest
@@ -95,6 +96,8 @@ def test_write_records(
                     "test message {}".format(i),
                 ),
                 role_at_backend=OpenAIBackendRole.USER,
+                timestamp=datetime.now().timestamp(),
+                agent_id=f"test_agent_{i}",
             )
             for i in range(5)
         ]
diff --git a/test/memories/test_blocks.py b/test/memories/test_blocks.py
index 45bbcf12d5..92d5ad2d6f 100644
--- a/test/memories/test_blocks.py
+++ b/test/memories/test_blocks.py
@@ -46,7 +46,8 @@ def generate_mock_records(num_records: int):
             },
             "role_at_backend": "user",
             "extra_info": {},
-            "timestamp": datetime.now().timestamp() + i,
+            "timestamp": datetime.now().timestamp(),
+            "agent_id": f"test_agent_{i}",
         }
         for i in range(num_records)
     ]
@@ -101,6 +102,8 @@ def test_write_records(self, mock_storage):
                     "test message {}".format(i),
                 ),
                 role_at_backend=OpenAIBackendRole.USER,
+                timestamp=datetime.now().timestamp(),
+                agent_id=f"test_agent_{i}",
             )
             for i in range(5)
         ]
@@ -176,6 +179,8 @@ def test_write_records(self, mock_storage, mock_embedding):
                     "test message {}".format(i),
                 ),
                 role_at_backend=OpenAIBackendRole.USER,
+                timestamp=datetime.now().timestamp(),
+                agent_id=f"test_agent_{i}",
             )
             for i in range(5)
         ]
diff --git a/test/memories/test_chat_history_memory.py b/test/memories/test_chat_history_memory.py
index 1e3c98295c..22daecfe84 100644
--- a/test/memories/test_chat_history_memory.py
+++ b/test/memories/test_chat_history_memory.py
@@ -13,6 +13,7 @@
 # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
 
 import tempfile
+from datetime import datetime
 from pathlib import Path
 
 import pytest
@@ -68,13 +69,22 @@ def test_chat_history_memory(memory: ChatHistoryMemory):
         content="OK",
     )
     system_record = MemoryRecord(
-        message=system_msg, role_at_backend=OpenAIBackendRole.SYSTEM
+        message=system_msg,
+        role_at_backend=OpenAIBackendRole.SYSTEM,
+        timestamp=datetime.now().timestamp(),
+        agent_id="system_agent_1",
     )
     user_record = MemoryRecord(
-        message=user_msg, role_at_backend=OpenAIBackendRole.USER
+        message=user_msg,
+        role_at_backend=OpenAIBackendRole.USER,
+        timestamp=datetime.now().timestamp(),
+        agent_id="user_agent_1",
     )
     assistant_record = MemoryRecord(
-        message=assistant_msg, role_at_backend=OpenAIBackendRole.ASSISTANT
+        message=assistant_msg,
+        role_at_backend=OpenAIBackendRole.ASSISTANT,
+        timestamp=datetime.now().timestamp(),
+        agent_id="assistant_agent_1",
     )
     memory.write_records([system_record, user_record, assistant_record])
     output_messages, _ = memory.get_context()
diff --git a/test/memories/test_vector_db_memory.py b/test/memories/test_vector_db_memory.py
index 24c59484c6..69cc6c60aa 100644
--- a/test/memories/test_vector_db_memory.py
+++ b/test/memories/test_vector_db_memory.py
@@ -12,6 +12,7 @@
 # limitations under the License.
 # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
 import random
+from datetime import datetime
 from typing import Dict, List, Tuple
 
 import pytest
@@ -44,6 +45,8 @@ def memory_and_message(request):
                     content="You are a helpful assistant",
                 ),
                 role_at_backend=OpenAIBackendRole.SYSTEM,
+                timestamp=datetime.now().timestamp(),
+                agent_id="system_agent_1",
             )
         ]
     )
@@ -53,7 +56,7 @@ def memory_and_message(request):
     random.shuffle(messages)
 
     # Process each conversation
-    for conv in messages:
+    for i, conv in enumerate(messages):
         records = [
             MemoryRecord(
                 message=BaseMessage(
@@ -63,6 +66,8 @@ def memory_and_message(request):
                     content=conv["user"],
                 ),
                 role_at_backend=OpenAIBackendRole.USER,
+                timestamp=datetime.now().timestamp(),
+                agent_id=f"user_agent_{i}",
             ),
             MemoryRecord(
                 message=BaseMessage(
@@ -72,6 +77,8 @@ def memory_and_message(request):
                     content=conv["assistant"],
                 ),
                 role_at_backend=OpenAIBackendRole.ASSISTANT,
+                timestamp=datetime.now().timestamp(),
+                agent_id=f"assistant_agent_{i}",
             ),
         ]
         memory.write_records(records)
@@ -144,7 +151,7 @@ def test_vector_db_memory(
 ):
     memory, messages, questions = memory_and_message
 
-    for question in questions:
+    for i, question in enumerate(questions):
         # Add user question to memory
         user_msg = BaseMessage(
             "AI user", RoleType.USER, None, question["user"]
@@ -152,15 +159,21 @@ def test_vector_db_memory(
         memory.write_records(
             [
                 MemoryRecord(
-                    message=user_msg, role_at_backend=OpenAIBackendRole.USER
+                    message=user_msg,
+                    role_at_backend=OpenAIBackendRole.USER,
+                    timestamp=datetime.now().timestamp(),
+                    agent_id=f"user_agent_{i}",
                 )
             ]
         )
         output_messages, _ = memory.get_context()
 
+        print(output_messages)
+
         # Check that context messages are from correct category
         for msg in output_messages[:-1]:
-            for conv in messages:
+            for i, conv in enumerate(messages):
+                print(i, conv)
                 if (
                     conv["assistant"] == msg["content"]
                     or conv["user"] == msg["content"]
diff --git a/test/toolkits/test_memory_toolkit.py b/test/toolkits/test_memory_toolkit.py
new file mode 100644
index 0000000000..cf6412eb69
--- /dev/null
+++ b/test/toolkits/test_memory_toolkit.py
@@ -0,0 +1,116 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+import json
+from enum import Enum
+from unittest.mock import MagicMock
+
+import pytest
+
+from camel.memories import MemoryRecord
+from camel.messages.base import BaseMessage
+from camel.storages.key_value_storages import CamelJSONEncoder
+from camel.toolkits.memory_toolkit import MemoryToolkit
+from camel.types import RoleType
+from camel.types.enums import OpenAIBackendRole
+
+
+class TestCamelJSONEncoder(CamelJSONEncoder):
+    def default(self, obj):
+        if isinstance(obj, Enum):
+            return obj.value  # Serialize enum to its string value
+        return super().default(obj)
+
+
+@pytest.fixture(scope="function")
+def memory_toolkit_fixture():
+    mock_agent = MagicMock()
+    mock_agent.model_backend.token_counter = MagicMock()
+    mock_agent.model_backend.token_limit = 1000
+    toolkit = MemoryToolkit(agent=mock_agent)
+    return toolkit
+
+
+def test_save_memory(memory_toolkit_fixture):
+    toolkit = memory_toolkit_fixture
+    path = "test_memory.json"
+
+    toolkit.agent.save_memory.return_value = f"Memory saved to {path}"
+    result = toolkit.save(path)
+
+    assert "Memory saved to" in result
+
+
+def test_load_memory_valid_json(memory_toolkit_fixture):
+    toolkit = memory_toolkit_fixture
+    mock_message = BaseMessage(
+        role_name="User",
+        role_type=RoleType.USER,
+        meta_dict={},
+        content="test message",
+    )
+    mock_record = MemoryRecord(
+        message=mock_message,
+        role_at_backend=OpenAIBackendRole.USER,
+        agent_id="test_agent",
+    )
+    memory_json = json.dumps([mock_record.to_dict()], cls=TestCamelJSONEncoder)
+
+    result = toolkit.load(memory_json)
+
+    assert "Loaded memory from provided JSON string." in result
+
+
+def test_load_memory_invalid_json(memory_toolkit_fixture):
+    toolkit = memory_toolkit_fixture
+    invalid_json = "{invalid json}"
+
+    result = toolkit.load(invalid_json)
+
+    assert "[ERROR] Invalid JSON string provided." in result
+
+
+def test_load_memory_wrong_format(memory_toolkit_fixture):
+    toolkit = memory_toolkit_fixture
+    incorrect_json = json.dumps({"not": "a list"})
+
+    result = toolkit.load(incorrect_json)
+
+    assert "[ERROR] Memory data should be a list of records." in result
+
+
+def test_load_from_path(memory_toolkit_fixture):
+    toolkit = memory_toolkit_fixture
+    path = "memory.json"
+
+    toolkit.agent.load_memory_from_path.return_value = (
+        f"Memory loaded from {path}"
+    )
+    result = toolkit.load_from_path(path)
+
+    assert "Memory loaded from" in result
+
+
+def test_clear_memory(memory_toolkit_fixture):
+    toolkit = memory_toolkit_fixture
+
+    toolkit.agent.clear_memory.return_value = "Memory has been cleared."
+    result = toolkit.clear_memory()
+
+    assert "Memory has been cleared." in result
+
+
+if __name__ == "__main__":
+    import sys
+
+    pytest.main([sys.argv[0]])


Code review comments:
diff_hunk: @@ -321,9 +337,77 @@ def update_memory(
             role (OpenAIBackendRole): The backend role type.
         """
         self.memory.write_record(
-            MemoryRecord(message=message, role_at_backend=role)
+            MemoryRecord(
+                message=message,
+                role_at_backend=role,
+                timestamp=datetime.now().timestamp(),
+                agent_id=self.agent_id,
+            )
         )
 
+    def load_memory(self, memory: AgentMemory) -> None:
+        r"""Load the provided memory into the agent.
+
+        Args:
+            memory (AgentMemory): The memory to load into the agent.
+
+        Returns:
+            None
+        """
+
+        for context_record in memory.retrieve():
+            self.memory.write_record(context_record.memory_record)
+        print(f"Memory loaded from {memory}")
+
+    def load_memory_from_path(self, path: str) -> None:
+        r"""
+        Loads memory records from a JSON file filtered by this agent's ID.
+
+        Args:
+            path (str): The file path to a JSON memory file that uses
+                JsonStorage.
+
+        Raises:
+            ValueError: If no matching records for the agent_id are found
+                (optional check; commented out below).
+        """
+        json_store = JsonStorage(Path(path))
+        all_records = json_store.load()
+
+        if not all_records:
+            raise ValueError(
+                f"No records found for agent_id={self.agent_id} in {path}"
+            )
+
+        for record_dict in all_records:
+            record = MemoryRecord.from_dict(record_dict)
path: camel/agents/chat_agent.py
text: @user1:
should we add check for the dict?

diff_hunk: @@ -321,9 +337,77 @@ def update_memory(
             role (OpenAIBackendRole): The backend role type.
         """
         self.memory.write_record(
-            MemoryRecord(message=message, role_at_backend=role)
+            MemoryRecord(
+                message=message,
+                role_at_backend=role,
+                timestamp=datetime.now().timestamp(),
+                agent_id=self.agent_id,
+            )
         )
 
+    def load_memory(self, memory: AgentMemory) -> None:
+        r"""Load the provided memory into the agent.
+
+        Args:
+            memory (AgentMemory): The memory to load into the agent.
+
+        Returns:
+            None
+        """
+
+        for context_record in memory.retrieve():
+            self.memory.write_record(context_record.memory_record)
+        print(f"Memory loaded from {memory}")
path: camel/agents/chat_agent.py
text: @user1:
never use print in core code, use logger

diff_hunk: @@ -109,6 +139,13 @@ def write_records(self, records: List[MemoryRecord]) -> None:
         for record in records:
             if record.role_at_backend == OpenAIBackendRole.USER:
                 self._current_topic = record.message.content
+
+            # assign the agent_id to the record
+            if record.agent_id == "":
+                # if the agent memory has an agent_id, use it
+                if self.agent_id is not None:
path: camel/memories/agent_memories.py
text: @user1:
if record.agent_id == "" and self.agent_id is not None:

diff_hunk: @@ -0,0 +1,136 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# memory_toolkit.py
+import json
+from typing import TYPE_CHECKING, Optional
+
+from camel.memories import (
+    ChatHistoryMemory,
+    MemoryRecord,
+    ScoreBasedContextCreator,
+)
+from camel.toolkits.base import BaseToolkit
+from camel.toolkits.function_tool import FunctionTool
+
+if TYPE_CHECKING:
+    from camel.agents import ChatAgent
+
+
+class MemoryToolkit(BaseToolkit):
+    r"""
+    A toolkit that provides methods for saving, loading, and clearing a
path: camel/toolkits/memory_toolkit.py
text: @user1:
```suggestion
    r"""A toolkit that provides methods for saving, loading, and clearing a
```

diff_hunk: @@ -321,9 +337,77 @@ def update_memory(
             role (OpenAIBackendRole): The backend role type.
         """
         self.memory.write_record(
-            MemoryRecord(message=message, role_at_backend=role)
+            MemoryRecord(
+                message=message,
+                role_at_backend=role,
+                timestamp=datetime.now().timestamp(),
+                agent_id=self.agent_id,
+            )
         )
 
+    def load_memory(self, memory: AgentMemory) -> None:
+        r"""Load the provided memory into the agent.
+
+        Args:
+            memory (AgentMemory): The memory to load into the agent.
+
+        Returns:
+            None
+        """
+
+        for context_record in memory.retrieve():
+            self.memory.write_record(context_record.memory_record)
+        print(f"Memory loaded from {memory}")
+
+    def load_memory_from_path(self, path: str) -> None:
+        r"""
+        Loads memory records from a JSON file filtered by this agent's ID.
+
+        Args:
+            path (str): The file path to a JSON memory file that uses
+                JsonStorage.
+
+        Raises:
+            ValueError: If no matching records for the agent_id are found
+                (optional check; commented out below).
+        """
+        json_store = JsonStorage(Path(path))
+        all_records = json_store.load()
+
+        if not all_records:
+            raise ValueError(
+                f"No records found for agent_id={self.agent_id} in {path}"
+            )
+
+        for record_dict in all_records:
+            record = MemoryRecord.from_dict(record_dict)
+            self.memory.write_records([record])
+        print(f"Memory loaded from {path}")
+
+    def save_memory(self, path: str) -> None:
+        r"""
+        Retrieves the current conversation data from memory and writes it
+        into a JSON file using JsonStorage.
+
+        Args:
+            path (str): Target file path to store JSON data.
+        """
+        json_store = JsonStorage(Path(path))
+        context_records = self.memory.retrieve()
+        to_save = [cr.memory_record.to_dict() for cr in context_records]
+        json_store.save(to_save)
+        print(f"Memory saved to {path}")
path: camel/agents/chat_agent.py
text: @user1:
use logger

diff_hunk: @@ -0,0 +1,136 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# memory_toolkit.py
+import json
+from typing import TYPE_CHECKING, Optional
+
+from camel.memories import (
+    ChatHistoryMemory,
+    MemoryRecord,
+    ScoreBasedContextCreator,
+)
+from camel.toolkits.base import BaseToolkit
+from camel.toolkits.function_tool import FunctionTool
+
+if TYPE_CHECKING:
+    from camel.agents import ChatAgent
+
+
+class MemoryToolkit(BaseToolkit):
+    r"""
+    A toolkit that provides methods for saving, loading, and clearing a
+    ChatAgent's memory.
+    These methods are exposed as FunctionTool objects for
+    function calling. Internally, it calls:
+      - agent.save_memory(path)
+      - agent.load_memory(new_memory_obj)
+      - agent.load_memory_from_path(path)
+      - agent.clear_memory()
+    """
+
+    def __init__(self, agent: 'ChatAgent', timeout: Optional[float] = None):
+        super().__init__(timeout=timeout)
+        self.agent = agent
+
+    def save(self, path: str) -> str:
+        r"""
+        Saves the agent's current memory to a JSON file by calling
+        agent.save_memory(path).
+
+        Args:
+            path (str): The file path to save the memory to.
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.save_memory(path)
+        return f"Memory saved to {path}"
+
+    def load(self, memory_json: str) -> str:
+        r"""
+        Loads memory into the agent from a JSON string:
path: camel/toolkits/memory_toolkit.py
text: @user1:
```suggestion
        r"""Loads memory into the agent from a JSON string:
```

diff_hunk: @@ -0,0 +1,136 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# memory_toolkit.py
+import json
+from typing import TYPE_CHECKING, Optional
+
+from camel.memories import (
+    ChatHistoryMemory,
+    MemoryRecord,
+    ScoreBasedContextCreator,
+)
+from camel.toolkits.base import BaseToolkit
+from camel.toolkits.function_tool import FunctionTool
+
+if TYPE_CHECKING:
+    from camel.agents import ChatAgent
+
+
+class MemoryToolkit(BaseToolkit):
+    r"""
+    A toolkit that provides methods for saving, loading, and clearing a
+    ChatAgent's memory.
+    These methods are exposed as FunctionTool objects for
+    function calling. Internally, it calls:
+      - agent.save_memory(path)
+      - agent.load_memory(new_memory_obj)
+      - agent.load_memory_from_path(path)
+      - agent.clear_memory()
+    """
+
+    def __init__(self, agent: 'ChatAgent', timeout: Optional[float] = None):
+        super().__init__(timeout=timeout)
+        self.agent = agent
+
+    def save(self, path: str) -> str:
+        r"""
+        Saves the agent's current memory to a JSON file by calling
+        agent.save_memory(path).
+
+        Args:
+            path (str): The file path to save the memory to.
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.save_memory(path)
+        return f"Memory saved to {path}"
+
+    def load(self, memory_json: str) -> str:
+        r"""
+        Loads memory into the agent from a JSON string:
+          1) Parse `memory_json` as a list of memory records
+          2) Create a fresh ChatHistoryMemory
+          3) Write records into that memory
+          4) Call agent.load_memory(...) with that new memory
path: camel/toolkits/memory_toolkit.py
text: @user1:
remove

diff_hunk: @@ -0,0 +1,136 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# memory_toolkit.py
+import json
+from typing import TYPE_CHECKING, Optional
+
+from camel.memories import (
+    ChatHistoryMemory,
+    MemoryRecord,
+    ScoreBasedContextCreator,
+)
+from camel.toolkits.base import BaseToolkit
+from camel.toolkits.function_tool import FunctionTool
+
+if TYPE_CHECKING:
+    from camel.agents import ChatAgent
+
+
+class MemoryToolkit(BaseToolkit):
+    r"""
+    A toolkit that provides methods for saving, loading, and clearing a
+    ChatAgent's memory.
+    These methods are exposed as FunctionTool objects for
+    function calling. Internally, it calls:
+      - agent.save_memory(path)
+      - agent.load_memory(new_memory_obj)
+      - agent.load_memory_from_path(path)
+      - agent.clear_memory()
+    """
+
+    def __init__(self, agent: 'ChatAgent', timeout: Optional[float] = None):
+        super().__init__(timeout=timeout)
+        self.agent = agent
+
+    def save(self, path: str) -> str:
+        r"""
+        Saves the agent's current memory to a JSON file by calling
+        agent.save_memory(path).
+
+        Args:
+            path (str): The file path to save the memory to.
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.save_memory(path)
+        return f"Memory saved to {path}"
+
+    def load(self, memory_json: str) -> str:
+        r"""
+        Loads memory into the agent from a JSON string:
+          1) Parse `memory_json` as a list of memory records
+          2) Create a fresh ChatHistoryMemory
+          3) Write records into that memory
+          4) Call agent.load_memory(...) with that new memory
+
+        Args:
+            memory_json (str): A JSON string containing memory records.
+
+        Returns:
+            str: Confirmation or error message.
+        """
+        try:
+            data = json.loads(memory_json.strip())
+            if not isinstance(data, list):
+                return "[ERROR] Memory data should be a list of records."
+
+            # Build a fresh ChatHistoryMemory
+            context_creator = ScoreBasedContextCreator(
+                token_counter=self.agent.model_backend.token_counter,
+                token_limit=self.agent.model_backend.token_limit,
+            )
+            new_memory = ChatHistoryMemory(context_creator)
+
+            # Convert each record dict -> MemoryRecord
+            for record_dict in data:
+                record = MemoryRecord.from_dict(record_dict)
+                new_memory.write_record(record)
+
+            # Load into the agent
+            self.agent.load_memory(new_memory)
+            return "Loaded memory from provided JSON string."
+        except json.JSONDecodeError:
+            return "[ERROR] Invalid JSON string provided."
+        except Exception as e:
+            return f"[ERROR] Failed to load memory: {e!s}"
+
+    def load_from_path(self, path: str) -> str:
+        r"""
+        Loads the agent's memory from a JSON file by calling
+        agent.load_memory_from_path(path).
+
+        Args:
+            path (str): The file path to load the memory from.
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.load_memory_from_path(path)
+        return f"Memory loaded from {path}"
+
+    def clear_memory(self) -> str:
+        r"""
+        Clears the agent's memory by calling agent.clear_memory().
path: camel/toolkits/memory_toolkit.py
text: @user1:
```suggestion
        r"""Clears the agent's memory.
```

diff_hunk: @@ -0,0 +1,136 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# memory_toolkit.py
+import json
+from typing import TYPE_CHECKING, Optional
+
+from camel.memories import (
+    ChatHistoryMemory,
+    MemoryRecord,
+    ScoreBasedContextCreator,
+)
+from camel.toolkits.base import BaseToolkit
+from camel.toolkits.function_tool import FunctionTool
+
+if TYPE_CHECKING:
+    from camel.agents import ChatAgent
+
+
+class MemoryToolkit(BaseToolkit):
+    r"""
+    A toolkit that provides methods for saving, loading, and clearing a
+    ChatAgent's memory.
+    These methods are exposed as FunctionTool objects for
+    function calling. Internally, it calls:
+      - agent.save_memory(path)
+      - agent.load_memory(new_memory_obj)
+      - agent.load_memory_from_path(path)
+      - agent.clear_memory()
+    """
+
+    def __init__(self, agent: 'ChatAgent', timeout: Optional[float] = None):
+        super().__init__(timeout=timeout)
+        self.agent = agent
+
+    def save(self, path: str) -> str:
+        r"""
+        Saves the agent's current memory to a JSON file by calling
+        agent.save_memory(path).
path: camel/toolkits/memory_toolkit.py
text: @user1:
in doc string no need to add `by calling agent.save_memory(path).`, since the tool would called by agent, it would make llm confused and call wrong tool

diff_hunk: @@ -0,0 +1,136 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# memory_toolkit.py
+import json
+from typing import TYPE_CHECKING, Optional
+
+from camel.memories import (
+    ChatHistoryMemory,
+    MemoryRecord,
+    ScoreBasedContextCreator,
+)
+from camel.toolkits.base import BaseToolkit
+from camel.toolkits.function_tool import FunctionTool
+
+if TYPE_CHECKING:
+    from camel.agents import ChatAgent
+
+
+class MemoryToolkit(BaseToolkit):
+    r"""
+    A toolkit that provides methods for saving, loading, and clearing a
+    ChatAgent's memory.
+    These methods are exposed as FunctionTool objects for
+    function calling. Internally, it calls:
+      - agent.save_memory(path)
+      - agent.load_memory(new_memory_obj)
+      - agent.load_memory_from_path(path)
+      - agent.clear_memory()
+    """
+
+    def __init__(self, agent: 'ChatAgent', timeout: Optional[float] = None):
+        super().__init__(timeout=timeout)
+        self.agent = agent
+
+    def save(self, path: str) -> str:
+        r"""
+        Saves the agent's current memory to a JSON file by calling
+        agent.save_memory(path).
+
+        Args:
+            path (str): The file path to save the memory to.
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.save_memory(path)
+        return f"Memory saved to {path}"
+
+    def load(self, memory_json: str) -> str:
+        r"""
+        Loads memory into the agent from a JSON string:
+          1) Parse `memory_json` as a list of memory records
+          2) Create a fresh ChatHistoryMemory
+          3) Write records into that memory
+          4) Call agent.load_memory(...) with that new memory
+
+        Args:
+            memory_json (str): A JSON string containing memory records.
+
+        Returns:
+            str: Confirmation or error message.
+        """
+        try:
+            data = json.loads(memory_json.strip())
+            if not isinstance(data, list):
+                return "[ERROR] Memory data should be a list of records."
+
+            # Build a fresh ChatHistoryMemory
+            context_creator = ScoreBasedContextCreator(
+                token_counter=self.agent.model_backend.token_counter,
+                token_limit=self.agent.model_backend.token_limit,
+            )
+            new_memory = ChatHistoryMemory(context_creator)
+
+            # Convert each record dict -> MemoryRecord
+            for record_dict in data:
+                record = MemoryRecord.from_dict(record_dict)
+                new_memory.write_record(record)
+
+            # Load into the agent
+            self.agent.load_memory(new_memory)
+            return "Loaded memory from provided JSON string."
+        except json.JSONDecodeError:
+            return "[ERROR] Invalid JSON string provided."
+        except Exception as e:
+            return f"[ERROR] Failed to load memory: {e!s}"
+
+    def load_from_path(self, path: str) -> str:
+        r"""
+        Loads the agent's memory from a JSON file by calling
+        agent.load_memory_from_path(path).
+
+        Args:
+            path (str): The file path to load the memory from.
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.load_memory_from_path(path)
+        return f"Memory loaded from {path}"
+
+    def clear_memory(self) -> str:
+        r"""
+        Clears the agent's memory by calling agent.clear_memory().
+
+        Returns:
+            str: Confirmation message.
+        """
+        self.agent.clear_memory()
+        return "Memory has been cleared."
+
+    def get_tools(self) -> list[FunctionTool]:
+        r"""
+        Expose the memory management methods as function tools
path: camel/toolkits/memory_toolkit.py
text: @user1:
```suggestion
        r"""Expose the memory management methods as function tools
```

diff_hunk: @@ -0,0 +1,127 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+import os
+from pathlib import Path
+
+from camel.agents import ChatAgent
+from camel.memories import ChatHistoryMemory
+from camel.memories.context_creators.score_based import (
+    ScoreBasedContextCreator,
+)
+from camel.messages import BaseMessage
+from camel.models.model_factory import ModelFactory
+from camel.types import ModelPlatformType, ModelType
+from camel.types.enums import OpenAIBackendRole
+from camel.utils import OpenAITokenCounter
+
+
+def test_chat_agent_memory():
path: examples/memories/agent_memory_test.py
text: @user1:
it's example code, don't name the function and file name as `test`

diff_hunk: @@ -0,0 +1,113 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+from camel.agents import ChatAgent
+from camel.memories import VectorDBMemory
+from camel.memories.context_creators.score_based import (
+    ScoreBasedContextCreator,
+)
+from camel.models.model_factory import ModelFactory
+from camel.storages.vectordb_storages import QdrantStorage
+from camel.types import ModelPlatformType, ModelType
+from camel.utils import OpenAITokenCounter
+
+
+def test_chat_agent_vectordb_memory():
path: examples/memories/vector_db_memory.py
text: @user1:
it's example code, don't name the function and file name as test

diff_hunk: @@ -63,6 +75,12 @@ def retrieve(self) -> List[ContextRecord]:
         return records
 
     def write_records(self, records: List[MemoryRecord]) -> None:
+        for record in records:
+            # assign the agent_id to the record
+            if record.agent_id == "":
+                # if the agent memory has an agent_id, use it
+                if self.agent_id is not None:
path: camel/memories/agent_memories.py
text: @user1:
if record.agent_id == "" and self.agent_id is not None:

diff_hunk: @@ -0,0 +1,119 @@
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
+import os
+from pathlib import Path
+
+from camel.agents import ChatAgent
+from camel.memories import VectorDBMemory
+from camel.memories.context_creators.score_based import (
+    ScoreBasedContextCreator,
+)
+from camel.models.model_factory import ModelFactory
+from camel.storages.vectordb_storages import QdrantStorage
+from camel.types import ModelPlatformType, ModelType
+from camel.utils import OpenAITokenCounter
+
+
+def test_chat_agent_vectordb_memory_save_and_load():
path: examples/memories/agent_memory_vector_db_test.py
text: @user1:
it's example code, don't name the function and file name as test