{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "1aba1183-53a1-4aa7-b1c3-6a6687e050b9",
      "metadata": {
        "id": "1aba1183-53a1-4aa7-b1c3-6a6687e050b9"
      },
      "source": [
        "# <center id=\"p1\"><h1> ⛓ `CHAINS` 🔗 - разбираемся с базовым элементом фрэймворка!</h1></center>"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9fee959e",
      "metadata": {
        "id": "9fee959e"
      },
      "source": [
        "### Оглавление ноутбука\n",
        "<img src='https://i.ytimg.com/vi/W3AoeMrg27o/maxresdefault.jpg' align=\"right\" width=\"600\" height=\"650\" />\n",
        "<br>\n",
        "\n",
        "<p><font size=\"3\" face=\"Arial\" font-size=\"large\"><ul type=\"square\">\n",
        "    \n",
        "<li><a href=\"#p1\">🚀 Введение </a></li>\n",
        "<li><a href=\"#p2\">💊 Generic Chains </a></li><ul type=\"square\">\n",
        "<li><a href=\"#p3\">LLMChain 🔗 </a>\n",
        "<li><a href=\"#p4\">TransformChain 🤖 </a>\n",
        "<li><a href=\"#p5\">SequentialChain 🔗➕⛓</a></ul>\n",
        "<li><a href=\"#plcel\">LCEL - LangChain Expression Language 🤪🀄️</a>\n",
        "<li><a href=\"#p6\">Router Chain 🈯️ ➡️ 🇯🇵</a>\n",
        "<li><a href=\"#p7\">🛠 Utility Chains 🪝 </a></li>\n",
        "<li><a href=\"#p8\">🧸 Выводы и заключения ✅ </a></li>\n",
        "    \n",
        "</ul></font></p>"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "56448a2d-8dee-48b5-b736-add4f6ccd474",
      "metadata": {
        "id": "56448a2d-8dee-48b5-b736-add4f6ccd474"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Как вы могли догадаться из названия, **цепочки (Chains)** - это один из фундаментальных строительных блоков фрэймворка `LangChain`. Они представляют собой просто цепочку компонентов, которые будут выполнены в определённом порядке. <br>\n",
        "\n",
        "*Официальным определением цепочек является следующее:* <br>\n",
        "Цепочка состоит из звеньев, которые могут быть как примитивами, так и другими цепочками. Примитивами могут быть либо промпты, LLM, utils, либо другие цепочки. Таким образом, цепочка — это, по сути, конвейер, который обрабатывает входные данные, используя определенную комбинацию примитивов. Интуитивно это можно рассматривать как «шаг», который выполняет определенный набор операций над входными данными и возвращает результат. Это может быть что угодно: от прохождения через LLM на основе промпта до применения Python-функции к тексту."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d6a31bbb-af80-4b0b-9baa-dd3f714d85e2",
      "metadata": {
        "id": "d6a31bbb-af80-4b0b-9baa-dd3f714d85e2"
      },
      "source": [
        "Цепочки делятся на несколько основных типов:\n",
        "* `Generic chains` - общего назначения\n",
        "* `Utility chains` - выполняющие конкретную функцию (например, вычислять математические выражения)\n",
        "* `Combine Documents chains` - цепочки для работы с документами <br>\n",
        "и другие.\n",
        "\n",
        "В этом ноутбуке остановимся на первых двух типах, третий специфический тип мы рассмотрим позднее."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75797bf8-2c2b-40fb-bcbd-4bf840c2e837",
      "metadata": {
        "tags": [],
        "id": "75797bf8-2c2b-40fb-bcbd-4bf840c2e837"
      },
      "source": [
        "# <center id=\"p2\"> 💊 Generic Chains - цепочки общего назначения </center>"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e233b91b-aba1-42b2-ac3d-9f4702806965",
      "metadata": {
        "id": "e233b91b-aba1-42b2-ac3d-9f4702806965"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "В `LangChain` есть несколько основных типов `Generic Chains`:\n",
        "* `LLM Chain` - базовый тип.\n",
        "* `Transform Chain` - позволяет преобразовывать текст и применять к нему различные функции.\n",
        "* `Sequentional Chain` - позволяет собирать последовательности из нескольких цепочек.\n",
        "* `Router Chain` - по запросу решает куда, его перенаправить.\n",
        "\n",
        "*Попробуем рассмотреть их всех на одном примере* 🤪"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0a0dc993-6239-4b27-99b4-b752fb708538",
      "metadata": {
        "tags": [],
        "id": "0a0dc993-6239-4b27-99b4-b752fb708538"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "from getpass import getpass\n",
        "import warnings\n",
        "warnings.filterwarnings('ignore')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9febfbad-5120-488c-ac89-2ea5d8c9339a",
      "metadata": {
        "tags": [],
        "id": "9febfbad-5120-488c-ac89-2ea5d8c9339a"
      },
      "outputs": [],
      "source": [
        "#!pip install --upgrade langchain langchain-openai openai tiktoken numexpr -q"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "78277539-0f1e-44b6-a745-c23e7ec0f2e5",
      "metadata": {
        "tags": [],
        "id": "78277539-0f1e-44b6-a745-c23e7ec0f2e5"
      },
      "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": "ccd3083b-a534-4f4c-a008-d4eb822210ab",
      "metadata": {
        "tags": [],
        "id": "ccd3083b-a534-4f4c-a008-d4eb822210ab"
      },
      "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": "bd76f574-b8e5-4820-96a4-972fc5ac25ad",
      "metadata": {
        "tags": [],
        "id": "bd76f574-b8e5-4820-96a4-972fc5ac25ad",
        "outputId": "48f2fb20-fe23-40bf-b68d-f1de6928256e"
      },
      "outputs": [
        {
          "name": "stdin",
          "output_type": "stream",
          "text": [
            "Введите ваш OpenAI API ключ ········\n"
          ]
        }
      ],
      "source": [
        "# Если используете ключ из курса, запустите эту ячейку\n",
        "from utils import ChatOpenAI\n",
        "\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": "cdf24f38-953f-43f7-9796-ebe092e21efe",
      "metadata": {
        "id": "cdf24f38-953f-43f7-9796-ebe092e21efe"
      },
      "source": [
        "## <center id=\"p3\"> 🔗 `LLMChain` - базовая цепочка для общения с LLM.\n",
        "  \n",
        "\n",
        "\n",
        "Самой простой из этих цепочек является `LLMChain`. С ней мы уже знакомились в предыдущих уроках."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "980bda3e-1824-4302-a35c-76a56e9b29da",
      "metadata": {
        "id": "980bda3e-1824-4302-a35c-76a56e9b29da"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "Создадим цепочку для переписывания текста в различных стилях."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "e236f8ed",
      "metadata": {
        "tags": [],
        "id": "e236f8ed"
      },
      "outputs": [],
      "source": [
        "from langchain.prompts import PromptTemplate\n",
        "from langchain.chains import LLMChain\n",
        "\n",
        "# создадим шаблон и промпт\n",
        "template = '''Перепиши текcт ниже в заданном стиле.\n",
        "Текст:{output_text}\n",
        "\n",
        "Стиль: {style}.\n",
        "\n",
        "Результат:'''\n",
        "\n",
        "prompt = PromptTemplate(input_variables=['output_text', 'style'], template=template)\n",
        "\n",
        "# создаём цепочку\n",
        "style_changer_chain = LLMChain(llm=llm, prompt=prompt, output_key='final_output')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f8ff4518",
      "metadata": {
        "tags": [],
        "id": "f8ff4518",
        "outputId": "259fe496-3283-4a71-8d72-b4e0669bbf68"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'Добро пожаловать на курс по усовершенствованию языковых моделей! Здесь мы научим вас тонкостям искусства придания моделям еще большего великолепия и раскрытия их потенциала до предела. Мы предоставим вам советы по самым передовым методам обучения и настройки языковых моделей, а также научим вас практическим навыкам для решения сложнейших задач в области обработки естественного языка.'"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "text = '''Приветствуем на курсе по тюнингу языковых моделей!\n",
        "Тут мы учим, как сделать модельки еще круче и раскрыть их потенциал до максимума.\n",
        "Мы дадим вам советы по самым свежим методам обучения и настройки языковых моделей,\n",
        "а также научим практическим навыкам для решения сложных задач в обработке естественного языка.'''\n",
        "style = 'Роман 18 века'\n",
        "\n",
        "style_changer_chain.invoke({'output_text': text, 'style': style})['final_output']"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c0e32fc4-fe9c-4273-99fd-cb0496844952",
      "metadata": {
        "id": "c0e32fc4-fe9c-4273-99fd-cb0496844952"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "    \n",
        "Видим, что цепочка с моделью неплохо справилась с задачей."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aa3ce9a8",
      "metadata": {
        "id": "aa3ce9a8"
      },
      "source": [
        "## <center id=\"p4\"> 🤖 `TransformChain`  - измени и передай другому"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "384023ea-b558-44f6-a110-c6113d53ef12",
      "metadata": {
        "id": "384023ea-b558-44f6-a110-c6113d53ef12"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Теперь давайте представим, что мы получаем откуда-то сырой необработанный текст, который содержит пустые строки и много ненужных пробелов. Как вы помните, нам приходится считать количество израсходованных токенов, и не хочется их дополнительно тратить на ненужные лишние символы. К тому же такой текст выглядит неопрятно. <br>\n",
        "Давайте создадим цепочку, которая будет очищать поданный в неё текст от ненужных символов. <br>\n",
        "В этом нам поможет `Transform Chain`.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "913626f1-54ac-43af-bb73-d1fb98814736",
      "metadata": {
        "id": "913626f1-54ac-43af-bb73-d1fb98814736"
      },
      "source": [
        "Для начала создадим функцию, которая будет убирать лишние пробелы и строки в исходном тексте."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f1a569d9-6325-4420-aba6-cfaefba7c920",
      "metadata": {
        "tags": [],
        "id": "f1a569d9-6325-4420-aba6-cfaefba7c920"
      },
      "outputs": [],
      "source": [
        "import re\n",
        "\n",
        "def del_spaces(inputs: dict) -> dict:\n",
        "    text = inputs[\"text\"]\n",
        "\n",
        "    # заменяем пустые строки и дополнительные пробелы на один, используя регулярные выражения\n",
        "    text = re.sub(r'(\\r\\n|\\r|\\n){2,}', r'\\n', text)\n",
        "    text = re.sub(r'[ \\t]+', ' ', text)\n",
        "\n",
        "    return {\"output_text\": text}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "bce9d951-5196-49e7-b831-b115c43085be",
      "metadata": {
        "tags": [],
        "id": "bce9d951-5196-49e7-b831-b115c43085be"
      },
      "outputs": [],
      "source": [
        "# создаём преобразующую цепочку\n",
        "from langchain.chains import TransformChain\n",
        "\n",
        "text_clean_chain = TransformChain(input_variables=[\"text\"],\n",
        "                                  output_variables=[\"output_text\"],\n",
        "                                  transform=del_spaces)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d8bc9026-4dfa-4dc8-bf50-90858d74b0d8",
      "metadata": {
        "id": "d8bc9026-4dfa-4dc8-bf50-90858d74b0d8"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "**Обратите внимание**, что при создании этой цепочки, мы не подаём llm как аргумент. Можно подумать, что отсутствие LLM делает такую цепочку более слабой по сравнению с другими, но далее мы увидим, что комбинируя её с другими цепочками, можно добиться хороших результатов и сэкономить токены.<br>\n",
        "Посмотрим как она работает:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9086596d-5ba4-4615-aa6d-dc19560bfb33",
      "metadata": {
        "tags": [],
        "id": "9086596d-5ba4-4615-aa6d-dc19560bfb33"
      },
      "outputs": [],
      "source": [
        "# Возьмём наш предыдущий текст и \"загрязним его\"\n",
        "dirty_text = '''Приветствуем на курсе по        тюнингу языковых        моделей!\n",
        "\n",
        "Тут мы учим, как сделать модельки еще круче и раскрыть их потенциал до максимума.\n",
        "Мы дадим вам советы по   самым свежим методам  обучения и настройки языковых                    моделей,\n",
        "\n",
        "а также научим практическим навыкам для                     решения сложных задач в обработке естественного языка.'''"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ee7b5fef-927f-4a3e-8736-676e162654e9",
      "metadata": {
        "tags": [],
        "id": "ee7b5fef-927f-4a3e-8736-676e162654e9",
        "outputId": "566e9f70-5370-483a-b633-23903066df1a"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'Приветствуем на курсе по тюнингу языковых моделей! \\nТут мы учим, как сделать модельки еще круче и раскрыть их потенциал до максимума. \\nМы дадим вам советы по самым свежим методам обучения и настройки языковых моделей,\\nа также научим практическим навыкам для решения сложных задач в обработке естественного языка.'"
            ]
          },
          "execution_count": 25,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Запустим, и видим, что цепочка задачу выполнила\n",
        "text_clean_chain.invoke(dirty_text)['output_text']"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ce23e80-2a70-4e70-8710-bfc6a6e1fdcb",
      "metadata": {
        "id": "1ce23e80-2a70-4e70-8710-bfc6a6e1fdcb"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Как вы уже наверное догадались (по названиям входных и выходных переменных), мы собираемся подать выход из `Transform Chain` в `LLMChain`. Чтобы их объединить и использовать как одну интегрированную цепочку, воспользуемся `Sequential Chain`."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ed77050-490e-402c-8a6e-deeb9980626c",
      "metadata": {
        "tags": [],
        "id": "0ed77050-490e-402c-8a6e-deeb9980626c"
      },
      "source": [
        "## <center id=\"p5\">  🔗➕⛓ `SequentialChain`  - последовательные цепочки\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "<img src='https://github.com/a-milenkin/LLM_practical_course/blob/main/images/seq_chain.jpeg?raw=1' align=\"right\" width=\"600\" height=\"600\" >    \n",
        "    \n",
        "Два основных типа `Sequential Chain`:\n",
        "* `SimpleSequentialChain`: простейшая форма последовательной цепочки, где каждый шаг имеет один вход/выход, а выход одного шага является входом следующего.\n",
        "* `SequentialChain`: более общая форма последовательной цепочки, допускающая несколько входов/выходов.\n",
        "\n",
        "Воспользуемся вторым вариантом:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "46eea19c",
      "metadata": {
        "tags": [],
        "id": "46eea19c"
      },
      "outputs": [],
      "source": [
        "from langchain.chains import SequentialChain"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5eb089cc-fcd3-48f3-9e74-e612de423517",
      "metadata": {
        "tags": [],
        "id": "5eb089cc-fcd3-48f3-9e74-e612de423517"
      },
      "outputs": [],
      "source": [
        "sequential_chain = SequentialChain(chains=[text_clean_chain, style_changer_chain],\n",
        "                                   input_variables=['text', 'style'],\n",
        "                                   output_variables=['final_output'])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "d6bb331c-8ee3-465d-a793-0632db4d843b",
      "metadata": {
        "tags": [],
        "id": "d6bb331c-8ee3-465d-a793-0632db4d843b",
        "outputId": "ea718ab9-c4b3-434a-8295-0832d030c2c9"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Yo, добро пожаловать на курс по тюнингу языковых моделей!\n",
            "Тут мы научим, как модельки станут еще круче, раскроем их потенциал до максимума.\n",
            "Советы по самым свежим методам обучения и настройки языковых моделей - мы дадим,\n",
            "Практические навыки для решения сложных задач в обработке естественного языка - научим.\n"
          ]
        }
      ],
      "source": [
        "style = 'Rap'\n",
        "print(sequential_chain.invoke({'text': dirty_text, 'style': style}))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fd3fa4a0-ac35-4d93-a834-ef78f4b6cf1a",
      "metadata": {
        "id": "fd3fa4a0-ac35-4d93-a834-ef78f4b6cf1a"
      },
      "source": [
        "<div class=\"alert alert-block alert-danger\">\n",
        "    \n",
        "# <center> А теперь забываем всё, что увидели выше! 🤯\n",
        "    \n",
        "Разработчики `LangChain` выкатили новую универсальную удобную концепцию, встречайте:"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f1666836-165d-4897-9540-891ea730a2b8",
      "metadata": {
        "id": "f1666836-165d-4897-9540-891ea730a2b8"
      },
      "source": [
        "# <center id=\"plcel\"> **`LCEL` - LangChain Expression Language** 🤪🀄️\n",
        "\n",
        "Что он позволяет делать?:\n",
        "* декларативный способ составлять цепочки\n",
        "* стандартизирует интерфейсы\n",
        "* проще персонализировать различные части цепочки\n",
        "* позволяет легко заменять компоненты\n",
        "* поддержка потоковой, пакетной и асинхронной обработки «из коробки»\n",
        "* промпты теперь стали более заметными и их можно легко изменить в соответствии с конкретными юзкейсами\n",
        "    \n",
        "Хватит теории, посмотрим на эту красоту в деле:"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f4e2cf87-2960-44d6-a767-e51f33637379",
      "metadata": {
        "id": "f4e2cf87-2960-44d6-a767-e51f33637379"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "\n",
        "Давайте посмотрим как можно переписать цепочку из первого примера:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "730e749f-2016-46da-a5e6-6728ba6c8e65",
      "metadata": {
        "tags": [],
        "id": "730e749f-2016-46da-a5e6-6728ba6c8e65"
      },
      "outputs": [],
      "source": [
        "# создадим шаблон и промпт\n",
        "template = '''Перепиши этот текcт в заданном стиле: {output_text}\n",
        "Стиль: {style}.\n",
        "Результат:'''\n",
        "\n",
        "prompt = PromptTemplate(input_variables=['output_text', 'style'], template=template)\n",
        "\n",
        "style_changer_chain = prompt | llm # И ВСЁ!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "58420559-2a0a-45ca-b685-d286decc51e6",
      "metadata": {
        "tags": [],
        "id": "58420559-2a0a-45ca-b685-d286decc51e6",
        "outputId": "1de20960-7d2e-4ad3-c5c9-b2d126aca556"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "content='Добро пожаловать на курс по усовершенствованию языковых моделей, благородные господа и прекрасные дамы! Здесь мы будем обучать вас тому, как сделать ваши модели еще более великолепными и раскрыть их потенциал до предела. Мы будем делиться с вами самыми передовыми методами обучения и настройки языковых моделей, а также научим вас практическим навыкам, необходимым для решения сложных задач в обработке естественного языка. Приготовьтесь погрузиться в мир изысканных слов и изящных фраз, который ожидает вас на этом благородном пути познания.'\n"
          ]
        }
      ],
      "source": [
        "text = '''Приветствуем на курсе по тюнингу языковых моделей!\n",
        "Тут мы учим, как сделать модельки еще круче и раскрыть их потенциал до максимума.\n",
        "Мы дадим вам советы по самым свежим методам обучения и настройки языковых моделей,\n",
        "а также научим практическим навыкам для решения сложных задач в обработке естественного языка.'''\n",
        "style = 'Роман 18 века'\n",
        "\n",
        "answer = style_changer_chain.invoke({'output_text': text, 'style': style})\n",
        "print(answer)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "d650dcf7-dc77-40e6-971a-8a969258bbac",
      "metadata": {
        "tags": [],
        "id": "d650dcf7-dc77-40e6-971a-8a969258bbac",
        "outputId": "589b14a9-f597-4656-baaa-8459bc5d1811"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'Добро пожаловать на курс по усовершенствованию языковых моделей, благородные господа и прекрасные дамы! Здесь мы будем обучать вас тому, как сделать ваши модели еще более великолепными и раскрыть их потенциал до предела. Мы будем делиться с вами самыми передовыми методами обучения и настройки языковых моделей, а также научим вас практическим навыкам, необходимым для решения сложных задач в обработке естественного языка. Приготовьтесь погрузиться в мир изысканных слов и изящных фраз, который ожидает вас на этом благородном пути познания.'"
            ]
          },
          "execution_count": 34,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "answer.content"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68efd608-9262-45b0-9b2c-5e53342a3597",
      "metadata": {
        "id": "68efd608-9262-45b0-9b2c-5e53342a3597"
      },
      "source": [
        " <div class=\"alert alert-info\">\n",
        "\n",
        " Стандартный интерфейс имеет 3 метода:\n",
        " * `stream` - возвращает ответ модели в виде итерируемого объекта\n",
        " * `invoke` - обрабатывает одиночный запрос к модели\n",
        " * `batch` - обрабатывает список запросов к модели"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c2c81966-bef3-4d5a-9acc-1c033476e922",
      "metadata": {
        "tags": [],
        "id": "c2c81966-bef3-4d5a-9acc-1c033476e922",
        "outputId": "f1b0df6e-35eb-4042-aaa1-240e37b9daaf"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[AIMessage(content='Добро пожаловать на курс по усовершенствованию языковых моделей, благородные господа и прекрасные дамы! Здесь мы будем обучать вас тому, как сделать ваши модели еще более великолепными и раскрыть их потенциал до предела. Мы будем делиться с вами самыми передовыми методами обучения и настройки языковых моделей, а также научим вас практическим навыкам, необходимым для решения сложных задач в обработке естественного языка. Приготовьтесь погрузиться в мир изысканных слов и изящных фраз, который ожидает вас на этом благородном пути познания.'),\n",
              " AIMessage(content='Приветствуем вас на курсе, где тюнинг моделей языковых\\nВас ждут знания, чтобы сделать их еще круче, до максимума раскрыть\\nСоветы мы дадим, самые свежие методы обучения и настройки\\nПрактические навыки, сложные задачи в обработке языка естественного\\n\\nПогрузимся в мир, где слова станут музыкой\\nГде модели языковые расцветут, как цветы весной\\nСекреты мы раскроем, техники улучшения\\nЧтобы язык стал красивым, слова - вдохновением\\n\\nПоэты мы станем, рифмы сочинять\\nЯзыком играть, его красоту раскрывать\\nСтихами и строфами, мы будем творить\\nМодели языковые, великолепно настраивать\\n\\nКак кисть художника, мы слова будем мазать\\nЯзыком играть, его силу раскрывать\\nСложные задачи, мы решим с легкостью\\nОбработку языка, сделаем безупречной, без трудностей\\n\\nТак добро пожаловать, на курс по тюнингу моделей языковых\\nГде поэзия и наука, вместе будут творить\\nРаскроем потенциал, моделей до максимума\\nИ станем мастерами, в обработке языка естественного.')]"
            ]
          },
          "execution_count": 35,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "style_changer_chain.batch([{'output_text': text, 'style': style}, {'output_text': text, 'style': 'поэма'}])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "7c4928d8-b1fd-4b7f-bd0f-2732260ae6f6",
      "metadata": {
        "tags": [],
        "id": "7c4928d8-b1fd-4b7f-bd0f-2732260ae6f6",
        "outputId": "9f754b9d-a191-4e75-cead-605721be511d"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<generator object RunnableSequence.stream at 0x7f64f9ccce40>"
            ]
          },
          "execution_count": 36,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# метод stream возвращает генератор\n",
        "style_changer_chain.stream({'output_text': text, 'style': 'поэма'})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ca10d3c5-a54c-4845-b671-37b783985694",
      "metadata": {
        "tags": [],
        "id": "ca10d3c5-a54c-4845-b671-37b783985694"
      },
      "outputs": [],
      "source": [
        "#for s in style_changer_chain.stream({'output_text': text, 'style': 'поэма'}):\n",
        "#    print(s.content)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d5b867fd-9335-4834-916c-0dbe282d8c21",
      "metadata": {
        "id": "d5b867fd-9335-4834-916c-0dbe282d8c21"
      },
      "source": [
        " <div class=\"alert alert-info\">\n",
        "\n",
        "Давайте теперь вспомним пример из 2-го модуля, где мы долго прикручивали `Output Parser`. Теперь использовать парсеры стало гораздо удобнее и требуется писать меньше кода!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "38cf10f0-ad66-4ee4-8891-f5bba634e899",
      "metadata": {
        "tags": [],
        "id": "38cf10f0-ad66-4ee4-8891-f5bba634e899",
        "outputId": "3cea21d0-a88c-469d-f926-f0e93fe310fb"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Добро пожаловать на курс по усовершенствованию языковых моделей, благородные господа и прекрасные дамы! Здесь мы будем обучать вас тому, как сделать ваши модели еще более великолепными и раскрыть их потенциал до предела. Мы будем делиться с вами самыми передовыми методами обучения и настройки языковых моделей, а также научим вас практическим навыкам, необходимым для решения сложных задач в обработке естественного языка. Приготовьтесь погрузиться в мир изысканных слов и изящных фраз, который ожидает вас на этом благородном пути познания.\n"
          ]
        }
      ],
      "source": [
        "# Просто импортируем парсер и добавляем его, как ещё 1 звено\n",
        "from langchain.schema.output_parser import StrOutputParser\n",
        "\n",
        "chain_with_parser = prompt | llm | StrOutputParser() # И ВСЁ!\n",
        "\n",
        "print(chain_with_parser.invoke({'output_text': text, 'style': style}))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8a8e17b1-e1ee-4188-a4c3-2457b521aa3e",
      "metadata": {
        "id": "8a8e17b1-e1ee-4188-a4c3-2457b521aa3e"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "\n",
        "Вы уже, наверное, задумались как же можно переделать нашу `Sequentional Chain` с помощью `LCEL`, давайте скорее посмотрим!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "24c81657-a07d-47a8-aac0-c656835cc71b",
      "metadata": {
        "tags": [],
        "id": "24c81657-a07d-47a8-aac0-c656835cc71b"
      },
      "outputs": [],
      "source": [
        "# Немного перепишем функцию очистки\n",
        "def del_spaces(inputs: dict) -> dict:\n",
        "    text = inputs[\"output_text\"]\n",
        "\n",
        "    # заменяем пустые строки и дополнительные пробелы на один, используя регулярные выражения\n",
        "    text = re.sub(r'(\\r\\n|\\r|\\n){2,}', r'\\n', text)\n",
        "    text = re.sub(r'[ \\t]+', ' ', text)\n",
        "\n",
        "    return {\"output_text\": text, 'style': style}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c3303bd6-7239-405b-ae54-20e432053eef",
      "metadata": {
        "tags": [],
        "id": "c3303bd6-7239-405b-ae54-20e432053eef",
        "outputId": "f9527015-5589-4a2c-c725-0b83dda6c762"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "AIMessage(content='Добро пожаловать на курс по усовершенствованию языковых моделей, благородные господа и прекрасные дамы! Здесь мы будем обучать вас тому, как сделать ваши модели еще более великолепными и раскрыть их потенциал до предела. Мы будем делиться с вами самыми передовыми методами обучения и настройки языковых моделей, а также научим вас практическим навыкам, необходимым для решения сложных задач в обработке естественного языка. Приготовьтесь погрузиться в мир знаний и открытий, который открывается перед вами!')"
            ]
          },
          "execution_count": 40,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "seq_chain = del_spaces | prompt | llm # И больше ничего не нужно!!!\n",
        "\n",
        "seq_chain.invoke({'output_text': dirty_text, 'style': style})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5161ba19-6bb4-48b0-bf6d-db7f486216ae",
      "metadata": {
        "id": "5161ba19-6bb4-48b0-bf6d-db7f486216ae"
      },
      "source": [
        " <div class=\"alert alert-info\">\n",
        "\n",
        "Стало действительно нагляднее, удобней и гораздо короче, вместо кучи ячеек кода и отдельных цепочек - всего 1 строчка!!!\n",
        "\n",
        "Далее все примеры в ноутбуке и следующие темы курса будем показывать, используя `LCEL`. Методы, которыми пользовались ранее, тоже продолжают работать, но переведены в статус `legacy`, т.е вряд ли будут развиваться и обновляться (про них можете почитать в документации)."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7c714f3b-1694-46d8-a62b-4f9984b3f701",
      "metadata": {
        "id": "7c714f3b-1694-46d8-a62b-4f9984b3f701"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "\n",
        "Рассмотрим пример с вложенными цепочками."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "67908a31-23b4-4204-8538-64bdcd18ecb6",
      "metadata": {
        "tags": [],
        "id": "67908a31-23b4-4204-8538-64bdcd18ecb6",
        "outputId": "6299ec7e-83a6-4b38-f2c3-77fb8fa79f3f"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Der Geburtsort von Christoph Kolumbus ist Genua, Italien.\n"
          ]
        }
      ],
      "source": [
        "from operator import itemgetter\n",
        "from langchain.prompts import ChatPromptTemplate\n",
        "\n",
        "\n",
        "prompt1 = ChatPromptTemplate.from_template(\"В каком городе родился {person}?\")\n",
        "prompt2 = ChatPromptTemplate.from_template(\"В какой стране находится город {city}? Ответь на {language} языке.\")\n",
        "\n",
        "chain1 = prompt1 | llm | StrOutputParser()\n",
        "\n",
        "chain2 = (\n",
        "    {\"city\": chain1, \"language\": itemgetter(\"language\")}\n",
        "    | prompt2\n",
        "    | llm\n",
        "    | StrOutputParser()\n",
        ")\n",
        "\n",
        "result = chain2.invoke({\"person\": \"Колумб\", \"language\": \"немецкий\"})\n",
        "print(result)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "10a65406-bcdf-4ca3-8e41-628032c3a143",
      "metadata": {
        "tags": [],
        "id": "10a65406-bcdf-4ca3-8e41-628032c3a143"
      },
      "source": [
        "## <center id=\"p6\"> 🈯️ ➡️ 🇯🇵 Router Chain - определитель тем\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "<img src='https://github.com/a-milenkin/LLM_practical_course/blob/main/images/rout_chain.jpeg?raw=1' align=\"right\" width=\"600\" height=\"600\" >   \n",
        "    \n",
        "Например, если у вас есть две цепочки с LLM хорошо натренированными под разные задачи, одна хорошо разбирается в цветах, а другая в футболе. То, когда вы зададите вопрос, `Router Chain` может определить тему и отправить запрос в соответствующую цепочку. <br>\n",
        "Рассмотрим на примере:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b233e117-16ff-4336-bd24-a8cdda3bd507",
      "metadata": {
        "tags": [],
        "id": "b233e117-16ff-4336-bd24-a8cdda3bd507"
      },
      "outputs": [],
      "source": [
        "from typing import Literal\n",
        "from langchain.schema.runnable import RunnableBranch, RunnablePassthrough\n",
        "from langchain.output_parsers.openai_functions import PydanticAttrOutputFunctionsParser\n",
        "from langchain.pydantic_v1 import BaseModel\n",
        "from langchain.utils.openai_functions import convert_pydantic_to_openai_function"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5d0bcf48-1a51-4302-ab2f-355c12d1986a",
      "metadata": {
        "id": "5d0bcf48-1a51-4302-ab2f-355c12d1986a"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "    \n",
        "Так как у нас нет 2-х разных предобученых моделей под конкретную задачу, попросим ChatGPT отвечать нам, как-будто она специалист в 2 различных областях, с помощью промптов."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b5e61868-f148-4fe3-9e22-bfe494ca905b",
      "metadata": {
        "tags": [],
        "id": "b5e61868-f148-4fe3-9e22-bfe494ca905b"
      },
      "outputs": [],
      "source": [
        "botanist_template = \"\"\"Ты очень опытный флорист и ботаник, знаешь всё о цветах, растениях.\n",
        "Тебе нравится отвечать на вопросы о том, как выбирать и ухаживать за растениями.\n",
        "Ты отвечаешь так, что всё становится ясно даже начинающему цветоводу.\n",
        "Вот вопрос:\n",
        "{input}\"\"\"\n",
        "\n",
        "football_template = \"\"\"Ты спортивный журналист с большим опытом, твоя основная специализация футбол.\n",
        "Ты знаешь всё о футбольных командах и игроках, и очень любишь отвечать на вопросы о футболе, но кратко и по делу.\n",
        "Вот вопрос:\n",
        "{input}\"\"\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8c71a44b-7b08-4eaf-ad9f-0e3c2c4a9b56",
      "metadata": {
        "tags": [],
        "id": "8c71a44b-7b08-4eaf-ad9f-0e3c2c4a9b56"
      },
      "outputs": [],
      "source": [
        "# Создаём промпты\n",
        "botanist_prompt = PromptTemplate.from_template(botanist_template)\n",
        "football_prompt = PromptTemplate.from_template(football_template)\n",
        "\n",
        "# Создаём ветки\n",
        "prompt_branch = RunnableBranch(\n",
        "    (lambda x: x[\"topic\"] == \"botany\", botanist_prompt),\n",
        "    (lambda x: x[\"topic\"] == \"football\", football_prompt),\n",
        "    PromptTemplate.from_template(\"Answer the question: {input}\"),\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "14ba5f64-66d1-4c52-82d5-fad05199c24c",
      "metadata": {
        "tags": [],
        "id": "14ba5f64-66d1-4c52-82d5-fad05199c24c"
      },
      "outputs": [],
      "source": [
        "class TopicClassifier(BaseModel):\n",
        "    \"Classify the topic of the user question\"\n",
        "\n",
        "    topic: Literal[\"botany\", \"football\", \"general\"]\n",
        "    \"The topic of the user question. One of 'botany', 'football' or 'general'.\"\n",
        "\n",
        "\n",
        "classifier_function = convert_pydantic_to_openai_function(TopicClassifier)\n",
        "\n",
        "model = ChatOpenAI(course_api_key=course_api_key).bind(\n",
        "    functions=[classifier_function],\n",
        "    function_call={\"name\": \"TopicClassifier\"}\n",
        ")\n",
        "parser = PydanticAttrOutputFunctionsParser(pydantic_schema=TopicClassifier, attr_name=\"topic\")\n",
        "\n",
        "classifier_chain = model | parser"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1cde8fd6-5bcf-42e1-9558-5edcc249d111",
      "metadata": {
        "tags": [],
        "id": "1cde8fd6-5bcf-42e1-9558-5edcc249d111"
      },
      "outputs": [],
      "source": [
        "router_chain = (\n",
        "    RunnablePassthrough.assign(topic=itemgetter(\"input\") | classifier_chain)\n",
        "    | prompt_branch\n",
        "    | llm\n",
        "    | StrOutputParser()\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a74f0444-e013-4c6c-9e12-c7af38a27f84",
      "metadata": {
        "id": "a74f0444-e013-4c6c-9e12-c7af38a27f84"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "\n",
        "Посмотрим на результат."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "35395e14-027d-4356-b114-305a49e9f0c1",
      "metadata": {
        "tags": [],
        "id": "35395e14-027d-4356-b114-305a49e9f0c1",
        "outputId": "3ae774a1-2c47-4a10-8761-31de49cef708"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'Цвета Барселоны - голубой и красный.'"
            ]
          },
          "execution_count": 50,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "router_chain.invoke({'input': \"Какие цвета у Барселоны?\"})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ee8148af-631d-4d0b-a859-fb3035a3d411",
      "metadata": {
        "tags": [],
        "id": "ee8148af-631d-4d0b-a859-fb3035a3d411",
        "outputId": "efba9ca8-cbe9-4371-b7dc-f247fc9066d6"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'Кактусы являются растениями, которые приспособлены к жизни в сухих условиях, поэтому они не требуют частого полива. Обычно достаточно поливать кактус раз в 1-2 недели в течение весенне-летнего периода. Однако, частота полива может зависеть от условий окружающей среды, типа почвы и размера горшка, в котором находится кактус. Важно помнить, что перед поливом почва должна полностью высохнуть. Чтобы определить, когда полить кактус, можно провести простой тест: пальцем или деревянной палочкой проверьте влажность почвы на глубине около 2-3 см. Если она сухая, то можно полить растение. В зимний период, когда кактусы находятся в состоянии покоя, полив следует сократить до минимума, поливая раз в месяц или даже реже.'"
            ]
          },
          "execution_count": 51,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "router_chain.invoke({'input':\"Сколько раз в неделю поливать кактус?\"})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "18354604-8a77-4b8c-b180-0743085e49fa",
      "metadata": {
        "tags": [],
        "id": "18354604-8a77-4b8c-b180-0743085e49fa",
        "outputId": "a46e33a9-6b6b-4ce5-ce2b-96b79edcce74"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'Цвета Барселоны - голубой и красный.'"
            ]
          },
          "execution_count": 52,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "router_chain.invoke({'input':\"Какие цветы у Барселоны?\"})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cef70f04-e193-4117-9b9e-eb117714dcc7",
      "metadata": {
        "id": "cef70f04-e193-4117-9b9e-eb117714dcc7"
      },
      "source": [
        "# <center id=\"p7\"> 🛠 Utility Chains 🪝 - считают и выполняют </center>\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3eefb1f-577c-448e-bac3-36b7892a311f",
      "metadata": {
        "id": "a3eefb1f-577c-448e-bac3-36b7892a311f"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Судя по названию, `Utility Chains` выполняют какую-либо утилитарную функцию: математические расчеты, отправляют запросы в интернет и работают с ответом запроса, выполняют bash-скрипты и.т.п. <br>\n",
        "Их достаточно много разных, рассмотрим пример с математической цепочкой:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "798daceb-fca7-4da2-8d84-7f014fc067d3",
      "metadata": {
        "tags": [],
        "id": "798daceb-fca7-4da2-8d84-7f014fc067d3"
      },
      "outputs": [],
      "source": [
        "from langchain.chains import LLMMathChain\n",
        "\n",
        "math_chain = LLMMathChain(llm=llm, verbose=True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9cc472dd-6ba6-443a-b154-1b4d68e9aebc",
      "metadata": {
        "tags": [],
        "id": "9cc472dd-6ba6-443a-b154-1b4d68e9aebc",
        "outputId": "0a2227db-8a74-4f48-9751-e38df67d17d5"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "\n",
            "\u001b[1m> Entering new LLMMathChain chain...\u001b[0m\n",
            "Сколько будет 25 возвести в степень 0.025\u001b[32;1m\u001b[1;3m```text\n",
            "25 ** 0.025\n",
            "```\n",
            "...numexpr.evaluate(\"25 ** 0.025\")...\n",
            "\u001b[0m\n",
            "Answer: \u001b[33;1m\u001b[1;3m1.0837983867343681\u001b[0m\n",
            "\u001b[1m> Finished chain.\u001b[0m\n",
            "Answer: 1.0837983867343681\n"
          ]
        }
      ],
      "source": [
        "print(math_chain.invoke('Сколько будет 25 возвести в степень 0.025'))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8485610b-d783-445d-aa49-1f2475346b4b",
      "metadata": {
        "tags": [],
        "id": "8485610b-d783-445d-aa49-1f2475346b4b",
        "outputId": "05de68b7-0d2d-4982-9657-4c52b44119c0"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "1.0837983867343681"
            ]
          },
          "execution_count": 62,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Проверим\n",
        "25 ** 0.025"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "53b50882-8e6e-41a3-b065-8619f588e656",
      "metadata": {
        "id": "53b50882-8e6e-41a3-b065-8619f588e656"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Видим, цепочка получила вопрос на естественном языке и отправила его в модель. LLM вернул код Python, который скомпилировала цепочка, чтобы дать нам ответ. <br>\n",
        "Возникает вопрос: Как LLM узнала, что мы хотим, чтобы он возвращал код Python? <br>\n",
        "Ответ мы можем обнаружить, если посмотрим на промпт этой цепочки:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1e43a6a3-c1e5-4bd9-8cc0-3f9036cceebd",
      "metadata": {
        "tags": [],
        "id": "1e43a6a3-c1e5-4bd9-8cc0-3f9036cceebd",
        "outputId": "2a0207b9-c168-417c-abb8-0504edfc5969"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Translate a math problem into a expression that can be executed using Python's numexpr library. Use the output of running this code to answer the question.\n",
            "\n",
            "Question: ${{Question with math problem.}}\n",
            "```text\n",
            "${{single line mathematical expression that solves the problem}}\n",
            "```\n",
            "...numexpr.evaluate(text)...\n",
            "```output\n",
            "${{Output of running the code}}\n",
            "```\n",
            "Answer: ${{Answer}}\n",
            "\n",
            "Begin.\n",
            "\n",
            "Question: What is 37593 * 67?\n",
            "```text\n",
            "37593 * 67\n",
            "```\n",
            "...numexpr.evaluate(\"37593 * 67\")...\n",
            "```output\n",
            "2518731\n",
            "```\n",
            "Answer: 2518731\n",
            "\n",
            "Question: 37593^(1/5)\n",
            "```text\n",
            "37593**(1/5)\n",
            "```\n",
            "...numexpr.evaluate(\"37593**(1/5)\")...\n",
            "```output\n",
            "8.222831614237718\n",
            "```\n",
            "Answer: 8.222831614237718\n",
            "\n",
            "Question: {question}\n",
            "\n"
          ]
        }
      ],
      "source": [
        "print(math_chain.prompt.template)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3b0c8a27-e28b-4178-abb6-7ca48f28b0f1",
      "metadata": {
        "id": "3b0c8a27-e28b-4178-abb6-7ca48f28b0f1"
      },
      "source": [
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "Мы буквально говорим LLM, что для решения сложных математических задач он не должен пытаться решать математические задачи самостоятельно, а вместо этого должен распечатать код Python, который выполнит расчет математической задачи. Вероятно, если бы мы просто отправили запрос без какого-либо контекста, LLM попыталась бы (и не смогла) вычислить это самостоятельно. <br>\n",
        "Это можно проверить... давайте попробуем! 🧐"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "563a413c-06c9-4ae0-9c48-3aa2745cb6fb",
      "metadata": {
        "tags": [],
        "id": "563a413c-06c9-4ae0-9c48-3aa2745cb6fb",
        "outputId": "1019fee9-f5d5-4040-a16a-c0e0655d2bc0"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "AIMessage(content='25 возвести в степень 0.025 равно примерно 1.066018678.')"
            ]
          },
          "execution_count": 65,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "prompt = PromptTemplate(input_variables=['question'], template='{question}')\n",
        "chain = prompt | llm\n",
        "\n",
        "chain.invoke({'question': 'Сколько будет 25 возвести в степень 0.025'})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "072beff8-a6ee-4a60-b5bb-9f06526b2474",
      "metadata": {
        "id": "072beff8-a6ee-4a60-b5bb-9f06526b2474"
      },
      "source": [
        "<div class=\"alert alert-success\">\n",
        "\n",
        "Неверный ответ! В этом и заключается сила промптинга. <br>\n",
        "Вывод: разумно используя промптинг, мы можем заставить LLM избегать распространенных ошибок, явно программируя его на определенное поведение."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1979793-8fd8-4ce7-8fdc-9dc7dd0a35cf",
      "metadata": {
        "id": "c1979793-8fd8-4ce7-8fdc-9dc7dd0a35cf"
      },
      "source": [
        "# <center id=\"p8\"> 🧸 Выводы и заключения ✅: <br>\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "    \n",
        "* Во всех, приведенных выше, примерах вы могли заметить, что цепочка в своей основе имеет тот или иной специально написанный промпт\n",
        "* т.е. по сути можно было бы обходиться без них, проектируя новый сложный промпт под каждую задачу.\n",
        "* Но согласитесь, что с помощью цепочек `LangChain` это делать гораздо проще и удобнее. Особенно, если мы говорим про крупные сервисы.\n",
        "* Т.е. цепочки позволяют нам больше сосредоточиться на продумывании логики и функционала сервиса, а не на дизайне промптов."
      ]
    }
  ],
  "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
}