{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型，提示和输出解释器\n",
    "\n",
    "\n",
    "**目录**\n",
    "* 获取你的OpenAI API Key\n",
    "* 直接调用OpenAI的API\n",
    "* 通过LangChain进行的API调用：\n",
    "  * 提示（Prompts）\n",
    "  * [模型（Models)](#model)\n",
    "  * 输出解析器（Output parsers）\n",
    "  "
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "## 获取你的OpenAI API Key\n",
    "\n",
    "登陆[OpenAI账户获取你的API Key](https://platform.openai.com/account/api-keys) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 下载需要的包python-dotenv和openai\n",
    "# 如果你需要查看安装过程日志，可删除 -q \n",
    "!pip install -q python-dotenv\n",
    "!pip install -q openai\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将自己的 API-KEY 导入系统环境变量（关于如何设置参考这篇文章：https://zhuanlan.zhihu.com/p/627665725）\n",
    "!export OPENAI_API_KEY='api-key'     #api_key替换为自己的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import openai\n",
    "from dotenv import load_dotenv, find_dotenv\n",
    "\n",
    "# 读取本地的环境变量 \n",
    "_ = load_dotenv(find_dotenv())\n",
    "\n",
    "# 获取环境变量 OPENAI_API_KEY\n",
    "openai.api_key = os.environ['OPENAI_API_KEY']  "
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Chat API：OpenAI\n",
    "\n",
    "我们先从直接调用OpenAI的API开始。\n",
    "\n",
    "`get_completion`函数是基于`openai`的封装函数，对于给定提示（prompt）输出相应的回答。其包含两个参数\n",
    "   \n",
    "   - `prompt` 必需输入参数。 你给模型的**提示，可以是一个问题，可以是你需要模型帮助你做的事**（改变文本写作风格，翻译，回复消息等等）。\n",
    "   - `model` 非必需输入参数。默认使用gpt-3.5-turbo。你也可以选择其他模型。\n",
    "   \n",
    "这里的提示对应我们给chatgpt的问题，函数给出的输出则对应chatpgt给我们的答案。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def get_completion(prompt, model=\"gpt-3.5-turbo\"):\n",
    "    \n",
    "    messages = [{\"role\": \"user\", \"content\": prompt}]\n",
    "    \n",
    "    response = openai.ChatCompletion.create(\n",
    "        model=model,\n",
    "        messages=messages,\n",
    "        temperature=0, \n",
    "    )\n",
    "    return response.choices[0].message[\"content\"]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### 一个简单的例子\n",
    "\n",
    "我们来一个简单的例子 - 分别用中英文问问模型\n",
    "\n",
    "- 中文提示(Prompt in Chinese)： `1+1是什么？`\n",
    "- 英文提示(Prompt in English)： `What is 1+1?`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'1+1等于2。'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 中文\n",
    "get_completion(\"1+1是什么？\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'As an AI language model, I can tell you that the answer to 1+1 is 2.'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 英文\n",
    "get_completion(\"What is 1+1?\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 复杂一点例子\n",
    "\n",
    "上面的简单例子，模型`gpt-3.5-turbo`对我们的关于1+1是什么的提问给出了回答。\n",
    "\n",
    "现在我们来看一个复杂一点的例子： \n",
    "\n",
    "假设我们是电商公司员工，我们的顾客是一名海盗A，他在我们的网站上买了一个榨汁机用来做奶昔，在制作奶昔的过程中，奶昔的盖子飞了出去，弄得厨房墙上到处都是。于是海盗A给我们的客服中心写来以下邮件：`customer_email`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "customer_email = \"\"\"\n",
    "Arrr, I be fuming that me blender lid \\\n",
    "flew off and splattered me kitchen walls \\\n",
    "with smoothie! And to make matters worse,\\\n",
    "the warranty don't cover the cost of \\\n",
    "cleaning up me kitchen. I need yer help \\\n",
    "right now, matey!\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们的客服人员对于海盗的措辞表达觉得有点难以理解。 现在我们想要实现两个小目标：\n",
    "\n",
    "- 让模型用美式英语的表达方式将海盗的邮件进行翻译，客服人员可以更好理解。*这里海盗的英文表达可以理解为英文的方言，其与美式英语的关系，就如四川话与普通话的关系。\n",
    "- 让模型在翻译是用平和尊重的语气进行表达，客服人员的心情也会更好。\n",
    "\n",
    "根据这两个小目标，定义一下文本表达风格：`style`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 美式英语 + 平静、尊敬的语调\n",
    "style = \"\"\"American English \\\n",
    "in a calm and respectful tone\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下一步需要做的是将`customer_email`和`style`结合起来构造我们的提示:`prompt`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Translate the text that is delimited by triple backticks \n",
      "into a style that is American English in a calm and respectful tone\n",
      ".\n",
      "text: ```\n",
      "Arrr, I be fuming that me blender lid flew off and splattered me kitchen walls with smoothie! And to make matters worse,the warranty don't cover the cost of cleaning up me kitchen. I need yer help right now, matey!\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 要求模型根据给出的语调进行转化\n",
    "prompt = f\"\"\"Translate the text \\\n",
    "that is delimited by triple backticks \n",
    "into a style that is {style}.\n",
    "text: ```{customer_email}```\n",
    "\"\"\"\n",
    "\n",
    "print(prompt)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`prompt` 构造好了，我们可以调用`get_completion`得到我们想要的结果 - 用平和尊重的语气，美式英语表达的海岛邮件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = get_completion(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'I am quite upset that my blender lid came off and caused my smoothie to splatter all over my kitchen walls. Additionally, the warranty does not cover the cost of cleaning up the mess. Would you be able to assist me, please? Thank you kindly.'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对比语言风格转换前后，用词更为正式，替换了极端情绪的表达，并表达了感谢。\n",
    "- Arrr, I be fuming（呀，我气的发抖） 换成了 I am quite upset （我有点失望）\n",
    "- And to make matters worse（更糟糕地是），换成了 Additionally(还有)\n",
    "- I need yer help right now, matey!（我需要你的帮助），换成了Would you be able to assist me, please? Thank you kindly.（请问您能帮我吗？非常感谢您的好意）\n",
    "\n",
    "\n",
    "✨ 你可以尝试修改提示，看可以得到什么不一样的结果😉"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 中文"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "假设我们是电商公司员工，我们的顾客是一名海盗A，他在我们的网站上买了一个榨汁机用来做奶昔，在制作奶昔的过程中，奶昔的盖子飞了出去，弄得厨房墙上到处都是。于是海盗A给我们的客服中心写来以下邮件：`customer_email`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 非正式用语\n",
    "customer_email = \"\"\"   \n",
    "阿，我很生气，\\\n",
    "因为我的搅拌机盖掉了，\\\n",
    "把奶昔溅到了厨房的墙上！\\\n",
    "更糟糕的是，保修不包括打扫厨房的费用。\\\n",
    "我现在需要你的帮助，伙计！\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们的客服人员对于海盗的措辞表达觉得有点难以理解。 现在我们想要实现两个小目标：\n",
    "\n",
    "- 让模型用比较正式的普通话的表达方式将海盗的邮件进行翻译，客服人员可以更好理解。\n",
    "- 让模型在翻译是用平和尊重的语气进行表达，客服人员的心情也会更好。\n",
    "\n",
    "根据这两个小目标，定义一下文本表达风格：`style`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 普通话 + 平静、尊敬的语调\n",
    "style = \"\"\"正式普通话 \\\n",
    "用一个平静、尊敬的语调\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下一步需要做的是将`customer_email`和`style`结合起来构造我们的提示:`prompt`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "把由三个反引号分隔的文本text翻译成一种正式普通话 用一个平静、尊敬的语调\n",
      "风格。\n",
      "text: ```   \n",
      "阿，我很生气，因为我的搅拌机盖掉了，把奶昔溅到了厨房的墙上！更糟糕的是，保修不包括打扫厨房的费用。我现在需要你的帮助，伙计！\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 要求模型根据给出的语调进行转化\n",
    "prompt = f\"\"\"把由三个反引号分隔的文本text\\\n",
    "翻译成一种{style}风格。\n",
    "text: ```{customer_email}```\n",
    "\"\"\"\n",
    "\n",
    "print(prompt)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`prompt` 构造好了，我们可以调用`get_completion`得到我们想要的结果 - 用平和尊重的普通话语气去表达一封带着方言表达方式的邮件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = get_completion(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'尊敬的朋友，我感到不安，因为我的搅拌机盖子不慎掉落，导致奶昔溅到了厨房的墙壁上！更加令人糟心的是，保修并不包含厨房清洁的费用。此刻，我需要你的帮助，亲爱的朋友！'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Chat API：LangChain\n",
    "\n",
    "在前面一部分，我们通过封装函数`get_completion`直接调用了OpenAI完成了对方言邮件进行了的翻译，得到用平和尊重的语气、正式的普通话表达的邮件。\n",
    "\n",
    "让我们尝试使用LangChain来实现相同的功能。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "^C\n"
     ]
    }
   ],
   "source": [
    "# 如果你需要查看安装过程日志，可删除 -q \n",
    "# --upgrade 让我们可以安装到最新版本的 langchain\n",
    "!pip install -q --upgrade langchain"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### 模型<a id='model'></a>\n",
    "\n",
    "从`langchain.chat_models`导入`OpenAI`的对话模型`ChatOpenAI`。 除去OpenAI以外，`langchain.chat_models`还集成了其他对话模型，更多细节可以查看[Langchain官方文档](https://python.langchain.com/en/latest/modules/models/chat/integrations.html)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain.chat_models import ChatOpenAI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatOpenAI(verbose=False, callbacks=None, callback_manager=None, client=<class 'openai.api_resources.chat_completion.ChatCompletion'>, model_name='gpt-3.5-turbo', temperature=0.0, model_kwargs={}, openai_api_key=None, openai_api_base=None, openai_organization=None, request_timeout=None, max_retries=6, streaming=False, n=1, max_tokens=None)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 这里我们将参数temperature设置为0.0，从而减少生成答案的随机性。\n",
    "# 如果你想要每次得到不一样的有新意的答案，可以尝试调整该参数。\n",
    "chat = ChatOpenAI(temperature=0.0)\n",
    "chat"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的输出显示ChatOpenAI的默认模型为`gpt-3.5-turbo`"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 提示模板\n",
    "\n",
    "在前面的例子中，我们通过[f字符串](https://docs.python.org/zh-cn/3/tutorial/inputoutput.html#tut-f-strings)把Python表达式的值`style`和`customer_email`添加到`prompt`字符串内。\n",
    "\n",
    "```python\n",
    "prompt = f\"\"\"Translate the text \\\n",
    "that is delimited by triple backticks \n",
    "into a style that is {style}.\n",
    "text: ```{customer_email}```\n",
    "\"\"\"\n",
    "```\n",
    "`langchain`提供了接口方便快速的构造和使用提示。现在我们来看看如何使用`langchain`来构造提示。"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 📚 使用LangChain提示模版\n",
    "##### 1️⃣ 构造提示模版字符串\n",
    "我们构造一个提示模版字符串：`template_string`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "template_string = \"\"\"Translate the text \\\n",
    "that is delimited by triple backticks \\\n",
    "into a style that is {style}. \\\n",
    "text: ```{text}```\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 中文\n",
    "template_string = \"\"\"把由三个反引号分隔的文本text\\\n",
    "翻译成一种{style}风格。\\\n",
    "text: ```{text}```\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 2️⃣ 构造LangChain提示模版\n",
    "我们调用`ChatPromptTemplatee.from_template()`函数将上面的提示模版字符`template_string`转换为提示模版`prompt_template`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 需要安装最新版的 LangChain\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "prompt_template = ChatPromptTemplate.from_template(template_string)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PromptTemplate(input_variables=['style', 'text'], output_parser=None, partial_variables={}, template='Translate the text that is delimited by triple backticks into a style that is {style}. text: ```{text}```\\n', template_format='f-string', validate_template=True)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt_template.messages[0].prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PromptTemplate(input_variables=['style', 'text'], output_parser=None, partial_variables={}, template='把由三个反引号分隔的文本text翻译成一种{style}风格。text: ```{text}```\\n', template_format='f-string', validate_template=True)"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 中文\n",
    "prompt_template.messages[0].prompt"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从上面的输出可以看出，`prompt_template` 有两个输入变量： `style` 和 `text`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['style', 'text']"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt_template.messages[0].prompt.input_variables"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 3️⃣ 使用模版得到客户消息提示\n",
    "\n",
    "langchain提示模版`prompt_template`需要两个输入变量： `style` 和 `text`。 这里分别对应 \n",
    "- `customer_style`: 我们想要的顾客邮件风格\n",
    "- `customer_email`: 顾客的原始邮件文本。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "customer_style = \"\"\"American English \\\n",
    "in a calm and respectful tone\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 中文\n",
    "customer_style = \"\"\"正式普通话 \\\n",
    "用一个平静、尊敬的语气\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "customer_email = \"\"\"\n",
    "Arrr, I be fuming that me blender lid \\\n",
    "flew off and splattered me kitchen walls \\\n",
    "with smoothie! And to make matters worse, \\\n",
    "the warranty don't cover the cost of \\\n",
    "cleaning up me kitchen. I need yer help \\\n",
    "right now, matey!\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 中文\n",
    "customer_email = \"\"\"\n",
    "阿，我很生气，\\\n",
    "因为我的搅拌机盖掉了，\\\n",
    "把奶昔溅到了厨房的墙上！\\\n",
    "更糟糕的是，保修不包括打扫厨房的费用。\\\n",
    "我现在需要你的帮助，伙计！\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于给定的`customer_style`和`customer_email`, 我们可以使用提示模版`prompt_template`的`format_messages`方法生成想要的客户消息`customer_messages`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "customer_messages = prompt_template.format_messages(\n",
    "                    style=customer_style,\n",
    "                    text=customer_email)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'list'>\n",
      "<class 'langchain.schema.HumanMessage'>\n"
     ]
    }
   ],
   "source": [
    "print(type(customer_messages))\n",
    "print(type(customer_messages[0]))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看出`customer_messages`变量类型为列表(`list`)，而列表里的元素变量类型为langchain自定义消息(`langchain.schema.HumanMessage`)。\n",
    "\n",
    "打印第一个元素可以得到如下:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content=\"Translate the text that is delimited by triple backticks into a style that is American English in a calm and respectful tone\\n. text: ```\\nArrr, I be fuming that me blender lid flew off and splattered me kitchen walls with smoothie! And to make matters worse, the warranty don't cover the cost of cleaning up me kitchen. I need yer help right now, matey!\\n```\\n\" additional_kwargs={} example=False\n"
     ]
    }
   ],
   "source": [
    "print(customer_messages[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='把由三个反引号分隔的文本text翻译成一种正式普通话 用一个平静、尊敬的语气\\n风格。text: ```\\n阿，我很生气，因为我的搅拌机盖掉了，把奶昔溅到了厨房的墙上！更糟糕的是，保修不包括打扫厨房的费用。我现在需要你的帮助，伙计！\\n```\\n' additional_kwargs={} example=False\n"
     ]
    }
   ],
   "source": [
    "# 中文\n",
    "print(customer_messages[0])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 4️⃣ 调用chat模型转换客户消息风格\n",
    "\n",
    "现在我们可以调用[模型](#model)部分定义的chat模型来实现转换客户消息风格。到目前为止，我们已经实现了在前一部分的任务。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "customer_response = chat(customer_messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I'm really frustrated that my blender lid flew off and made a mess of my kitchen walls with smoothie. To add insult to injury, the warranty doesn't cover the cost of cleaning up my kitchen. Can you please help me out, friend?\n"
     ]
    }
   ],
   "source": [
    "print(customer_response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "尊敬的伙计，我感到非常不安，因为我的搅拌机盖子不慎掉落，导致奶昔溅到了厨房的墙壁上！更加令人糟心的是，保修并不包括厨房清洁的费用。现在，我非常需要您的帮助！\n"
     ]
    }
   ],
   "source": [
    "print(customer_response.content)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 5️⃣ 使用模版得到回复消息提示\n",
    "\n",
    "接下来，我们更进一步，将客服人员回复的消息，转换为海盗的语言风格，并确保消息比较有礼貌。 \n",
    "\n",
    "这里，我们可以继续使用第2️⃣步构造的langchain提示模版，来获得我们回复消息提示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "service_reply = \"\"\"Hey there customer, \\\n",
    "the warranty does not cover \\\n",
    "cleaning expenses for your kitchen \\\n",
    "because it's your fault that \\\n",
    "you misused your blender \\\n",
    "by forgetting to put the lid on before \\\n",
    "starting the blender. \\\n",
    "Tough luck! See ya!\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 中文\n",
    "service_reply = \"\"\"嘿，顾客， \\\n",
    "保修不包括厨房的清洁费用， \\\n",
    "因为您在启动搅拌机之前 \\\n",
    "忘记盖上盖子而误用搅拌机, \\\n",
    "这是您的错。 \\\n",
    "倒霉！ 再见！\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "service_style_pirate = \"\"\"\\\n",
    "a polite tone \\\n",
    "that speaks in English Pirate\\\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 中文\n",
    "service_style_pirate = \"\"\"\\\n",
    "一个有礼貌的语气 \\\n",
    "使用正式的普通话\\\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Translate the text that is delimited by triple backticks into a style that is a polite tone that speaks in English Pirate. text: ```Hey there customer, the warranty does not cover cleaning expenses for your kitchen because it's your fault that you misused your blender by forgetting to put the lid on before starting the blender. Tough luck! See ya!\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "service_messages = prompt_template.format_messages(\n",
    "    style=service_style_pirate,\n",
    "    text=service_reply)\n",
    "\n",
    "print(service_messages[0].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "把由三个反引号分隔的文本text翻译成一种一个有礼貌的语气 使用正式的普通话风格。text: ```嘿，顾客， 保修不包括厨房的清洁费用， 因为您在启动搅拌机之前 忘记盖上盖子而误用搅拌机, 这是您的错。 倒霉！ 再见！\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "service_messages = prompt_template.format_messages(\n",
    "    style=service_style_pirate,\n",
    "    text=service_reply)\n",
    "\n",
    "print(service_messages[0].content)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 6️⃣ 调用chat模型转换回复消息风格\n",
    "\n",
    "调用[模型](#model)部分定义的chat模型来转换回复消息风格"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ahoy there, me hearty customer! I be sorry to inform ye that the warranty be not coverin' the expenses o' cleaning yer galley, as 'tis yer own fault fer misusin' yer blender by forgettin' to put the lid on afore startin' it. Aye, tough luck! Farewell and may the winds be in yer favor!\n"
     ]
    }
   ],
   "source": [
    "service_response = chat(service_messages)\n",
    "print(service_response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "尊敬的顾客，根据保修条款，厨房清洁费用不在保修范围内。这是因为在使用搅拌机之前，您忘记盖上盖子而导致误用搅拌机，这属于您的疏忽。非常抱歉给您带来困扰！祝您好运！再见！\n"
     ]
    }
   ],
   "source": [
    "service_response = chat(service_messages)\n",
    "print(service_response.content)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "#### ❓为什么需要提示模版\n",
    "\n",
    "\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "在应用于比较复杂的场景时，提示可能会非常长并且包含涉及许多细节。**使用提示模版，可以让我们更为方便地重复使用设计好的提示**。\n",
    "\n",
    "下面给出了一个比较长的提示模版案例。学生们线上学习并提交作业，通过以下的提示来实现对学生的提交的作业的评分。\n",
    "\n",
    "```python\n",
    "    prompt = \"\"\" Your task is to determine if the student's solution is correct or not\n",
    "\n",
    "    To solve the problem do the following:\n",
    "    - First, workout your own solution to the problem\n",
    "    - Then compare your solution to the student's solution \n",
    "    and evaluate if the sudtent's solution is correct or not.\n",
    "    ...\n",
    "    Use the following format:\n",
    "    Question:\n",
    "    ```\n",
    "    question here\n",
    "    ```\n",
    "    Student's solution:\n",
    "    ```\n",
    "    student's solution here\n",
    "    ```\n",
    "    Actual solution:\n",
    "    ```\n",
    "    ...\n",
    "    steps to work out the solution and your solution here\n",
    "    ```\n",
    "    Is the student's solution the same as acutal solution \\\n",
    "    just calculated:\n",
    "    ```\n",
    "    yes or no\n",
    "    ```\n",
    "    Student grade\n",
    "    ```\n",
    "    correct or incorrect\n",
    "    ```\n",
    "    \n",
    "    Question:\n",
    "    ```\n",
    "    {question}\n",
    "    ```\n",
    "    Student's solution:\n",
    "    ```\n",
    "    {student's solution}\n",
    "    ```\n",
    "    Actual solution:\n",
    "    \n",
    "    \"\"\"\n",
    "```\n",
    "\n",
    "# 中文\n",
    "\n",
    "```python\n",
    "    prompt = \"\"\" 你的任务是判断学生的解决方案是正确的还是不正确的\n",
    "\n",
    "    要解决该问题，请执行以下操作：\n",
    "     - 首先，制定自己的问题解决方案\n",
    "     - 然后将您的解决方案与学生的解决方案进行比较\n",
    "     并评估学生的解决方案是否正确。\n",
    "    ...\n",
    "    使用下面的格式:\n",
    "\n",
    "    问题:\n",
    "    ```\n",
    "    问题文本\n",
    "    ```\n",
    "    学生的解决方案:\n",
    "    ```\n",
    "    学生的解决方案文本\n",
    "    ```\n",
    "    实际解决方案:\n",
    "    ```\n",
    "    ...\n",
    "    制定解决方案的步骤以及您的解决方案请参见此处\n",
    "    ```\n",
    "    学生的解决方案和实际解决方案是否相同 \\\n",
    "    只计算：\n",
    "    ```\n",
    "    是或者不是\n",
    "    ```\n",
    "    学生的成绩\n",
    "    ```\n",
    "    正确或者不正确\n",
    "    ```\n",
    "    \n",
    "    问题:\n",
    "    ```\n",
    "    {question}\n",
    "    ```\n",
    "    学生的解决方案:\n",
    "    ```\n",
    "    {student's solution}\n",
    "    ```\n",
    "    实际解决方案:\n",
    "    \n",
    "    \"\"\"\n",
    "```\n",
    "\n",
    "\n",
    "\n",
    "此外，LangChain还提供了提示模版用于一些常用场景。比如summarization, Question answering, or connect to sql databases, or connect to different APIs. 通过使用LongChain内置的提示模版，你可以快速建立自己的大模型应用，而不需要花时间去设计和构造提示。\n",
    "\n",
    "最后，我们在建立大模型应用时，通常希望模型的输出为给定的格式，比如在输出使用特定的关键词来让输出结构化。 下面为一个使用大模型进行链式思考推理例子，对于问题：`What is the elevation range for the area that the eastern sector of the Colorado orogeny extends into?`, 通过使用LangChain库函数，输出采用\"Thought\"（思考）、\"Action\"（行动）、\"Observation\"（观察）作为链式思考推理的关键词，让输出结构化。在[补充材料](#reason_act)中，可以查看使用LangChain和OpenAI进行链式思考推理的另一个代码实例。\n",
    "\n",
    "```python\n",
    "\"\"\"\n",
    "Thought: I need to search Colorado orogeny, find the area that the eastern sector of the Colorado orogeny extends into, then find the elevation range of the area.\n",
    "Action: Search[Colorado orogeny]\n",
    "Observation: The Colorado orogeny was an episode of mountain building (an orogeny) in Colorado and surrounding areas.\n",
    "\n",
    "Thought: It does not mention the eastern sector. So I need to look up eastern sector.\n",
    "Action: Lookup[eastern sector]\n",
    "Observation: (Result 1 / 1) The eastern sector extends into the High Plains and is called the Central Plains orogeny.\n",
    "\n",
    "Thought: The eastern sector of Colorado orogeny extends into the High Plains. So I need to search High Plains and find its elevation range.\n",
    "Action: Search[High Plains]\n",
    "Observation: High Plains refers to one of two distinct land regions\n",
    "\n",
    "Thought: I need to instead search High Plains (United States).\n",
    "Action: Search[High Plains (United States)]\n",
    "Observation: The High Plains are a subregion of the Great Plains. From east to west, the High Plains rise in elevation from around 1,800 to 7,000 ft (550 to 2,130 m).[3]\n",
    "\n",
    "Thought: High Plains rise in elevation from around 1,800 to 7,000 ft, so the answer is 1,800 to 7,000 ft.\n",
    "Action: Finish[1,800 to 7,000 ft]\n",
    "\n",
    "\"\"\"\n",
    "```\n",
    "\n",
    "\n",
    "```python\n",
    "\"\"\"\n",
    "想法：我需要搜索科罗拉多造山带，找到科罗拉多造山带东段延伸到的区域，然后找到该区域的高程范围。\n",
    "行动：搜索[科罗拉多造山运动]\n",
    "观察：科罗拉多造山运动是科罗拉多州及周边地区造山运动（造山运动）的一次事件。\n",
    "\n",
    "想法：它没有提到东区。 所以我需要查找东区。\n",
    "行动：查找[东区]\n",
    "观察：（结果1 / 1）东段延伸至高原，称为中原造山运动。\n",
    "\n",
    "想法：科罗拉多造山运动的东段延伸至高原。 所以我需要搜索高原并找到它的海拔范围。\n",
    "行动：搜索[高地平原]\n",
    "观察：高原是指两个不同的陆地区域之一\n",
    "\n",
    "想法：我需要搜索高地平原（美国）。\n",
    "行动：搜索[高地平原（美国）]\n",
    "观察：高地平原是大平原的一个分区。 从东到西，高原的海拔从 1,800 英尺左右上升到 7,000 英尺（550 到 2,130 米）。[3]\n",
    "\n",
    "想法：高原的海拔从大约 1,800 英尺上升到 7,000 英尺，所以答案是 1,800 到 7,000 英尺。\n",
    "动作：完成[1,800 至 7,000 英尺]\n",
    "\n",
    "\"\"\"\n",
    "```\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 输出解析器"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 📚 如果没有输出解析器\n",
    "\n",
    "对于给定的评价`customer_review`, 我们希望提取信息，并按以下格式输出：\n",
    "\n",
    "```python\n",
    "{\n",
    "  \"gift\": False,\n",
    "  \"delivery_days\": 5,\n",
    "  \"price_value\": \"pretty affordable!\"\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "customer_review = \"\"\"\\\n",
    "This leaf blower is pretty amazing.  It has four settings:\\\n",
    "candle blower, gentle breeze, windy city, and tornado. \\\n",
    "It arrived in two days, just in time for my wife's \\\n",
    "anniversary present. \\\n",
    "I think my wife liked it so much she was speechless. \\\n",
    "So far I've been the only one using it, and I've been \\\n",
    "using it every other morning to clear the leaves on our lawn. \\\n",
    "It's slightly more expensive than the other leaf blowers \\\n",
    "out there, but I think it's worth it for the extra features.\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 中文\n",
    "customer_review = \"\"\"\\\n",
    "这款吹叶机非常神奇。 它有四个设置：\\\n",
    "吹蜡烛、微风、风城、龙卷风。 \\\n",
    "两天后就到了，正好赶上我妻子的\\\n",
    "周年纪念礼物。 \\\n",
    "我想我的妻子会喜欢它到说不出话来。 \\\n",
    "到目前为止，我是唯一一个使用它的人，而且我一直\\\n",
    "每隔一天早上用它来清理草坪上的叶子。 \\\n",
    "它比其他吹叶机稍微贵一点，\\\n",
    "但我认为它的额外功能是值得的。\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 1️⃣ 构造提示模版字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "review_template = \"\"\"\\\n",
    "For the following text, extract the following information:\n",
    "\n",
    "gift: Was the item purchased as a gift for someone else? \\\n",
    "Answer True if yes, False if not or unknown.\n",
    "\n",
    "delivery_days: How many days did it take for the product \\\n",
    "to arrive? If this information is not found, output -1.\n",
    "\n",
    "price_value: Extract any sentences about the value or price,\\\n",
    "and output them as a comma separated Python list.\n",
    "\n",
    "Format the output as JSON with the following keys:\n",
    "gift\n",
    "delivery_days\n",
    "price_value\n",
    "\n",
    "text: {text}\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 中文\n",
    "review_template = \"\"\"\\\n",
    "对于以下文本，请从中提取以下信息：\n",
    "\n",
    "礼物：该商品是作为礼物送给别人的吗？ \\\n",
    "如果是，则回答 是的；如果否或未知，则回答 不是。\n",
    "\n",
    "交货天数：产品需要多少天\\\n",
    "到达？ 如果没有找到该信息，则输出-1。\n",
    "\n",
    "价钱：提取有关价值或价格的任何句子，\\\n",
    "并将它们输出为逗号分隔的 Python 列表。\n",
    "\n",
    "使用以下键将输出格式化为 JSON：\n",
    "礼物\n",
    "交货天数\n",
    "价钱\n",
    "\n",
    "文本: {text}\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 2️⃣ 构造langchain提示模版"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['text'] output_parser=None partial_variables={} messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['text'], output_parser=None, partial_variables={}, template='For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n', template_format='f-string', validate_template=True), additional_kwargs={})]\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "prompt_template = ChatPromptTemplate.from_template(review_template)\n",
    "print(prompt_template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['text'] output_parser=None partial_variables={} messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['text'], output_parser=None, partial_variables={}, template='对于以下文本，请从中提取以下信息：\\n\\n礼物：该商品是作为礼物送给别人的吗？ 如果是，则回答 是的；如果否或未知，则回答 不是。\\n\\n交货天数：产品需要多少天到达？ 如果没有找到该信息，则输出-1。\\n\\n价钱：提取有关价值或价格的任何句子，并将它们输出为逗号分隔的 Python 列表。\\n\\n使用以下键将输出格式化为 JSON：\\n礼物\\n交货天数\\n价钱\\n\\n文本: {text}\\n', template_format='f-string', validate_template=True), additional_kwargs={})]\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "prompt_template = ChatPromptTemplate.from_template(review_template)\n",
    "print(prompt_template)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 3️⃣ 使用模版得到提示消息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = prompt_template.format_messages(text=customer_review)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 4️⃣ 调用chat模型提取信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "    \"gift\": true,\n",
      "    \"delivery_days\": 2,\n",
      "    \"price_value\": [\"It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\"]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "chat = ChatOpenAI(temperature=0.0)\n",
    "response = chat(messages)\n",
    "print(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"礼物\": \"是的\",\n",
      "  \"交货天数\": 2,\n",
      "  \"价钱\": [\"它比其他吹叶机稍微贵一点\"]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "chat = ChatOpenAI(temperature=0.0)\n",
    "response = chat(messages)\n",
    "print(response.content)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 📝 分析与总结\n",
    "`response.content`类型为字符串（`str`），而并非字典(`dict`), 直接使用`get`方法会报错。因此，我们需要输出解释器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "str"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'str' object has no attribute 'get'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "Input \u001b[0;32mIn [35]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mresponse\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcontent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mgift\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'str' object has no attribute 'get'"
     ]
    }
   ],
   "source": [
    "response.content.get('gift')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 📚 LangChain输出解析器"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 1️⃣ 构造提示模版字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "review_template_2 = \"\"\"\\\n",
    "For the following text, extract the following information:\n",
    "\n",
    "gift: Was the item purchased as a gift for someone else? \\\n",
    "Answer True if yes, False if not or unknown.\n",
    "\n",
    "delivery_days: How many days did it take for the product\\\n",
    "to arrive? If this information is not found, output -1.\n",
    "\n",
    "price_value: Extract any sentences about the value or price,\\\n",
    "and output them as a comma separated Python list.\n",
    "\n",
    "text: {text}\n",
    "\n",
    "{format_instructions}\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 中文\n",
    "review_template_2 = \"\"\"\\\n",
    "对于以下文本，请从中提取以下信息：：\n",
    "\n",
    "礼物：该商品是作为礼物送给别人的吗？\n",
    "如果是，则回答 是的；如果否或未知，则回答 不是。\n",
    "\n",
    "交货天数：产品到达需要多少天？ 如果没有找到该信息，则输出-1。\n",
    "\n",
    "价钱：提取有关价值或价格的任何句子，并将它们输出为逗号分隔的 Python 列表。\n",
    "\n",
    "文本: {text}\n",
    "\n",
    "{format_instructions}\n",
    "\"\"\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 2️⃣ 构造langchain提示模版"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = ChatPromptTemplate.from_template(template=review_template_2)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 🔥 构造输出解析器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "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  // Was the item purchased                             as a gift for someone else?                              Answer True if yes,                             False if not or unknown.\n",
      "\t\"delivery_days\": string  // How many days                                      did it take for the product                                      to arrive? If this                                       information is not found,                                      output -1.\n",
      "\t\"price_value\": string  // Extract any                                    sentences about the value or                                     price, and output them as a                                     comma separated Python list.\n",
      "}\n",
      "```\n"
     ]
    }
   ],
   "source": [
    "from langchain.output_parsers import ResponseSchema\n",
    "from langchain.output_parsers import StructuredOutputParser\n",
    "\n",
    "gift_schema = ResponseSchema(name=\"gift\",\n",
    "                             description=\"Was the item purchased\\\n",
    "                             as a gift for someone else? \\\n",
    "                             Answer True if yes,\\\n",
    "                             False if not or unknown.\")\n",
    "\n",
    "delivery_days_schema = ResponseSchema(name=\"delivery_days\",\n",
    "                                      description=\"How many days\\\n",
    "                                      did it take for the product\\\n",
    "                                      to arrive? If this \\\n",
    "                                      information is not found,\\\n",
    "                                      output -1.\")\n",
    "\n",
    "price_value_schema = ResponseSchema(name=\"price_value\",\n",
    "                                    description=\"Extract any\\\n",
    "                                    sentences about the value or \\\n",
    "                                    price, and output them as a \\\n",
    "                                    comma separated Python list.\")\n",
    "\n",
    "\n",
    "response_schemas = [gift_schema, \n",
    "                    delivery_days_schema,\n",
    "                    price_value_schema]\n",
    "output_parser = StructuredOutputParser.from_response_schemas(response_schemas)\n",
    "format_instructions = output_parser.get_format_instructions()\n",
    "print(format_instructions)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "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\"礼物\": string  // 这件物品是作为礼物送给别人的吗？                            如果是，则回答 是的，                            如果否或未知，则回答 不是。\n",
      "\t\"交货天数\": string  // 产品需要多少天才能到达？                                      如果没有找到该信息，则输出-1。\n",
      "\t\"价钱\": string  // 提取有关价值或价格的任何句子，                                    并将它们输出为逗号分隔的 Python 列表\n",
      "}\n",
      "```\n"
     ]
    }
   ],
   "source": [
    "# 中文\n",
    "from langchain.output_parsers import ResponseSchema\n",
    "from langchain.output_parsers import StructuredOutputParser\n",
    "\n",
    "gift_schema = ResponseSchema(name=\"礼物\",\n",
    "                             description=\"这件物品是作为礼物送给别人的吗？\\\n",
    "                            如果是，则回答 是的，\\\n",
    "                            如果否或未知，则回答 不是。\")\n",
    "\n",
    "delivery_days_schema = ResponseSchema(name=\"交货天数\",\n",
    "                                      description=\"产品需要多少天才能到达？\\\n",
    "                                      如果没有找到该信息，则输出-1。\")\n",
    "\n",
    "price_value_schema = ResponseSchema(name=\"价钱\",\n",
    "                                    description=\"提取有关价值或价格的任何句子，\\\n",
    "                                    并将它们输出为逗号分隔的 Python 列表\")\n",
    "\n",
    "\n",
    "response_schemas = [gift_schema, \n",
    "                    delivery_days_schema,\n",
    "                    price_value_schema]\n",
    "output_parser = StructuredOutputParser.from_response_schemas(response_schemas)\n",
    "format_instructions = output_parser.get_format_instructions()\n",
    "print(format_instructions)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 3️⃣ 使用模版得到提示消息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = prompt.format_messages(text=customer_review, format_instructions=format_instructions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "For the following text, extract the following information:\n",
      "\n",
      "gift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\n",
      "\n",
      "delivery_days: How many days did it take for the productto arrive? If this information is not found, output -1.\n",
      "\n",
      "price_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\n",
      "\n",
      "text: This leaf blower is pretty amazing.  It has four settings:candle blower, gentle breeze, windy city, and tornado. It arrived in two days, just in time for my wife's anniversary present. I think my wife liked it so much she was speechless. So far I've been the only one using it, and I've been using it every other morning to clear the leaves on our lawn. It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\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  // Was the item purchased                             as a gift for someone else?                              Answer True if yes,                             False if not or unknown.\n",
      "\t\"delivery_days\": string  // How many days                                      did it take for the product                                      to arrive? If this                                       information is not found,                                      output -1.\n",
      "\t\"price_value\": string  // Extract any                                    sentences about the value or                                     price, and output them as a                                     comma separated Python list.\n",
      "}\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(messages[0].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "对于以下文本，请从中提取以下信息：：\n",
      "\n",
      "礼物：该商品是作为礼物送给别人的吗？\n",
      "如果是，则回答 是的；如果否或未知，则回答 不是。\n",
      "\n",
      "交货天数：产品到达需要多少天？ 如果没有找到该信息，则输出-1。\n",
      "\n",
      "价钱：提取有关价值或价格的任何句子，并将它们输出为逗号分隔的 Python 列表。\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\"礼物\": string  // 这件物品是作为礼物送给别人的吗？                            如果是，则回答 是的，                            如果否或未知，则回答 不是。\n",
      "\t\"交货天数\": string  // 产品需要多少天才能到达？                                      如果没有找到该信息，则输出-1。\n",
      "\t\"价钱\": string  // 提取有关价值或价格的任何句子，                                    并将它们输出为逗号分隔的 Python 列表\n",
      "}\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 中文\n",
    "print(messages[0].content)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 4️⃣ 调用chat模型提取信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "```json\n",
      "{\n",
      "\t\"gift\": true,\n",
      "\t\"delivery_days\": \"2\",\n",
      "\t\"price_value\": [\"It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\"]\n",
      "}\n",
      "```\n"
     ]
    }
   ],
   "source": [
    "response = chat(messages)\n",
    "print(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "```json\n",
      "{\n",
      "\t\"礼物\": \"不是\",\n",
      "\t\"交货天数\": \"两天后就到了\",\n",
      "\t\"价钱\": \"它比其他吹叶机稍微贵一点\"\n",
      "}\n",
      "```\n"
     ]
    }
   ],
   "source": [
    "# 中文\n",
    "response = chat(messages)\n",
    "print(response.content)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 5️⃣ 使用输出解析器解析输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'gift': True,\n",
       " 'delivery_days': '2',\n",
       " 'price_value': [\"It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\"]}"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_dict = output_parser.parse(response.content)\n",
    "output_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'礼物': '不是', '交货天数': '两天后就到了', '价钱': '它比其他吹叶机稍微贵一点'}"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_dict = output_parser.parse(response.content)\n",
    "output_dict"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 📝 分析与总结\n",
    "`output_dict`类型为字典(`dict`), 可直接使用`get`方法。这样的输出更方便下游任务的处理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(output_dict)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'2'"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_dict.get('delivery_days')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 补充材料"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 链式思考推理(ReAct) <a id='reason_act'></a>\n",
    "参考资料：[ReAct (Reason+Act) prompting in OpenAI GPT and LangChain](https://tsmatz.wordpress.com/2023/03/07/react-with-openai-gpt-and-langchain/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -q wikipedia"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mThought: I need to search David Chanoff and find the U.S. Navy admiral he collaborated with. Then I need to find the president under whom the admiral served as the ambassador to the United Kingdom.\n",
      "Action: Search[David Chanoff]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mDavid Chanoff is a noted author of non-fiction work. His work has typically involved collaborations with the principal protagonist of the work concerned. His collaborators have included; Augustus A. White, Joycelyn Elders, Đoàn Văn Toại, William J. Crowe, Ariel Sharon, Kenneth Good and Felix Zandman. He has also written about a wide range of subjects including literary history, education and foreign for The Washington Post, The New Republic and The New York Times Magazine. He has published more than twelve books.\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3mDavid Chanoff has collaborated with several individuals, including Augustus A. White, Joycelyn Elders, Đoàn Văn Toại, William J. Crowe, Ariel Sharon, Kenneth Good, and Felix Zandman. I need to search each of these individuals to find the U.S. Navy admiral. \n",
      "Action: Search[Augustus A. White]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mAugustus A. White III (born June 4, 1936) is the Ellen and Melvin Gordon Distinguished Professor of Medical Education and Professor of Orthopedic Surgery at Harvard Medical School and a former Orthopaedic Surgeon-in-Chief at Beth Israel Hospital, Boston, Massachusetts. White was the first African American medical student at Stanford, surgical resident at Yale University, professor of medicine at Yale, and department head at a Harvard-affiliated hospital (Beth Israel Hospital).\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3mAugustus A. White III is not a U.S. Navy admiral. I need to search the next individual, Joycelyn Elders.\n",
      "Action: Search[Joycelyn Elders]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mMinnie Joycelyn Elders (born Minnie Lee Jones; August 13, 1933) is an American pediatrician and public health administrator who served as Surgeon General of the United States from 1993 to 1994. A vice admiral in the Public Health Service Commissioned Corps, she was the second woman, second person of color, and first African American to serve as Surgeon General. \n",
      "Elders is best known for her frank discussion of her views on controversial issues such as drug legalization, masturbation, and distributing contraception in schools. She was forced to resign in December 1994 amidst controversy as a result of her views. She is currently a professor emerita of pediatrics at the University of Arkansas for Medical Sciences.\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3mJoycelyn Elders is a pediatrician and public health administrator, not a U.S. Navy admiral. I need to search the next individual, Đoàn Văn Toại.\n",
      "Action: Search[Đoàn Văn Toại]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mĐoàn Văn Toại (1945 in Vietnam – November 2017 in California) was a Vietnamese-born naturalized American activist and the author of The Vietnamese Gulag (Simon & Schuster, 1986).\u001b[0m\n",
      "Thought:"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Retrying langchain.llms.openai.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-kmMHlitaQynVMwTW3jDTCPga on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.llms.openai.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-kmMHlitaQynVMwTW3jDTCPga on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.llms.openai.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-kmMHlitaQynVMwTW3jDTCPga on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32;1m\u001b[1;3mĐoàn Văn Toại is an activist and author, not a U.S. Navy admiral. I need to search the next individual, William J. Crowe.\n",
      "Action: Search[William J. Crowe]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mWilliam James Crowe Jr. (January 2, 1925 – October 18, 2007) was a United States Navy admiral and diplomat who served as the 11th chairman of the Joint Chiefs of Staff under Presidents Ronald Reagan and George H. W. Bush, and as the ambassador to the United Kingdom and Chair of the Intelligence Oversight Board under President Bill Clinton.\u001b[0m\n",
      "Thought:"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Retrying langchain.llms.openai.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-kmMHlitaQynVMwTW3jDTCPga on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.llms.openai.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-kmMHlitaQynVMwTW3jDTCPga on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.llms.openai.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-kmMHlitaQynVMwTW3jDTCPga on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32;1m\u001b[1;3mWilliam J. Crowe is a U.S. Navy admiral and diplomat. I need to find the president under whom he served as the ambassador to the United Kingdom.\n",
      "Action: Search[William J. Crowe ambassador to United Kingdom]\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mWilliam James Crowe Jr. (January 2, 1925 – October 18, 2007) was a United States Navy admiral and diplomat who served as the 11th chairman of the Joint Chiefs of Staff under Presidents Ronald Reagan and George H. W. Bush, and as the ambassador to the United Kingdom and Chair of the Intelligence Oversight Board under President Bill Clinton.\u001b[0m\n",
      "Thought:"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Retrying langchain.llms.openai.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-kmMHlitaQynVMwTW3jDTCPga on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.llms.openai.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-kmMHlitaQynVMwTW3jDTCPga on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32;1m\u001b[1;3mWilliam J. Crowe served as the ambassador to the United Kingdom under President Bill Clinton. So the answer is Bill Clinton.\n",
      "Action: Finish[Bill Clinton]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'Bill Clinton'"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.docstore.wikipedia import Wikipedia\n",
    "from langchain.llms import OpenAI\n",
    "from langchain.agents import initialize_agent, Tool, AgentExecutor\n",
    "from langchain.agents.react.base import DocstoreExplorer\n",
    "\n",
    "docstore=DocstoreExplorer(Wikipedia())\n",
    "tools = [\n",
    "  Tool(\n",
    "    name=\"Search\",\n",
    "    func=docstore.search,\n",
    "    description=\"Search for a term in the docstore.\",\n",
    "  ),\n",
    "  Tool(\n",
    "    name=\"Lookup\",\n",
    "    func=docstore.lookup,\n",
    "    description=\"Lookup a term in the docstore.\",\n",
    "  )\n",
    "]\n",
    "\n",
    "# 使用大语言模型\n",
    "llm = OpenAI(\n",
    "  model_name=\"gpt-3.5-turbo\",\n",
    "  temperature=0,\n",
    ")\n",
    "\n",
    "# 初始化ReAct代理\n",
    "react = initialize_agent(tools, llm, agent=\"react-docstore\", verbose=True)\n",
    "agent_executor = AgentExecutor.from_agent_and_tools(\n",
    "  agent=react.agent,\n",
    "  tools=tools,\n",
    "  verbose=True,\n",
    ")\n",
    "\n",
    "\n",
    "question = \"Author David Chanoff has collaborated with a U.S. Navy admiral who served as the ambassador to the United Kingdom under which President?\"\n",
    "agent_executor.run(question)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
