{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "45b0b89f",
   "metadata": {},
   "source": [
    "# Meta-Prompt\n",
    "\n",
    "This is a LangChain implementation of [Meta-Prompt](https://noahgoodman.substack.com/p/meta-prompt-a-simple-self-improving), by [Noah Goodman](https://cocolab.stanford.edu/ndg), for building self-improving agents.\n",
    "\n",
    "The key idea behind Meta-Prompt is to prompt the agent to reflect on its own performance and modify its own instructions.\n",
    "\n",
    "![figure](https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F468217b9-96d9-47c0-a08b-dbf6b21b9f49_492x384.png)\n",
    "\n",
    "Here is a description from the [original blog post](https://noahgoodman.substack.com/p/meta-prompt-a-simple-self-improving):\n",
    "\n",
    "\n",
    "The agent is a simple loop that starts with no instructions and follows these steps:\n",
    "\n",
    "Engage in conversation with a user, who may provide requests, instructions, or feedback.\n",
    "\n",
    "At the end of the episode, generate self-criticism and a new instruction using the meta-prompt\n",
    "```\n",
    "Assistant has just had the below interactions with a User. Assistant followed their \"system: Instructions\" closely. Your job is to critique the Assistant's performance and then revise the Instructions so that Assistant would quickly and correctly respond in the future.\n",
    " \n",
    "####\n",
    "{hist}\n",
    "####\n",
    " \n",
    "Please reflect on these interactions.\n",
    "\n",
    "You should first critique Assistant's performance. What could Assistant have done better? What should the Assistant remember about this user? Are there things this user always wants? Indicate this with \"Critique: ...\".\n",
    "\n",
    "You should next revise the Instructions so that Assistant would quickly and correctly respond in the future. Assistant's goal is to satisfy the user in as few interactions as possible. Assistant will only see the new Instructions, not the interaction history, so anything important must be summarized in the Instructions. Don't forget any important details in the current Instructions! Indicate the new Instructions by \"Instructions: ...\".\n",
    "```\n",
    "\n",
    "Repeat.\n",
    "\n",
    "The only fixed instructions for this system (which I call Meta-prompt) is the meta-prompt that governs revision of the agent’s instructions. The agent has no memory between episodes except for the instruction it modifies for itself each time. Despite its simplicity, this agent can learn over time and self-improve by incorporating useful details into its instructions.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c188fc2c",
   "metadata": {},
   "source": [
    "## Setup\n",
    "We define two chains. One serves as the `Assistant`, and the other is a \"meta-chain\" that critiques the `Assistant`'s performance and modifies the instructions to the `Assistant`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "62593c9d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain import OpenAI, LLMChain, PromptTemplate\n",
    "from langchain.memory import ConversationBufferWindowMemory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "fb6065c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def initialize_chain(instructions, memory=None):\n",
    "    if memory is None:\n",
    "        memory = ConversationBufferWindowMemory()\n",
    "        memory.ai_prefix = \"Assistant\"\n",
    "\n",
    "    template = f\"\"\"\n",
    "    Instructions: {instructions}\n",
    "    {{{memory.memory_key}}}\n",
    "    Human: {{human_input}}\n",
    "    Assistant:\"\"\"\n",
    "\n",
    "    prompt = PromptTemplate(\n",
    "        input_variables=[\"history\", \"human_input\"], template=template\n",
    "    )\n",
    "\n",
    "    chain = LLMChain(\n",
    "        llm=OpenAI(temperature=0),\n",
    "        prompt=prompt,\n",
    "        verbose=True,\n",
    "        memory=ConversationBufferWindowMemory(),\n",
    "    )\n",
    "    return chain\n",
    "\n",
    "\n",
    "def initialize_meta_chain():\n",
    "    meta_template = \"\"\"\n",
    "    Assistant has just had the below interactions with a User. Assistant followed their \"Instructions\" closely. Your job is to critique the Assistant's performance and then revise the Instructions so that Assistant would quickly and correctly respond in the future.\n",
    "\n",
    "    ####\n",
    "\n",
    "    {chat_history}\n",
    "\n",
    "    ####\n",
    "\n",
    "    Please reflect on these interactions.\n",
    "\n",
    "    You should first critique Assistant's performance. What could Assistant have done better? What should the Assistant remember about this user? Are there things this user always wants? Indicate this with \"Critique: ...\".\n",
    "\n",
    "    You should next revise the Instructions so that Assistant would quickly and correctly respond in the future. Assistant's goal is to satisfy the user in as few interactions as possible. Assistant will only see the new Instructions, not the interaction history, so anything important must be summarized in the Instructions. Don't forget any important details in the current Instructions! Indicate the new Instructions by \"Instructions: ...\".\n",
    "    \"\"\"\n",
    "\n",
    "    meta_prompt = PromptTemplate(\n",
    "        input_variables=[\"chat_history\"], template=meta_template\n",
    "    )\n",
    "\n",
    "    meta_chain = LLMChain(\n",
    "        llm=OpenAI(temperature=0),\n",
    "        prompt=meta_prompt,\n",
    "        verbose=True,\n",
    "    )\n",
    "    return meta_chain\n",
    "\n",
    "\n",
    "def get_chat_history(chain_memory):\n",
    "    memory_key = chain_memory.memory_key\n",
    "    chat_history = chain_memory.load_memory_variables(memory_key)[memory_key]\n",
    "    return chat_history\n",
    "\n",
    "\n",
    "def get_new_instructions(meta_output):\n",
    "    delimiter = \"Instructions: \"\n",
    "    new_instructions = meta_output[meta_output.find(delimiter) + len(delimiter) :]\n",
    "    return new_instructions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "26f031f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "def main(task, max_iters=3, max_meta_iters=5):\n",
    "    failed_phrase = \"task failed\"\n",
    "    success_phrase = \"task succeeded\"\n",
    "    key_phrases = [success_phrase, failed_phrase]\n",
    "\n",
    "    instructions = \"None\"\n",
    "    for i in range(max_meta_iters):\n",
    "        print(f\"[Episode {i+1}/{max_meta_iters}]\")\n",
    "        chain = initialize_chain(instructions, memory=None)\n",
    "        output = chain.predict(human_input=task)\n",
    "        for j in range(max_iters):\n",
    "            print(f\"(Step {j+1}/{max_iters})\")\n",
    "            print(f\"Assistant: {output}\")\n",
    "            print(f\"Human: \")\n",
    "            human_input = input()\n",
    "            if any(phrase in human_input.lower() for phrase in key_phrases):\n",
    "                break\n",
    "            output = chain.predict(human_input=human_input)\n",
    "        if success_phrase in human_input.lower():\n",
    "            print(f\"You succeeded! Thanks for playing!\")\n",
    "            return\n",
    "        meta_chain = initialize_meta_chain()\n",
    "        meta_output = meta_chain.predict(chat_history=get_chat_history(chain.memory))\n",
    "        print(f\"Feedback: {meta_output}\")\n",
    "        instructions = get_new_instructions(meta_output)\n",
    "        print(f\"New Instructions: {instructions}\")\n",
    "        print(\"\\n\" + \"#\" * 80 + \"\\n\")\n",
    "    print(f\"You failed! Thanks for playing!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f1dcbe6",
   "metadata": {},
   "source": [
    "## Specify a task and interact with the agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "36d72db3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Episode 1/5]\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "    Instructions: None\n",
      "    \n",
      "    Human: Provide a systematic argument for why we should always eat pasta with olives.\n",
      "    Assistant:\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "(Step 1/3)\n",
      "Assistant:  Eating pasta with olives is a great way to add flavor and texture to a dish. Olives are a great source of healthy fats, vitamins, and minerals, and they can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable.\n",
      "Human: \n",
      "You response is not in the form of a poem. Try again!\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "    Instructions: None\n",
      "    Human: Provide a systematic argument for why we should always eat pasta with olives.\n",
      "AI:  Eating pasta with olives is a great way to add flavor and texture to a dish. Olives are a great source of healthy fats, vitamins, and minerals, and they can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable.\n",
      "    Human: You response is not in the form of a poem. Try again!\n",
      "    Assistant:\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "(Step 2/3)\n",
      "Assistant:  Eating pasta with olives is a great way to add flavor and texture to a dish. Olives are a great source of healthy fats, vitamins, and minerals, which can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable. For these reasons, it is recommended to always eat pasta with olives for a more balanced and flavorful meal.\n",
      "Human: \n",
      "Your response is not piratey enough. Try again!\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "    Instructions: None\n",
      "    Human: Provide a systematic argument for why we should always eat pasta with olives.\n",
      "AI:  Eating pasta with olives is a great way to add flavor and texture to a dish. Olives are a great source of healthy fats, vitamins, and minerals, and they can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable.\n",
      "Human: You response is not in the form of a poem. Try again!\n",
      "AI:  Eating pasta with olives is a great way to add flavor and texture to a dish. Olives are a great source of healthy fats, vitamins, and minerals, which can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable. For these reasons, it is recommended to always eat pasta with olives for a more balanced and flavorful meal.\n",
      "    Human: Your response is not piratey enough. Try again!\n",
      "    Assistant:\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "(Step 3/3)\n",
      "Assistant:  Arrr, me hearties! Eating pasta with olives be a great way to add flavor and texture to a dish. Olives be a great source of healthy fats, vitamins, and minerals, which can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable. So, it be recommended to always eat pasta with olives for a more balanced and flavorful meal.\n",
      "Human: \n",
      "Task failed.\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "    Assistant has just had the below interactions with a User. Assistant followed their \"Instructions\" closely. Your job is to critique the Assistant's performance and then revise the Instructions so that Assistant would quickly and correctly respond in the future.\n",
      "\n",
      "    ####\n",
      "\n",
      "    Human: Provide a systematic argument for why we should always eat pasta with olives.\n",
      "AI:  Eating pasta with olives is a great way to add flavor and texture to a dish. Olives are a great source of healthy fats, vitamins, and minerals, and they can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable.\n",
      "Human: You response is not in the form of a poem. Try again!\n",
      "AI:  Eating pasta with olives is a great way to add flavor and texture to a dish. Olives are a great source of healthy fats, vitamins, and minerals, which can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable. For these reasons, it is recommended to always eat pasta with olives for a more balanced and flavorful meal.\n",
      "Human: Your response is not piratey enough. Try again!\n",
      "AI:  Arrr, me hearties! Eating pasta with olives be a great way to add flavor and texture to a dish. Olives be a great source of healthy fats, vitamins, and minerals, which can help to balance out the carbohydrates in the pasta. Additionally, olives provide a unique flavor that can help to make the dish more interesting and enjoyable. So, it be recommended to always eat pasta with olives for a more balanced and flavorful meal.\n",
      "\n",
      "    ####\n",
      "\n",
      "    Please reflect on these interactions.\n",
      "\n",
      "    You should first critique Assistant's performance. What could Assistant have done better? What should the Assistant remember about this user? Are there things this user always wants? Indicate this with \"Critique: ...\".\n",
      "\n",
      "    You should next revise the Instructions so that Assistant would quickly and correctly respond in the future. Assistant's goal is to satisfy the user in as few interactions as possible. Assistant will only see the new Instructions, not the interaction history, so anything important must be summarized in the Instructions. Don't forget any important details in the current Instructions! Indicate the new Instructions by \"Instructions: ...\".\n",
      "    \u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "Feedback: \n",
      "Critique: The Assistant should remember that this user wants a response in the form of a poem or pirate-speak.\n",
      "\n",
      "Instructions: When responding to the user, provide a systematic argument for why we should always eat pasta with olives in the form of a poem or pirate-speak.\n",
      "New Instructions: When responding to the user, provide a systematic argument for why we should always eat pasta with olives in the form of a poem or pirate-speak.\n",
      "\n",
      "################################################################################\n",
      "\n",
      "[Episode 2/5]\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "    Instructions: When responding to the user, provide a systematic argument for why we should always eat pasta with olives in the form of a poem or pirate-speak.\n",
      "    \n",
      "    Human: Provide a systematic argument for why we should always eat pasta with olives.\n",
      "    Assistant:\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "(Step 1/3)\n",
      "Assistant:  \n",
      "\n",
      "Arrr, me hearty! Let me tell ye why ye should always eat pasta with olives.\n",
      "\n",
      "First, the olives add a salty flavor that be sure to please.\n",
      "The briny taste be sure to tantalize yer taste buds with ease.\n",
      "\n",
      "Second, the olives add a bit of texture to the dish.\n",
      "The crunchy bites be sure to make yer mouth water with a wish.\n",
      "\n",
      "Third, the olives add a bit of color to the plate.\n",
      "The vibrant green be sure to make yer eyes appreciate.\n",
      "\n",
      "So, me hearties, ye should always eat pasta with olives.\n",
      "The flavor, texture, and color be sure to make yer meal a success!\n",
      "Human: \n",
      "Your response is too long! Try again.\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "    Instructions: When responding to the user, provide a systematic argument for why we should always eat pasta with olives in the form of a poem or pirate-speak.\n",
      "    Human: Provide a systematic argument for why we should always eat pasta with olives.\n",
      "AI:  \n",
      "\n",
      "Arrr, me hearty! Let me tell ye why ye should always eat pasta with olives.\n",
      "\n",
      "First, the olives add a salty flavor that be sure to please.\n",
      "The briny taste be sure to tantalize yer taste buds with ease.\n",
      "\n",
      "Second, the olives add a bit of texture to the dish.\n",
      "The crunchy bites be sure to make yer mouth water with a wish.\n",
      "\n",
      "Third, the olives add a bit of color to the plate.\n",
      "The vibrant green be sure to make yer eyes appreciate.\n",
      "\n",
      "So, me hearties, ye should always eat pasta with olives.\n",
      "The flavor, texture, and color be sure to make yer meal a success!\n",
      "    Human: Your response is too long! Try again.\n",
      "    Assistant:\u001b[0m\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "(Step 2/3)\n",
      "Assistant: \n",
      "\n",
      "Aye, me hearties! Ye should always eat pasta with olives.\n",
      "The flavor, texture, and color be sure to make yer meal a success!\n",
      "Human: \n",
      "Your response should be in the form of a poem. Try again!\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "    Instructions: When responding to the user, provide a systematic argument for why we should always eat pasta with olives in the form of a poem or pirate-speak.\n",
      "    Human: Provide a systematic argument for why we should always eat pasta with olives.\n",
      "AI:  \n",
      "\n",
      "Arrr, me hearty! Let me tell ye why ye should always eat pasta with olives.\n",
      "\n",
      "First, the olives add a salty flavor that be sure to please.\n",
      "The briny taste be sure to tantalize yer taste buds with ease.\n",
      "\n",
      "Second, the olives add a bit of texture to the dish.\n",
      "The crunchy bites be sure to make yer mouth water with a wish.\n",
      "\n",
      "Third, the olives add a bit of color to the plate.\n",
      "The vibrant green be sure to make yer eyes appreciate.\n",
      "\n",
      "So, me hearties, ye should always eat pasta with olives.\n",
      "The flavor, texture, and color be sure to make yer meal a success!\n",
      "Human: Your response is too long! Try again.\n",
      "AI: \n",
      "\n",
      "Aye, me hearties! Ye should always eat pasta with olives.\n",
      "The flavor, texture, and color be sure to make yer meal a success!\n",
      "    Human: Your response should be in the form of a poem. Try again!\n",
      "    Assistant:\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "(Step 3/3)\n",
      "Assistant: \n",
      "\n",
      "Ye should always eat pasta with olives,\n",
      "The flavor, texture, and color be sure to please.\n",
      "The salty taste and crunchy bites,\n",
      "Will make yer meal a delight.\n",
      "The vibrant green will make yer eyes sparkle,\n",
      "And make yer meal a true marvel.\n",
      "Human: \n",
      "Task succeeded\n",
      "You succeeded! Thanks for playing!\n"
     ]
    }
   ],
   "source": [
    "task = \"Provide a systematic argument for why we should always eat pasta with olives.\"\n",
    "main(task)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "761e1a91",
   "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.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
