{
"cells": [
{
"cell_type": "markdown",
"id": "cac06555-9ce8-4f01-bbef-3f8407f4b54d",
"metadata": {},
"source": [
"# Customizing Memory Management \n",
"\n",
"> Make sure you run the Letta server before running this example using `letta server`\n",
"\n",
"This tutorial goes over how to implement a custom memory class in Letta, which allows you to customize how memory is organized (via `Block` objects) and also how memory is maintained (through memory editing tools). \n"
]
},
{
"cell_type": "markdown",
"id": "aad3a8cc-d17a-4da1-b621-ecc93c9e2106",
"metadata": {},
"source": [
"## Section 0: Setup a MemGPT client "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "7ccd43f2-164b-4d25-8465-894a3bb54c4b",
"metadata": {},
"outputs": [],
"source": [
"from letta_client import CreateBlock, Letta, MessageCreate\n",
"\n",
"client = Letta(base_url=\"http://localhost:8283\")"
]
},
{
"cell_type": "markdown",
"id": "65bf0dc2-d1ac-4d4c-8674-f3156eeb611d",
"metadata": {},
"source": [
"## Section 1: Memory Blocks \n",
"Core memory consists of multiple memory *blocks*. A block represents a section of the LLM's context window, reservered to store the block's value (with an associated character limit). Blocks are persisted in the DB, so can be re-used or also shared accross agents. "
]
},
{
"cell_type": "markdown",
"id": "ce43919c-bd54-4da7-9b19-2e5a3f6bb66a",
"metadata": {},
"source": [
"## Understanding `ChatMemory`"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "a0c20727-89b8-4820-88bc-a7daa79be1d6",
"metadata": {},
"outputs": [],
"source": [
"from letta_client import ChatMemory "
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "5a41d77a-dcf2-445a-bdb9-16012b752510",
"metadata": {},
"outputs": [],
"source": [
"human_memory_block = client.blocks.create(\n",
" label=\"human\",\n",
" value=\"Name: Bob\",\n",
")\n",
"persona_memory_block = client.blocks.create(\n",
" label=\"persona\",\n",
" value=\"You are a helpful assistant\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "4fbda842-0f66-4afb-b4d7-c65b9fe4c87e",
"metadata": {},
"source": [
"#### Memory blocks \n",
"A memory class consists of a list of `Block` objects (labeled with a block name), as well as function definitions to edit these blocks. These blocks each represent a section of the context window reserved for memory. "
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "f66c25e6-d119-49af-a972-723f4c0c4415",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[Block(value='You are a helpful assistant', limit=2000, template_name=None, template=False, label='persona', description=None, metadata_={}, user_id=None, id='block-92112694-b5ab-4210-9af6-ccb9acad3456'),\n",
" Block(value='Name: Bob', limit=2000, template_name=None, template=False, label='human', description=None, metadata_={}, user_id=None, id='block-776d96df-7c07-4db1-b76a-1a8f1879c358')]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"client.blocks.list()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "845b027e-13de-46c6-a075-601d32f45d39",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Block(value='Name: Bob', limit=2000, template_name=None, template=False, label='human', description=None, metadata_={}, user_id=None, id='block-776d96df-7c07-4db1-b76a-1a8f1879c358')"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"client.blocks.list(label=\"human\")"
]
},
{
"cell_type": "markdown",
"id": "676e11d0-fad6-4683-99fe-7ae4435b617e",
"metadata": {},
"source": [
"#### Memory editing functions \n",
"The `Memory` class also consists of functions for editing memory, which are provided as tools to the agent (so it can call them to edit memory). The `ChatMemory` class provides `core_memory_append` and `core_memory_append` functions. "
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "3472325b-46eb-46ae-8909-0d8d10168076",
"metadata": {},
"outputs": [],
"source": [
"import inspect\n",
"from letta.functions.function_sets.base import core_memory_append"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "4a79d810-6b48-445f-a2a1-5a5e55809581",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" def core_memory_append(self: \"Agent\", label: str, content: str) -> Optional[str]: # type: ignore\n",
" \"\"\"\n",
" Append to the contents of core memory.\n",
"\n",
" Args:\n",
" label (str): Section of the memory to be edited (persona or human).\n",
" content (str): Content to write to the memory. All unicode (including emojis) are supported.\n",
"\n",
" Returns:\n",
" Optional[str]: None is always returned as this function does not produce a response.\n",
" \"\"\"\n",
" current_value = str(self.memory.get_block(label).value)\n",
" new_value = current_value + \"\\n\" + str(content)\n",
" self.memory.update_block_value(label=label, value=new_value)\n",
" return None\n",
"\n"
]
}
],
"source": [
"print(inspect.getsource(core_memory_append))"
]
},
{
"cell_type": "markdown",
"id": "42f25de0-d4f9-4954-a581-ca8125e13968",
"metadata": {},
"source": [
"#### Context compilation \n",
"Each time the LLM is called (for each reasoning step of the agent), the memory is \"compiled\" into a context window representation. "
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "34da47e1-a988-4995-afc9-e01881d36a11",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'{% for block in memory.values() %}<{{ block.label }} characters=\"{{ block.value|length }}/{{ block.limit }}\">\\n{{ block.value }}\\n{{ block.label }}>{% if not loop.last %}\\n{% endif %}{% endfor %}'"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"chat_memory.get_prompt_template()"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "3c71e302-11e0-4252-a3a9-65a65421f5fe",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'