{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "3d6bf25d-6a5e-48de-810c-4eafdec8783a",
      "metadata": {
        "id": "3d6bf25d-6a5e-48de-810c-4eafdec8783a"
      },
      "source": [
        "# <center id=\"d1\"><h1> 🦜🔗 📝 `MEMORY` - запускаем чат-режим!</h1></center>"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ec30b379-e427-4557-bf9f-7262ee23828c",
      "metadata": {
        "id": "ec30b379-e427-4557-bf9f-7262ee23828c"
      },
      "source": [
        "### Оглавление ноутбука\n",
        "<img src='https://github.com/a-milenkin/LLM_practical_course/blob/main/images/memories_conv.png?raw=1' align=\"right\" width=\"528\" height=\"428\" >\n",
        "<br>\n",
        "\n",
        "<p><font size=\"3\" face=\"Arial\" font-size=\"large\"><ul type=\"square\">\n",
        "    \n",
        "<li><a href=\"#d1\">🚀 Введение </a></li>\n",
        "<li><a href=\"#d2\">📝 Memory - сохранение истории диалога с LLM</a></li><ul type=\"square\">\n",
        "<li><a href=\"#d2\">🧠 ConversationBufferMemory </a>\n",
        "<li><a href=\"#d3\">🧠 ConversationBufferWindowMemory </a>\n",
        "<li><a href=\"#d3\">🧠 ConversationTokenBufferMemory </a>\n",
        "<li><a href=\"#d4\">🧠 ConversationSummaryMemory</a></ul>\n",
        "    \n",
        "</ul></font></p>"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d6e7d6af-042a-493e-99a1-329b150168ca",
      "metadata": {
        "id": "d6e7d6af-042a-493e-99a1-329b150168ca"
      },
      "source": [
        "### 🧑‍🎓 В этом ноутбуке рассмотрим подробнее работу с `Memory`"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6d789781-fdec-470e-b0d4-fc3f72d10668",
      "metadata": {
        "id": "6d789781-fdec-470e-b0d4-fc3f72d10668"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Память позволяет LLM запоминать предыдущие взаимодействия с пользователем. По умолчанию LLM является `stateless`, что означает независимую обработку каждого входящего запроса от других взаимодействий. На самом деле, когда мы вводим запросы в интерфейсе ChatGPT, под капотом работает память. <br>\n",
        "В `LangChain` есть различные реализации памяти, посмотрим как они функционируют.<br>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5187a29a-8918-4c17-b8e3-b5bef1c57c3b",
      "metadata": {
        "tags": [],
        "id": "5187a29a-8918-4c17-b8e3-b5bef1c57c3b"
      },
      "outputs": [],
      "source": [
        "#!pip install langchain langchain-openai openai tiktoken -q"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6ce555cd-8367-4ac3-a1ba-99f7fcffdc0f",
      "metadata": {
        "tags": [],
        "id": "6ce555cd-8367-4ac3-a1ba-99f7fcffdc0f"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "from getpass import getpass"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "fa3df72e-4070-4912-927d-620070f48fda",
      "metadata": {
        "tags": [],
        "id": "fa3df72e-4070-4912-927d-620070f48fda"
      },
      "outputs": [],
      "source": [
        "# Для работы в колабе загрузите наш скрипт для использования ChatGPT на сервере курса!\n",
        "#!wget https://raw.githubusercontent.com/a-milenkin/LLM_practical_course/main/notebooks/utils.py"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4ac87aa2-1813-409c-bbaf-51a715b38dca",
      "metadata": {
        "jupyter": {
          "source_hidden": true
        },
        "tags": [],
        "id": "4ac87aa2-1813-409c-bbaf-51a715b38dca"
      },
      "outputs": [],
      "source": [
        "# # Если используете ключ от OpenAI, запустите эту ячейку\n",
        "# from langchain_openai import ChatOpenAI\n",
        "\n",
        "# # os.environ['OPENAI_API_KEY'] = \"Введите ваш OpenAI API ключ\"\n",
        "# os.environ['OPENAI_API_KEY'] = getpass(prompt='Введите ваш OpenAI API ключ')\n",
        "\n",
        "# # инициализируем языковую модель\n",
        "# llm = ChatOpenAI(temperature=0.0)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a32eddae-5c2f-4b38-8c77-39a93741d6ae",
      "metadata": {
        "tags": [],
        "id": "a32eddae-5c2f-4b38-8c77-39a93741d6ae",
        "outputId": "975b193f-7751-4458-d54e-684d98d7b115"
      },
      "outputs": [
        {
          "name": "stdin",
          "output_type": "stream",
          "text": [
            "Введите ваш OpenAI API ключ ········\n"
          ]
        }
      ],
      "source": [
        "# Если используете ключ из курса, запустите эту ячейку\n",
        "from utils import ChatOpenAI\n",
        "\n",
        "#course_api_key= \"Введите ваш OpenAI API ключ\"\n",
        "course_api_key = getpass(prompt='Введите ваш OpenAI API ключ')\n",
        "\n",
        "# инициализируем языковую модель\n",
        "llm = ChatOpenAI(temperature=0.0, course_api_key=course_api_key)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f9a154df-99a3-4fbf-b6b2-4da2bbd15b3c",
      "metadata": {
        "id": "f9a154df-99a3-4fbf-b6b2-4da2bbd15b3c"
      },
      "source": [
        " ###  <center id=\"d2\">  `ConversationBufferMemory` - сохраняет всю историю диалога"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "96435c91-15d7-40ec-8a4e-9bebd2613f34",
      "metadata": {
        "tags": [],
        "id": "96435c91-15d7-40ec-8a4e-9bebd2613f34"
      },
      "outputs": [],
      "source": [
        "from langchain.chains import ConversationChain, LLMChain\n",
        "from langchain.memory import ConversationBufferMemory"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ad2391ca-4f82-4dc8-9fdf-2206b9261eea",
      "metadata": {
        "tags": [],
        "id": "ad2391ca-4f82-4dc8-9fdf-2206b9261eea"
      },
      "outputs": [],
      "source": [
        "# создаём цепочку диалога, в параметры передаём языковую модель и память\n",
        "memory = ConversationBufferMemory()\n",
        "conversation_buf_mem = ConversationChain(\n",
        "    llm=llm,  # языковая модель\n",
        "    memory = memory, # модуль памяти, теперь мы запоминаем\n",
        "    verbose=True # отображать логи в консоли или нет\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "7d51c809-5aea-4fbf-8559-415ffb5f633c",
      "metadata": {
        "tags": [],
        "id": "7d51c809-5aea-4fbf-8559-415ffb5f633c",
        "outputId": "774def2e-b030-418b-e07a-af11b180dec8"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "\n",
            "\u001b[1m> Entering new ConversationChain chain...\u001b[0m\n",
            "Prompt after formatting:\n",
            "\u001b[32;1m\u001b[1;3mThe following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n",
            "\n",
            "Current conversation:\n",
            "\n",
            "Human: Привет, ChatGPT! Меня зовут Иван. Как дела?\n",
            "AI:\u001b[0m\n",
            "\n",
            "\u001b[1m> Finished chain.\u001b[0m\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "{'input': 'Привет, ChatGPT! Меня зовут Иван. Как дела?',\n",
              " 'history': '',\n",
              " 'response': 'Привет, Иван! Рад видеть тебя здесь. У меня все отлично, спасибо за спрос. Как твои дела?'}"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Спросим Как дела? Видим, что пока контекст пустой (ключ history)\n",
        "\n",
        "conversation_buf_mem.invoke(\"Привет, ChatGPT! Меня зовут Иван. Как дела?\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "dfafe281-3af9-4fdf-a439-96cc85bb8ac2",
      "metadata": {
        "tags": [],
        "id": "dfafe281-3af9-4fdf-a439-96cc85bb8ac2",
        "outputId": "2b47cbf9-9765-4fb0-f35d-06959a770e28"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "\n",
            "\u001b[1m> Entering new ConversationChain chain...\u001b[0m\n",
            "Prompt after formatting:\n",
            "\u001b[32;1m\u001b[1;3mThe following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n",
            "\n",
            "Current conversation:\n",
            "Human: Привет, ChatGPT! Меня зовут Иван. Как дела?\n",
            "AI: Привет, Иван! Я рад с тобой поговорить. У меня все отлично! Что нового у тебя?\n",
            "Human: Сможешь помочь мне в написании кода на Python?\n",
            "AI:\u001b[0m\n",
            "\n",
            "\u001b[1m> Finished chain.\u001b[0m\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "{'input': 'Сможешь помочь мне в написании кода на Python?',\n",
              " 'history': 'Human: Привет, ChatGPT! Меня зовут Иван. Как дела?\\nAI: Привет, Иван! Я рад с тобой поговорить. У меня все отлично! Что нового у тебя?',\n",
              " 'response': 'Конечно! Я постараюсь помочь. Что именно тебе нужно сделать?'}"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "conversation_buf_mem.invoke(\"Сможешь помочь мне в написании кода на Python?\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b9a4a291-1e6b-48c9-9e23-a0c95cac3caa",
      "metadata": {
        "tags": [],
        "id": "b9a4a291-1e6b-48c9-9e23-a0c95cac3caa",
        "outputId": "d1d4bcf6-b458-4bbc-f39b-64dc9749019e"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "\n",
            "\u001b[1m> Entering new ConversationChain chain...\u001b[0m\n",
            "Prompt after formatting:\n",
            "\u001b[32;1m\u001b[1;3mThe following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n",
            "\n",
            "Current conversation:\n",
            "Human: Привет, ChatGPT! Меня зовут Иван. Как дела?\n",
            "AI: Привет, Иван! Я рад с тобой поговорить. У меня все отлично! Что нового у тебя?\n",
            "Human: Сможешь помочь мне в написании кода на Python?\n",
            "AI: Конечно! Я постараюсь помочь. Что именно тебе нужно сделать?\n",
            "Human: Как вывести на экран фразу 'Hello, world!' ?\n",
            "AI:\u001b[0m\n",
            "\n",
            "\u001b[1m> Finished chain.\u001b[0m\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "{'input': \"Как вывести на экран фразу 'Hello, world!' ?\",\n",
              " 'history': 'Human: Привет, ChatGPT! Меня зовут Иван. Как дела?\\nAI: Привет, Иван! Я рад с тобой поговорить. У меня все отлично! Что нового у тебя?\\nHuman: Сможешь помочь мне в написании кода на Python?\\nAI: Конечно! Я постараюсь помочь. Что именно тебе нужно сделать?',\n",
              " 'response': \"Чтобы вывести на экран фразу 'Hello, world!' на Python, тебе нужно использовать функцию print(). Вот пример кода:\\n\\nprint('Hello, world!')\\n\\nЭтот код выведет фразу 'Hello, world!' на экран.\"}"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "conversation_buf_mem.invoke(\"Как вывести на экран фразу 'Hello, world!' ?\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "34600c0d-2594-465d-aacc-dc63b1ad07fe",
      "metadata": {
        "id": "34600c0d-2594-465d-aacc-dc63b1ad07fe"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "    \n",
        "**Видно, что благодаря запоминанию контекста, модель знала, что нам нужен ответ именно c синтаксисом языка Python.**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ee588076-b67b-4700-83bf-f4a81d1aa5fc",
      "metadata": {
        "tags": [],
        "id": "ee588076-b67b-4700-83bf-f4a81d1aa5fc",
        "outputId": "c2dd5145-1337-4c47-ab56-0bc3fb39f259"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Human: Привет, ChatGPT! Меня зовут Иван. Как дела?\n",
            "AI: Привет, Иван! Я рад познакомиться с тобой. У меня все хорошо, спасибо! Как твои дела?\n",
            "Human: Сможешь помочь мне в написании кода на Python?\n",
            "AI: Конечно, я постараюсь помочь! Что именно тебе нужно сделать?\n",
            "Human: Как вывести на экран фразу 'Hello, world!' ?\n",
            "AI: Чтобы вывести на экран фразу 'Hello, world!' на Python, ты можешь использовать функцию print(). Просто напиши print('Hello, world!') и запусти программу.\n"
          ]
        }
      ],
      "source": [
        "# Посмотреть содержимое истории диалога можно с помощью атрибута buffer\n",
        "# При использовании буферной памяти, в history просто записываются все предыдущие сообщения.\n",
        "print(conversation_buf_mem.memory.buffer)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4eb5ec56-79f2-4f9e-bf61-b6f0cef16d22",
      "metadata": {
        "tags": [],
        "id": "4eb5ec56-79f2-4f9e-bf61-b6f0cef16d22",
        "outputId": "d6afd9f9-2ec2-4286-db1e-40133e8be474"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'history': \"Human: Привет, ChatGPT! Меня зовут Иван. Как дела?\\nAI: Привет, Иван! Я рад познакомиться с тобой. У меня все хорошо, спасибо! Как твои дела?\\nHuman: Сможешь помочь мне в написании кода на Python?\\nAI: Конечно, я постараюсь помочь! Что именно тебе нужно сделать?\\nHuman: Как вывести на экран фразу 'Hello, world!' ?\\nAI: Чтобы вывести на экран фразу 'Hello, world!' на Python, ты можешь использовать функцию print(). Просто напиши print('Hello, world!') и запусти программу.\"}"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Или воспользоваться методом load_memory_variables, так же можно обращаться напрямую к объекту memory\n",
        "memory.load_memory_variables({})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "51f61102-39fd-42f5-8166-a66c29655bf4",
      "metadata": {
        "id": "51f61102-39fd-42f5-8166-a66c29655bf4"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "**Так же контекст (историю) можно задавать вручную или подгружать из файла.**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "26e001ca-6142-4b76-b496-c64663691927",
      "metadata": {
        "height": 30,
        "tags": [],
        "id": "26e001ca-6142-4b76-b496-c64663691927",
        "outputId": "1500445c-ee34-40a1-a29e-6573d791f9c2"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Human: Привет!\n",
            "AI: Как дела?\n"
          ]
        }
      ],
      "source": [
        "# Создадим новый буфер\n",
        "memory = ConversationBufferMemory()\n",
        "\n",
        "# загрузим диалог в память\n",
        "memory.save_context({\"input\": \"Привет!\"},\n",
        "                    {\"output\": \"Как дела?\"})\n",
        "\n",
        "print(memory.buffer)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0da4f9c7-d9fc-4f2a-a70a-cfb3ee1b7976",
      "metadata": {
        "height": 30,
        "tags": [],
        "id": "0da4f9c7-d9fc-4f2a-a70a-cfb3ee1b7976",
        "outputId": "0afda9ba-fa04-4254-f763-f08798e16a0c"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'history': 'Human: Привет!\\nAI: Как дела?'}"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "memory.load_memory_variables({})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "93b13214-6d19-4b6d-aec4-2eed8a9618ea",
      "metadata": {
        "tags": [],
        "id": "93b13214-6d19-4b6d-aec4-2eed8a9618ea",
        "outputId": "5b1b7c5c-b851-4d9d-c56e-728451608277"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'history': 'Human: Привет!\\nAI: Как дела?\\nHuman: Спасибо, хорошо!\\nAI: Здорово!'}"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Добавим продолжение диалога\n",
        "memory.save_context({\"input\": \"Спасибо, хорошо!\"},\n",
        "                    {\"output\": \"Здорово!\"})\n",
        "\n",
        "# Буфер дополнился\n",
        "memory.load_memory_variables({})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cb6d6c95-032d-4d8f-90f1-715b92f6eb36",
      "metadata": {
        "id": "cb6d6c95-032d-4d8f-90f1-715b92f6eb36"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "**Заглянем под капот:**\n",
        "* Каждое новое сообщение подается в специальный промпт\n",
        "* В промпт подаются 2 переменные `history` и `input`\n",
        "* В параметре `template` описан шаблон промпта, в которые вставляются переменные.\n",
        "* Остальное делает ChatGPT 🪄"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "187df0b2-4659-4fd3-bdd4-99752ced93bd",
      "metadata": {
        "tags": [],
        "id": "187df0b2-4659-4fd3-bdd4-99752ced93bd",
        "outputId": "c60103b6-7707-473d-e739-2643469d7318"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PromptTemplate(input_variables=['history', 'input'], template='The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\\n\\nCurrent conversation:\\n{history}\\nHuman: {input}\\nAI:')"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Посмотрим на этот промпт\n",
        "conversation_buf_mem.prompt"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8a3255f2-7fcb-498d-a631-109be57d7243",
      "metadata": {
        "tags": [],
        "id": "8a3255f2-7fcb-498d-a631-109be57d7243",
        "outputId": "06b0c6d3-77d5-43b4-dcdc-de1b646cf260"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n",
            "\n",
            "Current conversation:\n",
            "{history}\n",
            "Human: {input}\n",
            "AI:\n"
          ]
        }
      ],
      "source": [
        "print(conversation_buf_mem.prompt.template)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b678d05f-3def-4dc2-9702-290265b9828a",
      "metadata": {
        "id": "b678d05f-3def-4dc2-9702-290265b9828a"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Этот тип памяти можно использовать в задачах с короткими диалогами и небольшим контекстом.<br>\n",
        "**Недостатки `ConversationBufferMemory`:**\n",
        "* Чем длиннее история, тем больше токенов придется подавать на вход модели\n",
        "* В случае платных моделей, это будет накладно по финансам\n",
        "* Контекст моделей тоже ограничен по количеству токенов (около 4K в случае ChatGPT)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bff12589-be18-4be9-85d8-68872d34cbd0",
      "metadata": {
        "tags": [],
        "id": "bff12589-be18-4be9-85d8-68872d34cbd0"
      },
      "source": [
        "### <center id=\"d3\"> `ConversationBufferWindowMemory` и `ConversationTokenBufferMemory`\n",
        "    Оба метода сохраняют лимитированную часть контекста диалога"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ffa20e7d-37ec-4e65-8807-d75af6484b30",
      "metadata": {
        "tags": [],
        "id": "ffa20e7d-37ec-4e65-8807-d75af6484b30"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "\n",
        "<img src='https://github.com/a-milenkin/LLM_practical_course/blob/main/images/comperison_memors.png?raw=1' align=\"right\" width=\"428\" height=\"328\" >\n",
        "    \n",
        "Похожие, по своей сути, 2 типа памяти:\n",
        "* `ConversationBufferWindowMemory` - сохраняет в буфере только последние `k` диалогов.\n",
        "* `ConversationTokenBufferMemory` - использует лимит токенов, который определяется количеством слов в сохраненных сообщениях."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ef25fd5b-8fe7-40aa-8af4-b235411ff97d",
      "metadata": {
        "height": 30,
        "tags": [],
        "id": "ef25fd5b-8fe7-40aa-8af4-b235411ff97d",
        "outputId": "14d74be2-e138-44e0-b2ce-a33bcc3bf774"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'history': 'Human: Спасибо, хорошо!\\nAI: Здорово!'}"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from langchain.memory import ConversationBufferWindowMemory\n",
        "\n",
        "memory = ConversationBufferWindowMemory(k=1)\n",
        "\n",
        "memory.save_context({\"input\": \"Привет!\"},\n",
        "                    {\"output\": \"Как дела?\"})\n",
        "memory.save_context({\"input\": \"Спасибо, хорошо!\"},\n",
        "                    {\"output\": \"Здорово!\"})\n",
        "\n",
        "\n",
        "memory.load_memory_variables({}) # Видим, что при k=1, в буфере остался только последний диалог"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9908725f-264a-4a87-a4c8-5038c0fbb122",
      "metadata": {
        "height": 132,
        "tags": [],
        "id": "9908725f-264a-4a87-a4c8-5038c0fbb122"
      },
      "outputs": [],
      "source": [
        "memory = ConversationBufferWindowMemory(k=1)\n",
        "conversation_bufwin_mem = ConversationChain(\n",
        "    llm=llm,\n",
        "    memory = memory,\n",
        "    verbose=False\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a54686f4-a2f8-441b-9594-686efa81dff3",
      "metadata": {
        "height": 30,
        "tags": [],
        "id": "a54686f4-a2f8-441b-9594-686efa81dff3",
        "outputId": "46aed9be-bf16-4b55-dfeb-a0376e2e53a3"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'input': 'Привет, ChatGPT! Меня зовут Иван. Как дела?',\n",
              " 'history': '',\n",
              " 'response': 'Привет, Иван! Рад вас видеть. У меня все отлично, спасибо за спрос. Как у вас дела?'}"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "conversation_bufwin_mem.invoke(\"Привет, ChatGPT! Меня зовут Иван. Как дела?\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "43dd05e0-401e-4ef1-b8a4-dab60e3b8a1e",
      "metadata": {
        "height": 30,
        "tags": [],
        "id": "43dd05e0-401e-4ef1-b8a4-dab60e3b8a1e",
        "outputId": "e02fae1b-9779-4a3f-edc3-b2a4edfb213d"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'Конечно, я могу помочь вам с кодом на Python. Что именно вам нужно сделать?'"
            ]
          },
          "execution_count": 16,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# так же можно указать ключ response и в аутпуте получим только ответ модели\n",
        "conversation_bufwin_mem.invoke(input=\"Сможешь помочь мне в написании кода на Python?\")['response']"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2d7cb0dc-5adc-477b-a71f-2150f657ca35",
      "metadata": {
        "height": 30,
        "tags": [],
        "id": "2d7cb0dc-5adc-477b-a71f-2150f657ca35",
        "outputId": "51eaf182-c97f-41f3-aa29-e3f094f10d6d"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'Извините, но я не знаю, как вас зовут. Я только могу помочь с вопросами и задачами, связанными с кодированием на Python.'"
            ]
          },
          "execution_count": 19,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Видим, что при k=1 первый диалог с именем не сохранился и модель не может ответить\n",
        "conversation_bufwin_mem.invoke(input=\"Как меня зовут?\")['response']"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "88cee7bf-2d1d-475c-a603-53a2e4f3eb38",
      "metadata": {
        "height": 30,
        "tags": [],
        "id": "88cee7bf-2d1d-475c-a603-53a2e4f3eb38",
        "outputId": "e1422018-6f6a-4ad2-b65d-559a5ab602b8",
        "colab": {
          "referenced_widgets": [
            "0d3d811545844cc48ae8668da8376f6f",
            "d93ebd4bf19940d482d2f54e494ca0cc",
            "b139563b147b4dd3949064916122729d",
            "2f30c202fe3a4b7e9d9c79a79c7d2bc7"
          ]
        }
      },
      "outputs": [
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "0d3d811545844cc48ae8668da8376f6f",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "Downloading (…)olve/main/vocab.json:   0%|          | 0.00/1.04M [00:00<?, ?B/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "d93ebd4bf19940d482d2f54e494ca0cc",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "Downloading (…)olve/main/merges.txt:   0%|          | 0.00/456k [00:00<?, ?B/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "b139563b147b4dd3949064916122729d",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "Downloading (…)/main/tokenizer.json:   0%|          | 0.00/1.36M [00:00<?, ?B/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "2f30c202fe3a4b7e9d9c79a79c7d2bc7",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "Downloading (…)lve/main/config.json:   0%|          | 0.00/665 [00:00<?, ?B/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "{'history': 'AI: Как дела?\\nHuman: Спасибо, хорошо!\\nAI: Здорово!'}"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from langchain.memory import ConversationTokenBufferMemory\n",
        "\n",
        "# Инициализируем новый token-буфер, подаем в него модель и лимит токенов\n",
        "# Модель подаём потому что модели по-разному токенизируют текст\n",
        "memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=50)\n",
        "\n",
        "memory.save_context({\"input\": \"Привет!\"},\n",
        "                    {\"output\": \"Как дела?\"})\n",
        "memory.save_context({\"input\": \"Спасибо, хорошо!\"},\n",
        "                    {\"output\": \"Здорово!\"})\n",
        "\n",
        "memory.load_memory_variables({}) # видим, что по лимиту прошли только последние 3 фразы"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ff31547-6ab1-4c21-9ce6-64ea2b98c746",
      "metadata": {
        "id": "0ff31547-6ab1-4c21-9ce6-64ea2b98c746"
      },
      "source": [
        "## <center id=\"d4\"> 🗜🗣 `ConversationSummaryMemory` - сохраняет \"смысл\"\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "\n",
        "Этот вид памяти суммаризует историю разговора перед передачей её в параметр `history`. Таким образом, можно сократить объем текста, обрабатываемого моделью и тратить меньше токенов."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "92388461-084d-4e4b-b7a6-f468f402765b",
      "metadata": {
        "tags": [],
        "id": "92388461-084d-4e4b-b7a6-f468f402765b",
        "outputId": "9aecbdf5-e50c-40f5-b98f-491c88ca1354"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Progressively summarize the lines of conversation provided, adding onto the previous summary returning a new summary.\n",
            "\n",
            "EXAMPLE\n",
            "Current summary:\n",
            "The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good.\n",
            "\n",
            "New lines of conversation:\n",
            "Human: Why do you think artificial intelligence is a force for good?\n",
            "AI: Because artificial intelligence will help humans reach their full potential.\n",
            "\n",
            "New summary:\n",
            "The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential.\n",
            "END OF EXAMPLE\n",
            "\n",
            "Current summary:\n",
            "{summary}\n",
            "\n",
            "New lines of conversation:\n",
            "{new_lines}\n",
            "\n",
            "New summary:\n"
          ]
        }
      ],
      "source": [
        "from langchain.chains.conversation.memory import ConversationSummaryMemory\n",
        "\n",
        "# Здесь тоже подаём модель, так как она необходима для суммаризации текста(не обязательно использовать ChatGPT)\n",
        "memory = ConversationSummaryMemory(llm=llm)\n",
        "\n",
        "conversation_sum_mem = ConversationChain(\n",
        "    llm=llm,\n",
        "    memory=memory\n",
        ")\n",
        "\n",
        "# посмотрим как поменялся шаблон для промпта\n",
        "print(conversation_sum_mem.memory.prompt.template)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ab4055e-66a9-4e08-afc8-1e9cd0e8808e",
      "metadata": {
        "id": "1ab4055e-66a9-4e08-afc8-1e9cd0e8808e"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Теперь у нас появляется этап предобработки истории. На вход LLM мы подаем предыдущую историю (в виде `summary`), добавляем к ней текущий вывод модели (`new_lines`) и получаем новое состояние `summary`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c70a45b0-c78f-48ac-936e-38ac6f3cdd02",
      "metadata": {
        "tags": [],
        "id": "c70a45b0-c78f-48ac-936e-38ac6f3cdd02",
        "outputId": "f37ab850-fe11-41c3-f7de-bf40ef94926c"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'input': 'Привет, ChatGPT! Меня зовут Иван. Как дела?',\n",
              " 'history': '',\n",
              " 'response': 'Привет, Иван! Я ChatGPT, и мне очень хорошо! Я всегда готов общаться и помогать тебе. Чем могу помочь сегодня?'}"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "conversation_sum_mem.invoke(\"Привет, ChatGPT! Меня зовут Иван. Как дела?\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "94232c61-0dc2-48e6-8f2e-17d6c80785b2",
      "metadata": {
        "tags": [],
        "id": "94232c61-0dc2-48e6-8f2e-17d6c80785b2",
        "outputId": "d182cbd0-6def-489c-803d-0efd28f04cc8"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'input': 'Сможешь помочь мне в написании кода на Python?',\n",
              " 'history': 'The human greets ChatGPT and introduces themselves as Ivan. ChatGPT responds that it is doing well and is always ready to communicate and help. It asks how it can assist Ivan today.',\n",
              " 'response': 'Конечно, я готов помочь в написании кода на Python! Расскажи мне, с чем именно тебе нужно помощь.'}"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "conversation_sum_mem.invoke(\"Сможешь помочь мне в написании кода на Python?\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "73e087ee-4e92-4034-b459-218ed6b4a0fb",
      "metadata": {
        "tags": [],
        "id": "73e087ee-4e92-4034-b459-218ed6b4a0fb"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "\n",
        "Видим в history, суммаризованый предыдущий диалог уже на английском"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "05326ec3-9c55-4d75-93a3-7ba16fca54c5",
      "metadata": {
        "tags": [],
        "id": "05326ec3-9c55-4d75-93a3-7ba16fca54c5",
        "outputId": "452b5767-47d7-4b96-d95d-3cd465f1109a"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'input': \"Как вывести на экран 'Hello, world!' ?\",\n",
              " 'history': 'The human greets ChatGPT and introduces themselves as Ivan. ChatGPT responds that it is always ready to communicate and help. It asks how it can assist Ivan today. Ivan asks if ChatGPT can help him with writing Python code. ChatGPT enthusiastically agrees and asks Ivan to tell it specifically what he needs help with.',\n",
              " 'response': \"Привет, Иван! Чтобы вывести на экран 'Hello, world!' на языке Python, вам нужно использовать функцию print(). Просто напишите print('Hello, world!') и запустите код. Это выведет фразу 'Hello, world!' на экран. Если у вас возникнут еще вопросы или если вам нужна помощь с другим кодом, пожалуйста, сообщите мне.\"}"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "conversation_sum_mem.invoke(\"Как вывести на экран 'Hello, world!' ?\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55034146-01aa-4ae0-91fc-20f8decd6593",
      "metadata": {
        "id": "55034146-01aa-4ae0-91fc-20f8decd6593"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "### Другие виды памяти:\n",
        "\n",
        "* `ConversationSummaryBufferMemory` - позволяет задать окно (в токенах), в рамках которого мы сохраняем диалоги в неизменной форме, а при превышении - суммаризируем.\n",
        "* `ConversationKGMemory` - использует граф знаний для сохранения памяти.\n",
        "* `ConversationEntityMemory` - сохраняет в памяти знания об определенном объекте."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "12cb6a46-c81b-4ea1-9d26-b5ac6d4e5ff8",
      "metadata": {
        "id": "12cb6a46-c81b-4ea1-9d26-b5ac6d4e5ff8"
      },
      "source": [
        "# <center><h1>🔗 Chains 🔗 - продолжение следует...</h1></center>"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.13"
    },
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}