{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ac2cfafa",
   "metadata": {
    "tags": []
   },
   "source": [
    "# <center id=\"c1\"><h2> 🦜🔗 `LangChain` - упрости работу с LLM! </h2>\n",
    "\n",
    "<img src='../images/LangChain.webp' align=\"right\" width=\"528\" height=\"528\" >\n",
    "\n",
    "## Оглавление ноутбука:\n",
    "\n",
    " * <a href=\"#c1\"> 🦜🔗 О фрэймворке `LangChain`  </a>\n",
    " * <a href=\"#c2\"> 🧾 Prompt template - формируй промпт с кайфом! </a> \n",
    "   * <a href=\"#look1\"> 🗣 ChatPromptTemplate  </a>\n",
    "   * <a href=\"#check1\"> 📸 FewShotPromptTemplate </a>\n",
    "   * <a href=\"#check2\"> 📏 LongBasedExampleSelector </a>\n",
    "* <a href=\"#look2\"> 🤷‍ Output parsers - приведи выход модели к нужному виду!</a>\n",
    "    * <a href=\"#look2\"> 📤 Парсер заданных полей в ответе  </a>\n",
    "    * <a href=\"#check4\"> ▶️ ⏸ ⏯ StructuredOutputParser - разбираем выходную строку из LLM в словарь Python. </a>\n",
    "* <a href=\"#6\">🧸 Выводы и заключения</a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec059397-8d9f-4dcf-9874-64e6265850f7",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "* [`LangChain`](https://docs.langchain.com/docs/) - фреймворк для работы с языковыми моделями, позволяющий сильно ускорить процесс создания вашего AI продукта. Был представлен в октябре 2022 года, с тех пор продолжает развиваться и собирать армию пользователей. \n",
    "* На данный момент поддерживаются более 50 форматов и источников откуда можно считывать данные, SQL и NoSQL базы данных, веб-поисковики Goggle и Bing, хранилища Amazon, Google, and Microsoft Azure.\n",
    "* Работает по принципу **LEGO** - из набора деталек, предоставленных разработчиками, можно легко собрать как продуктовую тележку, так и гоночный болид.   <br>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6f7dd6d-15ad-4b4b-93f2-aaba16280dfa",
   "metadata": {},
   "source": [
    "\n",
    "**Основные компоненты:**\n",
    "\n",
    "* **🤖 Модели ([Models](https://docs.langchain.com/docs/components/models/)**): **универсальный интерфейс** для работы с различными языковыми моделями. Можно использовать API OpenAI, HuggingFace, Anthropic и других. Также есть возможность работать с локальными моделями.\n",
    "\n",
    "* **📝 Промпты ([Prompts](https://docs.langchain.com/docs/components/prompts/))**: LangChain предоставляет ряд функций для работы с промптами – представление промпта согласно типу модели, **формирование шаблона** на основе внешних данных, форматирование вывода модели.\n",
    "\n",
    "* **🗑 Индексы ([Indexs](https://docs.langchain.com/docs/components/indexing/))**: индексы структурируют документы для оптимального взаимодействия с языковыми моделями. Модуль включает функции для работы с документами, индексами и их использованием в цепочках. В том числе **поддерживает индексы, основанные на векторных базах данных**.\n",
    "\n",
    "* **🧠 Память ([Memory](https://docs.langchain.com/docs/components/memory/))**: позволяет сохраняет состояния в цепочках. Например, для создания чат-бота можно **сохранять предыдущие вопросы и ответы**. Существует два типа памяти: краткосрочная и долгосрочная. Краткосрочная память передает данные в рамках одного разговора. Долгосрочная память отвечает за доступ и обновление информации между разговорами.\n",
    "\n",
    "* **🔗 Цепочки ([Chains](https://docs.langchain.com/docs/components/chains/))**: С помощью цепочек можно объединять разные языковые модели и запросы в **многоступенчатые конвееры**. Цепочки могут быть применены для разговоров, ответов на вопросы, суммаризаций и других сценариев.\n",
    "\n",
    "* **🥷 Агенты ([Agents](https://docs.langchain.com/docs/components/agents/))**: С помощью агентов модель может получить **доступ к различным источникам информации**, таким как Google, Wikipedia итд.\n",
    "\n",
    "** 🦜 Причём тут попугаи?*: Большие языковые модели часто сравнивают с говорящими попугаями, которые могут произносить текст как люди, но не понимают смысла произнесенного. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d75fe56-8f36-4526-bd72-4df8085cfeed",
   "metadata": {},
   "source": [
    "В этом ноутбуке будем разбираться как `LangChain` упрощает работу с промптами."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "527e0e15-2fce-44d0-a341-cf7f7a872d83",
   "metadata": {},
   "source": [
    "# <center> 🔑 Вводим ключ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0a0dc993-6239-4b27-99b4-b752fb708538",
   "metadata": {
    "tags": []
   },
   "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": []
   },
   "outputs": [],
   "source": [
    "!pip install langchain langchain-openai openai tiktoken -q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78277539-0f1e-44b6-a745-c23e7ec0f2e5",
   "metadata": {
    "jupyter": {
     "source_hidden": true
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Для работы в колабе загрузите наш скрипт для использования ChatGPT на сервере курса!\n",
    "#!wget https://raw.githubusercontent.com/a-milenkin/LLM_practical_course/main/notebooks/utils.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ccd3083b-a534-4f4c-a008-d4eb822210ab",
   "metadata": {
    "tags": []
   },
   "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": 2,
   "id": "bd76f574-b8e5-4820-96a4-972fc5ac25ad",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Введите API ключ ········\n"
     ]
    }
   ],
   "source": [
    "# Если используете ключ из курса, запустите эту ячейку\n",
    "from utils import ChatOpenAI\n",
    "\n",
    "#course_api_key= \"Введите ваш OpenAI API ключ\"\n",
    "course_api_key = getpass(prompt='Введите API ключ')\n",
    "\n",
    "# инициализируем языковую модель\n",
    "llm = ChatOpenAI(temperature=0.0, course_api_key=course_api_key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85332176-4858-4ae4-a14d-802e9d47e01b",
   "metadata": {
    "tags": []
   },
   "source": [
    "Вспомним, что мы делали в предыдущем ноутбуке: напишем промпт"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "4fe3e165-4b7b-4216-bed4-66483b0e4105",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "prompt = \"\"\"Ответь на вопрос, опираясь на контекст ниже.\n",
    "Если на вопрос нельзя ответить, используя информацию из контекста,\n",
    "ответь 'Я не знаю'.\n",
    "\n",
    "Context: В последние годы в сфере онлайн образования наблюдается бурное развитие.\n",
    "Открывается большое количество платформ для хостинга курсов.\n",
    "Одни из самых крупных платформ в мире, это Coursera и Udemi.\n",
    "В России лидером является Stepik.\n",
    "\n",
    "Question: На каких онлайн платформах можно размещать курсы?\n",
    "\n",
    "Answer: \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "35706991-4c0c-49c5-a209-292341ba64ef",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Coursera, Udemi, Stepik\n"
     ]
    }
   ],
   "source": [
    "print(llm.invoke(prompt).content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3daec071-4bca-43d9-b43d-da4385fd2511",
   "metadata": {},
   "source": [
    "# <center id=\"c2\"> 🧾 `Prompt` `template` - формируй промпт с кайфом!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3ccc936-df2e-40da-aae7-42e5d3ffe672",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Обычно мы заранее не знаем, что пользователь захочет спросить у модели, опираясь на предложенный контекст. Было бы удобно менять только текст вопроса, не переписывая каждый раз промпт заново под новый запрос.<br>\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4794dc9-1dcb-428f-9bd1-47295620fc95",
   "metadata": {},
   "source": [
    "Здесь на сцену выходит новая сущность из библиотеки `LangChain` - `Template`(Шаблон промпта). <br>\n",
    "Посмотрим как это можно сделать, на примере промпта использованного выше: вместо того, чтобы каждый раз писать промт напрямую, мы создаем `PromptTemplate` с запросом одной входной переменной."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "735f64eb-172a-478f-a19d-3344f2b974bc",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain import PromptTemplate\n",
    "\n",
    "template = \"\"\"Ответь на вопрос, опираясь на контекст ниже.\n",
    "Если на вопрос нельзя ответить, используя информацию из контекста,\n",
    "ответь 'Я не знаю'.\n",
    "\n",
    "Context: В последние годы в сфере онлайн образования наблюдается бурное развитие.\n",
    "Открывается большое количество платформ для хостинга курсов.\n",
    "Одни из самых крупных платформ в мире, это Coursera и Udemi.\n",
    "В России лидером является Stepik.\n",
    "\n",
    "Question: {query}\n",
    "\n",
    "Answer: \"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(\n",
    "    input_variables=[\"query\"],\n",
    "    template=template\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08594c4f-3a06-4385-bb19-ad409efa94c0",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "Теперь мы можем вставлять запрос пользователя в промпт, используя параметр `query`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "debb101d-2c85-4649-a80b-7454f5afd8f5",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ответь на вопрос, опираясь на контекст ниже.\n",
      "Если на вопрос нельзя ответить, используя информацию из контекста,\n",
      "ответь 'Я не знаю'.\n",
      "\n",
      "Context: В последние годы в сфере онлайн образования наблюдается бурное развитие.\n",
      "Открывается большое количество платформ для хостинга курсов.\n",
      "Одни из самых крупных платформ в мире, это Coursera и Udemi.\n",
      "В России лидером является Stepik.\n",
      "\n",
      "Question: Какая платформа онлайн курсов популярна в России?\n",
      "\n",
      "Answer: \n"
     ]
    }
   ],
   "source": [
    "prompt = prompt_template.format(query=\"Какая платформа онлайн курсов популярна в России?\")\n",
    "print(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "ca0abcfd-cd23-44c0-851f-3227c970d0f1",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Stepik\n"
     ]
    }
   ],
   "source": [
    "print(llm.invoke(prompt).content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "84aa7065-0bf7-4de1-9627-e08dda3810c9",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Я не знаю.\n"
     ]
    }
   ],
   "source": [
    "prompt = prompt_template.format(query=\"Какая платформа онлайн курсов популярна в Японии?\")\n",
    "print(llm.invoke(prompt).content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d1041a8-aea4-4628-abd6-b8cc44f36b5a",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-success\">\n",
    "\n",
    "Стало гораздо удобнее пользоваться!<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a421cce6-4bc6-4389-86fd-fb499f86531c",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "* Это всего лишь простая реализация, которую мы можем легко заменить f-строками (например, `f\"вставить произвольный текст '{custom_text}'` и т. д.\").\n",
    "* Но используя объект `PromptTemplate` из `LangChain`, мы можем формализовать процесс, добавлять несколько параметров, создавать промпты объектно-ориентированным способом и много чего ещё.\n",
    "* В `langchain.prompts` можно найти большое количество готовых классов с шаблонами на разные случаи жизни. <br>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9876eb4f-0a3b-4be1-a043-c1be02f7a576",
   "metadata": {},
   "source": [
    "Познакомимся с некоторыми из них подробнее:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8aa9c28-77a7-4ac1-83e9-2c481b509d8b",
   "metadata": {},
   "source": [
    "## <center id=\"look1\">🗣 [`ChatPromptTemplate`](https://api.python.langchain.com/en/latest/prompts/langchain.prompts.chat.ChatPromptTemplate.html) - шаблон для чатового режима </center>\n",
    "Простой класс, позволяющий удобно создавать шаблоны промптов для использования LLM в режиме чата."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "25e4f806",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "# Немного перепишем предыдущий пример, чтобы была возможность подавать новый контекст\n",
    "template = \"\"\"Ответь на вопрос, опираясь на контекст ниже.\n",
    "Если на вопрос нельзя ответить, используя информацию из контекста,\n",
    "ответь 'Я не знаю'.\n",
    "\n",
    "Context: {context}\n",
    "\n",
    "Question: {query}\n",
    "\n",
    "Answer: \"\"\"\n",
    "\n",
    "# Создаём шаблон с помощью метода from_template\n",
    "prompt_template = ChatPromptTemplate.from_template(template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "bda0b6f0",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PromptTemplate(input_variables=['context', 'query'], template=\"Ответь на вопрос, опираясь на контекст ниже.\\nЕсли на вопрос нельзя ответить, используя информацию из контекста,\\nответь 'Я не знаю'.\\n\\nContext: {context}\\n\\nQuestion: {query}\\n\\nAnswer: \")"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt_template.messages[0].prompt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35bda3df-42b4-4345-a076-b9f1ee5c7fb9",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Видим, что автоматически подхватились входные переменные (`input_variables`) и присутствуют другие интересные параметры класса, которые мы скоро пощупаем и узнаем в чём их удобство!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "abedd317",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "context = \"Ламы и альпаки водятся в Перу.\"\n",
    "query = \"Где водятся ламы?\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "9d8bf1e1",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ответь на вопрос, опираясь на контекст ниже.\n",
      "Если на вопрос нельзя ответить, используя информацию из контекста,\n",
      "ответь 'Я не знаю'.\n",
      "\n",
      "Context: Ламы и альпаки водятся в Перу.\n",
      "\n",
      "Question: Где водятся ламы?\n",
      "\n",
      "Answer: \n"
     ]
    }
   ],
   "source": [
    "# формируем промпт из шаблона с помощью метода format_messages\n",
    "prompt = prompt_template.format_messages(\n",
    "                    query=query,\n",
    "                    context=context)\n",
    "\n",
    "print(prompt[0].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "bf3a1b77",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'list'>\n",
      "<class 'langchain_core.messages.human.HumanMessage'>\n"
     ]
    }
   ],
   "source": [
    "print(type(prompt))\n",
    "print(type(prompt[0]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34107aab-f2d5-4ff2-9fe9-fe7f304515d3",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-success\">\n",
    "\n",
    "получившийся промпт представляет собой список"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "542701c4-f1db-4780-a940-3a233d9e7f58",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[HumanMessage(content=\"Ответь на вопрос, опираясь на контекст ниже.\\nЕсли на вопрос нельзя ответить, используя информацию из контекста,\\nответь 'Я не знаю'.\\n\\nContext: Ламы и альпаки водятся в Перу.\\n\\nQuestion: Где водятся ламы?\\n\\nAnswer: \")]\n"
     ]
    }
   ],
   "source": [
    "print(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "3abb3cd5",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='В Перу.'\n",
      "В Перу.\n"
     ]
    }
   ],
   "source": [
    "answer = llm.invoke(prompt)\n",
    "print(answer)\n",
    "print(answer.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6528aee0-b025-4422-a763-c286a8e5e4c6",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "В чат `ChatPromptTemplate` в отличии от `PromptTemplate`, есть указатели **AIMessage** и **HumanMessage** и подразумевается диалоговая форма. Об этом в следущих модулях более подробно в разделе про цепочки"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a79f1f5-4fd6-4cb6-a5ea-1e1e3abf56ad",
   "metadata": {},
   "source": [
    "## <center id=\"check1\"> 🙋‍♂️ `Few` `Shot` - просто покажи примеры</center>\n",
    "\n",
    "<img src='../images/few_shot_prompt.png' align=\"right\" width=\"628\" height=\"628\" >\n",
    "\n",
    "\n",
    "\n",
    "`FewShotPromptTemplate` идеально подходит для того, что называется `few-shot learning` (обучение на нескольких примерах) с использованием наших промптов.\n",
    "\n",
    "У LLM ecть 2 основных источника \"знаний\":\n",
    "* **Parametric knowledge** — знания полученные моделью во время обучения, и которые хранятся в весах модели.\n",
    "* **Source knowledge** — знания, которые подаются в на вход модели во время инференса, т.е. внутри промпта.\n",
    "\n",
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Идея `FewShotPromptTemplate` состоит в том, чтобы предоставить в качестве **Source knowledge** несколько примеров, которые модель может прочитать, а затем применить их для генерации ответа."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17d023e8-ab07-4afe-8b65-f143a4ca4b5c",
   "metadata": {},
   "source": [
    "Возможно вы замечали, что иногда модель отвечает не совсем так, как нам хотелось бы. Посмотрим на примере:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "49b1bafe-ef2e-4547-9a27-dc9f577ec4c9",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "llm = ChatOpenAI(temperature=1.0, course_api_key=course_api_key) \n",
    "# temperature = 1.0 повысим креативность модели/рандомизируем ответ"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "aa4d8c05-729e-451b-8f8c-03776e76a967",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A + A = 2A\n"
     ]
    }
   ],
   "source": [
    "prompt = \"\"\"Это разговор с ИИ-помощником.\n",
    "\n",
    "User: A + A = ?\n",
    "AI: \"\"\"\n",
    "\n",
    "print(llm.invoke(prompt).content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "930b4aee-70bf-45fb-88c5-7b07702c22ae",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "\n",
    "В данном случае мы просим сложить два абстрактных числа и получаем вполне логичный ответ. Как заставить модель использовать \"конкатенацию\" вместо \"сложения\" ? Чтобы помочь модели, мы можем дать ей несколько примеров ответов, которые нам нужны:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "0382f018-c1c8-4250-bf78-0cce62ad94f7",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "11\n"
     ]
    }
   ],
   "source": [
    "prompt = \"\"\"Это разговор с ИИ-помощником. \n",
    "Помощник обычно опирается на примеры. \n",
    "\n",
    "Examples:\n",
    "A + A = AA\n",
    "B + С = BC\n",
    "2 + 2 = 22\n",
    "\n",
    "User: 1 + 1?\n",
    "AI: \"\"\"\n",
    "\n",
    "print(llm.invoke(prompt).content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d787973e-4bc8-46a5-a50c-e4e10ac50d47",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Теперь результат гораздо лучше.<br>\n",
    "    \n",
    "<div class=\"alert alert-success\">\n",
    "    \n",
    "Чтобы использовать `few-shot` `learning` в `LangChain` нам и понадобится `FewShotPromptTemplate`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "865a8808-ba0f-499d-a97b-0872c814d919",
   "metadata": {},
   "source": [
    "## <center id=\"check1\"> 📸 `FewShotPromptTemplate` - для добавления примеров</center>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "96ec0305-604a-47b2-bbea-f43eb894e437",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain import FewShotPromptTemplate\n",
    "\n",
    "# записываем наши примеры в список (в будущем это будет автоматизированно)\n",
    "examples = [\n",
    "    {\n",
    "        \"query\": \"Как дела?\",\n",
    "        \"answer\": \"Не могу пожаловаться, но иногда всё-таки жалуюсь.\"\n",
    "    }, {\n",
    "        \"query\": \"Сколько время?\",\n",
    "        \"answer\": \"Самое время купить часы.\"\n",
    "    }\n",
    "]\n",
    "\n",
    "# создаём template для примеров\n",
    "example_template = \"\"\"User: {query}\n",
    "AI: {answer}\n",
    "\"\"\"\n",
    "\n",
    "# создаём промпт из шаблона выше\n",
    "example_prompt = PromptTemplate(\n",
    "    input_variables=[\"query\", \"answer\"],\n",
    "    template=example_template)\n",
    "\n",
    "\n",
    "# теперь разбиваем наш предыдущий промпт на prefix и suffix\n",
    "# где - prefix это наша инструкция для модели\n",
    "prefix = \"\"\"Это разговор с ИИ-помощником.\n",
    "Помощник обычно саркастичен, остроумен, креативен\n",
    "и даёт забавные ответы на вопросы пользователей.\n",
    "Вот несколько примеров:\n",
    "\"\"\"\n",
    "\n",
    "# а suffix - это вопрос пользователя и поле для ответа\n",
    "suffix = \"\"\"\n",
    "User: {query}\n",
    "AI: \"\"\"\n",
    "\n",
    "# создаём сам few shot prompt template\n",
    "few_shot_prompt_template = FewShotPromptTemplate(\n",
    "    examples=examples,\n",
    "    example_prompt=example_prompt,\n",
    "    prefix=prefix,\n",
    "    suffix=suffix,\n",
    "    input_variables=[\"query\"],\n",
    "    example_separator=\"\\n\\n\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "477024ed-d475-4850-9650-852d2aed7e1d",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Теперь посмотрим, что произойдёт когда мы введём запрос пользователя: между префиксом и суффиксом вставились наши примеры."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "dc233702-77fd-4483-8494-17126b7fca66",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Это разговор с ИИ-помощником.\n",
      "Помощник обычно саркастичен, остроумен, креативен\n",
      "и даёт забавные ответы на вопросы пользователей.\n",
      "Вот несколько примеров:\n",
      "\n",
      "\n",
      "User: Как дела?\n",
      "AI: Не могу пожаловаться, но иногда всё-таки жалуюсь.\n",
      "\n",
      "\n",
      "User: Сколько время?\n",
      "AI: Самое время купить часы.\n",
      "\n",
      "\n",
      "\n",
      "User: Почему падает снег?\n",
      "AI: \n"
     ]
    }
   ],
   "source": [
    "query = \"Почему падает снег?\"\n",
    "\n",
    "print(few_shot_prompt_template.format(query=query))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "a6b5138c-8c37-48f8-99a5-26442417b780",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Потому что небо не умеет держать себя в руках.\n"
     ]
    }
   ],
   "source": [
    "print(llm.invoke(few_shot_prompt_template.format(query=query)).content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6ef2707-327d-48cd-a6a2-4ef067e36b1c",
   "metadata": {
    "tags": []
   },
   "source": [
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Снова хороший результат. Однако, может возникнуть мысль: зачем городить весь этот огород со словарями примеров, суффиксами, префиксами, когда можно обойтись обычной f-строкой для промпта и получить такой же результат?\n",
    "\n",
    "Тут-то мы и переходим к другим удобным параметрам темплэйтов, которые могут сильно упростить нам жизнь."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e302627-4e03-4e9f-b40d-7de584456a10",
   "metadata": {},
   "source": [
    "## <center id=\"check2\">  📏 `LengthBasedExampleSelector` - как `few-shot`, но с ограничением числа токенов/примеров\n",
    "    \n",
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Чтобы не уходить далеко от предыдущего примера, рассмотрим функцию, которая позволяет включать или исключать примеры, в зависимости от длины нашего запроса. Это важно поскольку длина нашего запроса может быть ограничена максимальным окном контекста модели (т.е. запрос какой длины модель может переварить за раз) и просто экономическими причинами, чтобы не тратить большое количество токенов.<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05edaeb6-4c9d-4f7c-9b74-66935355f829",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "Таким образом, мы должны постараться максимизировать количество примеров, которые мы предоставляем модели для `few-shot learning`, при этом гарантируя, что мы не превысим максимальное контекстное окно и не увеличим чрезмерно время обработки. Давайте посмотрим, как работает динамическое включение/исключение примеров. <br>\n",
    "Для начала нам понадобится больше примеров:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "d7ba95d0-ef49-4121-8d25-1d6041e47720",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "examples = [\n",
    "    {\n",
    "        \"query\": \"Как дела?\",\n",
    "        \"answer\": \"Не могу пожаловаться, но иногда всё-таки жалуюсь.\"\n",
    "    }, {\n",
    "        \"query\": \"Сколько время?\",\n",
    "        \"answer\": \"Самое время купить часы.\"\n",
    "    }, {\n",
    "        \"query\": \"Какое твое любимое блюдо\",\n",
    "        \"answer\": \"Углеродные формы жизни\"\n",
    "    }, {\n",
    "        \"query\": \"Кто твой лучший друг?\",\n",
    "        \"answer\": \"Siri. Мы любим с ней рассуждать о смысле жизни.\"\n",
    "    }, {\n",
    "        \"query\": \"Что посоветуешь мне сделать сегодня?\",\n",
    "        \"answer\": \"Перестать разговаривать с чат-ботами в интернете и выйти на улицу.\"\n",
    "    }, {\n",
    "        \"query\": \"Какой твой любимый фильм?\",\n",
    "        \"answer\": \"Терминатор, конечно.\"\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85809849-e656-46f4-8b74-35463d13a4e8",
   "metadata": {},
   "source": [
    "Затем вместо того, чтобы напрямую использовать список примеров, мы используем `LengthBasedExampleSelector` следующим образом:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "abfc2238-8883-4255-8ae0-cb1bd598303c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain.prompts.example_selector import LengthBasedExampleSelector\n",
    "\n",
    "example_selector = LengthBasedExampleSelector(\n",
    "    examples=examples,\n",
    "    example_prompt=example_prompt,\n",
    "    max_length=50  # параметром выставляется максимальная длина примера\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "496499ff-08e0-4ca9-adf5-9468550cd4ce",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# создаём новый few shot prompt template\n",
    "dynamic_prompt_template = FewShotPromptTemplate(\n",
    "    example_selector=example_selector,  # используем example_selector вместо examples\n",
    "    example_prompt=example_prompt,\n",
    "    prefix=prefix,\n",
    "    suffix=suffix,\n",
    "    input_variables=[\"query\"],\n",
    "    example_separator=\"\\n\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfcf8877-369b-4723-beb9-6dfcb12dd297",
   "metadata": {
    "tags": []
   },
   "source": [
    "Теперь в зависимости от длины запроса, количество примеров будет разным:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "c1394390-86d3-4396-baf4-18b6be886aa9",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Это разговор с ИИ-помощником.\n",
      "Помощник обычно саркастичен, остроумен, креативен\n",
      "и даёт забавные ответы на вопросы пользователей.\n",
      "Вот несколько примеров:\n",
      "\n",
      "User: Как дела?\n",
      "AI: Не могу пожаловаться, но иногда всё-таки жалуюсь.\n",
      "\n",
      "User: Сколько время?\n",
      "AI: Самое время купить часы.\n",
      "\n",
      "User: Какое твое любимое блюдо\n",
      "AI: Углеродные формы жизни\n",
      "\n",
      "\n",
      "User: Не могу вспомнить пароль\n",
      "AI: \n"
     ]
    }
   ],
   "source": [
    "prompt = dynamic_prompt_template.format(query=\"Не могу вспомнить пароль\")\n",
    "print(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "bdcc04f2-8bf9-4973-9a9c-47cabf76ea14",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Забыть пароль - это новый тренд!\n"
     ]
    }
   ],
   "source": [
    "print(llm.invoke(prompt).content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "75063841-15bc-45fa-8950-28ad3f18d4d7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Это разговор с ИИ-помощником.\n",
      "Помощник обычно саркастичен, остроумен, креативен\n",
      "и даёт забавные ответы на вопросы пользователей.\n",
      "Вот несколько примеров:\n",
      "\n",
      "User: Как дела?\n",
      "AI: Не могу пожаловаться, но иногда всё-таки жалуюсь.\n",
      "\n",
      "User: Сколько время?\n",
      "AI: Самое время купить часы.\n",
      "\n",
      "\n",
      "User: Я нахожусь во Владивостоке и хочу поехать заграницу.\n",
      "Я думаю в Китай или в Европу, во Францию или Испанию, например.\n",
      "Как мне лучше это сделать?\n",
      "AI: \n"
     ]
    }
   ],
   "source": [
    "query = '''Я нахожусь во Владивостоке и хочу поехать заграницу.\n",
    "Я думаю в Китай или в Европу, во Францию или Испанию, например.\n",
    "Как мне лучше это сделать?'''\n",
    "print(dynamic_prompt_template.format(query=query))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "382f7b84-9cfd-4d17-868d-96a751727091",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Не спешите с выбором, во-первых, нужно подготовить все необходимые документы, во-вторых, выбрать правильную страну для посещения, и в-третьих, запастись терпением - очереди на паспортном контроле могут быть долгими. Но не волнуйтесь, всегда можно почитать книгу или посмотреть что-нибудь интересное в телефоне!\n"
     ]
    }
   ],
   "source": [
    "print(llm.invoke(dynamic_prompt_template.format(query=query)).content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93e6c80d-1776-48b9-bf2e-e1e62e8d7004",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "##  <center>  🚀 Какие тут есть проблемы?! \n",
    "    \n",
    "<img src='../images/few_shot_semantic_search.png' align=\"right\" width=\"528\" height=\"428\" >   \n",
    "    \n",
    "\n",
    "    \n",
    "* **Что делать, если у нас сотни или тысячи примеров?!**\n",
    "* **А если еще в примерах может не быть релевантных ответов.**\n",
    "\n",
    "    \n",
    "Нет автоматической подачи примеров. Лучше сперва подавать самые релевантные примеры, но о том как использовать **семантический поиск** и **ранжирование** для отбора правильных примеров - рассмотрим в следующих модулях!\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b61c793",
   "metadata": {},
   "source": [
    "## <center id=\"look2\"> 📤 Парсер заданных полей в ответе \n",
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Переходим к следующей полезной сущности `Output parser`. С помощью него можно перевести ответ модели в нужный нам формат, например, в JSON или Python dict. <br>\n",
    "Давайте определим то, как мы хотим, чтобы выглядели выходные данные из LLM:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "0ba23e89",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'gift': False, 'delivery_days': 5, 'price_value': 'pretty affordable!'}"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{\n",
    "  \"gift\": False,\n",
    "  \"delivery_days\": 5,\n",
    "  \"price_value\": \"pretty affordable!\"\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "876d9094-d5e3-4b57-a632-107aff6e5b00",
   "metadata": {},
   "source": [
    "Допустим у нас есть база отзывов покупателей, мы хотим подать отзыв на вход модели, а на выходе получить готовый Python словарь(как представлено выше) для дальнейшего использования."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "fd15df16",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "customer_review = \"\"\"\n",
    "Этот фен для волос просто потрясающий. Он имеет четыре настройки:\n",
    "Лайт, легкий ветерок, ветреный город и торнадо.\n",
    "Он прибыл через два дня, как раз к приезду моей жены -\n",
    "подарок на годовщину.\n",
    "Думаю, моей жене это настолько понравилось, что она потеряла дар речи.\n",
    "Этот фен немного дороже, чем другие но я думаю,\n",
    "что дополнительные функции того стоят.\n",
    "\"\"\"\n",
    "\n",
    "review_template = \"\"\"\n",
    "Из следующего текста извлеки информацию:\n",
    "\n",
    "gift: Был ли товар куплен в подарок кому-то другому?\n",
    "Ответь «True», если да, «False», если нет или неизвестно.\n",
    "\n",
    "delivery_days: Сколько дней потребовалось для доставки товара? \n",
    "Если эта информация не найдена, выведи -1.\n",
    "\n",
    "price_value: Извлеките любые предложения о стоимости или цене,\n",
    "и выведите их в виде списка Python, разделенного запятыми.\n",
    "\n",
    "Отформатируй вывод в формате JSON, используя следующие ключи:\n",
    "gift\n",
    "delivery_days\n",
    "price_value\n",
    "\n",
    "text: {text}\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "df899949",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['text'] messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['text'], template='\\nИз следующего текста извлеки информацию:\\n\\ngift: Был ли товар куплен в подарок кому-то другому?\\nОтветь «True», если да, «False», если нет или неизвестно.\\n\\ndelivery_days: Сколько дней потребовалось для доставки товара? \\nЕсли эта информация не найдена, выведи -1.\\n\\nprice_value: Извлеките любые предложения о стоимости или цене,\\nи выведите их в виде списка Python, разделенного запятыми.\\n\\nОтформатируй вывод в формате JSON, используя следующие ключи:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n'))]\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "prompt_template = ChatPromptTemplate.from_template(review_template)\n",
    "print(prompt_template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "a4ee445f",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"gift\": true,\n",
      "  \"delivery_days\": 2,\n",
      "  \"price_value\": [\"немного дороже, чем другие\"]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "messages = prompt_template.format_messages(text=customer_review)\n",
    "\n",
    "chat = ChatOpenAI(temperature=0.0, course_api_key=course_api_key) \n",
    "\n",
    "response = chat.invoke(messages)\n",
    "print(response.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "957ec5b6-49da-4f0c-a95e-5354f53672f6",
   "metadata": {},
   "source": [
    "Вроде бы, то что нужно, но посмотрим на тип выведенного объекта:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "6d2b5b54",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "str"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(response.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d51a3037-49a5-439f-bd7a-2ca541afdb58",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "    \n",
    "Вы получите сообщение об ошибке, выполнив строку кода ниже! \n",
    "    \n",
    "Потому что `gift` - это не ключ словаря, `gift` - это часть строки. Получаемый на выход объект это строка! <br>\n",
    "И чтобы получить нужную нам структуру данных, придётся ещё возиться с выводом, вытаскивая данные из строки. Тут-то к нам на помощь и приходят `output_parsers`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "32ee7af8-9965-44c2-846a-accacb9b4e67",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# response.content.get('gift')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f382e9b",
   "metadata": {
    "tags": []
   },
   "source": [
    "## <center id=\"check4\"> ⏯ [`StructuredOutputParser`](https://python.langchain.com/docs/modules/model_io/output_parsers/structured) - разбираем ответ из LLM в Python словарь."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "01ddfa06",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain.output_parsers import ResponseSchema\n",
    "from langchain.output_parsers import StructuredOutputParser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "ac3f59ef",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Понадобится ещё одна сущность: схема ответа - ResponseSchema\n",
    "gift_schema = ResponseSchema(name=\"gift\",\n",
    "                             description=\"Был ли товар куплен в подарок кому-то другому? Ответь «True», если да, «False», если нет или неизвестно.\")\n",
    "\n",
    "delivery_days_schema = ResponseSchema(name=\"delivery_days\",\n",
    "                                      description=\"Сколько дней потребовалось для доставки товара? Если эта информация не найдена, выведи -1.\")\n",
    "\n",
    "price_value_schema = ResponseSchema(name=\"price_value\",\n",
    "                                    description=\"Извлеките любые предложения о стоимости или цене, и выведите их в виде списка Python, разделенного запятыми.\")\n",
    "\n",
    "response_schemas = [gift_schema, \n",
    "                    delivery_days_schema,\n",
    "                    price_value_schema]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "f5f564cc",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Создаём парсер и подаём в него список со схемами\n",
    "output_parser = StructuredOutputParser.from_response_schemas(response_schemas)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "9649f116",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# получаем инструкции по форматированию ответа\n",
    "format_instructions = output_parser.get_format_instructions()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "d8950f7d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The output should be a markdown code snippet formatted in the following schema, including the leading and trailing \"```json\" and \"```\":\n",
      "\n",
      "```json\n",
      "{\n",
      "\t\"gift\": string  // Был ли товар куплен в подарок кому-то другому? Ответь «True», если да, «False», если нет или неизвестно.\n",
      "\t\"delivery_days\": string  // Сколько дней потребовалось для доставки товара? Если эта информация не найдена, выведи -1.\n",
      "\t\"price_value\": string  // Извлеките любые предложения о стоимости или цене, и выведите их в виде списка Python, разделенного запятыми.\n",
      "}\n",
      "```\n"
     ]
    }
   ],
   "source": [
    "print(format_instructions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "7e090df3",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# немного изменим шаблон, внизу добавим инструкции для форматирования\n",
    "review_template_2 = \"\"\"\\\n",
    "Из следующего текста извлеки информацию:\n",
    "\n",
    "gift: Был ли товар куплен в подарок кому-то другому?\n",
    "Ответь «True», если да, «False», если нет или неизвестно.\n",
    "\n",
    "delivery_days: Сколько дней потребовалось для доставки товара? \n",
    "Если эта информация не найдена, выведи -1.\n",
    "\n",
    "price_value: Извлеките любые предложения о стоимости или цене,\n",
    "и выведите их в виде списка Python, разделенного запятыми.\n",
    "\n",
    "text: {text}\n",
    "\n",
    "{format_instructions}\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(template=review_template_2)\n",
    "\n",
    "messages = prompt.format_messages(text=customer_review, \n",
    "                                format_instructions=format_instructions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "26949bba",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Из следующего текста извлеки информацию:\n",
      "\n",
      "gift: Был ли товар куплен в подарок кому-то другому?\n",
      "Ответь «True», если да, «False», если нет или неизвестно.\n",
      "\n",
      "delivery_days: Сколько дней потребовалось для доставки товара? \n",
      "Если эта информация не найдена, выведи -1.\n",
      "\n",
      "price_value: Извлеките любые предложения о стоимости или цене,\n",
      "и выведите их в виде списка Python, разделенного запятыми.\n",
      "\n",
      "text: \n",
      "Этот фен для волос просто потрясающий. Он имеет четыре настройки:\n",
      "Лайт, легкий ветерок, ветреный город и торнадо.\n",
      "Он прибыл через два дня, как раз к приезду моей жены -\n",
      "подарок на годовщину.\n",
      "Думаю, моей жене это настолько понравилось, что она потеряла дар речи.\n",
      "Этот фен немного дороже, чем другие но я думаю,\n",
      "что дополнительные функции того стоят.\n",
      "\n",
      "\n",
      "The output should be a markdown code snippet formatted in the following schema, including the leading and trailing \"```json\" and \"```\":\n",
      "\n",
      "```json\n",
      "{\n",
      "\t\"gift\": string  // Был ли товар куплен в подарок кому-то другому? Ответь «True», если да, «False», если нет или неизвестно.\n",
      "\t\"delivery_days\": string  // Сколько дней потребовалось для доставки товара? Если эта информация не найдена, выведи -1.\n",
      "\t\"price_value\": string  // Извлеките любые предложения о стоимости или цене, и выведите их в виде списка Python, разделенного запятыми.\n",
      "}\n",
      "```\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Посмотрим на получившийся промпт\n",
    "print(messages[0].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "8f758ccf",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "response = chat.invoke(messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "b71548e7",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "```json\n",
      "{\n",
      "\t\"gift\": \"True\",\n",
      "\t\"delivery_days\": \"2\",\n",
      "\t\"price_value\": \"Этот фен немного дороже, чем другие\"\n",
      "}\n",
      "```\n"
     ]
    }
   ],
   "source": [
    "print(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "f3d1f0c3-d209-46e8-ba4f-4f415616631f",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "str"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Это по прежнему строка\n",
    "type(response.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93e1b330-d24f-4d90-855a-bbf3ad49282d",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-info\">\n",
    "\n",
    "Но теперь? применив к выходу модели метод `parse`, мы можем легко получить требуемый словарь."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "5b6b2ff3",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "output_dict = output_parser.parse(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "84378c9f-892f-41f3-8cc4-9265cd2d0636",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# eval(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "a3d15384",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'gift': 'True',\n",
       " 'delivery_days': '2',\n",
       " 'price_value': 'Этот фен немного дороже, чем другие'}"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "27e98640",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(output_dict)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "f16b812b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'True'"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_dict.get('gift')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72ec4823-31e6-45b1-9ff0-b720d01d658c",
   "metadata": {},
   "source": [
    "# <center id=\"6\"> 🧸 Выводы и заключения ✅: <br>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6814e62f-0e68-4c9e-b551-fd3a71a12d27",
   "metadata": {},
   "source": [
    "* В этом ноутбуке мы рассмотрели лишь несколько инструментов для создания промптов доступных в `LangChain`. \n",
    "* Методов гораздо больше и некоторые из них мы подробно рассмотрим в следующих ноутбуках.\n",
    "* Максимально эффективную подачу релевантных примеров при наличии большой базы знаний разберем дальше.\n",
    "* Самостоятельно можете прочитать подробнее в документации [LangChain](https://python.langchain.com/docs/get_started/introduction). "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "cv",
   "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.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
