diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d801ea7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +src/.vs/** +src/sk-phi2-localserver-lmstudio/obj/** +src/sk-phi2-localserver-lmstudio/bin/** +src/sk-customllm/bin/** +src/sk-customllm/obj/** +src/sk-ollama-localserver-ubuntu/bin/** +src/sk-ollama-localserver-ubuntu/obj/** +src/sk-ollama-localserver-rpi/bin/** +src/sk-ollama-localserver-rpi/obj/** diff --git a/KernelMemory/Memories.cs b/KernelMemory/Memories.cs deleted file mode 100644 index ca2084d..0000000 --- a/KernelMemory/Memories.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.ComponentModel; -using System.Text.Json; -using Microsoft.KernelMemory; - -public class MemoryKernel -{ - static MemoryServerless memory; - - public static void Init(string openApiKey) - { - - KernelMemoryBuilder kernelMemoryBuilder; - - kernelMemoryBuilder = new KernelMemoryBuilder() - .WithOpenAIDefaults(openApiKey); - - memory = kernelMemoryBuilder.BuildServerlessClient(); - - LoadTextMemories(); - LoadDocs(); - } - - static async void LoadTextMemories() - { - await memory.ImportTextAsync("El súper héroe favorito de Gisela es Batman", documentId: "gis01", tags: new TagCollection { { "type", "people" } }); - await memory.ImportTextAsync("La última película que Gisela vio de súper héroes fue Guardianes de la Galaxia Vol. 3 ", documentId: "gis02", tags: new TagCollection { { "type", "people" } }); - } - - static async void LoadDocs() - { - await memory.ImportDocumentAsync("docs/Norma editorial - Septiembre 2020.pdf", documentId: "doc001", tags: new TagCollection { { "type", "document" } }); - } - - [SKFunction, Description("Sobre el volumen")] - public static async Task AboutTheDocuments(string ask) - { - var answer = await memory.AskAsync(ask, filter: new MemoryFilter().ByTag("type", "document")); - - // Answer - Console.WriteLine($"\nAnswer: {answer.Result}"); - - // Sources - foreach (var x in answer.RelevantSources) - { - Console.WriteLine($" - {x.SourceName} - {x.Link} [{x.Partitions.First().LastUpdate:D}]"); - } - - // return a json string with the answer and the sources - return JsonSerializer.Serialize(new { answer = answer.Result, references = answer.RelevantSources.Select(x => x.SourceName) }); - } - - [SKFunction, Description("Responde preguntas sobre Gisela")] - public static async Task AboutMe(string ask) - { - var answer = await memory.AskAsync(ask, filter: new MemoryFilter().ByTag("type", "people")); - - // Answer - Console.WriteLine($"\nAnswer: {answer.Result}"); - - // Sources - foreach (var x in answer.RelevantSources) - { - Console.WriteLine($" - {x.SourceName} - {x.Link} [{x.Partitions.First().LastUpdate:D}]"); - } - - // return a json string with the answer and the sources - return JsonSerializer.Serialize(new { answer = answer.Result, references = answer.RelevantSources.Select(x => x.SourceName) }); - - } -} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20072b0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 El Bruno + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NativeFunctions/GetHeroInfo.cs b/NativeFunctions/HeroInfo.cs similarity index 76% rename from NativeFunctions/GetHeroInfo.cs rename to NativeFunctions/HeroInfo.cs index 7424e83..97d401b 100644 --- a/NativeFunctions/GetHeroInfo.cs +++ b/NativeFunctions/HeroInfo.cs @@ -1,34 +1,34 @@ -using System.ComponentModel; -using System.Globalization; -using Microsoft.SemanticKernel; -using System.Net.Http; -using Newtonsoft.Json.Linq; - -public class Info -{ - string apiKey; - public Info(string superHeroApiKey) - { - apiKey = superHeroApiKey; - } - - [SKFunction, Description("Get the alter ego of a superhero")] - public string GetAlterEgo(string input) - { - // Call the API - var httpClient = new HttpClient(); - var response = httpClient.GetAsync($"https://superheroapi.com/api/{apiKey}/search/{input}").Result; - - // Get the response - var responseContent = response.Content.ReadAsStringAsync().Result; - - // Parse the response - var json = JObject.Parse(responseContent); - - // Get the hero info - var heroInfo = $"{json["results"][0]["biography"]["full-name"]}"; - - // Return the hero info - return heroInfo; - } -} +using System.ComponentModel; +using System.Globalization; +using Microsoft.SemanticKernel; +using System.Net.Http; +using Newtonsoft.Json.Linq; + +public class HeroInfo +{ + static string apiKey; + public HeroInfo(string superHeroApiKey) + { + apiKey = superHeroApiKey; + } + + [KernelFunction, Description("Get the alter ego of a superhero")] + public static string GetAlterEgo(string input) + { + // Call the API + var httpClient = new HttpClient(); + var response = httpClient.GetAsync($"https://superheroapi.com/api/{apiKey}/search/{input}").Result; + + // Get the response + var responseContent = response.Content.ReadAsStringAsync().Result; + + // Parse the response + var json = JObject.Parse(responseContent); + + // Get the hero info + var heroInfo = $"{json["results"][0]["biography"]["full-name"]}"; + + // Return the hero info + return heroInfo; + } +} diff --git a/NativeFunctions/sampleResponse.json b/NativeFunctions/sampleResponse.json new file mode 100644 index 0000000..0995503 --- /dev/null +++ b/NativeFunctions/sampleResponse.json @@ -0,0 +1,155 @@ +{ + "response": "success", + "results-for": "batman", + "results": [ + { + "id": "69", + "name": "Batman", + "powerstats": { + "intelligence": "81", + "strength": "40", + "speed": "29", + "durability": "55", + "power": "63", + "combat": "90" + }, + "biography": { + "full-name": "Terry McGinnis", + "alter-egos": "No alter egos found.", + "aliases": [ + "Batman II", + "The Tomorrow Knight", + "The second Dark Knight", + "The Dark Knight of Tomorrow", + "Batman Beyond" + ], + "place-of-birth": "Gotham City, 25th Century", + "first-appearance": "Batman Beyond #1", + "publisher": "DC Comics", + "alignment": "good" + }, + "appearance": { + "gender": "Male", + "race": "Human", + "height": [ + "5'10", + "178 cm" + ], + "weight": [ + "170 lb", + "77 kg" + ], + "eye-color": "Blue", + "hair-color": "Black" + }, + "work": { + "occupation": "-", + "base": "21st Century Gotham City" + }, + "connections": { + "group-affiliation": "Batman Family, Justice League Unlimited", + "relatives": "Bruce Wayne (biological father), Warren McGinnis (father, deceased), Mary McGinnis (mother), Matt McGinnis (brother)" + }, + "image": { + "url": "https://www.superherodb.com/pictures2/portraits/10/100/10441.jpg" + } + }, + { + "id": "70", + "name": "Batman", + "powerstats": { + "intelligence": "100", + "strength": "26", + "speed": "27", + "durability": "50", + "power": "47", + "combat": "100" + }, + "biography": { + "full-name": "Bruce Wayne", + "alter-egos": "No alter egos found.", + "aliases": [ + "Insider", + "Matches Malone" + ], + "place-of-birth": "Crest Hill, Bristol Township; Gotham County", + "first-appearance": "Detective Comics #27", + "publisher": "DC Comics", + "alignment": "good" + }, + "appearance": { + "gender": "Male", + "race": "Human", + "height": [ + "6'2", + "188 cm" + ], + "weight": [ + "210 lb", + "95 kg" + ], + "eye-color": "blue", + "hair-color": "black" + }, + "work": { + "occupation": "Businessman", + "base": "Batcave, Stately Wayne Manor, Gotham City; Hall of Justice, Justice League Watchtower" + }, + "connections": { + "group-affiliation": "Batman Family, Batman Incorporated, Justice League, Outsiders, Wayne Enterprises, Club of Heroes, formerly White Lantern Corps, Sinestro Corps", + "relatives": "Damian Wayne (son), Dick Grayson (adopted son), Tim Drake (adopted son), Jason Todd (adopted son), Cassandra Cain (adopted ward)\nMartha Wayne (mother, deceased), Thomas Wayne (father, deceased), Alfred Pennyworth (former guardian), Roderick Kane (grandfather, deceased), Elizabeth Kane (grandmother, deceased), Nathan Kane (uncle, deceased), Simon Hurt (ancestor), Wayne Family" + }, + "image": { + "url": "https://www.superherodb.com/pictures2/portraits/10/100/639.jpg" + } + }, + { + "id": "71", + "name": "Batman II", + "powerstats": { + "intelligence": "88", + "strength": "11", + "speed": "33", + "durability": "28", + "power": "36", + "combat": "100" + }, + "biography": { + "full-name": "Dick Grayson", + "alter-egos": "Nightwing, Robin", + "aliases": [ + "Dick Grayson" + ], + "place-of-birth": "-", + "first-appearance": "-", + "publisher": "Nightwing", + "alignment": "good" + }, + "appearance": { + "gender": "Male", + "race": "Human", + "height": [ + "5'10", + "178 cm" + ], + "weight": [ + "175 lb", + "79 kg" + ], + "eye-color": "Blue", + "hair-color": "Black" + }, + "work": { + "occupation": "-", + "base": "Gotham City; formerly Bludhaven, New York City" + }, + "connections": { + "group-affiliation": "Justice League Of America, Batman Family", + "relatives": "John Grayson (father, deceased), Mary Grayson (mother, deceased), Bruce Wayne / Batman (adoptive father), Damian Wayne / Robin (foster brother), Jason Todd / Red Hood (adoptive brother), Tim Drake / Red Robin (adoptive brother), Cassandra Cain / Batgirl IV (adoptive sister)" + }, + "image": { + "url": "https://www.superherodb.com/pictures2/portraits/10/100/1496.jpg" + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..411ee2c --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# Understanding Semantic Kernel with Super Heroes + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](/LICENSE) +[![Twitter: elbruno](https://img.shields.io/twitter/follow/elbruno.svg?style=social)](https://twitter.com/elbruno) +![GitHub: elbruno](https://img.shields.io/github/followers/elbruno?style=social) + +✨ +Understanding Semantic Kernel with Heroes is a set of Polyglot notebooks that explores the concept of Semantic Kernel. The notebooks demonstrates how to learn the basis of Semantic Kernel using comic book heroes based on their descriptions. For more details, check out the repository. 🚀📚 + +## Getting started - Semantic Kernel + +This repository contains a set of notebooks, in different languages, that helps you learn how to use Semantic Kernel. For more information, check out the [Semantic Kernel Overview](https://learn.microsoft.com/en-us/semantic-kernel/overview/?WT.mc_id=academic-00000-brunocapuano). + +Semantic Kernel is a powerful tool that allows developers to integrate cutting-edge language models into their applications with ease. Semantic Kernel provides a unified interface to access various language models from different sources, such as OpenAI, AzureOpenAI, and Hugging Face. Semantic Kernel also enables developers to store and retrieve memories, create dynamic prompts, and combine functions together with planners. + +With Semantic Kernel, developers can supercharge their problem-solving creativity and build AI solutions that can understand natural language, generate content, answer questions, and more. Semantic Kernel is an open-source project that can be run on any local machine or cloud environment. Semantic Kernel offers tutorials, guides, and examples to help developers get started quickly and learn how to use its features. + +## Topics + +1. Understanding Semantic Kernel +2. (Optional) - Creating an Azure Open AI 🤖 Service and Deployments +3. How to get started with Semantic Kernel +4. Using Semantic Kernel plugins +5. Semantic Functions +6. Native Functions +7. Planner +8. Advanced Planner Labs +9. Kernel Memory + +## Semantic Kernel C# Notebooks + +The current folder contains a few C# Polyglot Notebooks that demonstrate how to get started with the Semantic Kernel. + +To run the notebooks, we recommend the following steps: + +- [Install .NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) +- [Install Visual Studio Code (VS Code)](https://code.visualstudio.com) +- Launch VS Code and [install the "Polyglot" extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode). + Min version required: v1.0.4606021 (Dec 2023). + +The steps above should be sufficient, you can now **open all the C# notebooks in VS Code**. + +VS Code screenshot example: + +![image](/images/10VSCodeDemo.png) + +## Troubleshooting + +## Nuget + +If you are unable to get the Nuget package, first list your Nuget sources: + +```sh +dotnet nuget list source +``` + +If you see `No sources found.`, add the NuGet official package source: + +```sh +dotnet nuget add source "https://api.nuget.org/v3/index.json" --name "nuget.org" +``` + +Run `dotnet nuget list source` again to verify the source was added. + +## Polyglot Notebooks + +If somehow the notebooks don't work, run these commands: + +- Install .NET Interactive: `dotnet tool install -g Microsoft.dotnet-interactive` +- Register .NET kernels into Jupyter: `dotnet interactive jupyter install` (this might return some errors, ignore them) +- If you are still stuck, read the following pages: + - https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode + - https://devblogs.microsoft.com/dotnet/net-core-with-juypter-notebooks-is-here-preview-1/ + - https://docs.servicestack.net/jupyter-notebooks-csharp + - https://developers.refinitiv.com/en/article-catalog/article/using--net-core-in-jupyter-notebook + +Note: ["Polyglot Notebooks" used to be called ".NET Interactive Notebooks"](https://devblogs.microsoft.com/dotnet/dotnet-interactive-notebooks-is-now-polyglot-notebooks/), +so you might find online some documentation referencing the old name. + +## Author + +👤 **Bruno Capuano** + +* Website: https://elbruno.com +* Twitter: [@elbruno](https://twitter.com/elbruno) +* Github: [@elbruno](https://github.com/elbruno) +* LinkedIn: [@elbruno](https://linkedin.com/in/elbruno) + +## 🤝 Contributing + +Contributions, issues and feature requests are welcome! + +Feel free to check [issues page](https://github.com/elbruno/understanding-semantic-kernel-with-heroes/issues). + +## Show your support + +Give a ⭐️ if this project helped you! + + +## 📝 License + +Copyright © 2024 [Bruno Capuano](https://github.com/elbruno). + +This project is [MIT](/LICENSE) licensed. + +*** \ No newline at end of file diff --git a/SemanticFunctions/FunPlugin/Joke/skprompt.txt b/SemanticFunctions/FunPlugin/Joke/skprompt.txt index 9afadde..f872998 100644 --- a/SemanticFunctions/FunPlugin/Joke/skprompt.txt +++ b/SemanticFunctions/FunPlugin/Joke/skprompt.txt @@ -1,7 +1,7 @@ WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE SUBJECT BELOW JOKE MUST BE: -- IN SPANISH +- IN ENGLISH BE CREATIVE AND FUNNY. I WANT TO LAUGH. diff --git a/SemanticFunctions/FunPlugin/JokeSpa/config.json b/SemanticFunctions/FunPlugin/JokeSpa/config.json new file mode 100644 index 0000000..64e2753 --- /dev/null +++ b/SemanticFunctions/FunPlugin/JokeSpa/config.json @@ -0,0 +1,27 @@ +{ + "schema": 1, + "description": "Generate a funny joke about heroes", + "models": [ + { + "max_tokens": 150, + "temperature": 0.9, + "top_p": 0.5, + "presence_penalty": 0.2, + "frequency_penalty": 0.3 + } + ], + "input": { + "parameters": [ + { + "name": "input", + "description": "Joke subject", + "defaultValue": "" + }, + { + "name": "hero", + "description": "Give the name of the hero you want to joke about", + "defaultValue": "" + } + ] + } +} \ No newline at end of file diff --git a/SemanticFunctions/FunPlugin/JokeSpa/skprompt.txt b/SemanticFunctions/FunPlugin/JokeSpa/skprompt.txt new file mode 100644 index 0000000..0d6c6fb --- /dev/null +++ b/SemanticFunctions/FunPlugin/JokeSpa/skprompt.txt @@ -0,0 +1,12 @@ +WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE SUBJECT BELOW + +JOKE MUST BE: +- IN SPANISH + +BE CREATIVE AND FUNNY. I WANT TO LAUGH. + +Incorporate the hero if provided: {{$hero}} ++++++ + +{{$input}} ++++++ \ No newline at end of file diff --git a/SemanticFunctions/WriterPlugin/OOF/skprompt.txt b/SemanticFunctions/WriterPlugin/OOF/skprompt.txt index 1e4bc3d..1adae21 100644 --- a/SemanticFunctions/WriterPlugin/OOF/skprompt.txt +++ b/SemanticFunctions/WriterPlugin/OOF/skprompt.txt @@ -1,7 +1,7 @@ WRITE AN OUT OF OFFICE FOR THE HERO BELOW THE STORY MUST BE: -- IN SPANISH +- IN ENGLISH - USE EMOJIS AS PART OF THE MESSAGE - TAKE INTO ACCOUNT THE HERO AND HIS/HER POWERS - YOU MUST OFFER HELP FROM ANOTHER SUPER HERO IN YOUR ABSENCE diff --git a/SemanticFunctions/WriterPlugin/OOFSpa/config.json b/SemanticFunctions/WriterPlugin/OOFSpa/config.json new file mode 100644 index 0000000..e64d878 --- /dev/null +++ b/SemanticFunctions/WriterPlugin/OOFSpa/config.json @@ -0,0 +1,27 @@ +{ + "schema": 1, + "description": "Generate a out of office message for a given hero subject", + "models": [ + { + "max_tokens": 250, + "temperature": 0.3, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } + ], + "input": { + "parameters": [ + { + "name": "input", + "description": "The reason for being out of office", + "defaultValue": "" + }, + { + "name": "hero", + "description": "hero name", + "defaultValue": "Catwoman" + } + ] + } +} \ No newline at end of file diff --git a/SemanticFunctions/WriterPlugin/OOFSpa/skprompt.txt b/SemanticFunctions/WriterPlugin/OOFSpa/skprompt.txt new file mode 100644 index 0000000..e044e0c --- /dev/null +++ b/SemanticFunctions/WriterPlugin/OOFSpa/skprompt.txt @@ -0,0 +1,16 @@ +WRITE AN OUT OF OFFICE FOR THE HERO BELOW + +THE STORY MUST BE: +- IN SPANISH +- USE EMOJIS AS PART OF THE MESSAGE +- TAKE INTO ACCOUNT THE HERO AND HIS/HER POWERS +- YOU MUST OFFER HELP FROM ANOTHER SUPER HERO IN YOUR ABSENCE +- THE OFF MUST HAVE A GOODBYE + + +The hero is {{$hero}} + ++++++ + +{{$input}} ++++++ \ No newline at end of file diff --git a/SemanticFunctions/WriterPlugin/StoryGen/skprompt.txt b/SemanticFunctions/WriterPlugin/StoryGen/skprompt.txt index 736e8b0..7d5a52f 100644 --- a/SemanticFunctions/WriterPlugin/StoryGen/skprompt.txt +++ b/SemanticFunctions/WriterPlugin/StoryGen/skprompt.txt @@ -1,7 +1,7 @@ WRITE A SHORT STORY ABOUT THE HERO BELOW THE STORY MUST BE: -- IN SPANISH +- IN ENGLISH - USE EMOJIS AS PART OF THE STORY - THE STORY MUST HAVE A BEGINNING, MIDDLE AND END diff --git a/SemanticFunctions/WriterPlugin/StoryGenSpa/config.json b/SemanticFunctions/WriterPlugin/StoryGenSpa/config.json new file mode 100644 index 0000000..bcc2d22 --- /dev/null +++ b/SemanticFunctions/WriterPlugin/StoryGenSpa/config.json @@ -0,0 +1,27 @@ +{ + "schema": 1, + "description": "Generate a short story about the hero of your choice.", + "models": [ + { + "max_tokens": 350, + "temperature": 0.1, + "top_p": 0.0, + "presence_penalty": 0.1, + "frequency_penalty": 0.3 + } + ], + "input": { + "parameters": [ + { + "name": "input", + "description": "story subject", + "defaultValue": "" + }, + { + "name": "hero", + "description": "hero name", + "defaultValue": "Hulk" + } + ] + } +} \ No newline at end of file diff --git a/SemanticFunctions/WriterPlugin/StoryGenSpa/skprompt.txt b/SemanticFunctions/WriterPlugin/StoryGenSpa/skprompt.txt new file mode 100644 index 0000000..0de9c60 --- /dev/null +++ b/SemanticFunctions/WriterPlugin/StoryGenSpa/skprompt.txt @@ -0,0 +1,13 @@ +WRITE A SHORT STORY ABOUT THE HERO BELOW + +THE STORY MUST BE: +- IN SPANISH +- USE EMOJIS AS PART OF THE STORY +- THE STORY MUST HAVE A BEGINNING, MIDDLE AND END + +The hero of this story is {{$hero}} + ++++++ + +{{$input}} ++++++ \ No newline at end of file diff --git a/Utils/consoleUtils.cs b/Utils/consoleUtils.cs new file mode 100644 index 0000000..97c3678 --- /dev/null +++ b/Utils/consoleUtils.cs @@ -0,0 +1,44 @@ + +/// +/// Writes a long story to the console, splitting it into multiple lines if necessary. +/// +/// The long story to write. +public static void WriteLongStory(string story) +{ + var lines = SplitStory(story); + + foreach (var line in lines) + { + Console.WriteLine(line); + } +} + +/// +/// Splits a story into multiple lines, ensuring that each line does not exceed 80 characters. +/// +/// The story to split. +/// A list of lines containing the split story. +public static List SplitStory(string story) +{ + var words = story.Split(' '); + var lines = new List(); + var currentLine = ""; + + foreach (var word in words) + { + if ((currentLine + word).Length > 80) + { + lines.Add(currentLine.Trim()); + currentLine = ""; + } + + currentLine += word + " "; + } + + if (!string.IsNullOrWhiteSpace(currentLine)) + { + lines.Add(currentLine.Trim()); + } + + return lines; +} \ No newline at end of file diff --git a/create-azure-resources-en .ipynb b/create-azure-resources-en .ipynb new file mode 100644 index 0000000..bb6e52f --- /dev/null +++ b/create-azure-resources-en .ipynb @@ -0,0 +1,287 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 🦸🏻 Understanding Semantic Kernel with Heroes 🦸🏼‍♀️\n", + "\n", + "Semantic Kernel has been created to allow developers to seamlessly integrate Artificial Intelligence into their applications. To achieve this, it provides a set of features that will enable you to add models, prompts, native functions, and memories without requiring deep knowledge of AI 🥲. That's why it's said that Semantic Kernel simulates the brain 🧠 of your application.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## (Optional) - Creating an Azure Open AI 🤖 Service and Deployments\n", + "\n", + "Before diving into Semantic Kernel 🛝, you'll need to have one of the supported services set up. Currently, you can choose from the following options: [Azure Open AI](https://azure.microsoft.com/en-us/products/ai-services/openai-service), [Open AI](https://openai.com/), or [Hugging Face](https://huggingface.co/).\n", + "\n", + "In this example, I am going to use Azure Open AI.\n", + "\n", + "Therefore, through Azure CLI, I need to log in:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "tags": [], + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az login" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you need it, because you have many tenants and subscriptions under your account, you can select the one that interests you (or has access to Azure Open AI 😊) through the following command:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az account set -n \"Visual Studio Enterprise Subscription\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, to be able to create what you need for this notebook, set the following variables with your preferred values:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "$RESOURCE_GROUP=\"understanding-semantic-kernel\"\n", + "$LOCATION=\"canadaeast\"\n", + "$AZURE_OPEN_AI=\"ai-for-heroes\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With them, you can now create the resource group:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az group create `\n", + "--name $RESOURCE_GROUP `\n", + "--location $LOCATION" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An Azure Open AI resource:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az cognitiveservices account create `\n", + "--name $AZURE_OPEN_AI `\n", + "--custom-domain $AZURE_OPEN_AI `\n", + "--resource-group $RESOURCE_GROUP `\n", + "--kind OpenAI `\n", + "--sku S0 `\n", + "--location $LOCATION" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And finally, you need a deployment of any of the models you have available. In this example, I am going to use gpt-4:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az cognitiveservices account deployment create `\n", + "--name $AZURE_OPEN_AI `\n", + "--resource-group $RESOURCE_GROUP `\n", + "--deployment-name \"gpt-4\" `\n", + "--model-name \"gpt-4\" `\n", + "--model-version \"0613\" `\n", + "--model-format OpenAI `\n", + "--sku-capacity \"10\" `\n", + "--sku-name \"Standard\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The **sku-capacity** parameter allows us to specify how many tokens per minute we can send to this model. To see how your quota usage is, you can use this other command:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az cognitiveservices usage list `\n", + "-l $LOCATION" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load enviroment variables with your Azure Open AI endpoint and key" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "$env:AZURE_OPEN_AI_KEY =$(az cognitiveservices account keys list `\n", + "--name $AZURE_OPEN_AI `\n", + "--resource-group $RESOURCE_GROUP `\n", + "--query \"key1\" `\n", + "--output tsv)\n", + "\n", + "$env:AZURE_OPEN_AI_ENDPOINT =$(az cognitiveservices account show `\n", + "--name $AZURE_OPEN_AI `\n", + "--resource-group $RESOURCE_GROUP `\n", + "--query \"properties.endpoint\" `\n", + "--output tsv)\n", + "\n", + "#dir env:AZURE_OPEN_AI_KEY\n", + "#dir env:AZURE_OPEN_AI_ENDPOINT" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/images/10VSCodeDemo.png b/images/10VSCodeDemo.png new file mode 100644 index 0000000..65ddd79 Binary files /dev/null and b/images/10VSCodeDemo.png differ diff --git a/notebook-en-10min.ipynb b/notebook-en-10min.ipynb new file mode 100644 index 0000000..c31d101 --- /dev/null +++ b/notebook-en-10min.ipynb @@ -0,0 +1,616 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Azure Developer Days .NET 2024\n", + "\n", + "## 🦸🏻 Help your Super Hero friend with AI 🦸🏼‍♀️\n", + "\n", + "Semantic Kernel has been created to allow developers to seamlessly integrate Artificial Intelligence into their applications. To achieve this, it provides a set of features that will enable you to add models, prompts, native functions, and memories without requiring deep knowledge of AI 🥲. That's why it's said that Semantic Kernel simulates the brain 🧠 of your application.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Environment variables\n", + "\n", + "Let's start importing the keys and utils that we will need for this demo." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Azure OpenAI information\n", + "var keys = (await Microsoft.DotNet.Interactive.Kernel\n", + " .GetPasswordAsync(\"Give me your Azure OpenAI information\"))\n", + " .GetClearTextPassword().Split(';');\n", + "\n", + "var azureOpenAIDeploymentName = keys[0];\n", + "var azureOpenAIEndpoint = keys[1];\n", + "var azureOpenAIKey = keys[2];" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// get super hero api key\n", + "var superHeroApiKey = (await Microsoft.DotNet.Interactive.Kernel\n", + " .GetPasswordAsync(\"Give me your Super Hero Api key\")).GetClearTextPassword();" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#load \"Utils/consoleUtils.cs\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to get started with Semantic Kernel\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first thing you need to be able to run Semantic Kernel in this notebook is to install the **Microsoft.SemanticKernel** library, which is currently in version **1.7.1** (I promise to keep updating 🤓). \n", + "\n", + "You can find more information about this library and its updates on the official NuGet page [here](https://www.nuget.org/packages/Microsoft.SemanticKernel).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#r \"nuget: Microsoft.SemanticKernel, 1.7.1\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using Microsoft.SemanticKernel;\n", + "\n", + "// Create a Builder\n", + "var builder = Kernel.CreateBuilder();\n", + "builder.AddAzureOpenAIChatCompletion(\n", + " deploymentName: azureOpenAIDeploymentName, \n", + " endpoint: azureOpenAIEndpoint, \n", + " apiKey: azureOpenAIKey);\n", + "var kernel = builder.Build();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using Semantic Kernel plugins\n", + "\n", + "Plugins are the core of Semantic Kernel. With them, you encapsulate capabilities so that they can be reusable, maintainable, and plannable (you will understand it later 🙃). There are two types: those that consist of prompt templates called **Semantic Functions** and native functions of the chosen programming language called **Native Functions**.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Semantic Functions\n", + "\n", + "When you talk to artificial intelligence models, you must do so with what is known as a *prompt*. This can range from a simple phrase to something more elaborate, allowing the model to understand not only what we want but also how we want it. If you take a look at the official documentation, it defines this type of function as the mouth 👄 and ears 👂🏻 of your brain 🧠. 🤖🌐\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As part of this repo, you have a folder called **SemanticFunctions** that has different functions of this type:\n", + "\n", + "- **FunPlugin**: This allows us to ask the model to make jokes about heroes under certain conditions, through the **Joke** function.\n", + "- **WritePlugin**: To show you that within a plugin you can have different functions, in this directory we have two related to the art of writing: the first one, **OOF**, allows us to generate the \"Out of Office\" 🏢📧 message for superheroes and the second **StoryGen** will help us create stories, also about superheroes 🦸🏻‍♂️🦸🏻‍♀️.\n", + "\n", + "For our kernel to know that these plugins are available, you first need to get the path of the directory:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.IO;\n", + "\n", + "var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \n", + " \"SemanticFunctions\", \"FunPlugIn\");\n", + "\n", + "// Load the FunPlugin from the Plugins Directory\n", + "var funPluginFunctions = kernel.ImportPluginFromPromptDirectory(pluginsDirectory);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Tell me a April's fools joke\",\n", + " [\"hero\"] = \"Batman\"\n", + "};\n", + "var result = await kernel.InvokeAsync(funPluginFunctions[\"Joke\"], variables);\n", + "Console.WriteLine(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the same way, we can use the functions included in **WriterPlugin**:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Load the WriterPlugin from the Plugins Directory\n", + "var pluginsDirectoryWriter = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \n", + " \"SemanticFunctions\", \"WriterPlugin\");\n", + "\n", + "var writerPluginFunctions = kernel.ImportPluginFromPromptDirectory(pluginsDirectoryWriter);\n", + "\n", + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Tell me a Spring Story\",\n", + " [\"hero\"] = \"Superman\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(writerPluginFunctions[\"StoryGen\"], variables);\n", + "Console.WriteLine(result);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or even to create messages for when the heroes 🦸🏻‍♀️ are on vacation ✈️🚢🌴:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello there!\n", + "\n", + "🦸‍♂️ Hulk here, smashing through emails and saving the day!\n", + "\n", + "I'm\n", + "currently out of the office to celebrate the holiday season with my friends and\n", + "family. 🎄 Don't worry, I'll be back to smash some more villains soon!\n", + "\n", + "In the\n", + "meantime, if you need immediate superhero help, please contact my good friend\n", + "Thor. He's always ready to lend a helping hand...or hammer! ⚡️\n", + "\n", + "Thank you for\n", + "your understanding and patience. I'll respond to your messages as soon as I\n", + "return!\n", + "\n", + "👊 Stay incredible and have a smashing good time! 👊\n", + "\n", + "Best\n", + "regards,\n", + "Hulk\n" + ] + } + ], + "source": [ + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Create an OOF for Christmas\",\n", + " [\"hero\"] = \"Hulk\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(writerPluginFunctions[\"OOF\"], variables);\n", + "WriteLongStory(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Native Functions\n", + "\n", + "While semantic functions allow us to define and reuse prompts, **with native functions you can make the semantic kernel call functions written in C# or Python**, for tasks that go beyond a call through a prompt.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this example, I'm going to use an API called **SuperHero API**, which requires an API key. You can get it from their website: [https://superheroapi.com/](https://superheroapi.com/)\n", + "\n", + "Now, to load a native function, we must add this before we build the Kernel.\n", + "\n", + "Let's create a new Super Hero Info class and add it as a Plugin.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#load \"NativeFunctions/HeroInfo.cs\"\n", + "\n", + "var heroInfo = new HeroInfo(superHeroApiKey);\n", + "builder.Plugins.AddFromObject(heroInfo, \"HeroInfo\");\n", + "Kernel kernel = builder.Build();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The builder now can interact with the local function. Let's make a call to get the alter ego of a super hero." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var kernelArgs = new KernelArguments()\n", + "{\n", + " [\"input\"] = \"Wonder Woman\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(\"HeroInfo\", \"GetAlterEgo\", kernelArgs);\n", + "Console.WriteLine(result);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Planner\n", + "\n", + "So far, all the plugins you have seen have been executed intentionally. That is, no one has chosen them for you and you can run them based on your needs. However, this is the most *static* way to interact with Semantic Kernel. There is another option called **Planner** that will leave you astounded 😮\n", + "\n", + "Planner is a function that takes a user's request and returns a plan on how to carry out the request. To do this, it uses AI to combine the plugins registered in the core and recombine them into a series of steps that complete a goal.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#r \"nuget: Microsoft.SemanticKernel.Planners.Handlebars, 1.7.1-preview\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we are ready to create a planner." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.Text.Json;\n", + "using Microsoft.SemanticKernel.Planning.Handlebars;\n", + "\n", + "#pragma warning disable SKEXP0060\n", + "var planner = new HandlebarsPlanner();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var ask = @\"I would like you to tell me a joke about Batman, \" + \n", + "\"and with that joke, create an out-of-office message using the joke.\";\n", + "\n", + "#pragma warning disable SKEXP0060\n", + "var originalPlan = await planner.CreatePlanAsync(kernel, ask);\n", + "\n", + "Console.WriteLine(\"Original plan:\\n\");\n", + "Console.WriteLine(originalPlan);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// executing the plan\n", + "#pragma warning disable SKEXP0060\n", + "var originalPlanResult = await originalPlan.InvokeAsync(kernel, new KernelArguments());\n", + "\n", + "Console.WriteLine(\"Original Plan results:\\n\");\n", + "WriteLongStory(originalPlanResult.ToString());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advanced Scenario\n", + "\n", + "The kernel already has the plugins loaded, and the function, let's see if it can solve a more complex example:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var ask_complex = @\"I would like you to find out IronMan's alter ego,\" + \n", + "\" and create an out of office message for IronMan, signing it with his alter ego.\";\n", + "\n", + "#pragma warning disable SKEXP0060\n", + "var plan_complex = await planner.CreatePlanAsync(kernel, ask_complex);\n", + "\n", + "Console.WriteLine(\"Plan:\\n\");\n", + "Console.WriteLine(plan_complex);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#pragma warning disable SKEXP0060\n", + "var complexPlanResult = await plan_complex.InvokeAsync(kernel, new KernelArguments());\n", + "\n", + "Console.WriteLine(\"Complex Plan results:\\n\");\n", + "WriteLongStory(complexPlanResult.ToString());" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebook-en.ipynb b/notebook-en.ipynb new file mode 100644 index 0000000..836fe4f --- /dev/null +++ b/notebook-en.ipynb @@ -0,0 +1,1133 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 🦸🏻 Understanding Semantic Kernel with Heroes 🦸🏼‍♀️\n", + "\n", + "Semantic Kernel has been created to allow developers to seamlessly integrate Artificial Intelligence into their applications. To achieve this, it provides a set of features that will enable you to add models, prompts, native functions, and memories without requiring deep knowledge of AI 🥲. That's why it's said that Semantic Kernel simulates the brain 🧠 of your application.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to get started with Semantic Kernel\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first thing you need to be able to run Semantic Kernel in this notebook is to install the **Microsoft.SemanticKernel** library, which is currently in version **1.13.0** (I promise to keep updating 🤓). \n", + "\n", + "You can find more information about this library and its updates on the official NuGet page [here](https://www.nuget.org/packages/Microsoft.SemanticKernel).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#load \"Utils/consoleUtils.cs\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#r \"nuget: Microsoft.SemanticKernel, 1.13.0\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With it, you can now instantiate the brain of your application through **KernelBuilder**. It has a lot of connectors to the models and other things, but for now, let's start with the basics:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using Microsoft.SemanticKernel;\n", + "\n", + "// Create a Builder\n", + "var builder = Kernel.CreateBuilder();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Depending on the type of task you want to do, you can use different methods with the prefix **With**. For now, we are going to use some of the models of type **completion** or completion.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// OpenAI keys\n", + "var modelId = \"gpt-4\";\n", + "var apiKey = (await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Open AI key\")).GetClearTextPassword();\n", + "\n", + "// Create a chat completion service\n", + "builder.AddOpenAIChatCompletion(modelId, apiKey);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Azure OpenAI keys\n", + "var deploymentName = (await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Azure OpenAI deployment name\")).GetClearTextPassword();\n", + "var endpoint = (await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Azure Open AI endpoint\")).GetClearTextPassword();\n", + "var apiKey = (await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Azure Open AI key\")).GetClearTextPassword();\n", + "\n", + "// Create a chat completion service\n", + "builder.AddAzureOpenAIChatCompletion(deploymentName, endpoint, apiKey);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Azure OpenAI keys \n", + "var keys = (await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Azure OpenAI information\")).GetClearTextPassword().Split(';');\n", + "var deploymentName = keys[0];\n", + "var endpoint = keys[1];\n", + "var apiKey = keys[2];\n", + "\n", + "// Create a chat completion service\n", + "builder.AddAzureOpenAIChatCompletion(deploymentName, endpoint, apiKey);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With the configuration done, all that remains is to generate the kernel with everything set:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var kernel = builder.Build();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using Semantic Kernel plugins\n", + "\n", + "Plugins are the core of Semantic Kernel. With them, you encapsulate capabilities so that they can be reusable, maintainable, and plannable (you will understand it later 🙃). There are two types: those that consist of prompt templates called **Semantic Functions** and native functions of the chosen programming language called **Native Functions**.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Semantic Functions\n", + "\n", + "When you talk to artificial intelligence models, you must do so with what is known as a *prompt*. This can range from a simple phrase to something more elaborate, allowing the model to understand not only what we want but also how we want it. If you take a look at the official documentation, it defines this type of function as the mouth 👄 and ears 👂🏻 of your brain 🧠. 🤖🌐\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As part of this repo, you have a folder called **SemanticFunctions** that has different functions of this type:\n", + "\n", + "- **FunPlugin**: This allows us to ask the model to make jokes about heroes under certain conditions, through the **Joke** function.\n", + "- **WritePlugin**: To show you that within a plugin you can have different functions, in this directory we have two related to the art of writing: the first one, **OOF**, allows us to generate the \"Out of Office\" 🏢📧 message for superheroes and the second **StoryGen** will help us create stories, also about superheroes 🦸🏻‍♂️🦸🏻‍♀️.\n", + "\n", + "For our kernel to know that these plugins are available, you first need to get the path of the directory:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.IO;\n", + "\n", + "var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"SemanticFunctions\", \"FunPlugIn\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And then start loading the plugins you want:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Load the FunPlugin from the Plugins Directory\n", + "var funPluginFunctions = kernel.ImportPluginFromPromptDirectory(pluginsDirectory);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To be able to invoke a function of this type, you can do it in the following way:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var joke = new KernelArguments() { [\"input\"] = \"tell me a joke\" };\n", + "var result = await kernel.InvokeAsync(funPluginFunctions[\"Joke\"], joke);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, just by using **kernel.RunAsync** and passing my request as parameters and which function, in this case **Joke**, within the plugin, in this case **FunPlugin**, I want to use.\n", + "\n", + "Each of these functions consists of two files:\n", + "\n", + "- **skprompt.txt** is the file where the prompt to send to the model is defined in a template format, so that it can receive parameters and make them more reusable.\n", + "\n", + "```\n", + "WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE SUBJECT BELOW\n", + "\n", + "JOKE MUST BE:\n", + "- IN ENGLISH\n", + "\n", + "BE CREATIVE AND FUNNY. I WANT TO LAUGH.\n", + "\n", + "Incorporate the hero if provided: {{$hero}}\n", + "+++++\n", + "\n", + "{{$input}}\n", + "+++++\n", + "```\n", + "- **config.json**, which allows us to indicate the maximum number of tokens allowed for this call (**max_tokens**), the temperature to control the randomness of the responses (**temperature**), which means that closer to 1 they will be more random and closer to zero more determined and focused on the most likely response, **top_p** is used to control the diversity of the responses, where a value of 0.0 means that only the most likely responses will be considered and 1 where all possible responses will be considered and **presence_penalty** and **frequency_penalty** to adjust the penalty for the presence and frequency of the tokens in the generated responses. On the other hand, if the skprompt.txt file receives parameters, they must also be defined in this file in the **parameters** array of the **input** object.\n", + "\n", + "```javascript\n", + "{\n", + " \"schema\": 1,\n", + " \"description\": \"Generate a funny joke about heroes\",\n", + " \"models\": [\n", + " {\n", + " \"max_tokens\": 150,\n", + " \"temperature\": 0.9,\n", + " \"top_p\": 0.5,\n", + " \"presence_penalty\": 0.2,\n", + " \"frequency_penalty\": 0.3\n", + " }\n", + " ],\n", + " \"input\": {\n", + " \"parameters\": [\n", + " {\n", + " \"name\": \"input\",\n", + " \"description\": \"Joke subject\",\n", + " \"defaultValue\": \"\"\n", + " },\n", + " {\n", + " \"name\": \"hero\",\n", + " \"description\": \"Give a hint about the hero you want to joke about\",\n", + " \"defaultValue\": \"\"\n", + " }\n", + " ]\n", + " }\n", + "}\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To see the result generated from calling this Joke function, you can retrieve it in the following way:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "Console.WriteLine(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, in this execution we have only passed the input parameter, but not the hero about whom we wanted to generate this joke. To be able to send more than one parameter you need to create an object of the **ContextVariables** type:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Tell me a April's fools joke\",\n", + " [\"hero\"] = \"Batman\"\n", + "};" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To be able to use these two values as part of the call, simply put it as the first argument.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var result = await kernel.InvokeAsync(funPluginFunctions[\"Joke\"], variables);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now check if the joke is about the hero specified as a parameter.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "WriteLongStory(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the same way, we can use the functions included in **WriterPlugin**:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Load the WriterPlugin from the Plugins Directory\n", + "var pluginsDirectoryWriter = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"SemanticFunctions\", \"WriterPlugin\");\n", + "\n", + "var writerPluginFunctions = kernel.ImportPluginFromPromptDirectory(pluginsDirectoryWriter);\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One that generates stories:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Tell me a story about 2 pets that meet each other\",\n", + " [\"hero\"] = \"IronMan\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(writerPluginFunctions[\"StoryGen\"], variables);\n", + "WriteLongStory(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or even to create messages for when the heroes 🦸🏻‍♀️ are on vacation ✈️🚢🌴:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Create an OOF for Christmas\",\n", + " [\"hero\"] = \"Hulk\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(writerPluginFunctions[\"OOF\"], variables);\n", + "WriteLongStory(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Native Functions\n", + "\n", + "While semantic functions allow us to define and reuse prompts, **with native functions you can make the semantic kernel call functions written in C# or Python**, for tasks that go beyond a call through a prompt.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Why do I need native functions in these types of applications?\n", + "\n", + "Large language models (LLMs) are excellent for generating text, but there are several tasks they cannot perform on their own. These include, among others:\n", + "\n", + "- Retrieving data from external data sources\n", + "- Knowing what time it is\n", + "- Performing complex mathematical operations\n", + "- Completing tasks in the real world\n", + "- Memorizing and remembering information\n", + "\n", + "For these scenarios, and many others, native functions are very useful 👍🏻\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this example, I'm going to use an API called **SuperHero API**, which requires an API key. You can get it from their website: [https://superheroapi.com/](https://superheroapi.com/)\n", + "\n", + "Once you have it, pass it to the prompt that appears with the following line:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var superHeroApiKey = (await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Super Hero Api key\")).GetClearTextPassword();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, to load a native function, we must add this before we build the Kernel.\n", + "\n", + "Let's create a new Super Hero Info class and add it as a Plugin." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#load \"NativeFunctions/HeroInfo.cs\"\n", + "\n", + "var heroInfo = new HeroInfo(superHeroApiKey);\n", + "builder.Plugins.AddFromObject(heroInfo, \"HeroInfo\");\n", + "Kernel kernel = builder.Build();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The builder now can interact with the local function. Let's make a call to get the alter ego of a super hero." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var kernelArgs = new KernelArguments()\n", + "{\n", + " [\"input\"] = \"Batman\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(\"HeroInfo\", \"GetAlterEgo\", kernelArgs);\n", + "Console.WriteLine(result);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this repo, there is another directory called **NativeFunctions** where you can find a class called **HeroInfo.cs**. In it, there is a function decorated with the **KernelFunction** attribute, which allows us to indicate to the Semantic Kernel that it is a native function and, through the **Description** property, also give it information about what the purpose of this function is. In this case, what this method allows us to do is retrieve the alter ego of the superhero that we pass as a parameter, in this example that of Ironman. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Planner\n", + "\n", + "So far, all the plugins you have seen have been executed intentionally. That is, no one has chosen them for you and you can run them based on your needs. However, this is the most *static* way to interact with Semantic Kernel. There is another option called **Planner** that will leave you astounded 😮\n", + "\n", + "Planner is a function that takes a user's request and returns a plan on how to carry out the request. To do this, it uses AI to combine the plugins registered in the core and recombine them into a series of steps that complete a goal.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To see it in action, we are going to use the plugins that you already know.\n", + "\n", + "The first thing you need is to add the nuget reference to the Planner Handlebars.\n", + "\n", + "To learn more about this NuGet package, you can visit [here](https://www.nuget.org/packages/Microsoft.SemanticKernel.Planners.Handlebars/1.5.0-preview#show-readme-container).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#r \"nuget: Microsoft.SemanticKernel.Planners.Handlebars, 1.13.0-preview\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we are ready to create a planner." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using Microsoft.SemanticKernel.Planning.Handlebars;\n", + "\n", + "#pragma warning disable SKEXP0060\n", + "var planner = new HandlebarsPlanner();\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are different types as you can see [here](https://learn.microsoft.com/en-us/semantic-kernel/ai-orchestration/planners/?tabs=Csharp). In this example, we are going to use the one that executes tasks sequentially.\n", + "\n", + "Since you already have all the plugins loaded in your kernel instance, we can ask it something like this:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.Text.Json;\n", + "\n", + "var ask = \"I would like you to tell me a joke about Batman, and with that joke, create an out-of-office message using the joke.\";\n", + "#pragma warning disable SKEXP0060\n", + "var originalPlan = await planner.CreatePlanAsync(kernel, ask);\n", + "\n", + "Console.WriteLine(\"Original plan:\\n\");\n", + "WriteLongStory(originalPlan.ToString());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, the planner knows about the plugins thanks to the description that I included as part of their implementation.\n", + "\n", + "Time to run the plan and get an OOF for **Batman** that includes a **bad joke**.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// executing the plan\n", + "#pragma warning disable SKEXP0060\n", + "var originalPlanResult = await originalPlan.InvokeAsync(kernel, new KernelArguments());\n", + "\n", + "Console.WriteLine(\"Original Plan results:\\n\");\n", + "WriteLongStory(originalPlanResult.ToString());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advanced Planner Labs\n", + "\n", + "The kernel already has the plugins loaded, and the function, let's see if it can solve a more complex example:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var ask_complex = \"I would like you to find out IronMan's alter ego, and create an out of office message for IronMan, add a joke about the heroe and signing it with his alter ego.\";\n", + "\n", + "#pragma warning disable SKEXP0060\n", + "var plan_complex = await planner.CreatePlanAsync(kernel, ask_complex);\n", + "\n", + "Console.WriteLine(\"Plan:\\n\");\n", + "WriteLongStory(plan_complex.ToString());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's execute the plan and see its output:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#pragma warning disable SKEXP0060\n", + "var complexPlanResult = await plan_complex.InvokeAsync(kernel, new KernelArguments());\n", + "\n", + "Console.WriteLine(\"Complex Plan results:\\n\");\n", + "WriteLongStory(complexPlanResult.ToString());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kernel Memory\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To be able to use Kernel Memory, you need to add its nuget library, in addition to importing the class that I have generated in the **KernelMemory** directory.\n", + "\n", + "To learn more about the Kernel Memory library, you can visit the [NuGet package page](https://www.nuget.org/packages/Microsoft.SemanticKernel.Plugins.Memory/1.5.0-alpha#show-readme-container).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#r \"nuget: Microsoft.SemanticKernel.Plugins.Memory, 1.13.0-alpha\"\n", + "#r \"nuget: System.Linq.Async, 6.0.1\"\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to use memory, we need to instantiate the Memory Plugin with a Memory Storage and an Embedding backend. In this example, we make use of the VolatileMemoryStore which can be thought of as a temporary in-memory storage (not to be confused with Semantic Memory).\n", + "\n", + "This memory is not written to disk and is only available during the app session.\n", + "\n", + "When developing your app you will have the option to plug in persistent storage like Azure Cosmos Db, PostgreSQL, SQLite, etc. Semantic Memory allows also to index external data sources, without duplicating all the information, more on that later." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using Microsoft.SemanticKernel.Memory;\n", + "using Microsoft.SemanticKernel.Connectors.OpenAI;\n", + "\n", + "// create the memory builder\n", + "#pragma warning disable SKEXP0001,SKEXP0003, SKEXP0011, SKEXP0010, SKEXP0050, SKEXP0052\n", + "var memoryBuilder = new MemoryBuilder();\n", + "\n", + "// Using OpenAI\n", + "//memoryBuilder.WithOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey);\n", + "\n", + "// Using Azure OpenAI \n", + " memoryBuilder.WithAzureOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", endpoint, apiKey);\n", + "\n", + "memoryBuilder.WithMemoryStore(new VolatileMemoryStore());\n", + "var memory = memoryBuilder.Build();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's create some initial memories \"Fan Facts\". We can add memories to our VolatileMemoryStore by using SaveInformationAsync:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// add fan facts to the collection\n", + "const string MemoryCollectionName = \"fanFacts\";\n", + "\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info1\", text: \"Gisela's favourite super hero is Batman\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info2\", text: \"The last super hero movie watched by Gisela was Guardians of the Galaxy Vol 3\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info3\", text: \"Bruno's favourite super hero is Invincible\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info4\", text: \"The last super hero movie watched by Bruno was Aquaman II\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info5\", text: \"Bruno don't like the super hero movie: Eternals\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info6\", text: \"Alixia don't like the super hero movie: The Batman\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info7\", text: \"Susana likes a lot an Argentinian super hero named SuperHijitus\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info8\", text: \"Dave likes Robin, the sidekick of Batman\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info9\", text: \"Dave last movie was Oppenheimer, a movie about the scientist that created the atomic bomb\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And now we can test and search in the memory:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var questions = new[]\n", + "{\n", + " \"Does anyone watched a movie not from superheros?\",\n", + "};\n", + "\n", + "foreach (var q in questions)\n", + "{\n", + " var response = await memory.SearchAsync(MemoryCollectionName, q).FirstOrDefaultAsync();\n", + " Console.WriteLine(q + \" \" + response?.Metadata.Text + \" \" + response?.Relevance);\n", + "}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the following code cell, we will demonstrate how we can also search the memory kernel using another language. We will use the same questions as before, but this time they will be translated into Spanish.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var preguntas = new[]\n", + "{\n", + " \"¿Cuál es el superhéroe favorito de Bruno?\",\n", + " \"¿Cuál fue la última película vista por Gisela?\",\n", + " \"¿Cuál es el superhéroe preferido de Gisela?\",\n", + " \"¿Bruno ha visto alguna película de superhéroes en el pasado? ¿Cuál fue la última?\",\n", + " \"Cual es la pelicula que no le gusta a Bruno?\",\n", + " \"Sabes algo de Alixia?\"\n", + "};\n", + "\n", + "foreach (var q in preguntas)\n", + "{\n", + " var response = await memory.SearchAsync(MemoryCollectionName, q).FirstOrDefaultAsync();\n", + " Console.WriteLine(q + \" \" + response?.Metadata.Text + \" \" + response?.Metadata.Id + \" \" + response?.Relevance);\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebook-es.ipynb b/notebook-es.ipynb new file mode 100644 index 0000000..d6cfe4d --- /dev/null +++ b/notebook-es.ipynb @@ -0,0 +1,1318 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 🦸🏻 Comprendiendo el Semantic Kernel con Héroes 🦸🏼‍♀️\n", + "\n", + "Semantic Kernel ha sido creado para permitir a los desarrolladores integrar Inteligencia Artificial en sus aplicaciones de manera fluida. Para lograr esto, proporciona un conjunto de características que te permitirán añadir modelos, prompts, funciones nativas y memorias sin requerir un conocimiento profundo de IA 🥲. Por eso se dice que Semantic Kernel simula el cerebro 🧠 de tu aplicación.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## (Opcional) - Creando un Servicio Azure Open AI 🤖 y Despliegues\n", + "\n", + "Antes de sumergirnos en Semantic Kernel 🛝, necesitarás tener configurado uno de los servicios soportados. Actualmente, puedes elegir entre las siguientes opciones: [Azure Open AI](https://azure.microsoft.com/en-us/products/ai-services/openai-service), [Open AI](https://openai.com/), o [Hugging Face](https://huggingface.co/).\n", + "\n", + "En este ejemplo, voy a usar Azure Open AI.\n", + "\n", + "Por lo tanto, a través de Azure CLI, necesito iniciar sesión:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "tags": [], + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az login" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Si lo necesitas, porque tienes muchas suscripciones en tu cuenta, puedes seleccionar la que te interesa (o la que tiene acceso a Azure Open AI 😊) a través del siguiente comando:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az account set -n \"Visual Studio Enterprise Subscription\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ahora, para poder crear lo que necesitas para este cuaderno, establece las siguientes variables con tus valores preferidos:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "$RESOURCE_GROUP=\"understanding-semantic-kernel\"\n", + "$LOCATION=\"canadaeast\"\n", + "$AZURE_OPEN_AI=\"ai-for-heroes\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Con ellos, ahora puedes crear el grupo de recursos:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az group create `\n", + "--name $RESOURCE_GROUP `\n", + "--location $LOCATION" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Un recurso de Azure Open AI:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az cognitiveservices account create `\n", + "--name $AZURE_OPEN_AI `\n", + "--custom-domain $AZURE_OPEN_AI `\n", + "--resource-group $RESOURCE_GROUP `\n", + "--kind OpenAI `\n", + "--sku S0 `\n", + "--location $LOCATION" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Y finalmente, necesitas un despliegue de cualquiera de los modelos que tienes disponibles. En este ejemplo, voy a usar gpt-4:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az cognitiveservices account deployment create `\n", + "--name $AZURE_OPEN_AI `\n", + "--resource-group $RESOURCE_GROUP `\n", + "--deployment-name \"gpt-4\" `\n", + "--model-name \"gpt-4\" `\n", + "--model-version \"0613\" `\n", + "--model-format OpenAI `\n", + "--sku-capacity \"10\" `\n", + "--sku-name \"Standard\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "El parámetro **sku-capacity** nos permite especificar cuántos tokens por minuto podemos enviar a este modelo. Para ver cómo se está utilizando tu cuota, puedes usar este otro comando:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "az cognitiveservices usage list `\n", + "-l $LOCATION" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Carga las variables de entorno con tu endpoint y clave de Azure Open AI:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "$env:AZURE_OPEN_AI_KEY =$(az cognitiveservices account keys list `\n", + "--name $AZURE_OPEN_AI `\n", + "--resource-group $RESOURCE_GROUP `\n", + "--query \"key1\" `\n", + "--output tsv)\n", + "\n", + "$env:AZURE_OPEN_AI_ENDPOINT =$(az cognitiveservices account show `\n", + "--name $AZURE_OPEN_AI `\n", + "--resource-group $RESOURCE_GROUP `\n", + "--query \"properties.endpoint\" `\n", + "--output tsv)\n", + "\n", + "#dir env:AZURE_OPEN_AI_KEY\n", + "#dir env:AZURE_OPEN_AI_ENDPOINT" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cómo empezar con Semantic Kernel\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lo primero que necesitas para poder ejecutar Semantic Kernel en este cuaderno es instalar la biblioteca **Microsoft.SemanticKernel**, que actualmente está en la versión **1.5.0** (prometo seguir actualizando 🤓).\n", + "\n", + "Puedes encontrar más información sobre esta biblioteca y sus actualizaciones en la página oficial de NuGet [aquí](https://www.nuget.org/packages/Microsoft.SemanticKernel).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#r \"nuget: Microsoft.SemanticKernel, 1.5.0\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Con ello, ahora puedes instanciar el cerebro de tu aplicación a través de **KernelBuilder**. Tiene muchos conectores a los modelos y otras cosas, pero por ahora, comencemos con lo básico:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using Microsoft.SemanticKernel;\n", + "\n", + "// Create a Builder\n", + "var builder = Kernel.CreateBuilder();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dependiendo del tipo de tarea que quieras realizar, puedes usar diferentes métodos con el prefijo **With**. Por ahora, vamos a usar algunos de los modelos de tipo **completion** o completado.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// OpenAI keys\n", + "var modelId = \"gpt-4\";\n", + "var apiKey = (await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Open AI key\")).GetClearTextPassword();\n", + "\n", + "// Create a chat completion service\n", + "builder.AddOpenAIChatCompletion(modelId, apiKey);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Azure OpenAI keys\n", + "var deploymentName = \"gpt-4\";\n", + "var endpoint = Environment.GetEnvironmentVariable(\"AZURE_OPEN_AI_ENDPOINT\");\n", + "var apiKey = Environment.GetEnvironmentVariable(\"AZURE_OPEN_AI_KEY\");\n", + "\n", + "// Create a chat completion service\n", + "builder.AddAzureOpenAIChatCompletion(deploymentName, endpoint, apiKey);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Con la configuración realizada, lo único que queda es generar el kernel con todo configurado:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var kernel = builder.Build();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Uso de plugins de Semantic Kernel\n", + "\n", + "Los plugins son el núcleo de Semantic Kernel. Con ellos, encapsulas capacidades para que sean reutilizables, mantenibles y planificables (lo entenderás más tarde 🙃). Hay dos tipos: aquellos que consisten en plantillas de indicaciones llamadas **Semantic Functions** y funciones nativas del lenguaje de programación elegido llamadas **Native Functions**.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Funciones Semánticas\n", + "\n", + "Cuando hablas con modelos de inteligencia artificial, debes hacerlo con lo que se conoce como un *prompt*. Esto puede variar desde una simple frase hasta algo más elaborado, permitiendo que el modelo comprenda no solo lo que queremos sino también cómo lo queremos. Si echas un vistazo a la documentación oficial, define este tipo de función como la boca 👄 y los oídos 👂🏻 de tu cerebro 🧠. 🤖🌐\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Como parte de este repositorio, tienes una carpeta llamada **SemanticFunctions** que tiene diferentes funciones de este tipo:\n", + "\n", + "- **FunPlugin**: Esto nos permite pedirle al modelo que haga bromas sobre héroes bajo ciertas condiciones, a través de la función **Joke**.\n", + "- **WritePlugin**: Para mostrarte que dentro de un plugin puedes tener diferentes funciones, en este directorio tenemos dos relacionadas con el arte de escribir: la primera, **OOF**, nos permite generar el mensaje \"Fuera de la oficina\" 🏢📧 para superhéroes y la segunda **StoryGen** nos ayudará a crear historias, también sobre superhéroes 🦸🏻‍♂️🦸🏻‍♀️.\n", + "\n", + "Para que nuestro kernel sepa que estos plugins están disponibles, primero necesitas obtener la ruta del directorio:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.IO;\n", + "\n", + "var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"SemanticFunctions\", \"FunPlugIn\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Y luego comienza a cargar los plugins que deseas:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Load the FunPlugin from the Plugins Directory\n", + "var funPluginFunctions = kernel.ImportPluginFromPromptDirectory(pluginsDirectory);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Para poder invocar una función de este tipo, puedes hacerlo de la siguiente manera:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var joke = new KernelArguments() { [\"input\"] = \"cuentame un chiste\" };\n", + "var result = await kernel.InvokeAsync(funPluginFunctions[\"JokeSpa\"], joke);\n", + "\n", + "Console.WriteLine(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Como puedes ver, solo usando **kernel.RunAsync** y pasando mi solicitud como parámetros y qué función, en este caso **JokeSpa**, dentro del plugin, en este caso **FunPlugin**, quiero usar.\n", + "\n", + "Cada una de estas funciones consta de dos archivos:\n", + "\n", + "- **skprompt.txt** es el archivo donde se define el prompt para enviar al modelo en un formato de plantilla, de modo que pueda recibir parámetros y hacerlos más reutilizables.\n", + "\n", + "\n", + " ```\n", + " WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE SUBJECT BELOW\n", + "\n", + " JOKE MUST BE:\n", + " - IN SPANISH\n", + "\n", + " BE CREATIVE AND FUNNY. I WANT TO LAUGH.\n", + "\n", + " Incorporate the hero if provided: {{$hero}}\n", + " +++++\n", + "\n", + " {{$input}}\n", + " +++++\n", + " ```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- **config.json**, which allows us to indicate the maximum number of tokens allowed for this call (**max_tokens**), the temperature to control the randomness of the responses (**temperature**), which means that closer to 1 they will be more random and closer to zero more determined and focused on the most likely response, **top_p** is used to control the diversity of the responses, where a value of 0.0 means that only the most likely responses will be considered and 1 where all possible responses will be considered and **presence_penalty** and **frequency_penalty** to adjust the penalty for the presence and frequency of the tokens in the generated responses. On the other hand, if the skprompt.txt file receives parameters, they must also be defined in this file in the **parameters** array of the **input** object.\n", + "\n", + " ```javascript\n", + " {\n", + " \"schema\": 1,\n", + " \"description\": \"Generate a funny joke about heroes\",\n", + " \"models\": [\n", + " {\n", + " \"max_tokens\": 150,\n", + " \"temperature\": 0.9,\n", + " \"top_p\": 0.5,\n", + " \"presence_penalty\": 0.2,\n", + " \"frequency_penalty\": 0.3\n", + " }\n", + " ],\n", + " \"input\": {\n", + " \"parameters\": [\n", + " {\n", + " \"name\": \"input\",\n", + " \"description\": \"Joke subject\",\n", + " \"defaultValue\": \"\"\n", + " },\n", + " {\n", + " \"name\": \"hero\",\n", + " \"description\": \"Give a hint about the hero you want to joke about\",\n", + " \"defaultValue\": \"\"\n", + " }\n", + " ]\n", + " }\n", + " }\n", + " ```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Como podemos ver, en esta ejecución, solo hemos pasado el primer parámetro: INPUT, pero no hemos pasado el parámetro HERO. No hemos especificado el héroe que queremos utilizar para generar el chiste. Para poder enviar más de un parámetro necesitas crear un objeto del tipo **ContextVariables**:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Cuentame un chiste de futbol\",\n", + " [\"hero\"] = \"Batman\"\n", + "};" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Para poder usar estos dos valores como parte de la llamada, simplemente colócalos como el primer argumento.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var result = await kernel.InvokeAsync(funPluginFunctions[\"JokeSpa\"], variables);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ahora verifica si el chiste es sobre el héroe especificado como parámetro.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "Console.WriteLine(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "De la misma manera, podemos usar las funciones incluidas en **WriterPlugin**:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// Load the WriterPlugin from the Plugins Directory\n", + "var pluginsDirectoryWriter = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"SemanticFunctions\", \"WriterPlugin\");\n", + "\n", + "var writerPluginFunctions = kernel.ImportPluginFromPromptDirectory(pluginsDirectoryWriter);\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Una que genera historias:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Cuenta una historia de tennis\",\n", + " [\"hero\"] = \"Ironman\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(writerPluginFunctions[\"StoryGenSpa\"], variables);\n", + "Console.WriteLine(result);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "O incluso para crear mensajes para cuando los héroes 🦸🏻‍♀️ están de vacaciones ✈️🚢🌴:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var variables = new KernelArguments() {\n", + " [\"input\"] = \"Crea un OOF para Navidad\",\n", + " [\"hero\"] = \"Hulk\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(writerPluginFunctions[\"OOFSpa\"], variables);\n", + "Console.WriteLine(result.GetValue());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Funciones Nativas\n", + "\n", + "Mientras que las funciones semánticas nos permiten definir y reutilizar prompts, **con las funciones nativas puedes hacer que el kernel semántico llame a funciones escritas en C# o Python**, para tareas que van más allá de una llamada a través de un prompt.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ¿Por qué necesito funciones nativas en este tipo de aplicaciones?\n", + "\n", + "Los modelos de lenguaje grandes (LLMs) son excelentes para generar texto, pero hay varias tareas que no pueden realizar por sí mismos. Estos incluyen, entre otros:\n", + "\n", + "- Recuperar datos de fuentes de datos externas\n", + "- Saber qué hora es\n", + "- Realizar operaciones matemáticas complejas\n", + "- Completar tareas en el mundo real\n", + "- Memorizar y recordar información\n", + "\n", + "Para estos escenarios, y muchos otros, las funciones nativas son muy útiles 👍🏻\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Para este ejemplo, voy a utilizar una API llamada **SuperHero API**, que requiere una clave de API. Puedes obtenerla de su sitio web: [https://superheroapi.com/](https://superheroapi.com/)\n", + "\n", + "Una vez que la tengas, pásala al prompt que aparece con la siguiente línea:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var superHeroApiKey = (await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Super Hero Api key\")).GetClearTextPassword();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ahora, para cargar una función nativa, debemos agregar esto antes de construir el Kernel.\n", + "\n", + "Vamos a crear una nueva clase Super Hero Info y agregarla como un Plugin.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#load \"NativeFunctions/HeroInfo.cs\"\n", + "\n", + "var heroInfo = new HeroInfo(superHeroApiKey);\n", + "builder.Plugins.AddFromObject(heroInfo, \"HeroInfo\");\n", + "Kernel kernel = builder.Build();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "El constructor ahora puede interactuar con la función local. Hagamos una llamada para obtener el alter ego de un superhéroe.\n", + "The builder now can interact with the local function. Let's make a call to get the alter ego of a super hero." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var kernelArgs = new KernelArguments()\n", + "{\n", + " [\"input\"] = \"Batman\"\n", + "};\n", + "\n", + "var result = await kernel.InvokeAsync(\"HeroInfo\", \"GetAlterEgo\", kernelArgs);\n", + "Console.WriteLine(result);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "En este repositorio, hay otro directorio llamado **NativeFunctions** donde puedes encontrar una clase llamada **HeroInfo.cs**. En ella, hay una función decorada con el atributo **KernelFunction**, que nos permite indicar al Kernel Semántico que es una función nativa y, a través de la propiedad **Description**, también darle información sobre cuál es el propósito de esta función. En este caso, lo que este método nos permite hacer es recuperar el alter ego del superhéroe que pasamos como parámetro, en este ejemplo el de Ironman.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Planner (Planificador)\n", + "\n", + "Hasta ahora, todos los complementos que has visto se han ejecutado intencionalmente. Es decir, nadie los ha elegido por ti y puedes ejecutarlos según tus necesidades. Sin embargo, esta es la forma de interactuar con **Semantic Kernel** más *estática*. Existe otra opción llamada **Planificador** que te dejará asombrado 😮\n", + "\n", + "El Planificador es una función que toma la solicitud del usuario y devuelve un plan sobre cómo llevar a cabo dicha solicitud. Para hacerlo, utiliza la inteligencia artificial para combinar los complementos registrados en el núcleo y recombinarlos en una serie de pasos que completan un objetivo.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Para verlo en acción, vamos a utilizar los complementos que ya conoces.\n", + "\n", + "Lo primero que necesitas es agregar la referencia nuget al **Planner Handlebars**.\n", + "\n", + "Para aprender más sobre este paquete NuGet, puedes visitar [aquí](https://www.nuget.org/packages/Microsoft.SemanticKernel.Planners.Handlebars/1.5.0-preview#show-readme-container).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#r \"nuget: Microsoft.SemanticKernel.Planners.Handlebars, 1.5.0-preview\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ahora estamos listos para crear un planificador.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using Microsoft.SemanticKernel.Planning.Handlebars;\n", + "\n", + "#pragma warning disable SKEXP0060\n", + "var planner = new HandlebarsPlanner();\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Existen diferentes tipos como puedes ver [aquí](https://learn.microsoft.com/en-us/semantic-kernel/ai-orchestration/planners/?tabs=Csharp). En este ejemplo, vamos a utilizar el que ejecuta tareas de forma secuencial.\n", + "\n", + "Dado que ya tienes todos los complementos cargados en tu instancia de kernel, podemos preguntarle algo como esto:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.Text.Json;\n", + "\n", + "var ask = \"Me gustaría que me cuentes un chiste sobre Batman, y con ese chiste, crea un mensaje de fuera de la oficina utilizando la broma.\";\n", + "#pragma warning disable SKEXP0060\n", + "var originalPlan = await planner.CreatePlanAsync(kernel, ask);\n", + "\n", + "Console.WriteLine(\"Original plan:\\n\");\n", + "Console.WriteLine(originalPlan);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Como puedes ver, el planificador conoce los complementos gracias a la descripción que incluí como parte de su implementación.\n", + "\n", + "Es hora de ejecutar el plan y obtener un mensaje de fuera de la oficina para **Batman** que incluye un **chiste malo**.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// executing the plan\n", + "#pragma warning disable SKEXP0060\n", + "var originalPlanResult = await originalPlan.InvokeAsync(kernel, new KernelArguments());\n", + "\n", + "Console.WriteLine(\"Original Plan results:\\n\");\n", + "Console.WriteLine(originalPlanResult.ToString());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ejemplos avanzados con Planner\n", + "\n", + "El kernel ya tiene los plugins cargados, y la función, veamos si puede resolver un ejemplo más complejo:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var ask_complex = @\"Me gustaría que averigües el alter ego de IronMan y crees un mensaje de fuera de la oficina para IronMan, firmando con su alter ego.\";\n", + "\n", + "#pragma warning disable SKEXP0060\n", + "var plan_complex = await planner.CreatePlanAsync(kernel, ask_complex);\n", + "\n", + "Console.WriteLine(\"Plan:\\n\");\n", + "Console.WriteLine(plan_complex);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ejecutemos el plan y veamos su salida:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#pragma warning disable SKEXP0060\n", + "var complexPlanResult = await plan_complex.InvokeAsync(kernel, new KernelArguments());\n", + "\n", + "Console.WriteLine(\"Complex Plan results:\\n\");\n", + "Console.WriteLine(complexPlanResult.ToString());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Memoria del Kernel (Kernel Memory)\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Para poder utilizar la Memoria del Kernel, necesitas agregar su biblioteca nuget, además de importar la clase que he generado en el directorio **KernelMemory**.\n", + "\n", + "Para aprender más sobre la biblioteca de Memoria del Kernel, puedes visitar la [página del paquete NuGet](https://www.nuget.org/packages/Microsoft.SemanticKernel.Plugins.Memory/1.5.0-alpha#show-readme-container).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#r \"nuget: Microsoft.SemanticKernel.Plugins.Memory, 1.5.0-alpha\"\n", + "#r \"nuget: System.Linq.Async, 6.0.1\"\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Para utilizar la memoria, necesitamos instanciar el Plugin de Memoria con un Almacenamiento de Memoria y un backend de incrustación. En este ejemplo, hacemos uso de VolatileMemoryStore que puede considerarse como un almacenamiento temporal en memoria (no confundir con la Memoria Semántica).\n", + "\n", + "Esta memoria no se escribe en el disco y solo está disponible durante la sesión de la aplicación.\n", + "\n", + "Al desarrollar tu aplicación tendrás la opción de conectar un almacenamiento persistente como Azure Cosmos Db, PostgreSQL, SQLite, etc. La Memoria Semántica también permite indexar fuentes de datos externas, sin duplicar toda la información, más sobre eso más adelante.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using Microsoft.SemanticKernel.Memory;\n", + "using Microsoft.SemanticKernel.Connectors.OpenAI;\n", + "\n", + "// create the memory builder\n", + "#pragma warning disable SKEXP0003, SKEXP0011, SKEXP0052\n", + "var memoryBuilder = new MemoryBuilder();\n", + "\n", + "// Using OpenAI\n", + "memoryBuilder.WithOpenAITextEmbeddingGeneration(\"text-embedding-ada-002\", apiKey);\n", + "\n", + "// Using Azure OpenAI \n", + "// memoryBuilder.WithAzureOpenAITextEmbeddingGeneration(\n", + "// AzureOpenAI.EmbeddingsModel,\n", + "// AzureOpenAI.Endpoint,\n", + "// AzureOpenAI.ApiKey,\n", + "// \"text-embedding-ada-002\");\n", + "\n", + "memoryBuilder.WithMemoryStore(new VolatileMemoryStore());\n", + "var memory = memoryBuilder.Build();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Creemos algunas memorias iniciales \"Fan Facts\". Podemos agregar memorias a nuestro VolatileMemoryStore utilizando SaveInformationAsync:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "// add fan facts to the collection\n", + "const string MemoryCollectionName = \"fanFacts\";\n", + "\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info1\", text: \"El superhéroe favorito de Gisela es Batman\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info2\", text: \"La última película de superhéroes vista por Gisela fue Guardians of the Galaxy Vol 3\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info3\", text: \"El superhéroe favorito de Bruno es Invincible\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info4\", text: \"La última película de superhéroes vista por Bruno fue Aquaman II\");\n", + "await memory.SaveInformationAsync(MemoryCollectionName, id: \"info5\", text: \"A Bruno no le gusta la película de superhéroes: Eternals\");\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Y ahora podemos probar y buscar en la memoria:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var preguntas = new[]\n", + "{\n", + " \"¿Cuál es el superhéroe favorito de Bruno?\",\n", + " \"¿Cuál fue la última película vista por Gisela?\",\n", + " \"¿Cuál es el superhéroe preferido de Gisela?\",\n", + " \"¿Bruno ha visto alguna película de superhéroes en el pasado? ¿Cuál fue la última?\",\n", + " \"Cual es la pelicula que no le gusta a Bruno?\"\n", + "};\n", + "\n", + "foreach (var q in preguntas)\n", + "{\n", + " var response = await memory.SearchAsync(MemoryCollectionName, q).FirstOrDefaultAsync();\n", + " Console.WriteLine(q + \" \" + response?.Metadata.Text);\n", + "}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hacemos la misma prueba con preguntas en English!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "var questions = new[]\n", + "{\n", + " \"what is Bruno's favourite super hero?\",\n", + " \"what was the last movie watched by Gisela?\",\n", + " \"Which is the prefered super hero for Gisela?\",\n", + " \"Did Bruno watched a super hero movie in the past, which was the last one?\"\n", + "};\n", + "\n", + "foreach (var q in questions)\n", + "{\n", + " var response = await memory.SearchAsync(MemoryCollectionName, q).FirstOrDefaultAsync();\n", + " Console.WriteLine(q + \" \" + response?.Metadata.Text);\n", + "}\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebook.ipynb b/notebook.ipynb deleted file mode 100644 index caf6816..0000000 --- a/notebook.ipynb +++ /dev/null @@ -1,1181 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 🦸🏻 Entendiendo Semantic Kernel con Héroes 🦸🏼‍♀️\n", - "\n", - "Semantic Kernel ha sido creado para permitir a los desarrolladores integrar Inteligencia Artificial de una forma súper sencilla en sus aplicaciones. Para ello, proporciona un conjunto de características que te van a permitir añadir modelos, prompts, funciones nativas y memorias sin tener conocimientos profundos de IA 🥲. Es por ello que se dice que Semantic Kernel simula el cerebro 🧠 de tu aplicación.\n", - "\n", - "
\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Crear un servicio de Azure Open AI 🤖 y despliegues\n", - "\n", - "Antes de empezar a jugar 🛝 con Semantic Kernel necesitar tener algún servicio de los que soporta creado. A día de hoy son: [Azure Open AI](https://azure.microsoft.com/es-es/products/ai-services/openai-service), [Open AI](https://openai.com/) o [Hugging Face](https://huggingface.co/)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "En este ejemplo, voy a utilizar Azure Open AI.\n", - "\n", - "Por lo que, a través de Azure CLI necesito iniciar sesión:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "pwsh" - }, - "polyglot_notebook": { - "kernelName": "pwsh" - }, - "tags": [], - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "az login" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Si lo necesitas, porque tienes muchos tenants y suscripciones bajo tu cuenta, puedes seleccionar la que te interese (o tenga acceso a Azure Open AI 😊) a través del siguiente comando:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "pwsh" - }, - "polyglot_notebook": { - "kernelName": "pwsh" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "az account set -n \"Internal Azure Subscription Gisela\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ahora, para poder crear lo que necesitas para este notebook, establece las siguiente variables con los valores de tu preferencia:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "pwsh" - }, - "polyglot_notebook": { - "kernelName": "pwsh" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "$RESOURCE_GROUP=\"understanding-semantic-kernel\"\n", - "$LOCATION=\"canadaeast\"\n", - "$AZURE_OPEN_AI=\"ai-for-heroes\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Con ellas, ya puedes crear el grupo de recursos:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "pwsh" - }, - "polyglot_notebook": { - "kernelName": "pwsh" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "az group create `\n", - "--name $RESOURCE_GROUP `\n", - "--location $LOCATION" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Un recurso de Azure Open AI:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "pwsh" - }, - "polyglot_notebook": { - "kernelName": "pwsh" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "az cognitiveservices account create `\n", - "--name $AZURE_OPEN_AI `\n", - "--custom-domain $AZURE_OPEN_AI `\n", - "--resource-group $RESOURCE_GROUP `\n", - "--kind OpenAI `\n", - "--sku S0 `\n", - "--location $LOCATION" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Y por último, necesitas un despliegue de alguno de los modelos que tengas disponible. En este ejemplo voy a utilizar gpt-4:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "pwsh" - }, - "polyglot_notebook": { - "kernelName": "pwsh" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "az cognitiveservices account deployment create `\n", - "--name $AZURE_OPEN_AI `\n", - "--resource-group $RESOURCE_GROUP `\n", - "--deployment-name \"gpt-4\" `\n", - "--model-name \"gpt-4\" `\n", - "--model-version \"0613\" `\n", - "--model-format OpenAI `\n", - "--sku-capacity \"10\" `\n", - "--sku-name \"Standard\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "El parámetro **sku-capacity** es el que nos permitirá especificar cuántos tokens por minuto podemos mandarle a este modelo. Para poder ver cómo está el uso de tu cuota puedes utilizar este otro comando:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "pwsh" - }, - "polyglot_notebook": { - "kernelName": "pwsh" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "az cognitiveservices usage list `\n", - "-l $LOCATION" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load enviroment variables with your Azure Open AI endpoint and key" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "pwsh" - }, - "polyglot_notebook": { - "kernelName": "pwsh" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "$env:AZURE_OPEN_AI_KEY =$(az cognitiveservices account keys list `\n", - "--name $AZURE_OPEN_AI `\n", - "--resource-group $RESOURCE_GROUP `\n", - "--query \"key1\" `\n", - "--output tsv)\n", - "\n", - "$env:AZURE_OPEN_AI_ENDPOINT =$(az cognitiveservices account show `\n", - "--name $AZURE_OPEN_AI `\n", - "--resource-group $RESOURCE_GROUP `\n", - "--query \"properties.endpoint\" `\n", - "--output tsv)\n", - "\n", - "#dir env:AZURE_OPEN_AI_KEY\n", - "#dir env:AZURE_OPEN_AI_ENDPOINT" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Cómo empezar con Semantic Kernel" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Lo primero que necesitas para poder ejecutar Semantic Kernel en este notebook es instalar la librería **Microsoft.SemanticKernel** que a día de hoy está en su versión **1.0.0-beta8** (prometo ir actualizando 🤓)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta8\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Con ella ya puedes instanciar el cerebro de tu aplicación a través de **KernelBuilder**. Este tiene un montón de conectores a los modelos y otras cosas, pero por ahora vamos a empezar con lo básico:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "using Microsoft.SemanticKernel;\n", - "using Microsoft.SemanticKernel.Orchestration;\n", - "using System.IO;\n", - "\n", - "//Create Kernel builder\n", - "var builder = new KernelBuilder();" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Dependiendo del tipo de tarea que quieras hacer puede utilizar diferentes métodos con el prefijo **With**. Por ahora, vamos a utilizar alguno de los modelos de tipo **completion** o completado." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "builder\n", - ".WithAzureOpenAIChatCompletionService(\"gpt-4\", Environment.GetEnvironmentVariable(\"AZURE_OPEN_AI_ENDPOINT\"), Environment.GetEnvironmentVariable(\"AZURE_OPEN_AI_KEY\"));\n", - "//.WithOpenAIChatCompletionService(\"gpt-4\",(await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Open AI key\")).GetClearTextPassword());" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Con la configuración hecha, lo único que queda es generar el kernel con todo lo establecido:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var kernel = builder.Build();" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Los plugins\n", - "\n", - "Los plugins son el core de Semantic Kernel. Con ellos encapsulas las capacidades de forma que estas puedan ser reutilizables, mantenibles y planificables (más adelante lo entenderás 🙃). Hay dos tipos: aquellos que consisten en plantillas de prompts llamadas **Semantic Functions** y funciones nativas del lenguaje programación elegido llamadas **Native Functions**.\n", - "\n", - "
\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Semantic Functions\n", - "\n", - "Cuando hablas con los modelos de inteligencia artificial debes hacerlo con lo que se conoce como *prompt* este puede ser desde una simple frase a algo más elaborado que nos permite que el modelo conozca no solamente lo que queremos sino el cómo lo queremos. Si echas un vistazo a la documentación oficial define este tipo de funciones como la boca 👄 y los oidos 👂🏻 de tu cerebro 🧠.\n", - "\n", - "
\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Como parte de este repo tienes una carpeta llamada **SemanticFunctions** que tiene diferentes funciones de este tipo:\n", - "\n", - "- **FunPlugin**: Nos permite pedirle al modelo que haga bromas sobre héroes con unas determinadas condiciones, a trabés de la función **Joke**.\n", - "- **WritePlugin**: Para que veas que dentro de un plugin puedes tener diferentes funciones, en este directorio tenemos dos relacionadas con el arte de escribir: la primera de ellas, **OOF**, nos permite generar el mensaje de \"Fuera de la oficina\" 🏢📧 para super héroes y la segunda **StoryGen** nos ayudará a crear historias, también de super héroes 🦸🏻‍♂️🦸🏻‍♀️.\n", - "\n", - "Para que nuestro kernel sepa que estos plugins están disponibles necesitas primero obtener el path del directorio:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), \"SemanticFunctions\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Y después empezar a cargar los plugins que quieras:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "// Load the FunPlugin from the Plugins Directory\n", - "var funPluginFunctions = kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, \"FunPlugin\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Para poder invocar una función de este tipo puedes hacerlo de la siguiente manera:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var result = await kernel.RunAsync(\"Cuentame un chiste\", funPluginFunctions[\"Joke\"]);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Como ves, tan solo utilizando **kernel.RunAsync** y pasando como parámetros mi petición y cuál es la función, en este caso **Joke**, dentro del plugin, en este caso **FunPlugin**, que quiero utilizar.\n", - "\n", - "Cada una de estas funciones consta de dos archivos:\n", - "\n", - "- **skprompt.txt** es el archivo donde se define el prompt a mandar al modelo en un formato plantilla, de tal forma que puede recibir parámetros y hacerlos más reusables.\n", - "\n", - "```\n", - "WRITE EXACTLY ONE JOKE or HUMOROUS STORY ABOUT THE SUBJECT BELOW\n", - "\n", - "JOKE MUST BE:\n", - "- IN SPANISH\n", - "\n", - "BE CREATIVE AND FUNNY. I WANT TO LAUGH.\n", - "\n", - "Incorporate the hero if provided: {{$hero}}\n", - "+++++\n", - "\n", - "{{$input}}\n", - "+++++\n", - "```\n", - "- **config.json**, el cual nos permite indicar el número de tokens máximo permitido para esta llamada (**max_tokens**), la temperatura para controlar la aleatoriedad de las respuestas (**temperature**), lo cual significa que más cerca del 1 serán más aleatorias y más cerca del cero más determinadas y centradas en la respuesta más probable, **top_p** se utiliza para controlar la diversidad de las respuestas, donde un valor de 0.0 significa que solo se considerarán las respuestas más probables y 1 donde se considerarán todas las respuestas posibles y **presence_penaltty** y **frecuency_penalty** para ajustar la penalización por las presencia y frecuencia de los tokens en las respuesta generadas. Por otro lado, si el archivo skprompt.txt recibe parámetros se deben definir en este archivo también en el array **parameters** del objeto **input**\n", - "\n", - "```javascript\n", - "{\n", - " \"schema\": 1,\n", - " \"description\": \"Generate a funny joke about heroes\",\n", - " \"models\": [\n", - " {\n", - " \"max_tokens\": 150,\n", - " \"temperature\": 0.9,\n", - " \"top_p\": 0.5,\n", - " \"presence_penalty\": 0.2,\n", - " \"frequency_penalty\": 0.3\n", - " }\n", - " ],\n", - " \"input\": {\n", - " \"parameters\": [\n", - " {\n", - " \"name\": \"input\",\n", - " \"description\": \"Joke subject\",\n", - " \"defaultValue\": \"\"\n", - " },\n", - " {\n", - " \"name\": \"hero\",\n", - " \"description\": \"Give a hint about the hero you want to joke about\",\n", - " \"defaultValue\": \"\"\n", - " }\n", - " ]\n", - " }\n", - "}\n", - "```\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Para ver el resultado generado de llamar a esta función Joke puedes recuperarlo de la siguiente manera:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "Console.WriteLine(result.GetValue());" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Como puedes ver, en esta ejecución no le hemos pasado más que el parámetro input, pero no el héroe del cual queríamos generar este chiste. Para poder mandarle más de un parámetro necesitas crear un objeto del tipo **ContextVariables**:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var variables = new ContextVariables{\n", - " [\"input\"] = \"Cuentame un chiste sobre Halloween\",\n", - " [\"hero\"] = \"Ironman\"\n", - "};" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Para poder utilizar estos dos valores como parte de la llamara simplemente hay que ponerlo como primer argumento." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var result = await kernel.RunAsync(variables, funPluginFunctions[\"Joke\"]);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ahora comprueba si el chiste es del héroe especificado como parámetro." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "Console.WriteLine(result.GetValue());" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "De la misma forma, podemos utilizar las funciones incluídas en **WriterPlugin**:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "// Load the WriterPlugin from the Plugins Directory\n", - "var writerPluginFunctions = kernel.ImportSemanticFunctionsFromDirectory(pluginsDirectory, \"WriterPlugin\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Uno que genera historias:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var result = await kernel.RunAsync(\"Cuentame una historia sobre las navidades\", writerPluginFunctions[\"StoryGen\"]);\n", - "Console.WriteLine(result);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "O incluso para crear mensajes para cuando los héroes 🦸🏻‍♀️ están de vacaciones ✈️🚢🌴:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var result = await kernel.RunAsync(\"Crea un out of office para los días de Navidades\", writerPluginFunctions[\"OOF\"]);\n", - "Console.WriteLine(result.GetValue());" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Native Functions\n", - "\n", - "Si bien las funciones semánticas nos permiten definir y reutilizar prompts, **con las funciones nativas puedes hacer que semantic kernel pueda llamar a funciones escritas en C# o Python**, para tareas que se escapan propiamente de una llamada a través de un prompt.\n", - "\n", - "
\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### ¿Por qué necesito funciones nativas en este tipo de aplicaciones?\n", - "\n", - "Los LLM, o Large language models, son excelentes para generar texto, pero hay varias tareas que no pueden realizar por sí solos. Éstas incluyen, entre otras:\n", - "\n", - "- Recuperar datos de fuentes de datos externas\n", - "- Saber qué hora es\n", - "- Realizar operaciones matemáticas complejas\n", - "- Completar tareas en el mundo real\n", - "- Memorizar y recordar información\n", - "\n", - "Para estos escenarios, y muchos otros, las funciones nativas son de gran utilidad 👍🏻" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Para esto ejemplo, voy a usar una API llamada **SuperHero API**, la cual necesita una API key. Puedes conseguir la misma en su página web: [https://superheroapi.com/](https://superheroapi.com/)\n", - "\n", - "Una vez la tengas, pasasela al prompt que te aparece con la siguiente línea:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var superHeroApiKey = await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Super Hero Api key\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ahora, para cargar una función nativa, debemos hacerlo de la siguiente manera:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "#load \"NativeFunctions/GetHeroInfo.cs\"\n", - "\n", - "var infoPlugin = kernel.ImportFunctions(new Info(superHeroApiKey.GetClearTextPassword()), \"InfoPlugin\");\n", - "\n", - "var result = await kernel.RunAsync(\"catwoman\", infoPlugin[\"GetAlterEgo\"]);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "En este repo hay otro directorio llamado **NativeFunctions** donde puedes encontrar una clase llamada **GetHeroInfo.cs** en ella hay una función decorada con el atributo **SKFunction** el cual nos permite indicarle a Semantic Kernel que es una función nativa y, a través de la propiedad **Description**, también darle información sobre cuál es el objetivo de esta funcion. En este caso lo que nos permite este método es recuperar el alter ego del super héroe que le pasemos como parámetro, en este ejemplo el de Catwoman. Si echamos un vistazo al resultado podrás comprobar que el mismo es el esperado:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "Console.WriteLine(result.GetValue());" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Planner\n", - "\n", - "Hasta ahora, todos los plugins que has ido viendo los has ejecutado intencionadamente. Es decir, nadie los ha escogido por tí y tú los puedes ejecutar en base a las necesidades que tengas. Sin embargo, esta es la forma más *estática* de interactuar con Semantic Kernel. Exíste otra opción llamada **Planer** que te va a dejar boquiabierto 😮\n", - "\n", - "Planner es una función que toma la petición de un usuario y devuelve un plan sobre cómo llevar a cabo la solicitud. Para ello, utiliza la IA para combinar los plugins registrados en el núcleo y recombinarlos en una serie de pasos que completen un objetivo.\n", - "\n", - "
\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Para verlo en acción vamos a utilizar los plugins que ya conoces.\n", - "\n", - "Lo primero que necesitas es instanciar un planner:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "using Microsoft.SemanticKernel.Planners;\n", - "\n", - "// Create planner\n", - "var planner = new SequentialPlanner(kernel);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Hay de diferentes tipos como puedes ver [aquí](https://learn.microsoft.com/en-us/semantic-kernel/ai-orchestration/planners/?tabs=Csharp). En este ejemplo vamos a utilizar el que ejecuta las tareas de forma secuencial." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Como ya tienes todos los plugins cargados en tu instancia del kernel, podemos preguntarle algo como esto:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "using System.Text.Json;\n", - "\n", - "var ask = \"Me gustaría que me contaras un chiste sobre Batman y un out of office para las vacaciones de Navidad.\";\n", - "var plan = await planner.CreatePlanAsync(ask);\n", - "\n", - "Console.WriteLine(\"Plan:\\n\");\n", - "Console.WriteLine(JsonSerializer.Serialize(plan, new JsonSerializerOptions { WriteIndented = true }));" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Como puedes ver el planificador sabe sobre mis plugins gracias a la descripción que incluí como parte de su implementación." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var result = await kernel.RunAsync(plan);\n", - "\n", - "Console.WriteLine(\"Plan result:\\n\");\n", - "Console.WriteLine(result.GetValue());" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Kernel Memory\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Para este ejemplo voy a utilizar **Open AI** en lugar de Azure Open AI, por lo que necesitas guardar [una API Key](https://platform.openai.com/account/api-keys) de este en la siguiente variable:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Para poder utilizar Kernel Memory necesitas añadir su librería de nuget, además de importa la clase que he generado en el directorio **KernelMemory**." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "#r \"nuget: Microsoft.KernelMemory.Core, 0.11.231120.6-preview\"\n", - "\n", - "#!import \"KernelMemory/Memories.cs\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Lo primero que voy a hacer es importar a Kernel Memory unos cuantos *recuerdos* (textos) y *documentos*." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var openApiKey = await Microsoft.DotNet.Interactive.Kernel.GetPasswordAsync(\"Give me your Open AI key\");\n", - "\n", - "MemoryKernel.Init(openApiKey.GetClearTextPassword());" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Como puedes ver en el output, este ya se encarga de generar los embeddings de las frases/documentos que le pasamos para que el modelo de GPT-4 pueda generar la respuesta.\n", - "Ahora que ya tenemos algo de contenido sobre el que preguntar, vamos a cargar esta clase como un plugin más." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var memoriesPlugin = kernel.ImportFunctions(new MemoryKernel(), \"MemoriesPlugin\");" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Y ahora preguntemos sobre el contenido:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var planner = new SequentialPlanner(kernel);\n", - "\n", - "var plan = await planner.CreatePlanAsync(\"¿Quién es el héroe favorito de Gisela?\");\n", - "\n", - "var result = await kernel.RunAsync(plan);\n", - "\n", - "Console.WriteLine(result.GetValue());" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var planner = new SequentialPlanner(kernel);\n", - "\n", - "var plan = await planner.CreatePlanAsync(\"¿Cuál fue la última película que vio Gisela?\");\n", - "\n", - "var result = await kernel.RunAsync(plan);\n", - "\n", - "Console.WriteLine(result.GetValue());" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "También podemos preguntarle por el PDF que incluí:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "dotnet_interactive": { - "language": "csharp" - }, - "polyglot_notebook": { - "kernelName": "csharp" - }, - "vscode": { - "languageId": "polyglot-notebook" - } - }, - "outputs": [], - "source": [ - "var planner = new SequentialPlanner(kernel);\n", - "\n", - "var plan = await planner.CreatePlanAsync(\"¿Qué incluye este volumen de batman?\");\n", - "\n", - "var result = await kernel.RunAsync(plan);\n", - "\n", - "Console.WriteLine(result.GetValue());" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".NET (C#)", - "language": "C#", - "name": ".net-csharp" - }, - "language_info": { - "name": "python" - }, - "polyglot_notebook": { - "kernelInfo": { - "defaultKernelName": "csharp", - "items": [ - { - "aliases": [], - "name": "csharp" - } - ] - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -}