├── .devcontainer ├── Dockerfile ├── devcontainer.json └── docker-compose.yml ├── .env.example ├── .github └── workflows │ ├── build-acs-lc-python-api.yml │ ├── build-acs-sk-csharp-api.yml │ └── build-chainlitagent-ui.yml ├── .gitignore ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── docs ├── next_steps.md ├── prompt_writing_help.md └── troubleshooting.md ├── labs ├── 00-setup │ ├── README.md │ └── images │ │ ├── completion-deployment.png │ │ ├── deployment-link.png │ │ ├── deployments.png │ │ ├── embedding-deployment.png │ │ ├── endpoint.png │ │ ├── get-started.png │ │ ├── key-1-value.png │ │ ├── model-name.png │ │ ├── openai-service-list.png │ │ └── search-openai.png ├── 01-prompts │ ├── README.md │ └── solutions │ │ └── prompts.md ├── 02-integrating-ai │ ├── 00-PythonModules │ │ └── pythonmodules.ipynb │ ├── 01-AzureOpenAIAPI │ │ └── azureopenaiapi.ipynb │ ├── 02-OpenAIPackages │ │ └── openai.ipynb │ ├── 03-Langchain │ │ └── langchain.ipynb │ ├── 04-SemanticKernel │ │ └── semantickernel.ipynb │ └── README.md ├── 03-orchestration │ ├── 01-Tokens │ │ ├── movies.csv │ │ └── tokens.ipynb │ ├── 02-Embeddings │ │ ├── data │ │ │ └── movies │ │ │ │ ├── Ambush.md │ │ │ │ ├── Black Clover - Sword of the Wizard King.md │ │ │ │ ├── Confidential Informant.md │ │ │ │ ├── Creed III.md │ │ │ │ ├── Deadpool & Wolverine.md │ │ │ │ ├── Extraction 2.md │ │ │ │ ├── Fast X.md │ │ │ │ ├── Guy Ritchie's The Covenant.md │ │ │ │ ├── Indiana Jones and the Dial of Destiny.md │ │ │ │ ├── John Wick - Chapter 4.md │ │ │ │ ├── Knights of the Zodiac.md │ │ │ │ ├── Los bastardos.md │ │ │ │ ├── Sisu.md │ │ │ │ ├── The Flash.md │ │ │ │ ├── The Wrath of Becky.md │ │ │ │ ├── Transformers - Rise of the Beasts.md │ │ │ │ └── iNumber Number - Jozi Gold.md │ │ ├── embeddings.ipynb │ │ └── overview.drawio │ ├── 03-VectorStore │ │ ├── README.md │ │ ├── aisearch.ipynb │ │ ├── mongo.ipynb │ │ ├── movies.csv │ │ └── qdrant.ipynb │ ├── 04-Functions │ │ └── function-calling.ipynb │ └── README.md ├── 04-deploy-ai │ ├── 01-backend-api │ │ ├── aais-lc-python-api │ │ │ ├── README.md │ │ │ └── aais-lc-python │ │ │ │ ├── Dockerfile │ │ │ │ ├── README.md │ │ │ │ ├── main.py │ │ │ │ └── requirements.txt │ │ └── aais-sk-csharp-api │ │ │ ├── README.md │ │ │ └── aais-sk-csharp │ │ │ ├── Dockerfile │ │ │ ├── Plugins │ │ │ └── CustomPlugin │ │ │ │ └── GetMovieInfo │ │ │ │ ├── config.json │ │ │ │ └── skprompt.txt │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ └── launchSettings.json │ │ │ ├── aais-sk-csharp.csproj │ │ │ ├── aais-sk-csharp.sln │ │ │ └── appsettings.json │ ├── 02-frontend-ui │ │ └── chainlitagent-ui │ │ │ ├── .chainlit │ │ │ ├── config.toml │ │ │ └── translations │ │ │ │ └── en-US.json │ │ │ ├── .env.sample │ │ │ ├── Dockerfile │ │ │ ├── README.md │ │ │ ├── app.py │ │ │ ├── chainlit.md │ │ │ ├── docker-compose.yml │ │ │ └── requirements.txt │ ├── 04-streamlit │ │ ├── .env.template │ │ ├── Dockerfile │ │ ├── app.py │ │ ├── movieagent.jpg │ │ └── requirements.txt │ └── README.md └── 99-removed │ └── acs-sk-python.ipynb ├── presentations ├── README.md ├── aiapps-demos.md ├── intro-to-intelligent-apps-workshop.pdf └── prompts-demos.md └── requirements.txt /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] .NET version: 7.0, 6.0, 3.1, 6.0-bullseye, 3.1-bullseye, 6.0-focal, 3.1-focal 2 | # See here for devcontainer tags: https://mcr.microsoft.com/v2/devcontainers/dotnet/tags/list 3 | ARG VARIANT="8.0-jammy" 4 | FROM mcr.microsoft.com/vscode/devcontainers/dotnet:${VARIANT} 5 | 6 | # [Choice] Node.js version: none, lts/*, 18, 16, 14 7 | ARG NODE_VERSION="lts/*" 8 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi 9 | 10 | # [Optional] Uncomment this section to install additional OS packages. 11 | RUN export DEBIAN_FRONTEND=noninteractive \ 12 | && apt-get update \ 13 | && apt-get clean -y 14 | 15 | # [Optional] Uncomment this line to install global node packages. 16 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 17 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/dotnet 3 | { 4 | "name": "Intelligent Apps Dev Container", 5 | "dockerComposeFile": "docker-compose.yml", 6 | "service": "devmachine", 7 | "workspaceFolder": "/workspace", 8 | "shutdownAction": "stopCompose", 9 | 10 | // Configure tool-specific properties. 11 | "customizations": { 12 | // Configure properties specific to VS Code. 13 | "vscode": { 14 | // Add the IDs of extensions you want installed when the container is created. 15 | "extensions": [ 16 | "ms-dotnettools.csharp", 17 | "ms-dotnettools.csdevkit", 18 | "ms-dotnettools.dotnet-interactive-vscode", 19 | "ms-azuretools.vscode-bicep", 20 | "ms-azuretools.azure-dev", 21 | "ms-python.python", 22 | "ms-toolsai.jupyter", 23 | "ms-semantic-kernel.semantic-kernel", 24 | "esbenp.prettier-vscode", 25 | "GitHub.copilot", 26 | "ms-azuretools.vscode-docker" 27 | ] 28 | } 29 | }, 30 | 31 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 32 | // "forwardPorts": [5000, 5001], 33 | 34 | // Use 'postCreateCommand' to run commands after the container is created. 35 | // "postCreateCommand": "dotnet --version", 36 | "postCreateCommand": { 37 | "python-packages": "pip3 install --user -r requirements.txt" 38 | }, 39 | 40 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 41 | "remoteUser": "vscode", 42 | 43 | // Additional devcontainer feature configurations 44 | "features": { 45 | "ghcr.io/devcontainers/features/docker-in-docker:2": { 46 | "version": "latest", 47 | "moby": true 48 | }, 49 | "ghcr.io/devcontainers/features/git:1": { 50 | "version": "latest" 51 | }, 52 | "ghcr.io/devcontainers/features/azure-cli:1": { 53 | "version": "latest" 54 | }, 55 | "ghcr.io/devcontainers/features/python:1": { 56 | "version": "3.11", 57 | "installJupyterlab": true 58 | }, 59 | "ghcr.io/jlaundry/devcontainer-features/azure-functions-core-tools:1": { 60 | "version": "latest" 61 | } 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | devmachine: 5 | container_name: devmachine 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | args: 10 | # Update 'VARIANT' to pick an version of .NET: 6, 7. 11 | # Append -bullseye or -buster to pin to an OS version. 12 | # Use -bullseye variants on local arm64/Apple Silicon. 13 | VARIANT: 8.0-jammy 14 | # Options 15 | NODE_VERSION: "lts/*" 16 | environment: 17 | SAMPLE_ENV: test 18 | volumes: 19 | - ..:/workspace:cached 20 | # Overrides default command so things don't shut down after the process ends. 21 | command: sleep infinity 22 | network_mode: service:qdrant 23 | depends_on: 24 | - qdrant 25 | qdrant: 26 | container_name: qdrant 27 | image: qdrant/qdrant 28 | restart: unless-stopped 29 | ports: 30 | - 6333:6333 31 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_VERSION = "2024-07-01-preview" 2 | OPENAI_EMBEDDING_API_VERSION = "2024-07-01-preview" 3 | 4 | AZURE_OPENAI_API_KEY = "" 5 | AZURE_OPENAI_ENDPOINT = "" 6 | 7 | AZURE_OPENAI_COMPLETION_MODEL = "" 8 | AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME = "" 9 | 10 | AZURE_OPENAI_EMBEDDING_MODEL = "" 11 | AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME = "" 12 | 13 | #return here at lab 03 to fill the connection string 14 | MONGO_DB_CONNECTION_STRING = "mongodb+srv://:@.mongocluster.cosmos.azure.com/?tls=true&authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000" 15 | MONGO_DB_database_name = "movie_db" 16 | MONGO_DB_collection_name = "movie_data" 17 | MONGO_DB_cache_collection_name = "chat_cache" 18 | MONGO_DB_semcache_collection_name = "lc_chat_cache" 19 | MONGO_DB_chathistory_collection_name = "lc_chat_history_data" 20 | MONGO_DB_vector_property_name = "vector" 21 | 22 | storage_file_url = "https://cosmosdbcosmicworks.blob.core.windows.net/fabcondata/movielens_dataset.json" 23 | 24 | #return here to fill these at lab 03 - ACS 25 | AZURE_AI_SEARCH_SERVICE_NAME = "" 26 | AZURE_AI_SEARCH_ENDPOINT = " *Focus: Introduction, First Steps & Prompt Engineering* 18 | 19 | * 📣 Intro (30min) 20 | * Introductions & Setting Expectations 21 | * Use Case Ideation & Brainstorming 22 | * 📣 [Intro to Azure OpenAI, Prompt Engineering & Demos (105min)](presentations/README.md) 23 | * Azure OpenAI Service 24 | * Demo(s) 25 | * Break 26 | * 🧑🏼‍💻 [Lab #1 - Hands-on with Prompt Engineering Exercises](labs/01-prompts/README.md) 27 | * 📣 [Intro to AI Orchestration (60min)](presentations/README.md) 28 | * AI Orchestration 29 | * Demo(s) 30 | 31 | ### 🌆 Afternoon (1:15 – 4:30) 32 | 33 | > *Focus: Building AI Apps & Incorporating LLMs* 34 | 35 | * 📣 [Intro to AI Orchestration Continued (135min)](presentations/README.md) 36 | * 💻 [Lab #2 - Hands-on with Integrating AI Exercises](labs/02-integrating-ai/README.md) 37 | * 💻 [Lab #3 - Hands-on with AI Orchestration Exercises](labs/03-orchestration/README.md) 38 | * 💻 [Lab #4 - Hands-on with Deploying AI Exercises](labs/04-deploy-ai/README.md) 39 | * Break 40 | * Wrapping-up (60min) 41 | * Use Case Validation 42 | * QnA & Closing Remarks 43 | 44 | 45 | ## Getting Started with Workshop Preparation 46 | 47 | The steps in this section will take you through setting up Azure OpenAI and some configuration files so that you can complete all of the hands-on labs successfully. 48 | 49 | * [Preparation](labs/00-setup/README.md) 50 | 51 | ## Post Workshop Next Steps 52 | 53 | When you're done with this workshop and ready to move on, the following may be useful. 54 | 55 | * [Next Steps](docs/next_steps.md) 56 | 57 | ## Contributing 58 | 59 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 60 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 61 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 62 | 63 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 64 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 65 | provided by the bot. You will only need to do this once across all repos using our CLA. 66 | 67 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 68 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 69 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 70 | 71 | ## Trademarks 72 | 73 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 74 | trademarks or logos is subject to and must follow 75 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 76 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 77 | Any use of third-party trademarks or logos are subject to those third-party's policies. 78 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 10 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 11 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 12 | 13 | ## Microsoft Support Policy 14 | 15 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 16 | -------------------------------------------------------------------------------- /docs/next_steps.md: -------------------------------------------------------------------------------- 1 | # Additional Links 2 | 3 | For those eager to learn more, here are some additional links and deeper dive workshops: 4 | 5 | ## Additional Learning Resources 6 | 7 | * Details on Azure OpenAI: 8 | * :bulb: [Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/) 9 | * :bulb: [Azure OpenAI Models](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/concepts/models) 10 | * :bulb: [Data, Privacy & Security for Azure OpenAI](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/data-privacy) 11 | * Going Deeper with Prompts: 12 | * :bulb: [Azure OpenAI in a Day Workshop](https://github.com/microsoft/azure-openai-in-a-day-workshop/) 13 | * Going Deeper with AI Orchestration: 14 | * :bulb: [Semantic Kernel Docs](https://learn.microsoft.com/en-us/semantic-kernel/) 15 | * :bulb: [Langchain Docs](https://python.langchain.com/docs/get_started/introduction.html) 16 | 17 | ## Use Case Scenarios 18 | 19 | * :muscle: [Azure OpenAI Samples - Getting Started](https://github.com/Azure-Samples/openai) 20 | * :muscle: [MS PodcastCopilot (Build 2023)](https://github.com/microsoft/PodcastCopilot) 21 | * :muscle: [Question and Answer](https://github.com/ruoccofabrizio/azure-open-ai-embeddings-qna) 22 | * :muscle: [Contact Center](https://aka.ms/customerserviceinsights) 23 | * :muscle: [Revolutionize your Enterprise Data with ChatGPT](https://techcommunity.microsoft.com/t5/ai-applied-ai-blog/revolutionize-your-enterprise-data-with-chatgpt-next-gen-apps-w/ba-p/3762087) 24 | * :muscle: [Smart .NET Chat App with Caching](https://devblogs.microsoft.com/dotnet/transform-business-smart-dotnet-apps-azure-chatgpt/) 25 | * :bulb: [Semantic Search with Azure Cognitive Search](https://learn.microsoft.com/en-us/azure/search/semantic-search-overview) 26 | * :muscle: [Vector Search & AI Assistance w/Azure Cosmos DB](https://github.com/AzureCosmosDB/VectorSearchAiAssistant) 27 | 28 | ## Deep Dive Workshops 29 | 30 | * Deep Dive on Prompt Engineering: 31 | * :bulb: [Azure OpenAI in a Day Workshop](https://github.com/microsoft/azure-openai-in-a-day-workshop/) 32 | * Deep Dive on Intelligent Apps: 33 | * :muscle: [Intelligent Apps Workshop](https://github.com/Azure/intelligent-app-workshop) 34 | -------------------------------------------------------------------------------- /docs/prompt_writing_help.md: -------------------------------------------------------------------------------- 1 | # Writing good prompts for OpenAI GPT-3 2 | 3 | Here are a few tips that will help you to write good prompts with OpenAI. 4 | All examples use `text-davinci-002` as the model and use a temperature of `0.7`. 5 | 6 | ## Being descriptive 7 | 8 | Just like when telling somebody else what to do, GPT-3 heavily benefits if we tell it exactly what we want from it. 9 | 10 | :arrow_forward: **Prompt:** 11 | > Write an article about spring cleaning. 12 | 13 | :repeat: **Completion (shortened):** 14 | > After a long, cold winter, there’s nothing like the feeling of springtime in the air. And with that comes the urge to spring clean our homes! Whether you’re a neat freak or a bit of a messy person, there’s no denying that a good spring clean can make your home feel fresh and new again. 15 | > 16 | > Here are a few tips to help you get started on your spring cleaning: 17 | > 18 | > 1. Make a plan. Decide what areas of your home you want to focus on and make a list of all the tasks you need to do. This will help you stay organized and on track. 19 | > ... 20 | > With these tips, your spring cleaning will be a breeze! 21 | 22 | The article looks good, but maybe we just want to have 3 takeaways: 23 | 24 | :arrow_forward: **Prompt:** 25 | > Write an short article about spring cleaning. The article should have 3 bullet points as the main talking points and a short intro and summary. 26 | 27 | :repeat: **Completion:** 28 | > It's that time of year again! Time to break out the cleaning supplies and get to work. Spring cleaning is a great way to freshen up your home and get rid of all the dust and cobwebs that have accumulated over the winter. Here are a few tips to help you get the most out of your spring cleaning: 29 | > 30 | > -Start from the top and work your way down. Dust and cobwebs tend to collect on ceilings, so start there and work your way down to the floors. 31 | > 32 | > -Don't forget the nooks and crannies. Spring cleaning is a great opportunity to get into all those hard-to-reach places and give them a good cleaning. 33 | > 34 | > -Make it a family affair. Spring cleaning is a great way to get the whole family involved in cleaning up the house. Assign tasks to everyone and make it a fun activity. 35 | > 36 | > With these tips, your spring cleaning will be a breeze! 37 | 38 | ## Output formats for completions 39 | 40 | Let's say we want the article as markdown: 41 | 42 | :arrow_forward: **Prompt:** 43 | > Write an short article about spring cleaning. The article should have 3 bullet points as the main talking points and a short intro and summary. Write the article formatted as markdown with proper H1 and H2 headers. 44 | 45 | :repeat: **Completion:** 46 | > ``` 47 | > # Spring Cleaning 48 | > 49 | > It's that time of year again! Time to open up the windows, let in some fresh air, and deep clean our homes. Spring cleaning is a great way to get rid of the stuff we don't need and make our living spaces more inviting. Here are a few tips to help you get started: 50 | > 51 | > * Start with a plan: make a list of the rooms you want to tackle and what needs to be done in each. 52 | > 53 | > * Set some realistic goals: you don't have to do everything in one day, or even one weekend. Break your plan down into smaller chunks and take your time. 54 | > 55 | > * Get everyone involved: enlist the help of family and friends to make the job go faster. 56 | > 57 | > With a little planning and some elbow grease, your home will be sparkling in no time! 58 | > ``` 59 | 60 | Let's take a different example: 61 | 62 | :arrow_forward: **Prompt:** 63 | > For the companies Microsoft, UBS, FedEx, classify each company into its industry and also write their yearly revenue. 64 | 65 | :repeat: **Completion:** 66 | > ``` 67 | > Microsoft: Technology; $127.8 billion 68 | > UBS: Banking; $51.2 billion 69 | > FedEx: Courier services; $65.5 billion 70 | > ``` 71 | 72 | Now, we can manually "force" a more machine readable format: 73 | 74 | :arrow_forward: **Prompt:** 75 | > For the companies Microsoft, UBS, FedEx, classify each company into its industry and also write their yearly revenue. Write the results as COMPANY:INDUSTRY:REVENUE. 76 | 77 | :repeat: **Completion:** 78 | > ``` 79 | > Microsoft:Technology:$127.8 billion 80 | > UBS:Banking:$67.7 billion 81 | > FedEx:Transportation:$65.5 billion 82 | > ``` 83 | 84 | Or we can directly ask to generate a csv: 85 | 86 | :arrow_forward: **Prompt:** 87 | > For the companies Microsoft, UBS, FedEx, classify each company into its industry and also write their yearly revenue. Give the results as a CSV. Use COMPANY, INDUSTRY and REVENUE as headers. 88 | 89 | :repeat: **Completion:** 90 | > ``` 91 | > COMPANY,INDUSTRY,REVENUE 92 | > Microsoft,Software,118640 93 | > UBS,Banking,30549 94 | > FedEx,Transportation,65.45 95 | > ``` 96 | 97 | Here, the revenue seems to look off, so let's ask it to format it: 98 | 99 | :arrow_forward: **Prompt:** 100 | > For the companies Microsoft, UBS, FedEx, classify each company into its industry and also write their yearly revenue. Give the results as a CSV. Use COMPANY, INDUSTRY and REVENUE as headers and make sure revenue is printed as $x.xxbn. 101 | 102 | 103 | :repeat: **Completion:** 104 | > ``` 105 | > COMPANY,INDUSTRY,REVENUE 106 | > Microsoft,Technology,$127.8bn 107 | > UBS,Banking,$51.8bn 108 | > FedEx,Logistics,$69.0bn 109 | > ``` 110 | 111 | As we notice, the revenue numbers differ between calls. This is mostly because GPT-3 is not deterministic and we should try specifying the year. However, it might still remain that the numbers are incorrect. 112 | 113 | ## Operating on a "payload" 114 | 115 | Often, we want to extract various data points from a text "payload". Here, it helps to tell GPT-3 where the text starts, by explicitly naming it (e.g., "book chapter"): 116 | 117 | :arrow_forward: **Prompt:** 118 | > Extract all the names of all the persons who are mentioned in the book chapter below. 119 | > 120 | > Book chapter: 121 | > Peter went into the forest. Marry spend her day at the lake. 122 | > 123 | > Persons: 124 | 125 | :repeat: **Completion:** 126 | > Peter, Marry 127 | 128 | In this case, GPT-3 also does better when you end the prompt with "Persons:", so it knows what you expect. 129 | 130 | ## Dealing with non-English text 131 | 132 | Sometimes we have non-English text that we want to process. In many cases, OpenAI is pretty good at understanding that text, but it can struggle when the prompt is also written in non-English. Therefore, it often works better to the have the "payload" text in the non-English language, while keeping the prompt in English. OpenAI often answers in English: 133 | 134 | :arrow_forward: **Prompt:** 135 | > Summarize what things I did buy in the following sentence. 136 | > Give the results as a bullet point list and just list the items. 137 | > 138 | > Sentence: Ich ging in den Supermarkt und kaufte Äpfel, Karotten und Pasta. 139 | > Items: 140 | 141 | :repeat: **Completion:** 142 | > -Apples 143 | > -Carrots 144 | > -Pasta 145 | 146 | However, running the same prompt again might give you a German answer. 147 | 148 | :repeat: **Completion (re-generated):** 149 | > - Äpfel 150 | > - Karotten 151 | > - Pasta 152 | 153 | So in order to get an English completion, we can ask for an English-only answer: 154 | 155 | :arrow_forward: **Prompt:** 156 | > Summarize what things I did buy in the following sentence. 157 | > Give the results as a bullet point list and just list the items. Make sure to answer only in English. 158 | > 159 | > Sentence: Ich ging in den Supermarkt und kaufte Äpfel, Karotten und Pasta. 160 | > Items: 161 | 162 | :repeat: **Completion:** 163 | > -Apples 164 | > -Carrots 165 | > -Pasta 166 | 167 | Starting with `text-davinci-003`, GPT-3 is getting better at also answering in your preferred non-English language. Hence specifying that you want an, e.g. German response, might then give consistent results. However, this does not work reliably with `text-davinci-002`. 168 | 169 | ## Avoiding false information 170 | 171 | In some cases, we might want to answer certain questions given some text. If you ask a question where the answer is within the text, OpenAI does a good job: 172 | 173 | :arrow_forward: **Prompt:** 174 | > The insurance covers the driver and any family members. 175 | > 176 | > Does the insurance cover my wife? 177 | 178 | :repeat: **Completion:** 179 | > Yes, the insurance covers your wife and any other family members. 180 | 181 | However, when you ask a question that can't be answered from the text, OpenAI starts coming up with its own thoughts: 182 | 183 | :arrow_forward: **Prompt:** 184 | > The insurance covers the driver and any family members. 185 | > 186 | > Am I also insured when driving outside of Europe? 187 | 188 | :repeat: **Completion:** 189 | > No, the insurance does not cover driving outside of Europe. If you plan to drive outside of Europe, you will need to purchase additional insurance coverage. 190 | 191 | In order to avoid such a scenario, it helps to tell OpenAI how it should behave if the answer is not in the text: 192 | 193 | **Insurance policy:** 194 | > The insurance covers the driver and any family members. 195 | > 196 | > Am I also insured when driving outside of Europe? If the information is not present in the policy, then write "Answer not found in text!". 197 | 198 | :repeat: **Completion:** 199 | Answer not found in text! -------------------------------------------------------------------------------- /docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | This is a list of the most common errors that we saw occuring and their solutions 4 | 5 | ## Operation Not Supported 6 | 7 | This error might occur, when calling a GPT-3 model: 8 | 9 | ```json 10 | { 11 | "error": { 12 | "code": "OperationNotSupported", 13 | "message": "The completion operation does not work with the specified model, gpt-35-turbo. Please choose different model and try again. You can learn more about which models can be used with each operation here: https://go.microsoft.com/fwlink/?linkid=2197993." 14 | } 15 | } 16 | ``` 17 | 18 | This usually means, that you are using an unsupported version of the GPT 3.5 model. Please check your deployment and make sure you are using version `0301`. Newer versions might include breaking changes that this workshop has not yet been updated to. 19 | 20 | If you Azure Open AI servcie does not offer you to deploy a GPT 3.5 model with version `0301`, please try to deploy an Azure Open AI service to a different Azure Region like West Europe. 21 | -------------------------------------------------------------------------------- /labs/00-setup/README.md: -------------------------------------------------------------------------------- 1 | # Setup for the labs 2 | 3 | Here's a detailed walkthrough of what you need to get started with the labs. 4 | 5 | ## OpenAI Model Deployments 6 | 7 | During these labs, we'll make use of a *Completions* model and an *Embeddings* model, so you'll need access to at least one of each of these. Grant the participants access to Azure OpenAI Service service by assigning the `Cognitive Service OpenAI user`. If the participant is a `Cognitive Service OpenAI contributor`, they can create the following deployments themselves. 8 | 9 | **NOTE:** Please take extra care to make sure the correct version of each model is deployed. 10 | 11 | Someone may have already set these up for you to use, however if you need to deploy your own you can follow these instructions. 12 | 13 | 1. Check if you already have an Azure OpenAI Service deployed. Go to the Azure Portal and in the top search bar, enter `openai`. You'll then see **Azure OpenAI** listed under **Services**. Click on **Azure OpenAI** to see all deployed services. 14 | 15 | ![Alt text](images/search-openai.png) 16 | 17 | On the next page, you should see a list of deployed services. If you have one and are able to access it, click on it. Otherwise, click the **+ Create** button at the top of the page to create a new Azure OpenAI Service. 18 | 19 | ![Alt text](images/openai-service-list.png) 20 | 21 | 2. With a new or existing instance of Azure OpenAI deployed, click on the instance. On the main **Overview** page, you will see a **Get Started** section. Click on the **Develop** link. 22 | 23 | ![Alt text](images/get-started.png) 24 | 25 | On the **Develop** page you will see values for **Key 1**, **Key 2**, **Location / Region** and **Endpoint**. You will need the **Key 1** and **Endpoint** details later on. 26 | 27 | 3. Back at the **Overview** page in the **Get Started** section, click on **Explore**. This will launch the **Azure AI Studio**. Once in Azure AI Studio, click on the **Deployments** link on the left hand side. 28 | 29 | ![Alt text](images/deployment-link.png) 30 | 31 | 4. In the deployments section, we can make sure that we have one of each of the required models available. 32 | 33 | **NOTE:** Please take extra care to make sure the correct version of each model is deployed. 34 | 35 | ![Alt text](images/deployments.png) 36 | 37 | You can see above that we have a *completions* model `gpt-35-turbo` with version `1106` or newer and an *embeddings* model `text-embedding-ada-002` with version `2`. If you have both of these, then you're good to go. If not, click on the **+ Create new deployment** link and follow the steps to create two deployments. Ensure that one model deployment uses `text-embedding-ada-002` and the other uses a completions model such as `gpt-35-turbo`. 38 | 39 | Make a note of both the **deployment name** and the **model name** for each of the two deployments. 40 | 41 | ## Environment Configuration 42 | We need to update a configuration file in this repo so that the labs are able to make use of your Azure OpenAI Service. In the root of this repository, you will see a file named `env.example`. Make a copy of this file in the same location (the root of the repository) and rename the file to `.env` (don't forget the period at the beginning of the filename!) 43 | 44 | Here's a detailed list of what you need to enter into the `.env` file and where to find the information you need to supply. 45 | 46 | OPTIONAL: The `OPENAI_API_TYPE` value can be set to one of two values, depending on how you plan to authenticate to the Azure OpenAI Service. To use an API key, set this to `azure` (default). To use Azure AD authentication, set this to `azure_ad` (currently not supported by this lab) 47 | 48 | ``` 49 | OPENAI_API_TYPE = "azure" 50 | ``` 51 | 52 | If you're using Azure AD authentication, the `AZURE_OPENAI_API_KEY` value can be left empty. It will be populated with an Azure AD token at runtime. 53 | 54 | If you're using API keys, the `AZURE_OPENAI_API_KEY` is the **Key 1** value you found on the **Develop** tab of the **Overview** page for the **Azure OpenAI Service** in the Azure portal. 55 | 56 | ![Alt text](images/key-1-value.png) 57 | 58 | ``` 59 | AZURE_OPENAI_API_KEY = "5a8d1ea15ba00f1a833ab1ff245cdb0a" 60 | ``` 61 | 62 | The `AZURE_OPENAI_ENDPOINT` is the **Endpoint** value you found on the **Develop** tab of the **Overview** page for the **Azure OpenAI Service** in the Azure portal. 63 | 64 | ![Alt text](images/endpoint.png) 65 | 66 | It will look similar to the below, ending with `.openai.azure.com`. 67 | 68 | ``` 69 | AZURE_OPENAI_ENDPOINT = "https://my-openaiservice.openai.azure.com/" 70 | ``` 71 | 72 | The `OPENAI_API_VERSION` is used for the Azure OpenAI API to determine which version of the API to use. The value below should be fine, but you can view available versions of the API at the following link: https://learn.microsoft.com/en-us/azure/cognitive-services/openai/reference#rest-api-versioning 73 | 74 | ``` 75 | OPENAI_API_VERSION = "2023-09-01-preview" 76 | ``` 77 | 78 | The next sections all relate to the models you have deployed in the Azure OpenAI Service. 79 | 80 | First is `AZURE_OPENAI_COMPLETION_MODEL`. This is the name of the **completions model**. It's likely to be `gpt-35-turbo`, but if you're using a different completions model, provide the name of the model here. 81 | 82 | Note for this value, it's the name of the **model**, NOT the name of the **deployment**. 83 | 84 | ![Alt text](images/model-name.png) 85 | 86 | ``` 87 | AZURE_OPENAI_COMPLETION_MODEL = "gpt-35-turbo" 88 | ``` 89 | 90 | The next two items are the **deployments** you have created using the Azure OpenAI Service. First is the name of the deployment for the completions model and can be found in the **Deployment name** column of the **Deployments** page in Azure AI Studio. 91 | 92 | ![Alt text](images/completion-deployment.png) 93 | 94 | ``` 95 | AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME = "gpt35turbo" 96 | ``` 97 | 98 | The final value is the name of the deployment for the embeddings model and can be found in the **Deployment name** column of the **Deployments** page in Azure AI Studio. 99 | 100 | ![Alt text](images/embedding-deployment.png) 101 | 102 | ``` 103 | AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME = "embedding" 104 | ``` 105 | 106 | With all of the above updates to the `.env` file made, make sure you save the file and then you are ready to start the labs. 107 | 108 | **NOTE**: The `.gitignore` file in this repo is configured to ignore the `.env` file, so the secrets such as the API key will not be uploaded to a public repo. 109 | 110 | You can update the rest of the properties later in the labs. 111 | ___ 112 | 113 | ## Next Section 114 | 115 | 📣 [Prompts](../01-prompts/README.md) 116 | -------------------------------------------------------------------------------- /labs/00-setup/images/completion-deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/completion-deployment.png -------------------------------------------------------------------------------- /labs/00-setup/images/deployment-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/deployment-link.png -------------------------------------------------------------------------------- /labs/00-setup/images/deployments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/deployments.png -------------------------------------------------------------------------------- /labs/00-setup/images/embedding-deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/embedding-deployment.png -------------------------------------------------------------------------------- /labs/00-setup/images/endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/endpoint.png -------------------------------------------------------------------------------- /labs/00-setup/images/get-started.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/get-started.png -------------------------------------------------------------------------------- /labs/00-setup/images/key-1-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/key-1-value.png -------------------------------------------------------------------------------- /labs/00-setup/images/model-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/model-name.png -------------------------------------------------------------------------------- /labs/00-setup/images/openai-service-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/openai-service-list.png -------------------------------------------------------------------------------- /labs/00-setup/images/search-openai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/00-setup/images/search-openai.png -------------------------------------------------------------------------------- /labs/01-prompts/README.md: -------------------------------------------------------------------------------- 1 | # Prompts 2 | 3 | In this document you will find a few exercises for practicing prompt engineering. For each exercise, you'll get some input text and then an expected completion. Your task is to write the prompt to achieve the expected completion. 4 | ___ 5 | 6 | ## :question: Exercise 1 - German Translation 7 | 8 | * Exercise: Write a prompt that generates the expected completion 9 | * Input text: `I was enjoying the sun, but then a huge cloud came and covered the sky.` 10 | * Expected completion: `Ich genoss die Sonne, aber dann kam eine riesige Wolke und bedeckte den Himmel.` 11 | 12 | ___ 13 | 14 | ## :question: Exercise 2 - Negation 15 | 16 | * Exercise: Write a prompt that generates the expected completion 17 | * Input text: `I was enjoying the sun, but then a huge cloud came and covered the sky.` 18 | * Expected completion: `I was not enjoying the sun, and then a huge cloud did not come and cover the sky.` 19 | 20 | ___ 21 | 22 | ## :question: Exercise 3 - Classification 23 | 24 | * Exercise: Write a prompt that generates the expected completion 25 | * Input text: `Not much to write about here, but it does exactly what it's supposed to. filters out the pop sounds. now my recordings are much more crisp. it is one of the lowest prices pop filters on amazon so might as well buy it, they honestly work the same despite their pricing.` 26 | * Expected completion (or similar): 27 | ``` 28 | Positive: 0.75 29 | Neutral: 0.20 30 | Negative: 0.05 31 | ``` 32 | 33 | ___ 34 | 35 | ## :question: Exercise 4 - E-Mail Summarization 36 | 37 | * Exercise: Write a prompt that generates the expected completion 38 | * Input text: Use your own email... 39 | * Expected completion (or similar): 40 | ``` 41 | Summary: XYZ 42 | Open Questions: XYZ 43 | Action Items: XYZ 44 | ``` 45 | Find example emails in the Microsoft news site: https://news.microsoft.com/2022/01/18/satya-nadella-email-to-employees-bringing-the-joy-and-community-of-gaming-to-everyone/ 46 | ___ 47 | 48 | ## :question: Exercise 5 - Rewriting 49 | 50 | * Exercise: Write a prompt that generates the expected completion 51 | * Input text: `I was enjoying the sun, but then a huge cloud came and covered the sky.` 52 | * Expected completion: `She was enjoying the sun, but then a huge cloud came and covered the sky.` 53 | 54 | ___ 55 | 56 | ## :question: Exercise 6 - Multiple Tasks 57 | 58 | * Exercise: Write a prompt that generates the expected completion 59 | * Input text: `I was enjoying the sun, but then a huge cloud came and covered the sky.` 60 | * Expected completion: 61 | ``` 62 | { 63 |     "translated": "Ich genoss die Sonne, aber dann kam eine riesige Wolke und bedeckte den Himmel.", 64 |     "negated": "I was not enjoying the sun, and no huge cloud came and covered the sky.", 65 |     "third_person": "She was enjoying the sun, but then a huge cloud came and covered the sky." 66 | } 67 | ``` 68 | 69 | ___ 70 | 71 | ## :question: Exercise 7 - Data extraction to JSON 72 | 73 | * Exercise: Write a prompt that generates the expected completion 74 | * Input text: 75 | ``` 76 | Hello, my name is Mateo Gomez. I lost my Credit card on August 17th, and I would like to request its cancellation. The last purchase I made was of a Chicken parmigiana dish at Contoso Restaurant, located near the Hollywood Museum, for $40. Below is my personal information for validation: 77 | Profession: Accountant 78 | Social Security number is 123-45-6789 79 | Date of birth: 9-9-1989 80 | Phone number: 949-555-0110 81 | Personal address: 1234 Hollywood Boulevard Los Angeles CA 82 | Linked email account: mateo@contosorestaurant.com 83 | Swift code: CHASUS33XXX 84 | ``` 85 | * Expected completion: 86 | ``` 87 | { 88 |     "reason": "Lost card", 89 |     "classified_reason": "lost_card", 90 |     "name": "Mateo Gomez", 91 |     "ssn": "123-45-6789", 92 |     "dob": "09/09/1989" 93 | } 94 | ``` 95 | 96 | ___ 97 | 98 | ## :question: Exercise 8 - Fashion product description 99 | 100 | * Exercise: Write a prompt that generates the expected completion 101 | * Input text: 102 | ``` 103 | Season: Winter 104 | Style: Sweater 105 | Gender: Female 106 | Target group: Teenager 107 | Material: Cotton 108 | ``` 109 | * Expected completion (or similar): 110 | ``` 111 | Stay warm and stylish this winter with our cozy cotton sweaters, perfect for the fashion-forward teenager. Refresh your wardrobe with the latest winter styles from our collection. 112 | ``` 113 | 114 | ___ 115 | 116 | ## :question: Exercise 9 - Write a Blog Post 117 | 118 | * Exercise: Write a blog post about a topic of your choice 119 | * Input text: You choose 120 | * Expected completion: a blogpost with hashtages 121 | 122 | ___ 123 | 124 | ## :question: Exercise 10 - Call Center 125 | 126 | * Exercise: Analyze a call center conversation 127 | * Input text: 128 | ``` 129 | Employee: "Hello, this is Julia Schreider from Contoso Company. How can I help you today?" 130 | Customer: "Hi, I am Carsten Mueller. I ordered a package 10 days ago, on February 10th, and it was supposed to arrive in maximum 5 business days. I have called three times already and nobody could provide any more information. I want to know where the package is and I want the problem to be solved immediately. This is the worst service I had for a long time!" 131 | Employee: "I apologize for the inconvenience, Mr. Mueller. I understand your frustration and I'm here to help. Can you please provide me with your order number so I can look into this for you?" 132 | Customer: "Yes, it's ACZ456789." 133 | Employee: "Thank you. I'm looking into it now. Can you please hold for a few minutes while I check the status of your package?" 134 | Customer: "Okay." 135 | Employee: "Thank you for your patience. I am sorry to inform you that I am unable to find the status of your package. It appears to have left the sending address, but no up-to-date status on the current location. I will further investigate your case and get back to you as soon as possible via phone call. Could you please provide me your contact information?" 136 | Customer: "Ah not again. Anyway, my phone number is +4911112223344." 137 | Employee: "I apologize again for the inconvenience. Is there anything else I can help you with today?" 138 | Customer: "No." 139 | Employee: "Thank you. Have a great day!" 140 | ``` 141 | * Expected completion: 142 | ``` 143 | { 144 | "classified_reason": "lost_package", 145 | "resolve_status": "unresolved", 146 | "call_summary": "Customer ordered package 10 days ago and has not received it yet.", 147 | "customer_name": "Carsten Mueller", 148 | "employee_name": "Julia Schreider", 149 | "order_number": "ACZ456789", 150 | "customer_contact_nr": "+4911112223344", 151 | "new_address": "N/A", 152 | "sentiment_initial": ["angry", "frustrated"], 153 | "sentiment_final": ["calm"], 154 | "satisfaction_score_initial": 0, 155 | "satisfaction_score_final": 5, 156 | "eta": "N/A", 157 | "action_item": ["track_package", "inquire_package_status", "contact_customer"] 158 | } 159 | ``` 160 | 161 | ___ 162 | 163 | ## :question: Exercise 11 - Few-shot learning 164 | 165 | * Exercise: Write a few-shot learned prompt that classifies a movie summary. 166 | * Data samples: 167 | ``` 168 | Paul Atreides, a brilliant and gifted young man born into a great destiny beyond his understanding, must travel to the most dangerous planet in the universe to ensure the future of his family and his people. As malevolent forces explode into conflict over the planet's exclusive supply of the most precious resource in existence-a commodity capable of unlocking humanity's greatest potential-only those who can conquer their fear will survive. 169 | ['Action', 'Adventure', 'Science Fiction’] 170 | 171 | A botched store robbery places Wonder Woman in a global battle against a powerful and mysterious ancient force that puts her powers in jeopardy. 172 | ['Action', 'Adventure', 'Fantasy'] 173 | 174 | After the devastating events of Avengers: Infinity War, the universe is in ruins due to the efforts of the Mad Titan, Thanos. With the help of remaining allies, the Avengers must assemble once more in order to undo Thanos' actions and restore order to the universe once and for all, no matter what consequences may be in store. 175 | ['Adventure', 'Science Fiction', 'Action'] 176 | 177 | A widowed new dad copes with doubts, fears, heartache and dirty diapers as he sets out to raise his daughter on his own. Inspired by a true story. 178 | ['Drama', 'Family', 'Comedy’] 179 | 180 | New data: 181 | Harry, Ron and Hermione walk away from their last year at Hogwarts to find and destroy the remaining Horcruxes, putting an end to Voldemort's bid for immortality. But with Harry's beloved Dumbledore dead and Voldemort's unscrupulous Death Eaters on the loose, the world is more dangerous than ever. 182 | ``` 183 | * Expected completion: Classification of the new data point 184 | 185 | ___ 186 | 187 | ## :question: Exercise 12 - NL to SQL with Codex 188 | 189 | * Exercise: Write a prompt that generates the expected SQL statement 190 | * Table information: 191 | * Table: customer // Columns: firstname, name, customer_id, address 192 | * Table: orders // Columns: order_id, customer_id, product_id, product_amount 193 | * Table: products // Columns: product_id, price, name, description 194 | * Expected completion: a query that returns the top 10 orders and show the customer name 195 | 196 | ## Next Section 197 | 198 | 📣 [Integrating AI](../02-integrating-ai/README.md) 199 | -------------------------------------------------------------------------------- /labs/02-integrating-ai/00-PythonModules/pythonmodules.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Python Modules\n", 8 | "\n", 9 | "A handful of Python modules are necessary to use the labs in this repo. The module names are listed in the `requirements.txt` file which can be found in the root of this repository.\n", 10 | "\n", 11 | "Run the below to install the required packages.\n", 12 | "\n", 13 | "> **NOTE:** If you're running the labs using GitHub Codespaces or a dev container, these requirements should have already been installed as part of the container setup. However, you can run the below again to confirm everything is installed if you like!" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [ 22 | "%pip install -r ../../../requirements.txt" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "If there are no errors above, you can proceed with the rest of the labs." 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "## Next Section\n", 37 | "\n", 38 | "📣 [Azure OpenAI API](../01-AzureOpenAIAPI/azureopenaiapi.ipynb)" 39 | ] 40 | } 41 | ], 42 | "metadata": { 43 | "kernelspec": { 44 | "display_name": ".venv", 45 | "language": "python", 46 | "name": "python3" 47 | }, 48 | "language_info": { 49 | "codemirror_mode": { 50 | "name": "ipython", 51 | "version": 3 52 | }, 53 | "file_extension": ".py", 54 | "mimetype": "text/x-python", 55 | "name": "python", 56 | "nbconvert_exporter": "python", 57 | "pygments_lexer": "ipython3", 58 | "version": "3.10.14" 59 | }, 60 | "orig_nbformat": 4 61 | }, 62 | "nbformat": 4, 63 | "nbformat_minor": 2 64 | } 65 | -------------------------------------------------------------------------------- /labs/02-integrating-ai/01-AzureOpenAIAPI/azureopenaiapi.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# 01 - Working with the Azure OpenAI API directly\n", 9 | "\n", 10 | "In this lab, we will perform a simple call to the Azure OpenAI API. This will prove that everything is setup correctly and working for the rest of the labs.\n", 11 | "\n", 12 | "## Setup\n", 13 | "\n", 14 | "First, we need to retrieve values from the `.env` file which we will use to make calls to the Azure OpenAI API." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "import json\n", 24 | "import requests\n", 25 | "import os\n", 26 | "from dotenv import load_dotenv\n", 27 | "\n", 28 | "# Load environment variables\n", 29 | "if load_dotenv():\n", 30 | " print(\"Found Azure OpenAI API Base Endpoint: \" + os.getenv(\"AZURE_OPENAI_ENDPOINT\"))\n", 31 | "else: \n", 32 | " print(\"Azure OpenAI API Base Endpoint not found. Have you configured the .env file?\")\n", 33 | " \n", 34 | "API_KEY = os.getenv(\"AZURE_OPENAI_API_KEY\")\n", 35 | "API_VERSION = os.getenv(\"OPENAI_API_VERSION\")\n", 36 | "RESOURCE_ENDPOINT = os.getenv(\"AZURE_OPENAI_ENDPOINT\")" 37 | ] 38 | }, 39 | { 40 | "attachments": {}, 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "## Send a prompt to Azure OpenAI using the API\n", 45 | "\n", 46 | "Now let's call the Azure OpenAI API with a prompt. To do this, we'll need the `id` of the Azure OpenAI deployment that contains our completion model. This should already be setup in the `.env` file." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "DEPLOYMENT_ID = os.getenv(\"AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME\")" 56 | ] 57 | }, 58 | { 59 | "attachments": {}, 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "We'll construct a URL to call so that you can see what it looks like." 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "url = RESOURCE_ENDPOINT + \"/openai/deployments/\" + DEPLOYMENT_ID + \"/chat/completions?api-version=\" + API_VERSION\n", 73 | "\n", 74 | "print(url)" 75 | ] 76 | }, 77 | { 78 | "attachments": {}, 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "You will see the full URL that we are going to call. This URL will use the specified deployment to call the **chat completions** API.\n", 83 | "\n", 84 | "Next, we will call the Azure OpenAI API using the URL above. We pass the API key in the HTTP header. We also send a JSON formatted body as part of the request which contains the *prompt* that we want to use to get a response from the OpenAI model. In this case, our prompt is \"Once upon a time\", which should cause the model to complete the prompt by generating a story." 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "r = requests.post(url, headers={\"api-key\": API_KEY}, json={\"messages\":[{\"role\": \"assistant\", \"content\": \"Once upon a time \"}]})\n", 94 | "\n", 95 | "print(json.dumps(r.json(), indent=2))" 96 | ] 97 | }, 98 | { 99 | "attachments": {}, 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "The result of the API call will be JSON data similar to the below example. Note that the response has been edited to make it easier to read.\n", 104 | "\n", 105 | "```\n", 106 | "{\n", 107 | " \"id\": \"chatcmpl-7wVxE2LNxaY6ZoxcWAmhiyrqaLZgf\",\n", 108 | " \"object\": \"chat.completion\",\n", 109 | " \"created\": 1694180212,\n", 110 | " \"model\": \"gpt-35-turbo\",\n", 111 | " \"prompt_filter_results\": [\n", 112 | " {\n", 113 | " \"prompt_index\": 0,\n", 114 | " (content filter results removed for brevity)\n", 115 | " }\n", 116 | " ],\n", 117 | " \"choices\": [\n", 118 | " {\n", 119 | " \"index\": 0,\n", 120 | " \"finish_reason\": \"stop\",\n", 121 | " \"message\": {\n", 122 | " \"role\": \"assistant\",\n", 123 | " \"content\": \", there was a magical kingdom called Tildor ... )))\"\n", 124 | " }\n", 125 | " }\n", 126 | " ],\n", 127 | " \"usage\": {\n", 128 | " \"completion_tokens\": 366,\n", 129 | " \"prompt_tokens\": 13,\n", 130 | " \"total_tokens\": 379\n", 131 | " }\n", 132 | "}\n", 133 | "```\n", 134 | "\n", 135 | "Here's some information about some of the more interesting pieces of data contained in that response.\n", 136 | "\n", 137 | "Key | Description\n", 138 | "--- | ---\n", 139 | "`model` | The model that was used to generate the response to the prompt\n", 140 | "`content` | This is the response that was generated by the OpenAI model. Note that the response example above was edited to remove most of the output to make it easier to read\n", 141 | "`finish_reason` | This is the reason that the model stopped generating the response. In this case, `stop` indicates the model determined it had fully completed the response without hitting any limits\n", 142 | "`completion_tokens` | The number of tokens that were used in generating the response\n", 143 | "`prompt_tokens` | The number of tokens that were consumed by the prompt\n", 144 | "`total_tokens` | The total number of tokens that were consumed by the request (`prompt_tokens` + `completion_tokens`)" 145 | ] 146 | }, 147 | { 148 | "attachments": {}, 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "## Summary\n", 153 | "\n", 154 | "In this lab, we used the Azure OpenAI API directly to send a prompt to an OpenAI model." 155 | ] 156 | }, 157 | { 158 | "attachments": {}, 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "## Up Next\n", 163 | "\n", 164 | "In the next lab, we will look at using the OpenAI SDK to work with the Azure OpenAI service." 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "## Next Section\n", 172 | "\n", 173 | "📣 [OpenAI Packages/Libraries](../02-OpenAIPackages/openai.ipynb)" 174 | ] 175 | } 176 | ], 177 | "metadata": { 178 | "kernelspec": { 179 | "display_name": "Python 3", 180 | "language": "python", 181 | "name": "python3" 182 | }, 183 | "language_info": { 184 | "codemirror_mode": { 185 | "name": "ipython", 186 | "version": 3 187 | }, 188 | "file_extension": ".py", 189 | "mimetype": "text/x-python", 190 | "name": "python", 191 | "nbconvert_exporter": "python", 192 | "pygments_lexer": "ipython3", 193 | "version": "3.10.14" 194 | }, 195 | "orig_nbformat": 4 196 | }, 197 | "nbformat": 4, 198 | "nbformat_minor": 2 199 | } 200 | -------------------------------------------------------------------------------- /labs/02-integrating-ai/02-OpenAIPackages/openai.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# 02 - Using the OpenAI Library\n", 9 | "\n", 10 | "In the [first](../01-AzureOpenAIAPI/azureopenaiapi.ipynb) lab, we walked through calling the Azure OpenAI API directly to submit a prompt for completion. An easier way to work with an API is to use a *Library*. A Library is a collection of packages and modules that allow reusable code to be shared with the community.\n", 11 | "\n", 12 | "In this lab, we'll use the OpenAI Python library to perform the same operations as we did in the first lab." 13 | ] 14 | }, 15 | { 16 | "attachments": {}, 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "First, we use the `import` statement to let our application know that we're going to be using the OpenAI library in our code." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "import openai" 30 | ] 31 | }, 32 | { 33 | "attachments": {}, 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Next, we'll bring in the values from our `.env` file." 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "import os\n", 47 | "from dotenv import load_dotenv\n", 48 | "\n", 49 | "# Load environment variables\n", 50 | "if load_dotenv():\n", 51 | " print(\"Found Azure OpenAI API Base Endpoint: \" + os.getenv(\"AZURE_OPENAI_ENDPOINT\"))\n", 52 | "else: \n", 53 | " print(\"Azure OpenAI API Base Endpoint not found. Have you configured the .env file?\")" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "We'll create a new `AzureOpenAI` object and pass in the API key, API version and the endpoint URL to be used." 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "from openai import AzureOpenAI\n", 70 | "\n", 71 | "client = AzureOpenAI(\n", 72 | " azure_endpoint = os.getenv(\"AZURE_OPENAI_ENDPOINT\"),\n", 73 | " api_key = os.getenv(\"AZURE_OPENAI_API_KEY\"),\n", 74 | " api_version = os.getenv(\"OPENAI_API_VERSION\")\n", 75 | ")" 76 | ] 77 | }, 78 | { 79 | "attachments": {}, 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "## Send a prompt to Azure OpenAI using the OpenAI library\n", 84 | "\n", 85 | "Now that we have defined an Azure OpenAI instance, let's try a Chat Completion. We'll call the `chat.completions.create()` method. Note that for the `model` value, we actually pass in the id of our Azure OpenAI `deployment`. We'll also pass the `prompt` we want to use as the `content` of the `messages` parameter." 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "response = client.chat.completions.create(\n", 95 | " model = os.getenv(\"AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME\"),\n", 96 | " messages = [{\"role\" : \"assistant\", \"content\" : \"The one thing I love more than anything else is \"}],\n", 97 | ")\n", 98 | "\n", 99 | "print(response)" 100 | ] 101 | }, 102 | { 103 | "attachments": {}, 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "The response above should be similar JSON data to what we got when we called the API directly in the previous exercise, containing details of the model we called, the response that was generated and the token usage.\n", 108 | "\n", 109 | "We can use the structured response returned by the API to just extract the generated response text on its own." 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "print(response.choices[0].message.content)" 119 | ] 120 | }, 121 | { 122 | "attachments": {}, 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "## Summary\n", 127 | "\n", 128 | "The OpenAI library provides a more concise way to work with the OpenAI API. Once we've set up the initial parameters, we don't need to send them each time as we need to do with a direct API call. It's also easier to add information such as prompts to the call, as we can pass those values in as part of the call to the OpenAI library methods instead of having to pass in JSON objects as part of the request body.\n", 129 | "\n", 130 | "You can find more details about the completions API in the reference documentation:\n", 131 | "\n", 132 | "https://platform.openai.com/docs/api-reference/chat/create" 133 | ] 134 | }, 135 | { 136 | "attachments": {}, 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "## Up Next\n", 141 | "\n", 142 | "In the next lab we'll begin looking at AI orchestrators. Whereas the OpenAI library simplfies working with the OpenAI API, orchestrators take things to the next level!" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "## Next Section\n", 150 | "\n", 151 | "📣 [Langchain](../03-Langchain/langchain.ipynb)" 152 | ] 153 | } 154 | ], 155 | "metadata": { 156 | "kernelspec": { 157 | "display_name": "Python 3", 158 | "language": "python", 159 | "name": "python3" 160 | }, 161 | "language_info": { 162 | "codemirror_mode": { 163 | "name": "ipython", 164 | "version": 3 165 | }, 166 | "file_extension": ".py", 167 | "mimetype": "text/x-python", 168 | "name": "python", 169 | "nbconvert_exporter": "python", 170 | "pygments_lexer": "ipython3", 171 | "version": "3.10.14" 172 | }, 173 | "orig_nbformat": 4 174 | }, 175 | "nbformat": 4, 176 | "nbformat_minor": 2 177 | } 178 | -------------------------------------------------------------------------------- /labs/02-integrating-ai/03-Langchain/langchain.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# 03 - Langchain\n", 9 | "\n", 10 | "In this lab, we will introduce [Langchain](https://python.langchain.com/docs/get_started/introduction), a framework for developing applications powered by language models.\n", 11 | "\n", 12 | "Langchain supports Python and Javascript / Typescript. For this lab, we will use Python." 13 | ] 14 | }, 15 | { 16 | "attachments": {}, 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "We'll start by importing the `AzureOpenAI` specific components from the `langchain` package, including models and schemas for interacting with the API." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "from langchain_openai import AzureChatOpenAI" 30 | ] 31 | }, 32 | { 33 | "attachments": {}, 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "As with all the other labs, we'll need to provide our API key and endpoint details, so we'll load them from our `.env` file." 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "import os\n", 47 | "import openai\n", 48 | "from dotenv import load_dotenv\n", 49 | "\n", 50 | "if load_dotenv():\n", 51 | " print(\"Found Azure OpenAI Endpoint: \" + os.getenv(\"AZURE_OPENAI_ENDPOINT\"))\n", 52 | "else: \n", 53 | " print(\"No file .env found\")" 54 | ] 55 | }, 56 | { 57 | "attachments": {}, 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "Next, we'll configure Langchain by providing the Azure OpenAI deployment name. Langchain will automatically retrieve details for the Azure OpenAI endpoint and version from the environment variables we've set above." 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "# Create an instance of Azure OpenAI\n", 71 | "llm = AzureChatOpenAI(\n", 72 | " azure_deployment = os.getenv(\"AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME\")\n", 73 | ")" 74 | ] 75 | }, 76 | { 77 | "attachments": {}, 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "## Send a prompt to Azure OpenAI using Langchain\n", 82 | "\n", 83 | "We're now ready to send a request to Azure OpenAI. To do this, we invoke the `llm` instance we created above and pass in the prompt." 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "r = llm.invoke(\"What things could I make with a Raspberry Pi?\")\n", 93 | "\n", 94 | "# Print the response\n", 95 | "print(r.content)" 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "Compared to using the OpenAI Python library as we did in the previous lab, Langchain further simplified the process of interacting with the LLM by reducing it to a `llm.invoke` call." 103 | ] 104 | }, 105 | { 106 | "attachments": {}, 107 | "cell_type": "markdown", 108 | "metadata": {}, 109 | "source": [ 110 | "## Using templates and chains\n", 111 | "\n", 112 | "We've seen that we can use Langchain to interact with the LLM and it's a little easier to work with than the OpenAI Python library. However, that's just the start of how Langchain makes it easier to work with LLM's. Most OpenAI models are designed to be interacted with using a Chat style interface, where you provide a persona or system prompt which helps the LLM understand the context of the conversation. This will then be sent to the LLM along with the user's request.\n", 113 | "\n", 114 | "So that you don't have to setup the persona / system prompt every time you want to interact with the LLM, Langchain provides the concept of Templates. Templates are a way to define the persona and system prompt once and then reuse them across multiple interactions." 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "from langchain_core.prompts import ChatPromptTemplate\n", 124 | "prompt = ChatPromptTemplate.from_messages([\n", 125 | " (\"system\", \"You are a chatbot that helps people generate ideas for their next project. You can help them brainstorm ideas, come up with a plan, or even help them with their project.\"),\n", 126 | " (\"user\", \"{input}\")\n", 127 | "])" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "Above we've defined a \"system\" message which will tell the LLM how we're expecting it to respond, and an `{input}` placeholder for the user's prompt.\n", 135 | "\n", 136 | "Next, we define a chain. A chain allows us to define a sequence of operations that we want to perform. In this case, we're defining a simple chain that will take the prompt we've defined above and send it to the LLM." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "chain = prompt | llm" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "Now, we can invoke the chain in a similar fashion to how to invoked the LLM earlier. This time, we're passing in the user's input as a parameter to the chain, which will replace the `{input}` placeholder in the prompt." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "chain.invoke({\"input\": \"I've just purchased a Raspberry Pi and I'm looking for a project to work on. Can you help me brainstorm some ideas?\"})" 162 | ] 163 | }, 164 | { 165 | "cell_type": "markdown", 166 | "metadata": {}, 167 | "source": [ 168 | "The result will be an `AIMessage` object, which contains the response from the LLM.\n", 169 | "\n", 170 | "Let's enhance the chain further to get it to parse the output from the LLM and extract the text from the response. First, we define an output parser." 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "from langchain_core.output_parsers import StrOutputParser\n", 180 | "\n", 181 | "output_parser = StrOutputParser()" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "Next, we redefine our chain to include the output parser. So now when we invoke the chain, it will \n", 189 | "\n", 190 | "- Take the prompt template and add the user's input\n", 191 | "- Send the prompt to the LLM\n", 192 | "- Parse the response from the LLM and extract the text" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [ 201 | "chain = prompt | llm | output_parser" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "Now let's invoke the chain again with the same prompt as before." 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [ 217 | "chain.invoke({\"input\": \"I've just purchased a Raspberry Pi and I'm looking for a project to work on. Can you help me brainstorm some ideas?\"})" 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "This time, you should only get a string containing the text from the response.\n", 225 | "\n", 226 | "We can do much more powerful things with chains than simply setting up and passing prompts to the LLM and parsing the results. We can augment the prompt with external data retrieved from a database, we could add conversation history to provide context for a chatbot, or we could even chain multiple LLMs together to create a more powerful model. We'll explore some of these ideas in future labs." 227 | ] 228 | }, 229 | { 230 | "attachments": {}, 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "## Summary\n", 235 | "\n", 236 | "Langchain is an example of an AI orchestrator. It provides an alternative method to the raw API or using an SDK package to access the AI models, but on top of that can provide additional integrations, deal with issues related to rate limiting of the API and provide an abstraction layer over potentially complex operations. We'll get into those more complex use cases in later labs." 237 | ] 238 | }, 239 | { 240 | "attachments": {}, 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "## Up Next\n", 245 | "\n", 246 | "In the next lab, we'll look at another AI Orchestrator - Semantic Kernel." 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "## Next Section\n", 254 | "\n", 255 | "📣 [Semantic Kernel](../04-SemanticKernel/semantickernel.ipynb)" 256 | ] 257 | } 258 | ], 259 | "metadata": { 260 | "kernelspec": { 261 | "display_name": "Python 3", 262 | "language": "python", 263 | "name": "python3" 264 | }, 265 | "language_info": { 266 | "codemirror_mode": { 267 | "name": "ipython", 268 | "version": 3 269 | }, 270 | "file_extension": ".py", 271 | "mimetype": "text/x-python", 272 | "name": "python", 273 | "nbconvert_exporter": "python", 274 | "pygments_lexer": "ipython3", 275 | "version": "3.10.14" 276 | }, 277 | "orig_nbformat": 4 278 | }, 279 | "nbformat": 4, 280 | "nbformat_minor": 2 281 | } 282 | -------------------------------------------------------------------------------- /labs/02-integrating-ai/04-SemanticKernel/semantickernel.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# 04. Semantic Kernel\n", 9 | "\n", 10 | "In this lab, we will introduce **Semantic Kernel**. Like **Langchain**, it provides a framework for working with AI models and supports applications written in Python. Semantic Kernel also adds support for .NET and Java applications.\n", 11 | "\n", 12 | "As with the other labs, we'll start by reading in values from the `.env` file.\n", 13 | "\n", 14 | "___\n", 15 | "**NOTE**: This lab uses **.NET** so be sure to select .NET and not Python when starting to use this notebook.\n", 16 | "___" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": { 23 | "dotnet_interactive": { 24 | "language": "csharp" 25 | }, 26 | "polyglot_notebook": { 27 | "kernelName": "csharp" 28 | }, 29 | "vscode": { 30 | "languageId": "polyglot-notebook" 31 | } 32 | }, 33 | "outputs": [], 34 | "source": [ 35 | "#r \"nuget: dotenv.net, 3.2.0\"\n", 36 | "\n", 37 | "using dotenv.net;\n", 38 | "\n", 39 | "// Read values from .env file\n", 40 | "var envVars = DotEnv.Fluent()\n", 41 | " .WithoutExceptions()\n", 42 | " .WithEnvFiles(\"../../../.env\")\n", 43 | " .WithTrimValues()\n", 44 | " .WithDefaultEncoding()\n", 45 | " .WithOverwriteExistingVars()\n", 46 | " .WithoutProbeForEnv()\n", 47 | " .Read();\n", 48 | "\n", 49 | "// Load values into variables and strip quotes\n", 50 | "var model = envVars[\"AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME\"].Replace(\"\\\"\", \"\");\n", 51 | "var azureEndpoint = envVars[\"AZURE_OPENAI_ENDPOINT\"].Replace(\"\\\"\", \"\");\n", 52 | "var apiKey = envVars[\"AZURE_OPENAI_API_KEY\"].Replace(\"\\\"\", \"\");\n" 53 | ] 54 | }, 55 | { 56 | "attachments": {}, 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "To start working with Semantic Kernel, we'll need to load its nuget package." 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": { 67 | "dotnet_interactive": { 68 | "language": "csharp" 69 | }, 70 | "polyglot_notebook": { 71 | "kernelName": "csharp" 72 | }, 73 | "vscode": { 74 | "languageId": "polyglot-notebook" 75 | } 76 | }, 77 | "outputs": [], 78 | "source": [ 79 | "#r \"nuget: Microsoft.SemanticKernel, 1.19.0\"" 80 | ] 81 | }, 82 | { 83 | "attachments": {}, 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "Semantic Kernel works by creating an instance of the Kernel and then adding in various plugins to perform different functions. Those plugins or functions can then be called individually or chained together to perform more complex tasks.\n", 88 | "\n", 89 | "We use the standard .NET `builder` pattern to initialise the kernel. Notice that we pass in the details of the completion model that we're going to use, the Azure OpenAI API endpoint URL and the API key." 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 3, 95 | "metadata": { 96 | "dotnet_interactive": { 97 | "language": "csharp" 98 | }, 99 | "polyglot_notebook": { 100 | "kernelName": "csharp" 101 | }, 102 | "vscode": { 103 | "languageId": "polyglot-notebook" 104 | } 105 | }, 106 | "outputs": [], 107 | "source": [ 108 | "using Microsoft.SemanticKernel;\n", 109 | "\n", 110 | "var builder = Kernel.CreateBuilder();\n", 111 | "builder.Services.AddAzureOpenAIChatCompletion(model, azureEndpoint, apiKey);\n", 112 | "var kernel = builder.Build();" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "## Send a prompt to Azure OpenAI using Semantic Kernel\n", 120 | "\n", 121 | "Now that we've established a connection to the Azure OpenAI API, we can go ahead and send a prompt to the LLM." 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "metadata": { 128 | "dotnet_interactive": { 129 | "language": "csharp" 130 | }, 131 | "polyglot_notebook": { 132 | "kernelName": "csharp" 133 | }, 134 | "vscode": { 135 | "languageId": "polyglot-notebook" 136 | } 137 | }, 138 | "outputs": [], 139 | "source": [ 140 | "Console.WriteLine(await kernel.InvokePromptAsync(\"What things could I make with a Raspberry Pi?\"));" 141 | ] 142 | }, 143 | { 144 | "attachments": {}, 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "Let's take that simple prompt forward and create a function with a prompt template to perform a simple request to Azure OpenAI. The template allows us to define a prompt and add placeholders for values that we will provide later. These values could come from user input, or another function, for example." 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 5, 154 | "metadata": { 155 | "dotnet_interactive": { 156 | "language": "csharp" 157 | }, 158 | "polyglot_notebook": { 159 | "kernelName": "csharp" 160 | }, 161 | "vscode": { 162 | "languageId": "polyglot-notebook" 163 | } 164 | }, 165 | "outputs": [], 166 | "source": [ 167 | "var whatCanIMakeFunction = kernel.CreateFunctionFromPrompt(\n", 168 | " new PromptTemplateConfig()\n", 169 | " {\n", 170 | " Template = @\"What interesting things can I make with a {{$item}}?\",\n", 171 | " InputVariables = [\n", 172 | " new() { Name = \"item\", Description = \"An item to make something with.\", IsRequired=true }\n", 173 | " ]\n", 174 | " });\n" 175 | ] 176 | }, 177 | { 178 | "attachments": {}, 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "`{{$item}}` represents the value we will provide later and will be replaced when we make the call to Azure OpenAI.\n", 183 | "\n", 184 | "Next, we'll define a value for `item` and then call the function, passing in the `item` as we do so." 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "metadata": { 191 | "dotnet_interactive": { 192 | "language": "csharp" 193 | }, 194 | "polyglot_notebook": { 195 | "kernelName": "csharp" 196 | }, 197 | "vscode": { 198 | "languageId": "polyglot-notebook" 199 | } 200 | }, 201 | "outputs": [], 202 | "source": [ 203 | "string item = \"raspberry pi\";\n", 204 | "\n", 205 | "var response = await kernel.InvokeAsync(whatCanIMakeFunction, new () { { \"item\", item }});\n", 206 | "\n", 207 | "Console.WriteLine(response);" 208 | ] 209 | }, 210 | { 211 | "attachments": {}, 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "The result is a list of ideas for things we could make with a Raspberry Pi. \n", 216 | "\n", 217 | "We could then use Semantic Kernel to take that list of ideas and summarize it. First, we'll create a new prompt that will generate a summary of some text." 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 7, 223 | "metadata": { 224 | "dotnet_interactive": { 225 | "language": "csharp" 226 | }, 227 | "polyglot_notebook": { 228 | "kernelName": "csharp" 229 | }, 230 | "vscode": { 231 | "languageId": "polyglot-notebook" 232 | } 233 | }, 234 | "outputs": [], 235 | "source": [ 236 | "var thingsToMakeSummary = kernel.CreateFunctionFromPrompt(\n", 237 | " new PromptTemplateConfig()\n", 238 | " {\n", 239 | " Template = @\"Summarize the following text: {{$thingsToMake}}?\",\n", 240 | " InputVariables = [\n", 241 | " new() { Name = \"thingsToMake\", Description = \"A list of things you could make.\", IsRequired=true }\n", 242 | " ]\n", 243 | " });" 244 | ] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "metadata": {}, 249 | "source": [ 250 | "Next, we can call the kernel again, this time passing in the list of ideas, the `response` from the previous call, as the value for `thingsToMake`." 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": null, 256 | "metadata": { 257 | "dotnet_interactive": { 258 | "language": "csharp" 259 | }, 260 | "polyglot_notebook": { 261 | "kernelName": "csharp" 262 | }, 263 | "vscode": { 264 | "languageId": "polyglot-notebook" 265 | } 266 | }, 267 | "outputs": [], 268 | "source": [ 269 | "var summary = await kernel.InvokeAsync(thingsToMakeSummary, new () { { \"thingsToMake\", response }});\n", 270 | "\n", 271 | "Console.WriteLine(summary);" 272 | ] 273 | }, 274 | { 275 | "attachments": {}, 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "## Summary\n", 280 | "\n", 281 | "Semantic Kernel is another example of an AI orchestrator. Like other orchestrators, it can be used to simplify the process of creating complex AI applications." 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "metadata": {}, 287 | "source": [ 288 | "## Next Section\n", 289 | "\n", 290 | "📣 [AI Orchestration](../../03-orchestration/README.md)" 291 | ] 292 | } 293 | ], 294 | "metadata": { 295 | "kernelspec": { 296 | "display_name": ".NET (C#)", 297 | "language": "C#", 298 | "name": ".net-csharp" 299 | }, 300 | "language_info": { 301 | "name": "python" 302 | }, 303 | "orig_nbformat": 4, 304 | "polyglot_notebook": { 305 | "kernelInfo": { 306 | "defaultKernelName": "csharp", 307 | "items": [ 308 | { 309 | "aliases": [], 310 | "name": "csharp" 311 | } 312 | ] 313 | } 314 | } 315 | }, 316 | "nbformat": 4, 317 | "nbformat_minor": 2 318 | } 319 | -------------------------------------------------------------------------------- /labs/02-integrating-ai/README.md: -------------------------------------------------------------------------------- 1 | # 02 - Integrating AI into Applications 2 | 3 | In this folder you will find exercises for integrating large language models (LLMs) into applications. We introduce the OpenAI API's, the OpenAI Libraries and the LangChain and Semantic Kernel *orchestrators*. 4 | 5 | Below is a list of each of the labs in this section and what each one sets out to achieve. 6 | 7 | ## 00-PythonModules 8 | 9 | [Python Modules](00-PythonModules/pythonmodules.ipynb) 10 | 11 | Start with this lab initially. This will install some Python modules that are used in later labs. 12 | 13 | ## 01-AzureOpenAIAPI 14 | 15 | [Azure OpenAI API](01-AzureOpenAIAPI/azureopenaiapi.ipynb) 16 | 17 | In this lab, we'll walk through showing how to interact with an Azure OpenAI API service endpoint. This will not likely be the best option for most scenarios, but it highlights what a direct call to the underlying rest-based API looks like and will give you an appreciation of what's going on behind the scenes when you use the orchestrators in the other exercises. 18 | 19 | ## 02-OpenAIPackages 20 | 21 | [OpenAI Packages/Libraries](02-OpenAIPackages/openai.ipynb) 22 | 23 | In this lab we'll show how to interact with the Azure OpenAI API using the OpenAI Python library. This will provide some insight into the configuration and setup that is needed to use one of these higher level abstraction frameworks. 24 | 25 | ## 03-Langchain 26 | 27 | [Langchain](03-Langchain/langchain.ipynb) 28 | 29 | The third lab will demonstrate how to use Langchain with Azure OpenAI and how to set up a simple chain to perform basic AI orchestration. 30 | 31 | ## 04-SemanticKernel 32 | 33 | [Semantic Kernel](04-SemanticKernel/semantickernel.ipynb) 34 | 35 | The fourth lab is used to perform similar tasks to the third lab, but this time using Semantic Kernel instead of Langchain. 36 | -------------------------------------------------------------------------------- /labs/03-orchestration/01-Tokens/tokens.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# 01 - Tokens\n", 9 | "\n", 10 | "GPT models process text using *tokens*, which are common sequences of characters found in text. The models understand the statistical relationships between these tokens, and excel at producing the next token in a sequence of tokens.\n", 11 | "\n", 12 | "The conversion of a prompt into tokens happens automatically when you submit a prompt so you don't need to do anything yourself. However, OpenAI services like Azure OpenAI use the number of tokens processed as part of the pricing model, in the case of Azure OpenAI, charging per 1,000 tokens. So understanding how many tokens your prompts consume is an important part of planning and building any application that will use OpenAI.\n", 13 | "\n", 14 | "The prompt **\"Hello world, this is fun!\"** gets tokenized as follows:\n", 15 | "\n", 16 | "```\n", 17 | "Hello\n", 18 | " world,\n", 19 | " this\n", 20 | " is\n", 21 | " fun\n", 22 | "!\n", 23 | "\n", 24 | "(6 tokens)\n", 25 | "```\n", 26 | "Notice how spaces and punctuation are included as part of the tokens. A token doesn't always necessarily equate to a single word or phrase.\n", 27 | "\n", 28 | "Let's try the prompt **\"Example using words like indivisible and emojis\"**.\n", 29 | "\n", 30 | "```\n", 31 | "Example\n", 32 | " using\n", 33 | " words\n", 34 | " like\n", 35 | " ind\n", 36 | "iv\n", 37 | "isible\n", 38 | " and\n", 39 | " em\n", 40 | "oj\n", 41 | "is\n", 42 | "\n", 43 | "(11 tokens)\n", 44 | "```\n", 45 | "This time you can see that some of the words, **indivisible** and **emojis**, got broken up into smaller chunks.\n", 46 | "\n", 47 | "A helpful rule of thumb is that one token generally corresponds to ~4 characters of text for common English text. This translates to roughly ¾ of a word (so 100 tokens ~= 75 words).\n", 48 | "\n", 49 | ":thumbsup: You can experiment with this yourself using the *tokenizer* tool available on the OpenAI website at https://platform.openai.com/tokenizer" 50 | ] 51 | }, 52 | { 53 | "attachments": {}, 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "## Experimenting with tokens in code\n", 58 | "\n", 59 | "OpenAI provide the `tiktoken` package that you can use to experiment with tokenization in your code.\n", 60 | "\n", 61 | "`tiktoken` supports three encodings used by Azure OpenAI Service models:\n", 62 | "\n", 63 | "| Encoding name | Azure OpenAI Service models |\n", 64 | "| ------------- | -------------- |\n", 65 | "| cl100k_base | gpt-4, gpt-3.5-turbo, text-embedding-ada-002, text-embedding-3-small, text-embedding-3-large |\n", 66 | "| p50k_base | Code models, text-davinci-002, text-davinci-003 |\n", 67 | "| r50k_base (or gpt2) | Most GPT-3 models |\n", 68 | "\n", 69 | "You can use `tiktoken` as follows to tokenize a string and see what the output looks like." 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "import tiktoken\n", 79 | "\n", 80 | "encoding = tiktoken.get_encoding(\"cl100k_base\")\n", 81 | "encoding.encode(\"Hello world, this is fun!\")" 82 | ] 83 | }, 84 | { 85 | "attachments": {}, 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "Was the output of the above code what you were expecting?\n", 90 | "\n", 91 | "If you were expecting text broken up like the examples at the top of this page, you were probably wondering why you just got back a bunch of seemingly random numbers. This is because the AI models don't work on words. Instead, they use a method called *BPE* (Byte Pair Encoding) to convert the text into numeric tokens.\n", 92 | "\n", 93 | "One of the features of BPE is that it's reversible, so you can convert the tokens back into the original text." 94 | ] 95 | }, 96 | { 97 | "attachments": {}, 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "### Display the text instead of the tokens\n", 102 | "\n", 103 | "Check out the following code that displays the human readable text instead of the token values.\n", 104 | "\n", 105 | ":bulb: **NOTE:** See the following cookbook for some tips on working with `tiktoken`: [How to count tokens with tiktoken](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb)" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "# Write code to display the text from the tokens below\n", 115 | "[encoding.decode_single_token_bytes(token) for token in encoding.encode(\"Hello world, this is fun!\")]" 116 | ] 117 | }, 118 | { 119 | "attachments": {}, 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "You should see results similar to the following\n", 124 | "\n", 125 | "`[b'Hello', b' world', b',', b' this', b' is', b' fun', b'!']`" 126 | ] 127 | }, 128 | { 129 | "attachments": {}, 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "### Write a function to return the number of tokens\n", 134 | "\n", 135 | "Using what you've learned so far, let's create a function that returns the count of the number of tokens in a text string." 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "def get_num_tokens_from_string(string: str, encoding_name: str='cl100k_base') -> int:\n", 145 | " \"\"\"Returns the number of tokens in a text by a given encoding.\"\"\"\n", 146 | " encoding = tiktoken.get_encoding(encoding_name)\n", 147 | " return len(encoding.encode(string))\n", 148 | "\n", 149 | "get_num_tokens_from_string(\"Hello World, this is fun!\")" 150 | ] 151 | }, 152 | { 153 | "attachments": {}, 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "## Blowing up the prompt!\n", 158 | "\n", 159 | "Apart from cost, there's another reason that you'll want to be in control of the number of tokens you use. All AI models have a limit on the maximum number of tokens that a request can consume. The limit per request includes the number of tokens in the prompt **plus** the number of tokens in the response. Different models can have different token limits, but ultimately the overall size of your prompt and the response to that prompt have to be smaller than that limit.\n", 160 | "\n", 161 | "Let's show this in action by deliberately sending a prompt that's too large." 162 | ] 163 | }, 164 | { 165 | "attachments": {}, 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "We have a file, `movies.csv`, in this folder which contains a long list of movie data. Let's use `tiktoken` to see how big this file is in tokens." 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": {}, 176 | "outputs": [], 177 | "source": [ 178 | "import os\n", 179 | "import tiktoken\n", 180 | "\n", 181 | "# Open the file with information about movies\n", 182 | "movie_data = os.path.join(os.getcwd(), \"movies.csv\")\n", 183 | "content = open(movie_data, \"r\", encoding=\"utf-8\").read()\n", 184 | "\n", 185 | "# Use tiktoken to tokenize the content and get a count of tokens used.\n", 186 | "encoding = tiktoken.get_encoding(\"cl100k_base\")\n", 187 | "print (f\"Token count: {len(encoding.encode(content))}\")" 188 | ] 189 | }, 190 | { 191 | "attachments": {}, 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "You should have a result of something like 60,000 tokens. Which is a huge amount of tokens! But let's continue regardless.\n", 196 | "\n", 197 | "Let's setup the OpenAI API to use for this example." 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 4, 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [ 206 | "import os\n", 207 | "import openai\n", 208 | "from dotenv import load_dotenv\n", 209 | "from openai import AzureOpenAI\n", 210 | "\n", 211 | "load_dotenv()\n", 212 | "\n", 213 | "client = AzureOpenAI(\n", 214 | " azure_endpoint = os.getenv(\"AZURE_OPENAI_ENDPOINT\"),\n", 215 | " api_key = os.getenv(\"AZURE_OPENAI_API_KEY\"),\n", 216 | " api_version = os.getenv(\"OPENAI_API_VERSION\")\n", 217 | ")" 218 | ] 219 | }, 220 | { 221 | "attachments": {}, 222 | "cell_type": "markdown", 223 | "metadata": {}, 224 | "source": [ 225 | "As you've seen in the Prompts section of this workshop, we can provide additional data to an AI model by including that data in the prompt. So, let's construct a prompt that asks for the highest rated movie and then provides the list of movies for the AI to work with." 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": null, 231 | "metadata": {}, 232 | "outputs": [], 233 | "source": [ 234 | "query = \"What's the highest rated movie from the following list\\n\"\n", 235 | "query += \"CSV list of movies:\\n\"\n", 236 | "query += content\n", 237 | "\n", 238 | "print (f\"{query[0:3000]} ...[{len(query)} characters]\")" 239 | ] 240 | }, 241 | { 242 | "attachments": {}, 243 | "cell_type": "markdown", 244 | "metadata": {}, 245 | "source": [ 246 | "You can see we've output the first few lines of the prompt and we've printed the overall size (in characters) of the prompt. Now, let's see what happens if we submit that query to the AI.\n", 247 | "\n", 248 | "**NOTE:** This exercise is intended to fail, but it does depend on the model you're using. Some newer models have a higher token limit than others. If you're using a model with a high token limit, you might want to increase the size of the `movies.csv` file to make this fail, or you could try using a different model." 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": {}, 255 | "outputs": [], 256 | "source": [ 257 | "response = client.chat.completions.create(\n", 258 | " model = os.getenv(\"AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME\"),\n", 259 | " messages = [{\"role\" : \"assistant\", \"content\" : query}],\n", 260 | ")\n", 261 | "\n", 262 | "print (response)" 263 | ] 264 | }, 265 | { 266 | "attachments": {}, 267 | "cell_type": "markdown", 268 | "metadata": {}, 269 | "source": [ 270 | "This request will fail. At the end of the output, you should see an error message something like the following:\n", 271 | "\n", 272 | "```bash\n", 273 | "InvalidRequestError: This model's maximum context length is 16385 tokens. However, your messages resulted in 59265 tokens. Please reduce the length of the messages.\n", 274 | "```\n", 275 | "\n", 276 | "You can see quite clearly from the error message that we've exceeded the model's maximum length. Its maximum is **16385** tokens, we've sent a request requiring **59265** tokens." 277 | ] 278 | }, 279 | { 280 | "attachments": {}, 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "## Summary\n", 285 | "\n", 286 | "In this lab, we've learned about tokens. Your prompts will be broken up and sent to the AI as tokens and all AI models have a maximum token size which you must take care not to exceed. You will also be charged based on the number of tokens that your queries consume." 287 | ] 288 | }, 289 | { 290 | "attachments": {}, 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "## Up Next\n", 295 | "\n", 296 | "In the next lab, we'll look at one of the ways you can take control of the number of tokens your prompts consume by introducing the concept of **Embeddings**." 297 | ] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": {}, 302 | "source": [ 303 | "## Next Section\n", 304 | "\n", 305 | "📣 [Embeddings](../02-Embeddings/embeddings.ipynb)" 306 | ] 307 | } 308 | ], 309 | "metadata": { 310 | "kernelspec": { 311 | "display_name": "Python 3", 312 | "language": "python", 313 | "name": "python3" 314 | }, 315 | "language_info": { 316 | "codemirror_mode": { 317 | "name": "ipython", 318 | "version": 3 319 | }, 320 | "file_extension": ".py", 321 | "mimetype": "text/x-python", 322 | "name": "python", 323 | "nbconvert_exporter": "python", 324 | "pygments_lexer": "ipython3", 325 | "version": "3.10.14" 326 | }, 327 | "orig_nbformat": 4 328 | }, 329 | "nbformat": 4, 330 | "nbformat_minor": 2 331 | } 332 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Ambush.md: -------------------------------------------------------------------------------- 1 | # Ambush 2 | 3 | ## Overview 4 | 5 | When a small outpost is ambushed, a US Army squad must take the battle below ground on a high-stakes mission in a new type of warfare the likes of which they have never seen. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-02-24 10 | 11 | **Genres:** Action, War, Thriller 12 | 13 | **Popularity:** 386.559 14 | 15 | **Vote Average:** 5.8 16 | 17 | **Keywords:** 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Black Clover - Sword of the Wizard King.md: -------------------------------------------------------------------------------- 1 | # Black Clover: Sword of the Wizard King 2 | 3 | ## Overview 4 | 5 | As a lionhearted boy who can’t wield magic strives for the title of Wizard King, four banished Wizard Kings of yore return to crush the Clover Kingdom. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-06-16 10 | 11 | **Genres:** Animation, Fantasy, Action, Adventure 12 | 13 | **Popularity:** 336.12 14 | 15 | **Vote Average:** 8.4 16 | 17 | **Keywords:** magic, demon, wizard, shounen, anime 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Confidential Informant.md: -------------------------------------------------------------------------------- 1 | # Confidential Informant 2 | 3 | ## Overview 4 | 5 | During a crack epidemic two narcotics agents hunting for a cop killer. Hoping for leads, Moran and Thorton pay off a junkie informant. To provide for his wife and son, Moran involves the stool pigeon in a deadly scheme. This causes the partners to come under the scrutiny of a suspicious internal affairs agent. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-06-27 10 | 11 | **Genres:** Mystery, Thriller, Action 12 | 13 | **Popularity:** 408.062 14 | 15 | **Vote Average:** 6.4 16 | 17 | **Keywords:** gritty, crime 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Creed III.md: -------------------------------------------------------------------------------- 1 | # Creed III 2 | 3 | ## Overview 4 | 5 | After dominating the boxing world, Adonis Creed has been thriving in both his career and family life. When a childhood friend and former boxing prodigy, Damian Anderson, resurfaces after serving a long sentence in prison, he is eager to prove that he deserves his shot in the ring. The face-off between former friends is more than just a fight. To settle the score, Adonis must put his future on the line to battle Damian — a fighter who has nothing to lose. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-03-01 10 | 11 | **Genres:** Drama, Action 12 | 13 | **Popularity:** 433.823 14 | 15 | **Vote Average:** 7.2 16 | 17 | **Keywords:** philadelphia, pennsylvania, husband wife relationship, deaf, sports, sequel, orphan, former best friend, ex-con, childhood friends, juvenile detention center, boxing, prodigy 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Extraction 2.md: -------------------------------------------------------------------------------- 1 | # Extraction 2 2 | 3 | ## Overview 4 | 5 | Tasked with extracting a family who is at the mercy of a Georgian gangster, Tyler Rake infiltrates one of the world's deadliest prisons in order to save them. But when the extraction gets hot, and the gangster dies in the heat of battle, his equally ruthless brother tracks down Rake and his team to Vienna, in order to get revenge. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-06-09 10 | 11 | **Genres:** Action, Thriller 12 | 13 | **Popularity:** 1422.453 14 | 15 | **Vote Average:** 7.6 16 | 17 | **Keywords:** mercenary, sequel, rescue mission, long take, based on graphic novel 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Fast X.md: -------------------------------------------------------------------------------- 1 | # Fast X 2 | 3 | ## Overview 4 | 5 | Over many missions and against impossible odds, Dom Toretto and his family have outsmarted, out-nerved and outdriven every foe in their path. Now, they confront the most lethal opponent they've ever faced: A terrifying threat emerging from the shadows of the past who's fueled by blood revenge, and who is determined to shatter this family and destroy everything—and everyone—that Dom loves, forever. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-05-17 10 | 11 | **Genres:** Action, Crime, Thriller 12 | 13 | **Popularity:** 3283.417 14 | 15 | **Vote Average:** 7.3 16 | 17 | **Keywords:** sequel, revenge, racing, family, cars 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Guy Ritchie's The Covenant.md: -------------------------------------------------------------------------------- 1 | # Guy Ritchie's The Covenant 2 | 3 | ## Overview 4 | 5 | During the war in Afghanistan, a local interpreter risks his own life to carry an injured sergeant across miles of grueling terrain. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-04-19 10 | 11 | **Genres:** War, Action, Thriller 12 | 13 | **Popularity:** 544.105 14 | 15 | **Vote Average:** 7.8 16 | 17 | **Keywords:** ambush, interpreter, afghanistan, afghanistan war (2001- ), rescue, war 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Indiana Jones and the Dial of Destiny.md: -------------------------------------------------------------------------------- 1 | # Indiana Jones and the Dial of Destiny 2 | 3 | ## Overview 4 | 5 | Finding himself in a new era, approaching retirement, Indy wrestles with fitting into a world that seems to have outgrown him. But as the tentacles of an all-too-familiar evil return in the form of an old rival, Indy must don his hat and pick up his whip once more to make sure an ancient and powerful artifact doesn't fall into the wrong hands. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-06-28 10 | 11 | **Genres:** Adventure, Action, Fantasy 12 | 13 | **Popularity:** 740.388 14 | 15 | **Vote Average:** 6.5 16 | 17 | **Keywords:** feminism, sequel, flashback, prologue, knife fight, archaeologist, adventurer, 1960s, relic hunter 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/John Wick - Chapter 4.md: -------------------------------------------------------------------------------- 1 | # John Wick: Chapter 4 2 | 3 | ## Overview 4 | 5 | With the price on his head ever increasing, John Wick uncovers a path to defeating The High Table. But before he can earn his freedom, Wick must face off against a new enemy with powerful alliances across the globe and forces that turn old friends into foes. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-03-22 10 | 11 | **Genres:** Action, Thriller, Crime 12 | 13 | **Popularity:** 1437.077 14 | 15 | **Vote Average:** 7.9 16 | 17 | **Keywords:** new york city, martial arts, hitman, sequel, organized crime, osaka, japan, aftercreditsstinger, hunted, professional assassin, neo-noir, berlin 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Knights of the Zodiac.md: -------------------------------------------------------------------------------- 1 | # Knights of the Zodiac 2 | 3 | ## Overview 4 | 5 | When a headstrong street orphan, Seiya, in search of his abducted sister unwittingly taps into hidden powers, he discovers he might be the only person alive who can protect a reincarnated goddess, sent to watch over humanity. Can he let his past go and embrace his destiny to become a Knight of the Zodiac? 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-04-27 10 | 11 | **Genres:** Fantasy, Action, Adventure 12 | 13 | **Popularity:** 3355.755 14 | 15 | **Vote Average:** 6.5 16 | 17 | **Keywords:** superhero, based on manga, live action anime 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Los bastardos.md: -------------------------------------------------------------------------------- 1 | # Los bastardos 2 | 3 | ## Overview 4 | 5 | 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-03-30 10 | 11 | **Genres:** Action, Thriller, Crime, Drama 12 | 13 | **Popularity:** 431.075 14 | 15 | **Vote Average:** 6.1 16 | 17 | **Keywords:** 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Sisu.md: -------------------------------------------------------------------------------- 1 | # Sisu 2 | 3 | ## Overview 4 | 5 | Deep in the wilderness of Lapland, Aatami Korpi is searching for gold but after he stumbles upon Nazi patrol, a breathtaking and gold-hungry chase through the destroyed and mined Lapland wilderness begins. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-01-27 10 | 11 | **Genres:** Action, War 12 | 13 | **Popularity:** 520.792 14 | 15 | **Vote Average:** 7.5 16 | 17 | **Keywords:** world war ii, nordic mythology, lapland, finnish mythology 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/The Flash.md: -------------------------------------------------------------------------------- 1 | # The Flash 2 | 3 | ## Overview 4 | 5 | When his attempt to save his family inadvertently alters the future, Barry Allen becomes trapped in a reality in which General Zod has returned and there are no Super Heroes to turn to. In order to save the world that he is in and return to the future that he knows, Barry's only hope is to race for his life. But will making the ultimate sacrifice be enough to reset the universe? 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-06-13 10 | 11 | **Genres:** Science Fiction, Action, Adventure 12 | 13 | **Popularity:** 786.283 14 | 15 | **Vote Average:** 6.7 16 | 17 | **Keywords:** secret identity, hero, superhero, based on comic, superhuman, super power, aftercreditsstinger, butterfly effect, dc extended universe (dceu), superhuman abilities, loss of mother 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/The Wrath of Becky.md: -------------------------------------------------------------------------------- 1 | # The Wrath of Becky 2 | 3 | ## Overview 4 | 5 | Two years after she escaped a violent attack on her family, 16-year-old Becky attempts to rebuild her life in the care of an older woman -- a kindred spirit named Elena. However, when a violent group known as the Noble Men break into their home, attack them and take their beloved dog, Becky must return to her old ways to protect herself and her loved ones. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-05-26 10 | 11 | **Genres:** Action, Horror, Thriller 12 | 13 | **Popularity:** 759.396 14 | 15 | **Vote Average:** 6.8 16 | 17 | **Keywords:** machete, sequel, revenge, murder, gore, diner, dog, blood, terrorists 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/Transformers - Rise of the Beasts.md: -------------------------------------------------------------------------------- 1 | # Transformers: Rise of the Beasts 2 | 3 | ## Overview 4 | 5 | When a new threat capable of destroying the entire planet emerges, Optimus Prime and the Autobots must team up with a powerful faction known as the Maximals. With the fate of humanity hanging in the balance, humans Noah and Elena will do whatever it takes to help the Transformers as they engage in the ultimate battle to save Earth. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-06-06 10 | 11 | **Genres:** Action, Adventure, Science Fiction 12 | 13 | **Popularity:** 1301.343 14 | 15 | **Vote Average:** 7.3 16 | 17 | **Keywords:** peru, alien, end of the world, based on cartoon, based on toy, robot, duringcreditsstinger, 1990s, brother brother relationship 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/data/movies/iNumber Number - Jozi Gold.md: -------------------------------------------------------------------------------- 1 | # iNumber Number: Jozi Gold 2 | 3 | ## Overview 4 | 5 | When an undercover cop is tasked with investigating a historic gold heist in Johannesburg, he’s forced to choose between his conscience and the law. 6 | 7 | ## Details 8 | 9 | **Release Date:** 2023-06-23 10 | 11 | **Genres:** Crime, Action, Thriller 12 | 13 | **Popularity:** 719.085 14 | 15 | **Vote Average:** 6.3 16 | 17 | **Keywords:** 18 | 19 | -------------------------------------------------------------------------------- /labs/03-orchestration/02-Embeddings/overview.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /labs/03-orchestration/03-VectorStore/README.md: -------------------------------------------------------------------------------- 1 | # 03 - Vector Stores 2 | 3 | In this folder you will find exercises showing how to work with different vector stores. We introduce concepts like Qdrant, Azure CosmosDB for MongoDB (vCore), and Azure AI Search which allow AI Orchestration to overcome the size limitation of prompts. 4 | 5 | Below is a list of each of the labs in this section and what each one sets out to achieve. 6 | 7 | ## Qdrant 8 | 9 | [Qdrant](qdrant.ipynb) 10 | 11 | This lab uses a local instance of Qdrant to demonstrate how to work with a vector store. 12 | 13 | ## Azure AI Search 14 | 15 | [Azure AI Search](aisearch.ipynb) 16 | 17 | This lab will walk you through how to use Azure AI Search as a vector store and search ranking capabilities. 18 | 19 | ## Azure CosmosDB for MongoDB (vCore) 20 | 21 | [Azure CosmosDB for MongoDB (vCore)](mongo.ipynb) 22 | 23 | In this lab, you will see how you can use Azure CosmosDB as a vector store and as a semantic cache. 24 | -------------------------------------------------------------------------------- /labs/03-orchestration/03-VectorStore/qdrant.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# Langchain with Qdrant\n", 9 | "\n", 10 | "In this lab, we will do a deeper dive around the Qdrant vector store and different ways to interact with it. We'll look at how we can use different search methods to vary the results and how we can use the results with a large language model.\n", 11 | "\n", 12 | "We'll start as usual by loading the values from the `.env` file in the root of this repository." 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import os\n", 22 | "from dotenv import load_dotenv\n", 23 | "\n", 24 | "# Load environment variables\n", 25 | "if load_dotenv():\n", 26 | " print(\"Found Azure OpenAI Endpoint: \" + os.getenv(\"AZURE_OPENAI_ENDPOINT\"))\n", 27 | "else: \n", 28 | " print(\"No file .env found\")" 29 | ] 30 | }, 31 | { 32 | "attachments": {}, 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "In this lab, we'll use the data from the movies.csv file in this folder, which contains details of around 500 different movies. We'll use a Langchain document loader to load all of the movie data into memory." 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "from langchain.document_loaders.csv_loader import CSVLoader\n", 46 | "\n", 47 | "loader = CSVLoader(file_path='./movies.csv', source_column='original_title', encoding='utf-8', csv_args={'delimiter':',', 'fieldnames': ['id', 'original_language', 'original_title', 'popularity', 'release_date', 'vote_average', 'vote_count', 'genre', 'overview', 'revenue', 'runtime', 'tagline']})\n", 48 | "data = loader.load()\n", 49 | "# data = data[1:51] # You can uncomment this line to load a smaller subset of the data if you experience issues with token limits or timeouts later in this lab.\n", 50 | "print('Loaded %s movies' % len(data))" 51 | ] 52 | }, 53 | { 54 | "attachments": {}, 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "Next, we'll create Azure OpenAI embedding and chat completion deployments. The embeddings instance will be used to create the vector representation of the movies in the loaded CSV file and the chat completion instance will be used to ask questions." 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 3, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "from langchain_openai import AzureChatOpenAI\n", 68 | "from langchain_openai import AzureOpenAIEmbeddings\n", 69 | "\n", 70 | "# Create an Embeddings Instance of Azure OpenAI\n", 71 | "embeddings = AzureOpenAIEmbeddings(\n", 72 | " azure_deployment = os.getenv(\"AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME\"),\n", 73 | " openai_api_version = os.getenv(\"OPENAI_EMBEDDING_API_VERSION\"),\n", 74 | " model= os.getenv(\"AZURE_OPENAI_EMBEDDING_MODEL\")\n", 75 | ")\n", 76 | "\n", 77 | "\n", 78 | "# Create a Chat Completion Instance of Azure OpenAI\n", 79 | "llm = AzureChatOpenAI(\n", 80 | " azure_deployment = os.getenv(\"AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME\")\n", 81 | ")" 82 | ] 83 | }, 84 | { 85 | "attachments": {}, 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "## Start Qdrant Server Locally\n", 90 | "\n", 91 | "**NOTE**: Please read the following carefully.\n", 92 | "\n", 93 | "In previous labs, we ran the Qdrant vector store in memory, so it's contents were lost once Qdrant stopped running. In this lab, we'll run Qdrant to persist data to disk.\n", 94 | "\n", 95 | "If you are running this lab in Codespaces or a VS Code local devcontainer, then Qdrant is already running and you can continue to the next section.\n", 96 | "\n", 97 | "Otherwise, we need to start a local instance of Qdrant. The easiest way to do this is with Docker. If you don't have Docker on your device, consider running this workshop in a Codespace or a VS Code devcontainer." 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "# Start Qdrant Server\n", 107 | "# !docker run -d --name qdrant -p 6333:6333 -p 6334:6334 -v \"$(pwd)\\qdrantstorage\" qdrant/qdrant\n", 108 | "# for mac and linux use the following command\n", 109 | "!docker run -d --name qdrant -p 6333:6333 -p 6334:6334 -v \"$(pwd)/qdrantstorage:/qdrant/storage\" qdrant/qdrant\n", 110 | "\n", 111 | "# If you want to stop and cleanup the Qdrant server, uncomment and run the following commands:\n", 112 | "# !docker stop qdrant\n", 113 | "# !docker rm qdrant\n", 114 | "# !rm -rf labs/03-orchestration/03-Qdrant/qdrantstorage" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "## Load Movies into Qdrant\n", 122 | "\n", 123 | "Now that we have the Qdrant server running and persisting data locally, let's load the movies into the vector store.\n", 124 | "\n", 125 | "We'll configure Langchain to use Qdrant as the vector store, embed the loaded documents and store the embeddings in the vector store.\n", 126 | "\n", 127 | "**NOTE**: Depending on the number of movies loaded and rate limiting, this might take a while." 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 5, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "from langchain.vectorstores import Qdrant\n", 137 | "\n", 138 | "url = \"http://localhost:6333\"\n", 139 | "qdrant = Qdrant.from_documents(\n", 140 | " data,\n", 141 | " embeddings,\n", 142 | " url=url,\n", 143 | " prefer_grpc=False,\n", 144 | " collection_name=\"my_movies\",\n", 145 | ")" 146 | ] 147 | }, 148 | { 149 | "attachments": {}, 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "## Vector Store Searching using Qdrant\n", 154 | "\n", 155 | "Now we are going to demonstrate how the results of searching a vector store can be affected by using different search methods.\n", 156 | "\n", 157 | "The most common method used is a similarity search. This search method aims to return the most similar documents to the query text, using a metric such as cosine similarity." 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "vectorstore = qdrant\n", 167 | "\n", 168 | "query = \"Can you suggest similar movies to The Matrix?\"\n", 169 | "\n", 170 | "query_results = qdrant.similarity_search(query)\n", 171 | "\n", 172 | "for doc in query_results:\n", 173 | " print(doc.metadata['source'])" 174 | ] 175 | }, 176 | { 177 | "attachments": {}, 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "Another commonly used method is MMR - Maximal Marginal Relevance - which also executes a similarity search, but the algorithm is geared towards returning a more diverse set of results." 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "retriever = vectorstore.as_retriever(search_type=\"mmr\")\n", 191 | "\n", 192 | "query = \"Can you suggest similar movies to The Matrix?\"\n", 193 | "\n", 194 | "for doc in retriever.invoke(query):\n", 195 | " print(doc.metadata['source'])\n" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "In both of the above examples, a list of movies will have been returned. Both should be relevant to the query, but the MMR search should return a more diverse set of results. To explain that a little more, the first set of results might, for example, all be from the same genre, whereas the MMR search would return results from a wider range of genres." 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "## Vector Store Searching using Langchain Retriever\n", 210 | "\n", 211 | "In this part we will use Langchain to search the Qdrant vector store and parse the results via a large language model. This is different from the previous section where we were simply returning the results of a Qdrant search directly.\n", 212 | "\n", 213 | "As you will have seen in the **02-Embeddings** lab, we can use the `VectorstoreIndexCreator` to quickly create a vector store index and a retriever.\n" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": 14, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "from langchain.indexes import VectorstoreIndexCreator\n", 223 | "from langchain.chains import RetrievalQA\n", 224 | "\n", 225 | "index_creator = VectorstoreIndexCreator(embedding=embeddings, vectorstore_cls=Qdrant)\n", 226 | "docsearch = index_creator.from_loaders([loader])" 227 | ] 228 | }, 229 | { 230 | "attachments": {}, 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "Now we can use a Langchain QA (Question Answering) retrieval chain to ask questions about the movies." 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "chain = RetrievalQA.from_chain_type(llm=llm, chain_type=\"stuff\", retriever=docsearch.vectorstore.as_retriever(), input_key=\"question\", return_source_documents=True)\n", 244 | "\n", 245 | "query = \"Do you have a column called popularity?\"\n", 246 | "response = chain.invoke({\"question\": query})\n", 247 | "print(response['result'])" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": {}, 253 | "source": [ 254 | "You can see that we've retrieved a set of search results from the vector store, and then used the LLM to parse the results and provide a natural language answer to the question.\n", 255 | "\n", 256 | "We can also see the documents that were retrieved from the search." 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [ 265 | "print(response['source_documents'])" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "We could ask a different question, such as \"What is the name of the most popular movie?\"." 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": null, 278 | "metadata": {}, 279 | "outputs": [], 280 | "source": [ 281 | "query = \"If the popularity score is defined as a higher value being a more popular movie, what is the name of the most popular movie in the data provided?\"\n", 282 | "response = chain.invoke({\"question\": query})\n", 283 | "print(response['result'])" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "Again, you'll see that instead of just returning the search result, we've got a natural language answer to the question.\n", 291 | "\n", 292 | "And we can see the set of documents that were retrieved, which will be different to those returned for the previous question because we asked a different question." 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": null, 298 | "metadata": {}, 299 | "outputs": [], 300 | "source": [ 301 | "print(response['source_documents'])" 302 | ] 303 | }, 304 | { 305 | "cell_type": "markdown", 306 | "metadata": {}, 307 | "source": [ 308 | "## Next Section\n", 309 | "\n", 310 | "📣 [Implement Retrieval Augmented Generation with Azure CosmosDB as vector store and as semantic cache](./mongo.ipynb)\n", 311 | "\n", 312 | "📣 [Implement Retrieval Augmented Generation with Azure AI Search as vector store with semantic ranking](./aisearch.ipynb)" 313 | ] 314 | } 315 | ], 316 | "metadata": { 317 | "kernelspec": { 318 | "display_name": "Python 3", 319 | "language": "python", 320 | "name": "python3" 321 | }, 322 | "language_info": { 323 | "codemirror_mode": { 324 | "name": "ipython", 325 | "version": 3 326 | }, 327 | "file_extension": ".py", 328 | "mimetype": "text/x-python", 329 | "name": "python", 330 | "nbconvert_exporter": "python", 331 | "pygments_lexer": "ipython3", 332 | "version": "3.10.14" 333 | }, 334 | "orig_nbformat": 4 335 | }, 336 | "nbformat": 4, 337 | "nbformat_minor": 2 338 | } 339 | -------------------------------------------------------------------------------- /labs/03-orchestration/README.md: -------------------------------------------------------------------------------- 1 | # 03 - AI Orchestration 2 | 3 | In this folder you will find exercises to help increase your understanding of the building blocks behind AI Orchestration of large language models (LLMs) into applications. We introduce concepts like Tokens, Embeddings and Vectors which allow AI Orchestration to overcome the size limitation of Prompts. 4 | 5 | Below is a list of each of the labs in this section and what each one sets out to achieve. 6 | 7 | ## 01-Tokens 8 | 9 | [Tokens](01-Tokens/tokens.ipynb) 10 | 11 | In this lab, we'll walk through the basics of Tokens, how they relate to LLMs, and why counting them is important. 12 | 13 | ## 02-Embeddings 14 | 15 | [Embeddings](02-Embeddings/embeddings.ipynb) 16 | 17 | In this lab, we'll walk through the basics of creating Vectors using Embeddings and why they are important when it comes to AI Orchestration. 18 | 19 | ## 03-VectorStore 20 | 21 | [Qdrant](https://github.com/Azure/intro-to-intelligent-apps/blob/main/labs/03-orchestration/03-VectorStore/qdrant.ipynb ) 22 | 23 | In this lab, we'll walk through using an open source vector store called Qdrant to understand the basics. 24 | 25 | [Mongo](https://github.com/Azure/intro-to-intelligent-apps/blob/main/labs/03-orchestration/03-VectorStore/mongo.ipynb) 26 | 27 | In this lab, we'll use Azure CosmosDB for MongoDB (vCore) as a vector store and as a semantic cache. 28 | 29 | [AI Search](https://github.com/Azure/intro-to-intelligent-apps/blob/main/labs/03-orchestration/03-VectorStore/aisearch.ipynb) 30 | 31 | In this lab, we'll walk through Azure AI Search vector store and search ranking capabilities. 32 | 33 | ## 04-Functions 34 | 35 | [Function calling](04-Functions/function-calling.ipynb) 36 | 37 | In this lab, we'll walk through integrating external APIs into your chain. 38 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-lc-python-api/README.md: -------------------------------------------------------------------------------- 1 | # 04 - Deploy ACS Langchain Python API 2 | 3 | In this folder you will find a sample AI App that is built using Python, Langchain and Azure AI Search. 4 | 5 | The entire solution is in this folder, but we also have all the step by step instructions so you can see how it was built. 6 | 7 | ## Complete Solution 8 | 9 | To test the version of the app in this folder, you should just be able to run the command below. It should read the environment variable values from the `.env` file located in the root of this repository, so if you've already configured that there shouldn't be anything else to do. Otherwise, you'll need to fill in the `.env` file with the necessary values. 10 | 11 | You can run the app using the command below. 12 | 13 | ```bash 14 | uvicorn main:app --reload --host=0.0.0.0 --port=5291 15 | ``` 16 | 17 | ## Step by Step Instructions 18 | 19 | ### Create Python Project and Solution 20 | 21 | ```bash 22 | mkdir aais-lc-python 23 | cd aais-lc-python 24 | ``` 25 | 26 | ### Add Dependencies 27 | 28 | ```bash 29 | echo "azure-core==1.30.1 30 | azure-identity==1.16.0 31 | azure-search-documents==11.4.0 32 | fastapi==0.110.1 33 | uvicorn==0.25.0 34 | openai==1.27.0 35 | langchain==0.1.19 36 | langchain-openai==0.1.3 37 | tiktoken==0.6.0 38 | python-dotenv==1.0.1 39 | chainlit==1.0.506" > requirements.txt 40 | ``` 41 | 42 | ```bash 43 | pip install -r requirements.txt 44 | ``` 45 | 46 | ### Create main.py 47 | 48 | ```bash 49 | echo "" > main.py 50 | ``` 51 | 52 | The first thing we need to do is to add some import and from statements to the `main.py` file. 53 | 54 | ```python 55 | import os 56 | import logging 57 | from langchain_openai import AzureOpenAIEmbeddings, AzureChatOpenAI 58 | from dotenv import load_dotenv 59 | from fastapi import FastAPI 60 | from pydantic import BaseModel 61 | from fastapi.responses import JSONResponse, HTMLResponse 62 | from langchain.prompts import PromptTemplate 63 | from azure.search.documents import SearchClient 64 | from azure.search.documents.models import VectorizedQuery 65 | from azure.core.credentials import AzureKeyCredential 66 | from langchain.chains import LLMChain 67 | from langchain_core.output_parsers import StrOutputParser 68 | ``` 69 | 70 | We configure some logging so we can see what's happening behind the scenes 71 | 72 | ```python 73 | logging.basicConfig(format='%(levelname)-10s%(message)s', level=logging.INFO) 74 | ``` 75 | 76 | Next we read in the environment variables. 77 | 78 | ```python 79 | if load_dotenv(): 80 | logging.info("Azure OpenAI Endpoint: " + os.getenv("AZURE_OPENAI_ENDPOINT")) 81 | logging.info("Azure AI Search: " + os.getenv("AZURE_AI_SEARCH_SERVICE_NAME")) 82 | else: 83 | print("No file .env found") 84 | ``` 85 | 86 | Now we add the Embeddings and ChatCompletion instances of Azure OpenAI that we will be using. 87 | 88 | ```python 89 | # Create an Embeddings Instance of Azure OpenAI 90 | azure_openai_embeddings = AzureOpenAIEmbeddings( 91 | azure_deployment = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME"), 92 | openai_api_version = os.getenv("OPENAI_EMBEDDING_API_VERSION"), 93 | model= os.getenv("AZURE_OPENAI_EMBEDDING_MODEL") 94 | ) 95 | 96 | azure_openai = AzureChatOpenAI( 97 | azure_deployment = os.getenv("AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME"), 98 | temperature=0.1, 99 | max_tokens=500 100 | ) 101 | logging.info('Completed creation of embedding and completion instances.') 102 | ``` 103 | 104 | Next we start the app. 105 | 106 | ```python 107 | # Start the App 108 | app = FastAPI() 109 | ``` 110 | 111 | Next we define the class and routing methods that will be used. 112 | 113 | ```python 114 | class CompletionRequest(BaseModel): 115 | Question: str 116 | 117 | class CompletionResponse(BaseModel): 118 | completion: str 119 | ``` 120 | 121 | This route is for the root of the app, it will return a link to the Swagger UI. 122 | 123 | ```python 124 | @app.get("/", response_class=HTMLResponse) 125 | def read_root(): 126 | return "Swagger Endpoint: docs" 127 | ``` 128 | 129 | This route is for the completion endpoint, it will take a question and return a completion. The question is first passed to Azure AI Search to get the top 5 results, then the question and results are passed to Azure OpenAI to get a completion. 130 | 131 | ```python 132 | @app.post("/completion/", response_class=JSONResponse) 133 | def execute_completion(request: CompletionRequest): 134 | # Ask the question 135 | # The question is being passed in via the message body. 136 | # request: CompletionRequest 137 | 138 | # Create a prompt template with variables, note the curly braces 139 | prompt = PromptTemplate( 140 | input_variables=["original_question","search_results"], 141 | template=""" 142 | Question: {original_question} 143 | 144 | Do not use any other data. 145 | Only use the movie data below when responding. 146 | {search_results} 147 | """, 148 | ) 149 | 150 | # Search Vector Store 151 | search_client = SearchClient( 152 | os.getenv("AZURE_AI_SEARCH_ENDPOINT"), 153 | os.getenv("AZURE_AI_SEARCH_INDEX_NAME"), 154 | AzureKeyCredential(os.getenv("AZURE_AI_SEARCH_API_KEY")) 155 | ) 156 | 157 | vector = VectorizedQuery(vector=azure_openai_embeddings.embed_query(request.Question), k_nearest_neighbors=5, fields="vector") 158 | 159 | results = list(search_client.search( 160 | search_text=request.Question, 161 | query_type="semantic", 162 | semantic_configuration_name="movies-semantic-config", 163 | include_total_count=True, 164 | vector_queries=[vector], 165 | select=["title","genre","overview","tagline","release_date","popularity","vote_average","vote_count","runtime","revenue","original_language"], 166 | top=5 167 | )) 168 | 169 | output_parser = StrOutputParser() 170 | chain = prompt | azure_openai | output_parser 171 | response = chain.invoke({"original_question": request.Question, "search_results": results}) 172 | logging.info("Response from LLM: " + response) 173 | return CompletionResponse(completion = response) 174 | ``` 175 | 176 | ### Test the App 177 | 178 | Now that we have all the code in place let's run it. 179 | 180 | Remember to create a `.env` file in the same folder as the `main.py` file and add the environment variables and values we used from the `.env` file in the Jupyter notebooks. 181 | 182 | ```bash 183 | uvicorn main:app --reload --host=0.0.0.0 --port=5291 184 | ``` 185 | 186 | Once the app is started, open a browser and navigate to http://127.0.0.1:5291/docs 187 | >**Note:** the port number may be different to `5291`, so double check the output from the `uvicorn main:app` command. 188 | 189 | Click on the "POST /completion" endpoint, click on "Try it out", enter a Prompt, "List the movies about ships on the water.", then click on "Execute". 190 | 191 | ### Build and Test Docker Image 192 | 193 | Let's now package the solution into a Docker Image so it can be deployed to a container service like Azure Kubernetes Serivce (AKS) or Azure Container Apps (ACA). 194 | 195 | First you'll need to create a Dockerfile 196 | 197 | ```bash 198 | echo "" > Dockerfile 199 | ``` 200 | 201 | Next, paste the following into the Dockerfile 202 | 203 | ```Dockerfile 204 | FROM python:3.11-slim AS builder 205 | WORKDIR /app 206 | ENV PYTHONDONTWRITEBYTECODE 1 207 | ENV PYTHONUNBUFFERED 1 208 | RUN apt-get update && apt-get install -y \ 209 | build-essential \ 210 | curl \ 211 | software-properties-common \ 212 | git \ 213 | && rm -rf /var/lib/apt/lists/* 214 | # Create a virtualenv to keep dependencies together 215 | RUN python -m venv /opt/venv 216 | ENV PATH="/opt/venv/bin:$PATH" 217 | COPY requirements.txt . 218 | RUN pip install --no-cache-dir -r requirements.txt 219 | 220 | # Stage 2 - Copy only necessary files to the runner stage 221 | FROM python:3.11-slim 222 | WORKDIR /app 223 | COPY --from=builder /opt/venv /opt/venv 224 | ENV PATH="/opt/venv/bin:$PATH" 225 | COPY main.py . 226 | EXPOSE 5291 227 | CMD ["uvicorn", "main:app", "--host=0.0.0.0", "--port=5291"] 228 | ``` 229 | 230 | Next, we build the Docker Image. 231 | 232 | ```bash 233 | docker build -t aais-lc-python:v1 . 234 | ``` 235 | 236 | Finally, we can test the image. We pass in the environment variable values as we don't want to have sensitive information embedded directly into the image. 237 | 238 | ```bash 239 | docker run -it --rm \ 240 | --name aaislcpython \ 241 | -p 5291:5291 \ 242 | -e AZURE_OPENAI_API_KEY="" \ 243 | -e AZURE_OPENAI_ENDPOINT="" \ 244 | -e OPENAI_API_VERSION="2024-03-01-preview" \ 245 | -e AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME="" \ 246 | -e AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME="" \ 247 | -e AZURE_OPENAI_EMBEDDING_MODEL="" \ 248 | -e AZURE_AI_SEARCH_SERVICE_NAME="" \ 249 | -e AZURE_AI_SEARCH_ENDPOINT="Swagger Endpoint: docs" 62 | 63 | @app.post("/completion/", response_class=JSONResponse) 64 | def execute_completion(request: CompletionRequest): 65 | # Ask the question 66 | # The question is being passed in via the message body. 67 | # request: CompletionRequest 68 | 69 | # Create a prompt template with variables, note the curly braces 70 | 71 | system_message_template = """ 72 | You are a support agent helping provide answers to software developers questions. 73 | Use the following context to answer questions, do not use any other data. 74 | If you need more information, ask the user for more details. 75 | If you don't know the answer, let the user know. 76 | 77 | {context} 78 | """ 79 | 80 | human_message_template = "{question}" 81 | 82 | prompt = ChatPromptTemplate.from_messages( 83 | [ 84 | ( 85 | "system", system_message_template, 86 | 87 | ), 88 | MessagesPlaceholder(variable_name="question"), 89 | ] 90 | ) 91 | 92 | # Search Vector Store 93 | search_client = SearchClient( 94 | azure_ai_search_endpoint, 95 | azure_ai_search_index_name, 96 | AzureKeyCredential(azure_ai_search_api_key) 97 | ) 98 | 99 | vector = VectorizedQuery(vector=azure_openai_embeddings.embed_query(request.Question), k_nearest_neighbors=5, fields="vector") 100 | 101 | results = list(search_client.search( 102 | search_text=request.Question, 103 | query_type="semantic", 104 | semantic_configuration_name="movies-semantic-config", 105 | include_total_count=True, 106 | vector_queries=[vector], 107 | select=["title","genre","overview","tagline","release_date","popularity","vote_average","vote_count","runtime","revenue","original_language"], 108 | top=5 109 | )) 110 | 111 | 112 | 113 | output_parser = StrOutputParser() 114 | chain = prompt | azure_openai | output_parser 115 | response = chain.invoke({"context": results, "question": request.Question, }) 116 | logging.info("Response from LLM: " + response) 117 | return CompletionResponse(completion = response) 118 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-lc-python-api/aais-lc-python/requirements.txt: -------------------------------------------------------------------------------- 1 | azure-core==1.30.1 2 | azure-identity==1.16.0 3 | azure-search-documents==11.4.0 4 | fastapi==0.110.1 5 | uvicorn==0.25.0 6 | openai==1.27.0 7 | langchain==0.1.19 8 | langchain-openai==0.1.3 9 | tiktoken==0.6.0 10 | python-dotenv==1.0.1 11 | chainlit==1.0.506 -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base 2 | WORKDIR /app 3 | EXPOSE 5291 4 | ENV ASPNETCORE_URLS=http://+:5291 5 | 6 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build 7 | ARG configuration=Release 8 | WORKDIR /src 9 | COPY ["aais-sk-csharp.csproj", "."] 10 | RUN dotnet restore "aais-sk-csharp.csproj" 11 | COPY ["Program.cs", "."] 12 | COPY ["Plugins/", "./Plugins/"] 13 | RUN dotnet build "aais-sk-csharp.csproj" -c $configuration -o /app/build 14 | 15 | FROM build AS publish 16 | ARG configuration=Release 17 | RUN dotnet publish "aais-sk-csharp.csproj" -c $configuration -o /app/publish 18 | 19 | FROM base AS final 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "aais-sk-csharp.dll"] -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Plugins/CustomPlugin/GetMovieInfo/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": 1, 3 | "type": "completion", 4 | "description": "Answers questions about provided movie data.", 5 | "completion": { 6 | "max_tokens": 500, 7 | "temperature": 0.1, 8 | "top_p": 0.5, 9 | "presence_penalty": 0.0, 10 | "frequency_penalty": 0.0 11 | }, 12 | "input": { 13 | "parameters": [ 14 | { 15 | "name": "original_question", 16 | "description": "The user's request.", 17 | "defaultValue": "" 18 | }, 19 | { 20 | "name": "search_results", 21 | "description": "Vector Search results from Azure AI Search.", 22 | "defaultValue": "" 23 | } 24 | ] 25 | } 26 | } -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Plugins/CustomPlugin/GetMovieInfo/skprompt.txt: -------------------------------------------------------------------------------- 1 | Question: {{$original_question}} 2 | 3 | Do not use any other data. 4 | Only use the movie data below when responding. 5 | {{$search_results}} 6 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Microsoft.SemanticKernel; 3 | using Microsoft.SemanticKernel.Connectors.OpenAI; 4 | using Azure; 5 | using Azure.Search.Documents; 6 | using Azure.Search.Documents.Models; 7 | using Azure.Search.Documents.Indexes; 8 | using Azure.AI.OpenAI; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.SemanticKernel.ChatCompletion; 11 | 12 | var builder = WebApplication.CreateBuilder(args); 13 | 14 | // Load values into variables 15 | var config = builder.Configuration; 16 | var azure_openai_api_key = config["AZURE_OPENAI_API_KEY"] ?? String.Empty; 17 | var azure_openai_endpoint = config["AZURE_OPENAI_ENDPOINT"] ?? String.Empty; 18 | var openai_api_version = config["OPENAI_API_VERSION"] ?? String.Empty; 19 | var azure_openai_completion_deployment_name = config["AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME"] ?? String.Empty; 20 | var azure_openai_embedding_deployment_name = config["AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME"] ?? String.Empty; 21 | var azure_ai_search_name = config["AZURE_AI_SEARCH_SERVICE_NAME"] ?? String.Empty; 22 | var azure_ai_search_endpoint = config["AZURE_AI_SEARCH_ENDPOINT"] ?? String.Empty; 23 | var azure_ai_search_index_name = config["AZURE_AI_SEARCH_INDEX_NAME"] ?? String.Empty; 24 | var azure_ai_search_api_key = config["AZURE_AI_SEARCH_API_KEY"] ?? String.Empty; 25 | Console.WriteLine("Configuration loaded."); 26 | 27 | // Add Semantic Kernel service to the container. 28 | // Add in configuration options and required services. 29 | builder.Services.AddSingleton(sp => sp.GetRequiredService>()); // some services require an un-templated ILogger 30 | 31 | builder.Services.AddSingleton(sp => 32 | { 33 | return new AzureOpenAIChatCompletionService(azure_openai_completion_deployment_name, azure_openai_endpoint, azure_openai_api_key); 34 | }); 35 | 36 | builder.Services.AddAzureOpenAIChatCompletion(azure_openai_completion_deployment_name, azure_openai_endpoint, azure_openai_api_key); 37 | builder.Services.AddKernel(); 38 | builder.Services.AddEndpointsApiExplorer(); 39 | builder.Services.AddSwaggerGen(); 40 | 41 | var app = builder.Build(); 42 | 43 | // Configure the HTTP request pipeline. 44 | app.UseSwagger(); 45 | app.UseSwaggerUI(); 46 | app.UseHttpsRedirection(); 47 | 48 | // Configure Routing 49 | app.MapPost("/completion", async ([FromServices] Kernel kernel, [FromBody] CompletionRequest request) => 50 | { 51 | try 52 | { 53 | // Read values from .env file 54 | // These are loaded during startup, see above for details. 55 | 56 | // Setup Semantic Kernel 57 | // This has already been setup as part of the ASP.NET Core dependency injection setup 58 | // and is passed into this function as a parameter. 59 | // [FromServices] Kernel kernel 60 | 61 | // Ask the question 62 | // The question is being passed in via the message body. 63 | // [FromBody] CompletionRequest request 64 | 65 | // The PromptTemplate which was setup as an inline function in the earlier labs has been moved 66 | // into the Plugins directory so it is easier to manage and configure. This gives the ability to mount updated 67 | // prompt files into a container without having to rewrite the source code. 68 | var pluginsDirectory = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Plugins"); 69 | var customPlugin = kernel.ImportPluginFromPromptDirectory(Path.Combine(pluginsDirectory, "CustomPlugin")); 70 | Console.WriteLine("Plugin GetMovieInfo loaded."); 71 | 72 | // Get Embedding for the original question 73 | OpenAIClient azureOpenAIClient = new OpenAIClient(new Uri(azure_openai_endpoint),new AzureKeyCredential(azure_openai_api_key)); 74 | float[] queryEmbedding = azureOpenAIClient.GetEmbeddings(new EmbeddingsOptions(azure_openai_embedding_deployment_name, new List() { request.Question })).Value.Data[0].Embedding.ToArray(); 75 | 76 | Console.WriteLine("Embedding of original question has been completed."); 77 | 78 | // Search Vector Store 79 | 80 | string semanticSearchConfigName = "movies-semantic-config"; 81 | 82 | SearchOptions searchOptions = new SearchOptions 83 | { 84 | QueryType = SearchQueryType.Semantic, 85 | SemanticConfigurationName = semanticSearchConfigName, 86 | VectorQueries = { new RawVectorQuery() { Vector = queryEmbedding, KNearestNeighborsCount = 5, Fields = { "vector" } } }, 87 | Size = 5, 88 | Select = { "title", "genre" }, 89 | }; 90 | 91 | AzureKeyCredential indexCredential = new AzureKeyCredential(azure_ai_search_api_key); 92 | SearchIndexClient indexClient = new SearchIndexClient(new Uri(azure_ai_search_endpoint), indexCredential); 93 | SearchClient searchClient = indexClient.GetSearchClient(azure_ai_search_index_name); 94 | 95 | //Perform the search 96 | SearchResults response = searchClient.Search(request.Question, searchOptions); 97 | Pageable> results = response.GetResults(); 98 | 99 | // Create string from the results 100 | 101 | StringBuilder stringBuilderResults = new StringBuilder(); 102 | foreach (SearchResult result in results) 103 | { 104 | stringBuilderResults.AppendLine($"{result.Document["title"]}"); 105 | }; 106 | 107 | Console.WriteLine(stringBuilderResults.ToString()); 108 | 109 | Console.WriteLine("Searching of Vector Store has been completed."); 110 | 111 | // Build the Prompt and Execute against the Azure OpenAI to get the completion 112 | 113 | var completion = await kernel.InvokeAsync(customPlugin["GetMovieInfo"], new () { { "original_question", request.Question }, { "search_results", stringBuilderResults.ToString() }}); 114 | Console.WriteLine("Implementation of RAG using SK, C# and Azure Cognitive Search has been completed."); 115 | Console.WriteLine(completion.ToString()); 116 | return new CompletionResponse(completion.ToString()); 117 | } 118 | catch (Exception exc) 119 | { 120 | Console.WriteLine($"Error: {exc.Message}"); 121 | return new CompletionResponse("Something unexpected happened."); 122 | } 123 | }) 124 | .WithName("Completion") 125 | .WithOpenApi(); 126 | 127 | // Start the Process 128 | await app.RunAsync(); 129 | 130 | public record CompletionRequest (string Question) {} 131 | 132 | public record CompletionResponse (string completion) {} -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:49120", 8 | "sslPort": 44335 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "http://localhost:5291", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "applicationUrl": "https://localhost:7148;http://localhost:5291", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "swagger", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | acs_sk_csharp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Always 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/aais-sk-csharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aais-sk-csharp", "aais-sk-csharp.csproj", "{1F24A854-4F36-4C69-BCF7-B974651A792C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(SolutionProperties) = preSolution 14 | HideSolutionNode = FALSE 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {1F24A854-4F36-4C69-BCF7-B974651A792C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {1F24A854-4F36-4C69-BCF7-B974651A792C}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {1F24A854-4F36-4C69-BCF7-B974651A792C}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {1F24A854-4F36-4C69-BCF7-B974651A792C}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/01-backend-api/aais-sk-csharp-api/aais-sk-csharp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "OPENAI_API_VERSION": "2024-03-01-preview", 10 | "AZURE_OPENAI_API_KEY": "", 11 | "AZURE_OPENAI_ENDPOINT": "", 12 | "AZURE_OPENAI_COMPLETION_MODEL": "", 13 | "AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME": "", 14 | "AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME": "", 15 | "AZURE_AI_SEARCH_SERVICE_NAME": "", 16 | "AZURE_AI_SEARCH_ENDPOINT": "", 17 | "AZURE_AI_SEARCH_INDEX_NAME": "", 18 | "AZURE_AI_SEARCH_API_KEY": "" 19 | } -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/.chainlit/config.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | # If true (default), the app will be available to anonymous users. 3 | # If false, users will need to authenticate and be part of the project to use the app. 4 | public = true 5 | 6 | # The project ID (found on https://cloud.chainlit.io). 7 | # The project ID is required when public is set to false or when using the cloud database. 8 | #id = "" 9 | 10 | # Uncomment if you want to persist the chats. 11 | # local will create a database in your .chainlit directory (requires node.js installed). 12 | # cloud will use the Chainlit cloud database. 13 | # custom will load use your custom client. 14 | # database = "local" 15 | 16 | # Whether to enable telemetry (default: true). No personal data is collected. 17 | enable_telemetry = true 18 | 19 | # List of environment variables to be provided by each user to use the app. 20 | user_env = [] 21 | 22 | # Duration (in seconds) during which the session is saved when the connection is lost 23 | session_timeout = 3600 24 | 25 | # Enable third parties caching (e.g LangChain cache) 26 | cache = false 27 | 28 | # Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317) 29 | # follow_symlink = false 30 | 31 | # Chainlit server address 32 | # chainlit_server = "" 33 | 34 | [UI] 35 | # Name of the app and chatbot. 36 | name = "Chatbot" 37 | 38 | # Description of the app and chatbot. This is used for HTML tags. 39 | # description = "" 40 | 41 | # The default value for the expand messages settings. 42 | default_expand_messages = false 43 | 44 | # Hide the chain of thought details from the user in the UI. 45 | hide_cot = false 46 | 47 | # Link to your github repo. This will add a github button in the UI's header. 48 | # github = "" 49 | 50 | # Override default MUI light theme. (Check theme.ts) 51 | [UI.theme.light] 52 | #background = "#FAFAFA" 53 | #paper = "#FFFFFF" 54 | 55 | [UI.theme.light.primary] 56 | #main = "#F80061" 57 | #dark = "#980039" 58 | #light = "#FFE7EB" 59 | 60 | # Override default MUI dark theme. (Check theme.ts) 61 | [UI.theme.dark] 62 | #background = "#FAFAFA" 63 | #paper = "#FFFFFF" 64 | 65 | [UI.theme.dark.primary] 66 | #main = "#F80061" 67 | #dark = "#980039" 68 | #light = "#FFE7EB" 69 | 70 | 71 | [meta] 72 | generated_by = "0.6.402" 73 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/.chainlit/translations/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "atoms": { 4 | "buttons": { 5 | "userButton": { 6 | "menu": { 7 | "settings": "Settings", 8 | "settingsKey": "S", 9 | "APIKeys": "API Keys", 10 | "logout": "Logout" 11 | } 12 | } 13 | } 14 | }, 15 | "molecules": { 16 | "newChatButton": { 17 | "newChat": "New Chat" 18 | }, 19 | "tasklist": { 20 | "TaskList": { 21 | "title": "\ud83d\uddd2\ufe0f Task List", 22 | "loading": "Loading...", 23 | "error": "An error occured" 24 | } 25 | }, 26 | "attachments": { 27 | "cancelUpload": "Cancel upload", 28 | "removeAttachment": "Remove attachment" 29 | }, 30 | "newChatDialog": { 31 | "createNewChat": "Create new chat?", 32 | "clearChat": "This will clear the current messages and start a new chat.", 33 | "cancel": "Cancel", 34 | "confirm": "Confirm" 35 | }, 36 | "settingsModal": { 37 | "settings": "Settings", 38 | "expandMessages": "Expand Messages", 39 | "hideChainOfThought": "Hide Chain of Thought", 40 | "darkMode": "Dark Mode" 41 | }, 42 | "detailsButton": { 43 | "using": "Using", 44 | "running": "Running", 45 | "took_one": "Took {{count}} step", 46 | "took_other": "Took {{count}} steps" 47 | }, 48 | "auth": { 49 | "authLogin": { 50 | "title": "Login to access the app.", 51 | "form": { 52 | "email": "Email address", 53 | "password": "Password", 54 | "noAccount": "Don't have an account?", 55 | "alreadyHaveAccount": "Already have an account?", 56 | "signup": "Sign Up", 57 | "signin": "Sign In", 58 | "or": "OR", 59 | "continue": "Continue", 60 | "forgotPassword": "Forgot password?", 61 | "passwordMustContain": "Your password must contain:", 62 | "emailRequired": "email is a required field", 63 | "passwordRequired": "password is a required field" 64 | }, 65 | "error": { 66 | "default": "Unable to sign in.", 67 | "signin": "Try signing in with a different account.", 68 | "oauthsignin": "Try signing in with a different account.", 69 | "redirect_uri_mismatch": "The redirect URI is not matching the oauth app configuration.", 70 | "oauthcallbackerror": "Try signing in with a different account.", 71 | "oauthcreateaccount": "Try signing in with a different account.", 72 | "emailcreateaccount": "Try signing in with a different account.", 73 | "callback": "Try signing in with a different account.", 74 | "oauthaccountnotlinked": "To confirm your identity, sign in with the same account you used originally.", 75 | "emailsignin": "The e-mail could not be sent.", 76 | "emailverify": "Please verify your email, a new email has been sent.", 77 | "credentialssignin": "Sign in failed. Check the details you provided are correct.", 78 | "sessionrequired": "Please sign in to access this page." 79 | } 80 | }, 81 | "authVerifyEmail": { 82 | "almostThere": "You're almost there! We've sent an email to ", 83 | "verifyEmailLink": "Please click on the link in that email to complete your signup.", 84 | "didNotReceive": "Can't find the email?", 85 | "resendEmail": "Resend email", 86 | "goBack": "Go Back", 87 | "emailSent": "Email sent successfully.", 88 | "verifyEmail": "Verify your email address" 89 | }, 90 | "providerButton": { 91 | "continue": "Continue with {{provider}}", 92 | "signup": "Sign up with {{provider}}" 93 | }, 94 | "authResetPassword": { 95 | "newPasswordRequired": "New password is a required field", 96 | "passwordsMustMatch": "Passwords must match", 97 | "confirmPasswordRequired": "Confirm password is a required field", 98 | "newPassword": "New password", 99 | "confirmPassword": "Confirm password", 100 | "resetPassword": "Reset Password" 101 | }, 102 | "authForgotPassword": { 103 | "email": "Email address", 104 | "emailRequired": "email is a required field", 105 | "emailSent": "Please check the email address {{email}} for instructions to reset your password.", 106 | "enterEmail": "Enter your email address and we will send you instructions to reset your password.", 107 | "resendEmail": "Resend email", 108 | "continue": "Continue", 109 | "goBack": "Go Back" 110 | } 111 | } 112 | }, 113 | "organisms": { 114 | "chat": { 115 | "history": { 116 | "index": { 117 | "showHistory": "Show history", 118 | "lastInputs": "Last Inputs", 119 | "noInputs": "Such empty...", 120 | "loading": "Loading..." 121 | } 122 | }, 123 | "inputBox": { 124 | "input": { 125 | "placeholder": "Type your message here..." 126 | }, 127 | "speechButton": { 128 | "start": "Start recording", 129 | "stop": "Stop recording" 130 | }, 131 | "SubmitButton": { 132 | "sendMessage": "Send message", 133 | "stopTask": "Stop Task" 134 | }, 135 | "UploadButton": { 136 | "attachFiles": "Attach files" 137 | }, 138 | "waterMark": { 139 | "text": "Built with" 140 | } 141 | }, 142 | "Messages": { 143 | "index": { 144 | "running": "Running", 145 | "executedSuccessfully": "executed successfully", 146 | "failed": "failed", 147 | "feedbackUpdated": "Feedback updated", 148 | "updating": "Updating" 149 | } 150 | }, 151 | "dropScreen": { 152 | "dropYourFilesHere": "Drop your files here" 153 | }, 154 | "index": { 155 | "failedToUpload": "Failed to upload", 156 | "cancelledUploadOf": "Cancelled upload of", 157 | "couldNotReachServer": "Could not reach the server", 158 | "continuingChat": "Continuing previous chat" 159 | }, 160 | "settings": { 161 | "settingsPanel": "Settings panel", 162 | "reset": "Reset", 163 | "cancel": "Cancel", 164 | "confirm": "Confirm" 165 | } 166 | }, 167 | "threadHistory": { 168 | "sidebar": { 169 | "filters": { 170 | "FeedbackSelect": { 171 | "feedbackAll": "Feedback: All", 172 | "feedbackPositive": "Feedback: Positive", 173 | "feedbackNegative": "Feedback: Negative" 174 | }, 175 | "SearchBar": { 176 | "search": "Search" 177 | } 178 | }, 179 | "DeleteThreadButton": { 180 | "confirmMessage": "This will delete the thread as well as it's messages and elements.", 181 | "cancel": "Cancel", 182 | "confirm": "Confirm", 183 | "deletingChat": "Deleting chat", 184 | "chatDeleted": "Chat deleted" 185 | }, 186 | "index": { 187 | "pastChats": "Past Chats" 188 | }, 189 | "ThreadList": { 190 | "empty": "Empty...", 191 | "today": "Today", 192 | "yesterday": "Yesterday", 193 | "previous7days": "Previous 7 days", 194 | "previous30days": "Previous 30 days" 195 | }, 196 | "TriggerButton": { 197 | "closeSidebar": "Close sidebar", 198 | "openSidebar": "Open sidebar" 199 | } 200 | }, 201 | "Thread": { 202 | "backToChat": "Go back to chat", 203 | "chatCreatedOn": "This chat was created on" 204 | } 205 | }, 206 | "header": { 207 | "chat": "Chat", 208 | "readme": "Readme" 209 | } 210 | } 211 | }, 212 | "hooks": { 213 | "useLLMProviders": { 214 | "failedToFetchProviders": "Failed to fetch providers:" 215 | } 216 | }, 217 | "pages": { 218 | "Design": {}, 219 | "Env": { 220 | "savedSuccessfully": "Saved successfully", 221 | "requiredApiKeys": "Required API Keys", 222 | "requiredApiKeysInfo": "To use this app, the following API keys are required. The keys are stored on your device's local storage." 223 | }, 224 | "Page": { 225 | "notPartOfProject": "You are not part of this project." 226 | }, 227 | "ResumeButton": { 228 | "resumeChat": "Resume Chat" 229 | } 230 | } 231 | } -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/.env.sample: -------------------------------------------------------------------------------- 1 | BACKEND_API_BASE="http://localhost:5291" -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim AS builder 2 | WORKDIR /app 3 | ENV PYTHONDONTWRITEBYTECODE 1 4 | ENV PYTHONUNBUFFERED 1 5 | RUN apt-get update && apt-get install -y \ 6 | build-essential \ 7 | curl \ 8 | software-properties-common \ 9 | git \ 10 | && rm -rf /var/lib/apt/lists/* 11 | # Create a virtualenv to keep dependencies together 12 | RUN python -m venv /opt/venv 13 | ENV PATH="/opt/venv/bin:$PATH" 14 | COPY requirements.txt . 15 | RUN pip install --no-cache-dir -r requirements.txt 16 | 17 | # Stage 2 - Copy only necessary files to the runner stage 18 | FROM python:3.11-slim 19 | WORKDIR /app 20 | COPY --from=builder /opt/venv /opt/venv 21 | ENV PATH="/opt/venv/bin:$PATH" 22 | COPY app.py . 23 | COPY chainlit.md . 24 | COPY ./.chainlit ./.chainlit 25 | EXPOSE 8000 26 | CMD chainlit run app.py --port=8000 -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/README.md: -------------------------------------------------------------------------------- 1 | # Building an app with Chainlit 2 | 3 | The following section will demonstrate how to build a sample AI App using Chainlit. Chainlit is a tool that allows you to build AI applications with ease, by providing a simple interface to interact with AI models. 4 | 5 | The official docs can be found here: https://docs.chainlit.io. 6 | 7 | The app in this folder will connect to one of the backend applications in the `01-backend-api` folder. You can use either the Python/Langchain or .NET/Semantic Kernel version, either will work. 8 | 9 | Start one of the backend applications first, test and ensure the backend is working before starting the frontend. 10 | 11 | To run the chainlit application, follow the steps below: 12 | 13 | 1. Install any requirements 14 | 15 | ```bash 16 | pip install -r requirements.txt 17 | ``` 18 | 19 | 2. Start the application using the following command: 20 | 21 | ```bash 22 | chainlit run app.py -w 23 | ``` 24 | 25 | 3. After a few moments, a browser window should open automatically. If not, go to a browser and navigate to http://localhost:8000/ 26 | 4. Enter a prompt in the box at the bottom of the screen and hit "Enter". Remember that the backend has been configured to answer questions about the movies that we uploaded to Azure AI Search. -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import chainlit as cl 3 | from dotenv import load_dotenv 4 | import urllib.parse 5 | import asyncio 6 | import aiohttp 7 | import json 8 | 9 | # Load environment variables 10 | if load_dotenv(): 11 | print("Found OpenAPI Base Endpoint: " + os.getenv("BACKEND_API_BASE")) 12 | else: 13 | print("No file .env found") 14 | 15 | backend_api_base = os.getenv("BACKEND_API_BASE") 16 | 17 | @cl.on_chat_start 18 | def main(): 19 | # Add any objects or information needed for the user session. 20 | welcome = "Hello!" 21 | 22 | # Store objects or information user session 23 | cl.user_session.set("welcome", welcome) 24 | 25 | 26 | @cl.on_message 27 | async def main(message: cl.Message): 28 | # Retrieve any objects or information from the user session 29 | welcome = cl.user_session.get("welcome") # type: welcome 30 | 31 | # Call the backend api httprequest asynchronously 32 | # encoded_data = urllib.parse.urlencode(message).encode("utf-8") 33 | print (message.content) 34 | headers = {'accept': 'application/json', 'content-type': 'application/json'} 35 | async with aiohttp.ClientSession(headers=headers) as session: 36 | async with session.post( 37 | url=backend_api_base + "/completion/", 38 | data='{ "Question": "' + message.content + '"}' 39 | ) as response: 40 | res = await response.text() 41 | json_response = json.loads(res) 42 | print(json_response["completion"]) 43 | 44 | # Do any post processing here 45 | 46 | # Send the response 47 | await cl.Message(content=json_response["completion"]).send() -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/chainlit.md: -------------------------------------------------------------------------------- 1 | # Welcome to Chainlit! 🚀🤖 2 | 3 | Hi there, Developer! 👋 We're excited to have you on board. Chainlit is a powerful tool designed to help you prototype, debug and share applications built on top of LLMs. 4 | 5 | ## Useful Links 🔗 6 | 7 | - **Documentation:** Get started with our comprehensive [Chainlit Documentation](https://docs.chainlit.io) 📚 8 | - **Discord Community:** Join our friendly [Chainlit Discord](https://discord.gg/ZThrUxbAYw) to ask questions, share your projects, and connect with other developers! 💬 9 | 10 | This is a sample UI for the Azure AI Search + AI Orchestrator + Retrieval Augmented Generation (RAG) pattern API deployed in the previous step. This UI takes your question, sends it to the back-end API, and then displays the result from the back-end API which is the LLM completion. 11 | 12 | Sample question for the movie data that has been vectorized: **List the movies about ships on the water.** 13 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | chainlitagent-ui: 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | ports: 8 | - 8000:8000 9 | environment: 10 | - BACKEND_API_BASE=http://aais-lc-python:5291 11 | 12 | aais-lc-python: 13 | build: 14 | context: ../../01-backend-api/aais-lc-python-api/aais-lc-python 15 | dockerfile: Dockerfile 16 | ports: 17 | - 5291:5291 18 | environment: 19 | - AZURE_OPENAI_API_KEY='key_goes_here' 20 | - AZURE_OPENAI_ENDPOINT=azure_open_ai_endpoint_goes_here 21 | - OPENAI_API_VERSION=2024-03-01-preview 22 | - AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME=gpt-35-turbo 23 | - AZURE_OPENAI_EMBEDDING_MODEL=text-embedding-ada-002 24 | - AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=text-embedding-ada-002 25 | - AZURE_AI_SEARCH_SERVICE_NAME=azure_ai_search_service_name_goes_here 26 | - AZURE_AI_SEARCH_ENDPOINT=azure_ai_search_endpoint_goes_here 27 | - AZURE_AI_SEARCH_INDEX_NAME=ai-search-index-name_goes_here 28 | - AZURE_AI_SEARCH_API_KEY=azure_ai_search_api_key_goes_here 29 | 30 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/02-frontend-ui/chainlitagent-ui/requirements.txt: -------------------------------------------------------------------------------- 1 | chainlit==1.0.506 2 | fastapi==0.110.1 3 | python-dotenv==1.0.1 4 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/04-streamlit/.env.template: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY = "" 2 | OPENAI_API_BASE = "" 3 | OPENAI_API_TYPE = "azure" 4 | OPENAI_API_VERSION = "2023-05-15" 5 | OPENAI_COMPLETION_MODEL = "" 6 | OPENAI_COMPLETION_DEPLOYMENT_NAME = "" 7 | OPENAI_EMBEDDING_DEPLOYMENT_NAME = "" 8 | QDRANT_URL = "http://localhost:6333" 9 | QDRANT_COLLECTION = "my_movies" -------------------------------------------------------------------------------- /labs/04-deploy-ai/04-streamlit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim AS builder 2 | WORKDIR /app 3 | ENV PYTHONDONTWRITEBYTECODE 1 4 | ENV PYTHONUNBUFFERED 1 5 | RUN apt-get update && apt-get install -y \ 6 | build-essential \ 7 | curl \ 8 | software-properties-common \ 9 | git \ 10 | && rm -rf /var/lib/apt/lists/* 11 | # Create a virtualenv to keep dependencies together 12 | RUN python -m venv /opt/venv 13 | ENV PATH="/opt/venv/bin:$PATH" 14 | COPY requirements.txt . 15 | RUN pip install --no-cache-dir -r requirements.txt 16 | 17 | # Stage 2 - Copy only necessary files to the runner stage 18 | FROM python:3.11-slim 19 | ARG FILENAME 20 | ARG PORT=8000 21 | WORKDIR /app 22 | COPY --from=builder /opt/venv /opt/venv 23 | ENV PATH="/opt/venv/bin:$PATH" 24 | COPY app.py . 25 | COPY movieagent.jpg . 26 | EXPOSE $PORT 27 | CMD streamlit run app.py --port=$PORT -------------------------------------------------------------------------------- /labs/04-deploy-ai/04-streamlit/app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from langchain.callbacks.base import BaseCallbackHandler 3 | from langchain.chains import RetrievalQAWithSourcesChain 4 | from langchain.chains import RetrievalQA 5 | from langchain.chains import ConversationalRetrievalChain, LLMChain, RetrievalQA 6 | 7 | import os 8 | from dotenv import load_dotenv 9 | import qdrant_client 10 | 11 | load_dotenv() 12 | 13 | openai_api_version = os.getenv("OPENAI_API_VERSION") 14 | deployment_name = os.getenv("OPENAI_COMPLETION_DEPLOYMENT_NAME") 15 | embedding_name = os.getenv("OPENAI_EMBEDDING_DEPLOYMENT_NAME") 16 | qdrant_url = os.getenv("QDRANT_URL") 17 | qdrant_collection = os.getenv("QDRANT_COLLECTION") 18 | 19 | st.set_page_config(page_title="Movie expert AI", page_icon="📺") 20 | 21 | 22 | def settings(): 23 | 24 | # Vectorstore 25 | from langchain.vectorstores import Qdrant 26 | from langchain_openai import AzureOpenAIEmbeddings 27 | 28 | embeddings_model = AzureOpenAIEmbeddings( 29 | azure_deployment=embedding_name, 30 | chunk_size=1 31 | ) 32 | 33 | client = qdrant_client.QdrantClient( 34 | qdrant_url 35 | ) 36 | 37 | vector_store = Qdrant( 38 | client=client, collection_name=qdrant_collection, 39 | embeddings=embeddings_model, 40 | ) 41 | 42 | # LLM 43 | from langchain_openai import AzureChatOpenAI 44 | 45 | llm = AzureChatOpenAI( 46 | openai_api_version=openai_api_version, 47 | deployment_name=deployment_name 48 | ) 49 | 50 | retriever = vector_store.as_retriever(search_type="mmr") 51 | 52 | return retriever, llm 53 | 54 | 55 | class StreamHandler(BaseCallbackHandler): 56 | def __init__(self, container, initial_text=""): 57 | self.container = container 58 | self.text = initial_text 59 | 60 | def on_llm_new_token(self, token: str, **kwargs) -> None: 61 | self.text += token 62 | self.container.info(self.text) 63 | 64 | 65 | class PrintRetrievalHandler(BaseCallbackHandler): 66 | def __init__(self, container): 67 | self.container = container.expander("Context Retrieval") 68 | 69 | def on_retriever_start(self, query: str, **kwargs): 70 | self.container.write(f"**Question:** {query}") 71 | 72 | def on_retriever_end(self, documents, **kwargs): 73 | # self.container.write(documents) 74 | for idx, doc in enumerate(documents): 75 | source = doc.metadata["source"] 76 | self.container.write(f"**Results from {source}**") 77 | self.container.text(doc.page_content) 78 | 79 | 80 | st.sidebar.image("movieagent.jpg") 81 | st.header("`Movie AI Agent`") 82 | st.info("`I am an AI that can answer questions about all the movies you told me earlier.`") 83 | 84 | # Make retriever and llm 85 | if 'retriever' not in st.session_state: 86 | st.session_state['retriever'], st.session_state['llm'] = settings() 87 | retriever = st.session_state.retriever 88 | llm = st.session_state.llm 89 | 90 | # User input 91 | question = st.text_input("`Ask a question:`") 92 | 93 | if question: 94 | qa_sources_chain = RetrievalQAWithSourcesChain.from_chain_type( 95 | llm, retriever=retriever) 96 | retrieval_streamer_cb = PrintRetrievalHandler(st.container()) 97 | answer = st.empty() 98 | stream_handler = StreamHandler(answer, initial_text="`Answer:`\n\n") 99 | result = qa_sources_chain({"question": question}, callbacks=[ 100 | retrieval_streamer_cb, stream_handler]) 101 | print(result) 102 | answer.info('`Answer:`\n\n' + result['answer']) 103 | st.info('`Sources:`\n\n' + result['sources']) 104 | -------------------------------------------------------------------------------- /labs/04-deploy-ai/04-streamlit/movieagent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/labs/04-deploy-ai/04-streamlit/movieagent.jpg -------------------------------------------------------------------------------- /labs/04-deploy-ai/04-streamlit/requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit==1.34.0 2 | langchain==0.1.19 3 | qdrant-client==1.9.1 4 | html2text==2024.2.26 5 | tiktoken==0.7.0 6 | python-dotenv==1.0.1 -------------------------------------------------------------------------------- /labs/04-deploy-ai/README.md: -------------------------------------------------------------------------------- 1 | # 04 - Deploy AI 2 | 3 | In this folder you will find exercises to help increase your understanding of how to deploy AI Apps to Azure. 4 | 5 | Below is a list of each of the labs in this section and what each one sets out to achieve. 6 | 7 | ## 01-Backend API 8 | 9 | Pick one of the following APIs to deploy depending on whether you are more oriented towards C# or Python development. 10 | 11 | [ACS + Semantic Kernel C#](01-backend-api/acs-sk-csharp-api/README.md) 12 | 13 | In this lab, we'll turn the 03-orchestration/04-ACS/acs-sk-csharp.ipynb notebook lab into an ASP.NET Core API that can then be consumed by the frontend UI application. 14 | 15 | [ACS + Langchain Python](01-backend-api/acs-lc-python-api/README.md) 16 | 17 | In this lab, we'll turn the 03-orchestration/04-ACS/acs-lc-python.ipynb notebook lab into a Python FastAPI API that can then be consumed by the frontend UI application. 18 | 19 | ## 02-Frontend UI 20 | 21 | [Chat UI using Chainlit](02-frontend-ui/chainlitagent-ui/README.md) 22 | 23 | In this lab, we'll walk through deploying a simple UI that will consume the ASP.NET Core or Python FastAPI API in the first deployment section above. The UI will be built using a ChainLit App built in Python which is a simple and easy way to quickly mockup a UI for a ChatGPT like experience. -------------------------------------------------------------------------------- /presentations/README.md: -------------------------------------------------------------------------------- 1 | # Presentation Materials 2 | 3 | [Intro to Intelligent Apps Workshop PDF](intro-to-intelligent-apps-workshop.pdf) 4 | 5 | In addition to the PDF, this folder contains links to documents that will assist with the demos. 6 | 7 | 1. Intro to Azure OpenAI, Prompt Engineering 8 | * [Accompanying Demos](prompts-demos.md) 9 | 2. Intro to AI Orchestration 10 | * [Accompanying Demos](aiapps-demos.md) 11 | -------------------------------------------------------------------------------- /presentations/aiapps-demos.md: -------------------------------------------------------------------------------- 1 | # AI Apps Demo 2 | 3 | In this document you will find demos for the "Intro to Azure OpenAI, Prompt Engineering & Demos" morning session. The demos are aimed at showing how certain prompts can be made better by following recommended prompt engineering practices. 4 | 5 | ## List of Candidate Demo Links 6 | 7 | * :muscle: Paper Search - https://docsearch.redisventures.com/ 8 | * :muscle: Fashion Finder - https://ecommerce.redisventures.com/ 9 | * :muscle: Knowledge Mining - https://conversational-knowledge-mining.azurewebsites.net/ 10 | * :muscle: Enterprise ChatGPT - https://github.com/Azure-Samples/azure-search-openai-demo 11 | * :muscle: Vector Search AI Assistant - https://github.com/Azure/Vector-Search-AI-Assistant/tree/cognitive-search-vector 12 | -------------------------------------------------------------------------------- /presentations/intro-to-intelligent-apps-workshop.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/intro-to-intelligent-apps/e585f52501ce27c00930808515ee4084e074680e/presentations/intro-to-intelligent-apps-workshop.pdf -------------------------------------------------------------------------------- /presentations/prompts-demos.md: -------------------------------------------------------------------------------- 1 | # Prompts Demos 2 | 3 | In this document you will find demos for the "Intro to Azure OpenAI, Prompt Engineering & Demos" morning session. The demos are aimed at showing how certain prompts can be made better by following recommended prompt engineering practices. 4 | 5 | **All of these demos can be done using Azure AI Studio.** 6 | 7 | ## :muscle: Demo 1 - Separate instruction and context 8 | 9 | * Good: 10 | 11 | ```text 12 | Summarize the text below as a bullet point list of the most important points. 13 | 14 | We’re happy to announce that OpenAI and Microsoft are extending our partnership. This multi-year, multi-billion dollar investment from Microsoft follows their previous investments in 2019 and 2021, and will allow us to continue our independent research and develop AI that is increasingly safe, useful, and powerful. 15 | 16 | In pursuit of our mission to ensure advanced AI benefits all of humanity, OpenAI remains a capped-profit company and is governed by the OpenAI non-profit. This structure allows us to raise the capital we need to fulfill our mission without sacrificing our core beliefs about broadly sharing benefits and the need to prioritize safety. Microsoft shares this vision and our values, and our partnership is instrumental to our progress. 17 | ``` 18 | 19 | * Better: 20 | 21 | ```text 22 | Summarize the text below as a bullet point list of the most important points. 23 | 24 | """ 25 | We’re happy to announce that OpenAI and Microsoft are extending our partnership. This multi-year, multi-billion dollar investment from Microsoft follows their previous investments in 2019 and 2021, and will allow us to continue our independent research and develop AI that is increasingly safe, useful, and powerful. 26 | 27 | In pursuit of our mission to ensure advanced AI benefits all of humanity, OpenAI remains a capped-profit company and is governed by the OpenAI non-profit. This structure allows us to raise the capital we need to fulfill our mission without sacrificing our core beliefs about broadly sharing benefits and the need to prioritize safety. Microsoft shares this vision and our values, and our partnership is instrumental to our progress. 28 | """ 29 | ``` 30 | 31 | ## :muscle: Demo 2 - Be specific, descriptive and as detailed as possible 32 | 33 | * Good: 34 | 35 | ```text 36 | Write a poem about OpenAI. 37 | ``` 38 | 39 | * Better: 40 | 41 | ```text 42 | Write a short inspiring poem about OpenAI, focusing on the recent DALL-E product launch in the style of Ernest Hemingway. 43 | ``` 44 | 45 | ## :muscle: Demo 3 - Articulate the desired output format through examples 46 | 47 | * Good: 48 | 49 | ```text 50 | Extract the company names then years in the following text below and output start index and end index of each entity. Generate output as {"text": "OpenAI", "start": 28, "end": 34} 51 | 52 | ### 53 | We’re happy to announce that OpenAI and Microsoft are extending our partnership. This multi-year, multi-billion dollar investment from Microsoft follows their previous investments in 2019 and 2021, and will allow us to continue our independent research and develop AI that is increasingly safe, useful, and powerful. 54 | 55 | 56 | ### 57 | ``` 58 | 59 | * Better: 60 | 61 | ```text 62 | Extract the entities mentioned in the text below. Extract the important entities mentioned in the text below. First extract all company names, then extract all years, then extract specific topics which fit the content and finally extract general overarching themes. 63 | 64 | Desired format: 65 | Company names: 66 | Years: -||- 67 | Specific topics: -||- 68 | General themes: -||- 69 | 70 | """ 71 | We’re happy to announce that OpenAI and Microsoft are extending our partnership. This multi-year, multi-billion dollar investment from Microsoft follows their previous investments in 2019 and 2021, and will allow us to continue our independent research and develop AI that is increasingly safe, useful, and powerful. 72 | 73 | 74 | """ 75 | ``` 76 | 77 | ## :muscle: Demo 4 - Reduce fluffy and imprecise descriptions 78 | 79 | * Good: 80 | 81 | ```text 82 | Write a description for a new product. This product is a new generation of car seat. The description for this product should be fairly short, a few sentences only, and not too much more. 83 | ``` 84 | 85 | * Better: 86 | 87 | ```text 88 | Write a description for a new product. This product is a new generation of car seat. Use a 3 to 5 sentence paragraph to describe this product. 89 | ``` 90 | 91 | ## :muscle: Demo 5 - Instead of just saying what not to do, say what to do instead 92 | 93 | * Good: 94 | 95 | ```text 96 | The following is a conversation between an Agent and a Customer. DO NOT ASK USERNAME OR PASSWORD. DO NOT REPEAT. 97 | 98 | Customer: I can’t log in to my account. 99 | Agent: 100 | ``` 101 | 102 | * Better: 103 | 104 | ```text 105 | The following is a conversation between an Agent and a Customer. The agent will attempt to diagnose the problem and suggest a solution, whilst refraining from asking any questions related to PII. Instead of asking for PII, such as username or password, refer the user to the help article www.samplewebsite.com/help/faq 106 | 107 | Customer: I can’t log in to my account. 108 | Agent: 109 | ``` 110 | 111 | ## :muscle: Demo 6 - Start with zero-shot, then few-shot (example), neither of them worked, then fine-tune 112 | 113 | * Good: 114 | 115 | ```text 116 | Extract keywords from the below text. Text: 117 | We’re happy to announce that OpenAI and Microsoft are extending our partnership. This multi-year, multi-billion dollar investment from Microsoft follows their previous investments in 2019 and 2021, and will allow us to continue our independent research and develop AI that is increasingly safe, useful, and powerful. 118 | 119 | Keywords: 120 | ``` 121 | 122 | * Better: 123 | 124 | ```text 125 | Extract keywords from the corresponding texts below. 126 | 127 | Text 1: Stripe provides APIs that web developers can use to integrate payment processing into their websites and mobile applications. 128 | Keywords 1: Stripe, payment processing, APIs, web developers, websites, mobile applications 129 | ## 130 | Text 2: OpenAI has trained cutting-edge language models that are very good at understanding and generating text. Our API provides access to these models and can be used to solve virtually any task that involves processing language. 131 | Keywords 2: OpenAI, language models, text processing, API. 132 | ## 133 | Text 3: We’re happy to announce that OpenAI and Microsoft are extending our partnership. This multi-year, multi-billion dollar investment from Microsoft follows their previous investments in 2019 and 2021, and will allow us to continue our independent research and develop AI that is increasingly safe, useful, and powerful. 134 | Keywords 3: 135 | ``` -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | azure-core==1.30.2 2 | azure-identity==1.17.0 3 | azure-search-documents==11.5.1 #11.4.0 4 | semantic-kernel==1.8.3 #0.9.5b1 5 | openai==1.44.0 6 | langchain==0.2.16 7 | langchain-openai==0.1.23 8 | tiktoken==0.7.0 9 | python-dotenv==1.0.1 10 | requests==2.32.3 11 | unstructured==0.15.9 12 | markdown==3.7 13 | qdrant-client==1.11.1 14 | chromadb==0.5.5 15 | yfinance==0.2.43 16 | langchain-community==0.2.16 17 | pymongo==4.8.0 18 | ijson==3.3.0 --------------------------------------------------------------------------------