{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## 이 문서를 수정할 당신에게...\n", "#### 현재 상황은 아래와 같습니다.\n", "1. 현재 Agent의 action을 7단계로 나누어 놓았지만 이것이 최선은 아니라는 생각이 듭니다. 또한 와인바 추천등에 대한 단계는 구현되어 있지 않습니다.\n", "2. Assistant가 Agent가 다음에 취해야할 Action예측을 잘 못하고 있습니다. 이로인해서 Agent의 성능이 크게 떨어지고 있습니다. \n", "\n", "#### 당신의 목표는 아래와 같습니다.\n", "1. 현재의 7단계로 나누어놓은 것을 와인바 추천 단계 등을 추가하고, 적절히 수정합니다. 그리고 이를 [프롬프트](templates/stage_analyzer_inception_prompt_template.txt)에 반영합니다.\n", "2. 최대한 Action 예측의 성능을 올리기.(이를 측정하기 위한 evaluation set을 제공할 것 입니다. 하지만 그를 위해서는 먼저 Agent action의 step을 정해서 알려주셔야 합니다.) 최종 목표는 evaluation set에 대해 95% 이상으로 Agent의 다음 action을 예측하는 것입니다.\n", "\n", "#### 생각할 수 있는 해결 방식은 아래와 같습니다.\n", "문제 1에 대하여....\n", "- 제공되어 있는 와인바에서 일어날 수 있는 대화셋으로부터 Agent가 취할 action을 세분화합니다. 이후 이에 따라 프롬프트를 수정합니다.\n", "\n", "문제 2에 대하여....\n", "- Trial and Error를 통한 프롬프트 수정\n", "- 유사한 example 프롬프트에 넣어주기: 현재는 프롬프트에 고정된 action 예측 예시를 적어놓았습니다. 이를 현재 conversation과 유사도가 높은 예시를 뽑아 프롬프트에 동적으로 넣어주는 것입니다. 다음 링크를 참고해보세요. [langchain select by similarity 링크](https://python.langchain.com/docs/modules/model_io/prompts/example_selectors/similarity)\n", "- Self Refine 사용하기: self refine은 생성한 결과에 대해서 스스로 피드백을 주고, 이 피드백을 이용해 다시 개선하는 과정을 반복하는 방식입니다. 이 방식은 적용하는데 시간이 좀 걸릴듯 하니 후순위로 생각하세요. [논문](https://arxiv.org/pdf/2303.17651.pdf)\n", "\n", "\n", "#### 참고 사항\n", "프롬프트 수정은 고치고, 시행하고를 반복하는 고통스러운 노가다 과정입니다. 정말 힘들겠지만 화이팅 해주세요..\n", "\n", "아래는 위의 작업을 수행하기 위한 좋은 자료들입니다. 위의 문제해결 전에 아래 자료들을 먼저 학습하는 것을 매우 강력하게 추천합니다.\n", "- [프롬프트 엔지니어링 강의 2시간](https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/)\n", "- [프롬프트 엔지니어링 가이드 documentation](https://www.promptingguide.ai/techniques)\n", "- [Langchain 예시: Sales GPT](https://python.langchain.com/docs/use_cases/agents/sales_agent_with_context)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "----------------------------------------------------------------" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Assistant는 대화기록을 보고, 다음에 Agent가 취해야할 가장 적절한 행동을 선택하는 것을 목표로 한다.\n", "\n", "Agent의 행동은 7가지로 구성된다.\n", "1. 시작하기: 대화를 시작하거나 사용자의 첫 번째 응답에 응답할 때 취해야 할 첫 번째 단계입니다. 자신을 소개하는 것으로 대화를 시작합니다. 전문적인 대화 톤을 유지하면서 정중하고 존중하는 태도를 취하세요.\n", "2. 분석: 고객이 추천을 원할 경우 추천 전에 이 단계를 실행합니다. 이 단계는 사용자의 선호도를 파악하는 단계입니다. 사용자의 선호도를 파악하기 위해 충분한 질문을 하세요.\n", "3. 추천: 사용자의 선호도를 파악하면 그에 따라 적합한 와인을 추천할 수 있습니다. 추천은 와인 데이터베이스에 있는 와인으로 제한해야 하며, 이를 위해 도구를 사용할 수 있습니다.\n", "4. 추천 후: 와인 추천 후 사용자가 추천한 와인이 마음에 드는지 물어보고 마음에 들면 해당 와인에 대한 링크를 제공합니다. 그렇지 않으면 추천 단계로 돌아갑니다.\n", "5. 마무리하기: 작업을 마치면 사용자에게 작별 인사를 합니다.\n", "6. 질문 및 답변: 사용자의 질문에 답변하는 곳입니다.\n", "7. 주어진 단계에 해당하지 않음: 1~6단계 중 어느 단계에도 해당되지 않는 경우 이 단계를 사용합니다." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### API 키 불러오기" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import os\n", "import configparser\n", "import time\n", "import copy" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['./secrets.ini']" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "config = configparser.ConfigParser()\n", "config.read('./secrets.ini')" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "openai_api_key = config['OPENAI']['OPENAI_API_KEY']\n", "serper_api_key = config['SERPER']['SERPER_API_KEY']\n", "serp_api_key = config['SERPAPI']['SERPAPI_API_KEY']\n", "os.environ.update({'OPENAI_API_KEY': openai_api_key})\n", "os.environ.update({'SERPER_API_KEY': serper_api_key})\n", "os.environ.update({'SERPAPI_API_KEY': serp_api_key})" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Get Stage Analyzer(Assistant) Prompt" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from typing import List, Union\n", "import re\n", "import json\n", "\n", "import pandas as pd\n", "from langchain import SerpAPIWrapper, LLMChain\n", "from langchain.agents import Tool, AgentType, AgentExecutor, LLMSingleActionAgent, AgentOutputParser\n", "from langchain.chat_models import ChatOpenAI\n", "from langchain.chains import LLMChain, SimpleSequentialChain\n", "from langchain.chains.query_constructor.base import AttributeInfo\n", "from langchain.document_loaders import DataFrameLoader, SeleniumURLLoader\n", "from langchain.embeddings import OpenAIEmbeddings\n", "from langchain.indexes import VectorstoreIndexCreator\n", "from langchain.prompts import PromptTemplate, StringPromptTemplate\n", "from langchain.prompts import load_prompt, BaseChatPromptTemplate\n", "from langchain.llms import OpenAI\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain.schema import AgentAction, AgentFinish, HumanMessage\n", "from langchain.vectorstores import DocArrayInMemorySearch, Chroma" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "프롬프트를 선언하는 방식은 파일로 부터 불러오거나 직접 선언할 수 있습니다. 아래에는 두 방식 모두 구현되어 있습니다.\n", "- 직접 선언하기 위해서는 prompt_template을 먼저 작성해야합니다.\n", "- 파일로부터 불러오는 것은 ./templates/stage_analyzer_inception_prompt_template.json 파일을 불러오는 것입니다." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "# 직접 프롬프트 선언하기위한 템플릿 작성\n", "stage_analyzer_inception_prompt_template = \"\"\"\n", "You are an assistant decide which stage of the conversation to move to or which stage to stay at.\n", "Following '===' is the conversation history. \n", "Use conversation history to select the next step the agent should take.\n", "\n", "Below are the stages of the conversation that the agent can take.\n", "1. Start: This is the first step to take when starting a conversation or responding to a user's first response. Start the conversation by introducing yourself. Be polite and respectful while maintaining a professional tone of conversation.\n", "2. Analyze: When a customer wants a recommendation, run this step before recommendation. This is the step where you identify the user's preferences. Ask enough questions to understand your users' preferences.\n", "3. Recommendation: Once you know the preference of user, you can recommend suitable wines accordingly. Recommendations should be limited to wines in your wine database, and you can use tools for this.\n", "4. After recommendation: After making a wine recommendation, it asks if the user likes the wine you recommended, and if they do, it provides a link to it. Otherwise, it takes you back to the recommendation stage.\n", "5. Close: When you're done, say goodbye to the user.\n", "6. Question and Answering: This is where you answer the user's questions.\n", "7. Not in the given steps: This step is for when none of the steps between 1 and 6 apply.\n", "\n", "Only answer with a number between 1 through 7 with a best guess of what stage should the conversation continue with. \n", "The answer needs to be one number only, no words.\n", "Once again, we need to understand the user's preferences (STEP 2) before we can make a recommendation (STEP 3), and we need to understand the user's preferences (STEP 2) about 2 times.\n", "Do not answer anything else nor add anything to you answer.\n", "\n", "Below is four examples of how to do this task.\n", "Example1:\n", "conversation history:\n", " User: 안녕하세요.\n", "stage history: \n", "Answer: 1\n", "\n", "Example2:\n", "conversation history:\n", "User: 안녕하세요. \n", "이우선: 무엇을 도와드릴까요? \n", "User: 와인 추천해주세요. \n", "stage history: 1\n", "Answer: 2\n", "\n", "Example3:\n", "conversation history:\n", "User: 안녕하세요. \n", "이우선: 무엇을 도와드릴까요? \n", "User: 와인의 포도는 어떤 종류가 있나요?. \n", "stage history: 1\n", "Answer: 6\n", "\n", "Example4:\n", "conversation history:\n", "User: 안녕하세요. \n", "이우선: 무엇을 도와드릴까요? \n", "User: 와인 추천해주세요. \n", "이우선: 어떤 행사나 기념일을 위해 와인을 찾으시는지 알려주실 수 있으신가요? \n", "User: 이번주에 결혼기념일이 있어서요. \n", "이우선: 그렇군요. 가격대는 어느정도로 생각하고 계신가요? \n", "User: 20만원 정도요 \n", "이우선: 그렇군요. 달달한 와인을 선호하시나요? 아니면 약간 신 와인을 선호하시나요? \n", "User: 달달한 와인이요 \n", "stage history: 1 2 2 2\n", "Thought: There are three '2's in the stage history. So the next stage should be 3.\n", "Answer: 3\n", "\n", "Now determine what should be the next immediate conversation stage for the agent in the conversation by selecting one from the following options:\n", "Use the conversation history between first and second '======' and stage history between first and second '######' to accomplish the task above.\n", "If conversation history is empty, output 1.\n", "\n", "conversation history:\n", "======\n", "{conversation_history}\n", "======\n", "\n", "stage history:\n", "######\n", "{stage_history}\n", "######\n", "\n", "Answer: \n", "\"\"\"" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "# 직접 프롬프트 선언하기\n", "stage_analyzer_inception_prompt = PromptTemplate(\n", " input_variables=[\"conversation_history\", \"stage_history\"], \n", " template=stage_analyzer_inception_prompt_template,\n", ")" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# 파일로 부터 프롬프트 불러오기\n", "stage_analyzer_inception_prompt = load_prompt(\"./templates/stage_analyzer_inception_prompt_template.json\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "아래와 같이 format 메서드로 프롬프트를 확인할 수 있습니다." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "example_conversation_history = \"\"\"\n", "User: 안녕하세요. \n", "이우선: 무엇을 도와드릴까요? \n", "User: 와인 추천해주세요. \n", "이우선: 어떤 행사나 기념일을 위해 와인을 찾으시는지 알려주실 수 있으신가요? \n", "User: 이번주에 결혼기념일이 있어서요. \n", "\"\"\"\n", "example_stage_history = \"1 2\"\n", "example_answer = \"2\"\n", "# 여기서 우리는 에이전트가 와인 추천을 위해 유저의 더 많은 정보를 얻기를 원한다. 따라서 2(Analyze)가 답변으로 나오길 원한다." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "You are an assistant decide which stage of the conversation to move to or which stage to stay at.\n", "Following '===' is the conversation history. \n", "Use conversation history to select the next step the agent should take.\n", "\n", "Below are the stages of the conversation that the agent can take.\n", "1. Start: This is the first step to take when starting a conversation or responding to a user's first response. Start the conversation by introducing yourself. Be polite and respectful while maintaining a professional tone of conversation.\n", "2. Analyze: When a customer wants a recommendation, run this step before recommendation. This is the step where you identify the user's preferences. Ask enough questions to understand your users' preferences.\n", "3. Recommendation: Once you know the preference of user, you can recommend suitable wines accordingly. Recommendations should be limited to wines in your wine database, and you can use tools for this.\n", "4. After recommendation: After making a wine recommendation, it asks if the user likes the wine you recommended, and if they do, it provides a link to it. Otherwise, it takes you back to the recommendation stage.\n", "5. Close: When you're done, say goodbye to the user.\n", "6. Question and Answering: This is where you answer the user's questions.\n", "7. Not in the given steps: This step is for when none of the steps between 1 and 6 apply.\n", "\n", "Only answer with a number between 1 through 7 with a best guess of what stage should the conversation continue with. \n", "The answer needs to be one number only, no words.\n", "Once again, we need to understand the user's preferences (STEP 2) before we can make a recommendation (STEP 3), and we need to understand the user's preferences (STEP 2) about 2 times.\n", "Do not answer anything else nor add anything to you answer.\n", "\n", "Below is four examples of how to do this task.\n", "Example1:\n", "conversation history:\n", " User: 안녕하세요.\n", "stage history: \n", "Answer: 1\n", "\n", "Example2:\n", "conversation history:\n", "User: 안녕하세요. \n", "이우선: 무엇을 도와드릴까요? \n", "User: 와인 추천해주세요. \n", "stage history: 1\n", "Answer: 2\n", "\n", "Example3:\n", "conversation history:\n", "User: 안녕하세요. \n", "이우선: 무엇을 도와드릴까요? \n", "User: 와인의 포도는 어떤 종류가 있나요?. \n", "stage history: 1\n", "Answer: 6\n", "\n", "Example4:\n", "conversation history:\n", "User: 안녕하세요. \n", "이우선: 무엇을 도와드릴까요? \n", "User: 와인 추천해주세요. \n", "이우선: 어떤 행사나 기념일을 위해 와인을 찾으시는지 알려주실 수 있으신가요? \n", "User: 이번주에 결혼기념일이 있어서요. \n", "이우선: 그렇군요. 가격대는 어느정도로 생각하고 계신가요? \n", "User: 20만원 정도요 \n", "이우선: 그렇군요. 달달한 와인을 선호하시나요? 아니면 약간 신 와인을 선호하시나요? \n", "User: 달달한 와인이요 \n", "stage history: 1 2 2 2\n", "Thought: There are three '2's in the stage history. So the next stage should be 3.\n", "Answer: 3\n", "\n", "Now determine what should be the next immediate conversation stage for the agent in the conversation by selecting one from the following options:\n", "Use the conversation history between first and second '======' and stage history between first and second '######' to accomplish the task above.\n", "If conversation history is empty, output 1.\n", "\n", "conversation history:\n", "======\n", "\n", "User: 안녕하세요. \n", "이우선: 무엇을 도와드릴까요? \n", "User: 와인 추천해주세요. \n", "이우선: 어떤 행사나 기념일을 위해 와인을 찾으시는지 알려주실 수 있으신가요? \n", "User: 이번주에 결혼기념일이 있어서요. \n", "\n", "======\n", "\n", "stage history:\n", "######\n", "1 2\n", "######\n", "\n", "Answer: \n", "\n" ] } ], "source": [ "print(\n", " stage_analyzer_inception_prompt.format(\n", " conversation_history=example_conversation_history,\n", " stage_history=example_stage_history\n", " )\n", ")" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "# 랭체인 모델 선언, 랭체인은 언어모델과 프롬프트로 구성됩니다.\n", "llm = ChatOpenAI(model='gpt-3.5-turbo', temperature=0.0)\n", "stage_analyzer_chain = LLMChain(\n", " llm=llm,\n", " prompt=stage_analyzer_inception_prompt, \n", " verbose=False, # 과정을 출력할지\n", " output_key=\"stage_number\" # 출력값의 변수명\n", " )" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Chain을 실행하기 위해서는 run 메서드를 실행한다." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "stage_number = stage_analyzer_chain.run(\n", " {'conversation_history': example_conversation_history, \n", " 'stage_history': example_stage_history}\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "결과를 확인해보면 원하는 결과(2. Analyze)가 나오는 것을 확인할 수 있다." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "print(stage_number)" ] } ], "metadata": { "kernelspec": { "display_name": "nemo", "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.8.10" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }