{ "cells": [ { "cell_type": "markdown", "id": "99a80181", "metadata": {}, "source": [ "# Introduction to the Planner\n", "\n", "The Planner is one of the fundamental concepts of the Semantic Kernel.\n", "\n", "It makes use of the collection of native and semantic functions that have been registered to the kernel and using AI, will formulate a plan to execute the given ask.\n", "\n", "From our own testing, planner works best with more powerful models like `gpt4` but sometimes you might get working plans with cheaper models like `gpt-35-turbo`. We encourage you to implement your own versions of the planner and use different models that fit your user needs.\n", "\n", "Read more about planner [here](https://aka.ms/sk/concepts/planner).\n" ] }, { "cell_type": "markdown", "id": "1ebf9c0e", "metadata": {}, "source": [ "Import Semantic Kernel SDK from pypi.org" ] }, { "cell_type": "code", "execution_count": null, "id": "07eb35d2", "metadata": {}, "outputs": [], "source": [ "# Note: if using a virtual environment, do not run this cell\n", "%pip install -U semantic-kernel\n", "from semantic_kernel import __version__\n", "\n", "__version__" ] }, { "cell_type": "markdown", "id": "5f8e96d7", "metadata": {}, "source": [ "Initial configuration for the notebook to run properly." ] }, { "cell_type": "code", "execution_count": null, "id": "e5d9ed81", "metadata": {}, "outputs": [], "source": [ "# Make sure paths are correct for the imports\n", "\n", "import os\n", "import sys\n", "\n", "notebook_dir = os.path.abspath(\"\")\n", "parent_dir = os.path.dirname(notebook_dir)\n", "grandparent_dir = os.path.dirname(parent_dir)\n", "\n", "\n", "sys.path.append(grandparent_dir)" ] }, { "cell_type": "markdown", "id": "1dcd7e04", "metadata": {}, "source": [ "### Configuring the Kernel\n", "\n", "Let's get started with the necessary configuration to run Semantic Kernel. For Notebooks, we require a `.env` file with the proper settings for the model you use. Create a new file named `.env` and place it in this directory. Copy the contents of the `.env.example` file from this directory and paste it into the `.env` file that you just created.\n", "\n", "**NOTE: Please make sure to include `GLOBAL_LLM_SERVICE` set to either OpenAI, AzureOpenAI, or HuggingFace in your .env file. If this setting is not included, the Service will default to AzureOpenAI.**\n", "\n", "#### Option 1: using OpenAI\n", "\n", "Add your [OpenAI Key](https://openai.com/product/) key to your `.env` file (org Id only if you have multiple orgs):\n", "\n", "```\n", "GLOBAL_LLM_SERVICE=\"OpenAI\"\n", "OPENAI_API_KEY=\"sk-...\"\n", "OPENAI_ORG_ID=\"\"\n", "OPENAI_CHAT_MODEL_ID=\"\"\n", "OPENAI_TEXT_MODEL_ID=\"\"\n", "OPENAI_EMBEDDING_MODEL_ID=\"\"\n", "```\n", "The names should match the names used in the `.env` file, as shown above.\n", "\n", "#### Option 2: using Azure OpenAI\n", "\n", "Add your [Azure Open AI Service key](https://learn.microsoft.com/azure/cognitive-services/openai/quickstart?pivots=programming-language-studio) settings to the `.env` file in the same folder:\n", "\n", "```\n", "GLOBAL_LLM_SERVICE=\"AzureOpenAI\"\n", "AZURE_OPENAI_API_KEY=\"...\"\n", "AZURE_OPENAI_ENDPOINT=\"https://...\"\n", "AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=\"...\"\n", "AZURE_OPENAI_TEXT_DEPLOYMENT_NAME=\"...\"\n", "AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=\"...\"\n", "AZURE_OPENAI_API_VERSION=\"...\"\n", "```\n", "The names should match the names used in the `.env` file, as shown above.\n", "\n", "For more advanced configuration, please follow the steps outlined in the [setup guide](./CONFIGURING_THE_KERNEL.md)." ] }, { "cell_type": "markdown", "id": "1907665d", "metadata": {}, "source": [ "We will load our settings and get the LLM service to use for the notebook." ] }, { "cell_type": "code", "execution_count": null, "id": "1a1e3c4e", "metadata": {}, "outputs": [], "source": [ "from services import Service\n", "\n", "from samples.service_settings import ServiceSettings\n", "\n", "service_settings = ServiceSettings.create()\n", "\n", "# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n", "selectedService = (\n", " Service.AzureOpenAI\n", " if service_settings.global_llm_service is None\n", " else Service(service_settings.global_llm_service.lower())\n", ")\n", "print(f\"Using service type: {selectedService}\")" ] }, { "cell_type": "markdown", "id": "4d888f62", "metadata": {}, "source": [ "Let's define some imports that will be used in this example." ] }, { "cell_type": "code", "execution_count": null, "id": "3852961c", "metadata": {}, "outputs": [], "source": [ "from semantic_kernel.contents.chat_history import ChatHistory # noqa: F401\n", "from semantic_kernel.functions.kernel_arguments import KernelArguments # noqa: F401\n", "from semantic_kernel.prompt_template.input_variable import InputVariable # noqa: F401" ] }, { "cell_type": "markdown", "id": "deff5675", "metadata": {}, "source": [ "Define your ASK. What do you want the Kernel to do?\n" ] }, { "cell_type": "code", "execution_count": null, "id": "925b4ae8", "metadata": {}, "outputs": [], "source": [ "ask = \"\"\"\n", "Tomorrow is Valentine's day. I need to come up with a few short poems.\n", "She likes Shakespeare so write using his style. She speaks French so write it in French.\n", "Convert the text to uppercase.\"\"\"" ] }, { "cell_type": "markdown", "id": "b61bacf1", "metadata": {}, "source": [ "### Providing plugins to the planner\n", "\n", "The planner needs to know what plugins are available to it. Here we'll give it access to the `SummarizePlugin` and `WriterPlugin` we have defined on disk. This will include many semantic functions, of which the planner will intelligently choose a subset.\n", "\n", "You can also include native functions as well. Here we'll add the TextPlugin." ] }, { "cell_type": "code", "execution_count": null, "id": "a3161dcf", "metadata": {}, "outputs": [], "source": [ "from semantic_kernel.connectors.ai.open_ai import OpenAIChatPromptExecutionSettings\n", "from semantic_kernel.core_plugins.text_plugin import TextPlugin\n", "from semantic_kernel.functions.kernel_function_from_prompt import KernelFunctionFromPrompt\n", "from semantic_kernel.kernel import Kernel\n", "\n", "kernel = Kernel()\n", "service_id = None\n", "if selectedService == Service.OpenAI:\n", " from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n", "\n", " service_id = \"default\"\n", " kernel.add_service(\n", " OpenAIChatCompletion(\n", " service_id=service_id,\n", " ),\n", " )\n", "elif selectedService == Service.AzureOpenAI:\n", " from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n", "\n", " service_id = \"default\"\n", " kernel.add_service(\n", " AzureChatCompletion(\n", " service_id=service_id,\n", " ),\n", " )\n", "\n", "plugins_directory = \"../../../prompt_template_samples/\"\n", "summarize_plugin = kernel.add_plugin(plugin_name=\"SummarizePlugin\", parent_directory=plugins_directory)\n", "writer_plugin = kernel.add_plugin(\n", " plugin_name=\"WriterPlugin\",\n", " parent_directory=plugins_directory,\n", ")\n", "text_plugin = kernel.add_plugin(plugin=TextPlugin(), plugin_name=\"TextPlugin\")\n", "\n", "shakespeare_func = KernelFunctionFromPrompt(\n", " function_name=\"Shakespeare\",\n", " plugin_name=\"WriterPlugin\",\n", " prompt=\"\"\"\n", "{{$input}}\n", "\n", "Rewrite the above in the style of Shakespeare.\n", "\"\"\",\n", " prompt_execution_settings=OpenAIChatPromptExecutionSettings(\n", " service_id=service_id,\n", " max_tokens=2000,\n", " temperature=0.8,\n", " ),\n", " description=\"Rewrite the input in the style of Shakespeare.\",\n", ")\n", "kernel.add_function(plugin_name=\"WriterPlugin\", function=shakespeare_func)\n", "\n", "for plugin_name, plugin in kernel.plugins.items():\n", " for function_name, function in plugin.functions.items():\n", " print(f\"Plugin: {plugin_name}, Function: {function_name}\")" ] }, { "cell_type": "markdown", "id": "e8a9b6b7", "metadata": {}, "source": [ "# The Plan Object Model\n" ] }, { "cell_type": "markdown", "id": "e50f8859", "metadata": {}, "source": [ "To build more advanced planners, we need to introduce a proper Plan object that can contain all the necessary state and information needed for high quality plans.\n", "\n", "To see what that object model is, look at (https://github.com/microsoft/semantic-kernel/blob/main/python/semantic_kernel/planners/plan.py)\n" ] }, { "cell_type": "markdown", "id": "0a0cb2a2", "metadata": {}, "source": [ "# Sequential Planner\n" ] }, { "cell_type": "markdown", "id": "a1c66d83", "metadata": {}, "source": [ "The sequential planner is an XML-based step-by-step planner. You can see the prompt used for it [here](https://github.com/microsoft/semantic-kernel/blob/main/python/semantic_kernel/planners/sequential_planner/Plugins/SequentialPlanning/skprompt.txt).\n" ] }, { "cell_type": "code", "execution_count": null, "id": "e2e90624", "metadata": {}, "outputs": [], "source": [ "from semantic_kernel.planners import SequentialPlanner\n", "\n", "planner = SequentialPlanner(kernel, service_id)" ] }, { "cell_type": "code", "execution_count": null, "id": "0d537981", "metadata": {}, "outputs": [], "source": [ "sequential_plan = await planner.create_plan(goal=ask)" ] }, { "cell_type": "markdown", "id": "ee2f462b", "metadata": {}, "source": [ "To see the steps that the Sequential Planner will take, we can iterate over them and print their descriptions\n" ] }, { "cell_type": "code", "execution_count": null, "id": "e7007418", "metadata": {}, "outputs": [], "source": [ "print(\"The plan's steps are:\")\n", "for step in sequential_plan._steps:\n", " print(\n", " f\"- {step.description.replace('.', '') if step.description else 'No description'} using {step.metadata.fully_qualified_name} with parameters: {step.parameters}\"\n", " )" ] }, { "cell_type": "markdown", "id": "4db5f844", "metadata": {}, "source": [ "Let's ask the sequential planner to execute the plan.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "88411884", "metadata": {}, "outputs": [], "source": [ "result = await sequential_plan.invoke(kernel)" ] }, { "cell_type": "code", "execution_count": null, "id": "36d27aa0", "metadata": {}, "outputs": [], "source": [ "print(result)" ] }, { "cell_type": "markdown", "id": "789b651a", "metadata": {}, "source": [ "# Function Calling Stepwise Planner\n" ] }, { "cell_type": "markdown", "id": "8a4bbcc3", "metadata": {}, "source": [ "The Function Calling Stepwise Planner is based off the paper from MRKL (Modular Reasoning, Knowledge and Language) and is similar to other papers like ReACT (Reasoning and Acting in Language Models). At the core, the stepwise planner allows for the AI to form \"thoughts\" and \"observations\" and execute actions based off those to achieve a user's goal. This continues until all required functions are complete and a final output is generated.\n", "\n", "Please note that the Function Calling Stepwise Planner uses OpenAI function calling, and so it can only use either the AzureChatCompletion or the OpenAIChatCompletion service.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "771bafa2", "metadata": {}, "outputs": [], "source": [ "from semantic_kernel.kernel import Kernel\n", "\n", "kernel = Kernel()\n", "service_id = None\n", "if selectedService == Service.OpenAI:\n", " from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n", "\n", " service_id = \"default\"\n", " kernel.add_service(\n", " OpenAIChatCompletion(\n", " service_id=service_id,\n", " ),\n", " )\n", "elif selectedService == Service.AzureOpenAI:\n", " from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n", "\n", " service_id = \"default\"\n", " kernel.add_service(\n", " AzureChatCompletion(\n", " service_id=service_id,\n", " ),\n", " )" ] }, { "cell_type": "markdown", "id": "e0a00bde", "metadata": {}, "source": [ "Let's create a sample `EmailPlugin` that simulates handling a request to `get_email_address()` and `send_email()`.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "6cb43d0f", "metadata": {}, "outputs": [], "source": [ "from typing import Annotated\n", "\n", "from semantic_kernel.functions.kernel_function_decorator import kernel_function\n", "\n", "\n", "class EmailPlugin:\n", " \"\"\"\n", " Description: EmailPlugin provides a set of functions to send emails.\n", "\n", " Usage:\n", " kernel.add_plugin(EmailPlugin(), plugin_name=\"email\")\n", "\n", " Examples:\n", " {{email.SendEmail}} => Sends an email with the provided subject and body.\n", " \"\"\"\n", "\n", " @kernel_function(name=\"SendEmail\", description=\"Given an e-mail and message body, send an e-email\")\n", " def send_email(\n", " self,\n", " subject: Annotated[str, \"the subject of the email\"],\n", " body: Annotated[str, \"the body of the email\"],\n", " ) -> Annotated[str, \"the output is a string\"]:\n", " \"\"\"Sends an email with the provided subject and body.\"\"\"\n", " return f\"Email sent with subject: {subject} and body: {body}\"\n", "\n", " @kernel_function(name=\"GetEmailAddress\", description=\"Given a name, find the email address\")\n", " def get_email_address(\n", " self,\n", " input: Annotated[str, \"the name of the person\"],\n", " ):\n", " email = \"\"\n", " if input == \"Jane\":\n", " email = \"janedoe4321@example.com\"\n", " elif input == \"Paul\":\n", " email = \"paulsmith5678@example.com\"\n", " elif input == \"Mary\":\n", " email = \"maryjones8765@example.com\"\n", " else:\n", " email = \"johndoe1234@example.com\"\n", " return email" ] }, { "cell_type": "markdown", "id": "9feef46b", "metadata": {}, "source": [ "We'll add this new plugin to the kernel." ] }, { "cell_type": "code", "execution_count": null, "id": "032d5981", "metadata": {}, "outputs": [], "source": [ "kernel.add_plugin(plugin_name=\"EmailPlugin\", plugin=EmailPlugin())" ] }, { "cell_type": "markdown", "id": "effdf3ab", "metadata": {}, "source": [ "Let's also add a couple more plugins." ] }, { "cell_type": "code", "execution_count": null, "id": "abe150e0", "metadata": {}, "outputs": [], "source": [ "from semantic_kernel.core_plugins.math_plugin import MathPlugin\n", "from semantic_kernel.core_plugins.time_plugin import TimePlugin\n", "\n", "kernel.add_plugin(plugin_name=\"MathPlugin\", plugin=MathPlugin())\n", "kernel.add_plugin(plugin_name=\"TimePlugin\", plugin=TimePlugin())" ] }, { "cell_type": "markdown", "id": "06796ade", "metadata": {}, "source": [ "We will define our FunctionCallingStepPlanner and the questions we want to ask." ] }, { "cell_type": "code", "execution_count": null, "id": "06d08549", "metadata": {}, "outputs": [], "source": [ "from semantic_kernel.planners.function_calling_stepwise_planner import (\n", " FunctionCallingStepwisePlanner,\n", " FunctionCallingStepwisePlannerOptions,\n", ")\n", "\n", "questions = [\n", " \"What is the current hour number, plus 5?\",\n", " \"What is 387 minus 22? Email the solution to John and Mary.\",\n", " \"Write a limerick, translate it to Spanish, and send it to Jane\",\n", "]\n", "\n", "options = FunctionCallingStepwisePlannerOptions(\n", " max_iterations=10,\n", " max_tokens=4000,\n", ")\n", "\n", "planner = FunctionCallingStepwisePlanner(service_id=service_id, options=options)" ] }, { "cell_type": "markdown", "id": "27ed7874", "metadata": {}, "source": [ "Let's loop through the questions and invoke the planner." ] }, { "cell_type": "code", "execution_count": null, "id": "d00c6f71", "metadata": {}, "outputs": [], "source": [ "for question in questions:\n", " result = await planner.invoke(kernel, question)\n", " print(f\"Q: {question}\\nA: {result.final_answer}\\n\")\n", "\n", " # Uncomment the following line to view the planner's process for completing the request\n", " # print(f\"Chat history: {result.chat_history}\\n\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.12.7" } }, "nbformat": 4, "nbformat_minor": 5 }