chore: import upstream snapshot with attribution
Code Quality / Python Lint & Format (push) Has been cancelled
Code Quality / Python Tests (push) Has been cancelled
Code Quality / JavaScript/TypeScript Lint (advisory) (push) Has been cancelled
Security Scan / CodeQL Analysis (python) (push) Has been cancelled
Security Scan / Dependency Review (push) Has been cancelled
Security Scan / CodeQL Analysis (javascript-typescript) (push) Has been cancelled
@@ -0,0 +1,8 @@
|
||||
This file will use the official Python image as the base image for the
|
||||
development container. It will also install some VS Code extensions for Python development,
|
||||
such as the Python extension and the Jupyter.
|
||||
|
||||
Finally, it will run the command pip install -r requirements.txt after the container is
|
||||
created, which will install all the Python libraries listed in the requirements.txt file.
|
||||
You can learn more about how to create and use dev containers in VS Code from this link or this link.
|
||||
I hope this helps you with your project https://code.visualstudio.com?WT.mc_id=academic-105485-koreyst
|
||||
@@ -0,0 +1,41 @@
|
||||
// Adopting the default GitHub codespaces-jupyter template as starter
|
||||
// https://github.com/github/codespaces-jupyter
|
||||
{
|
||||
"name": "Generative AI For Beginners",
|
||||
"image": "mcr.microsoft.com/devcontainers/universal:2.13",
|
||||
"hostRequirements": {
|
||||
"cpus": 4
|
||||
},
|
||||
"waitFor": "onCreateCommand",
|
||||
"updateContentCommand": "python3 -m pip install -r requirements.txt",
|
||||
"postCreateCommand": "bash .devcontainer/post-create.sh",
|
||||
"customizations": {
|
||||
"codespaces": {
|
||||
"openFiles": []
|
||||
},
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"ms-toolsai.jupyter",
|
||||
"ms-python.black-formatter",
|
||||
"charliermarsh.ruff",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"github.copilot"
|
||||
],
|
||||
"settings": {
|
||||
"editor.formatOnSave": true,
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
name: dev
|
||||
channels:
|
||||
- defaults
|
||||
dependencies:
|
||||
- python=3.10.0
|
||||
- openai
|
||||
- python-dotenv
|
||||
- pip
|
||||
- pip:
|
||||
- azure-ai-inference
|
||||
@@ -0,0 +1,90 @@
|
||||
<svg width="44" height="51" viewBox="0 0 44 51" version="2.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:figma="http://www.figma.com/figma/ns">
|
||||
<title>Group.svg</title>
|
||||
<desc>Created using Figma 0.90</desc>
|
||||
<g id="Canvas" transform="translate(-1640 -2453)" figma:type="canvas">
|
||||
<g id="Group" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="Group" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="Group" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="g" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path9 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path0_fill" transform="translate(1640.54 2474.36)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path10 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path1_fill" transform="translate(1645.68 2474.37)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path11 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path2_fill" transform="translate(1653.39 2474.26)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path12 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path3_fill" transform="translate(1660.43 2474.39)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path13 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path4_fill" transform="translate(1667.55 2472.54)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path14 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path5_fill" transform="translate(1672.47 2474.29)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path15 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path6_fill" transform="translate(1679.98 2474.24)" fill="#4E4E4E" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path16 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path7_fill" transform="translate(1673.48 2453.69)" fill="#767677" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path17 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path8_fill" transform="translate(1643.21 2484.27)" fill="#F37726" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path18 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path9_fill" transform="translate(1643.21 2457.88)" fill="#F37726" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path19 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path10_fill" transform="translate(1643.28 2496.09)" fill="#9E9E9E" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path" style="mix-blend-mode:normal;" figma:type="group">
|
||||
<g id="path20 fill" style="mix-blend-mode:normal;" figma:type="vector">
|
||||
<use xlink:href="#path11_fill" transform="translate(1641.87 2458.43)" fill="#616262" style="mix-blend-mode:normal;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<path id="path0_fill" d="M 1.74498 5.47533C 1.74498 7.03335 1.62034 7.54082 1.29983 7.91474C 0.943119 8.23595 0.480024 8.41358 0 8.41331L 0.124642 9.3036C 0.86884 9.31366 1.59095 9.05078 2.15452 8.56466C 2.45775 8.19487 2.6834 7.76781 2.818 7.30893C 2.95261 6.85005 2.99341 6.36876 2.93798 5.89377L 2.93798 0L 1.74498 0L 1.74498 5.43972L 1.74498 5.47533Z"/>
|
||||
<path id="path1_fill" d="M 5.50204 4.76309C 5.50204 5.43081 5.50204 6.02731 5.55545 6.54368L 4.496 6.54368L 4.42478 5.48423C 4.20318 5.85909 3.88627 6.16858 3.50628 6.38125C 3.12628 6.59392 2.69675 6.70219 2.26135 6.69503C 1.22861 6.69503 0 6.13415 0 3.84608L 0 0.0445149L 1.193 0.0445149L 1.193 3.6057C 1.193 4.84322 1.57583 5.67119 2.65309 5.67119C 2.87472 5.67358 3.09459 5.63168 3.29982 5.54796C 3.50505 5.46424 3.69149 5.34039 3.84822 5.18366C 4.00494 5.02694 4.1288 4.84049 4.21252 4.63527C 4.29623 4.43004 4.33813 4.21016 4.33575 3.98853L 4.33575 0L 5.52874 0L 5.52874 4.72748L 5.50204 4.76309Z"/>
|
||||
<path id="path2_fill" d="M 0.0534178 2.27264C 0.0534178 1.44466 0.0534178 0.768036 0 0.153731L 1.06836 0.153731L 1.12177 1.2666C 1.3598 0.864535 1.70247 0.534594 2.11325 0.311954C 2.52404 0.0893145 2.98754 -0.0176786 3.45435 0.00238095C 5.03908 0.00238095 6.23208 1.32892 6.23208 3.30538C 6.23208 5.63796 4.7987 6.79535 3.24958 6.79535C 2.85309 6.81304 2.45874 6.7281 2.10469 6.54874C 1.75064 6.36937 1.44888 6.10166 1.22861 5.77151L 1.22861 5.77151L 1.22861 9.33269L 0.0534178 9.33269L 0.0534178 2.29935L 0.0534178 2.27264ZM 1.22861 4.00872C 1.23184 4.17026 1.24972 4.33117 1.28203 4.48948C 1.38304 4.88479 1.61299 5.23513 1.93548 5.48506C 2.25798 5.735 2.65461 5.87026 3.06262 5.86944C 4.31794 5.86944 5.05689 4.8456 5.05689 3.3588C 5.05689 2.05897 4.36246 0.946096 3.10714 0.946096C 2.61036 0.986777 2.14548 1.20726 1.79965 1.5662C 1.45382 1.92514 1.25079 2.3979 1.22861 2.89585L 1.22861 4.00872Z"/>
|
||||
<path id="path3_fill" d="M 1.31764 0.0178059L 2.75102 3.85499C 2.90237 4.28233 3.06262 4.7987 3.16946 5.18153C 3.2941 4.7898 3.42764 4.29123 3.5879 3.82828L 4.88773 0.0178059L 6.14305 0.0178059L 4.36246 4.64735C 3.47216 6.87309 2.92908 8.02158 2.11 8.71601C 1.69745 9.09283 1.19448 9.35658 0.649917 9.48166L 0.356119 8.48453C 0.736886 8.35942 1.09038 8.16304 1.39777 7.90584C 1.8321 7.55188 2.17678 7.10044 2.4038 6.5882C 2.45239 6.49949 2.48551 6.40314 2.50173 6.3033C 2.49161 6.19586 2.46457 6.0907 2.42161 5.9917L 0 0L 1.29983 0L 1.31764 0.0178059Z"/>
|
||||
<path id="path4_fill" d="M 2.19013 0L 2.19013 1.86962L 3.8995 1.86962L 3.8995 2.75992L 2.19013 2.75992L 2.19013 6.26769C 2.19013 7.06896 2.42161 7.53191 3.08043 7.53191C 3.31442 7.53574 3.54789 7.5088 3.77486 7.45179L 3.82828 8.34208C 3.48794 8.45999 3.12881 8.51431 2.76882 8.50234C 2.53042 8.51726 2.29161 8.48043 2.06878 8.39437C 1.84595 8.30831 1.64438 8.17506 1.47789 8.00377C 1.11525 7.51873 0.949826 6.91431 1.01494 6.31221L 1.01494 2.75102L 0 2.75102L 0 1.86072L 1.03274 1.86072L 1.03274 0.275992L 2.19013 0Z"/>
|
||||
<path id="path5_fill" d="M 1.17716 3.57899C 1.153 3.88093 1.19468 4.18451 1.29933 4.46876C 1.40398 4.75301 1.5691 5.01114 1.78329 5.22532C 1.99747 5.43951 2.2556 5.60463 2.53985 5.70928C 2.8241 5.81393 3.12768 5.85561 3.42962 5.83145C 4.04033 5.84511 4.64706 5.72983 5.21021 5.49313L 5.41498 6.38343C 4.72393 6.66809 3.98085 6.80458 3.23375 6.78406C 2.79821 6.81388 2.36138 6.74914 1.95322 6.59427C 1.54505 6.43941 1.17522 6.19809 0.869071 5.88688C 0.562928 5.57566 0.327723 5.2019 0.179591 4.79125C 0.0314584 4.38059 -0.0260962 3.94276 0.0108748 3.50777C 0.0108748 1.54912 1.17716 0 3.0824 0C 5.21911 0 5.75329 1.86962 5.75329 3.06262C 5.76471 3.24644 5.76471 3.43079 5.75329 3.61461L 1.15046 3.61461L 1.17716 3.57899ZM 4.66713 2.6887C 4.70149 2.45067 4.68443 2.20805 4.61709 1.97718C 4.54976 1.74631 4.43372 1.53255 4.2768 1.35031C 4.11987 1.16808 3.92571 1.0216 3.70739 0.920744C 3.48907 0.81989 3.25166 0.767006 3.01118 0.765656C 2.52201 0.801064 2.06371 1.01788 1.72609 1.37362C 1.38847 1.72935 1.19588 2.19835 1.18607 2.6887L 4.66713 2.6887Z"/>
|
||||
<path id="path6_fill" d="M 0.0534178 2.19228C 0.0534178 1.42663 0.0534178 0.767806 0 0.162404L 1.06836 0.162404L 1.06836 1.43553L 1.12177 1.43553C 1.23391 1.04259 1.4656 0.694314 1.78468 0.439049C 2.10376 0.183783 2.4944 0.034196 2.90237 0.0110538C 3.01466 -0.00368459 3.12839 -0.00368459 3.24068 0.0110538L 3.24068 1.12393C 3.10462 1.10817 2.9672 1.10817 2.83114 1.12393C 2.427 1.13958 2.04237 1.30182 1.7491 1.58035C 1.45583 1.85887 1.27398 2.23462 1.23751 2.63743C 1.20422 2.8196 1.18635 3.00425 1.1841 3.18941L 1.1841 6.65267L 0.00890297 6.65267L 0.00890297 2.20118L 0.0534178 2.19228Z"/>
|
||||
<path id="path7_fill" d="M 6.03059 2.83565C 6.06715 3.43376 5.92485 4.02921 5.6218 4.54615C 5.31875 5.0631 4.86869 5.47813 4.32893 5.73839C 3.78917 5.99864 3.18416 6.09233 2.59097 6.00753C 1.99778 5.92272 1.44326 5.66326 0.998048 5.26219C 0.552837 4.86113 0.23709 4.33661 0.0910307 3.75546C -0.0550287 3.17431 -0.0247891 2.56283 0.177897 1.99893C 0.380583 1.43503 0.746541 0.944221 1.22915 0.589037C 1.71176 0.233853 2.28918 0.0303686 2.88784 0.00450543C 3.28035 -0.0170932 3.67326 0.0391144 4.04396 0.169896C 4.41467 0.300677 4.75587 0.503453 5.04794 0.766561C 5.34 1.02967 5.57718 1.34792 5.74582 1.70301C 5.91446 2.0581 6.01124 2.44303 6.03059 2.83565L 6.03059 2.83565Z"/>
|
||||
<path id="path8_fill" d="M 18.6962 7.12238C 10.6836 7.12238 3.64131 4.24672 0 0C 1.41284 3.82041 3.96215 7.1163 7.30479 9.44404C 10.6474 11.7718 14.623 13.0196 18.6962 13.0196C 22.7695 13.0196 26.745 11.7718 30.0877 9.44404C 33.4303 7.1163 35.9796 3.82041 37.3925 4.0486e-13C 33.7601 4.24672 26.7445 7.12238 18.6962 7.12238Z"/>
|
||||
<path id="path9_fill" d="M 18.6962 5.89725C 26.7089 5.89725 33.7512 8.77291 37.3925 13.0196C 35.9796 9.19922 33.4303 5.90333 30.0877 3.57559C 26.745 1.24785 22.7695 4.0486e-13 18.6962 0C 14.623 4.0486e-13 10.6474 1.24785 7.30479 3.57559C 3.96215 5.90333 1.41284 9.19922 0 13.0196C 3.64131 8.76401 10.648 5.89725 18.6962 5.89725Z"/>
|
||||
<path id="path10_fill" d="M 7.59576 3.56656C 7.64276 4.31992 7.46442 5.07022 7.08347 5.72186C 6.70251 6.3735 6.13619 6.89698 5.45666 7.22561C 4.77713 7.55424 4.01515 7.67314 3.26781 7.56716C 2.52046 7.46117 1.82158 7.13511 1.26021 6.63051C 0.698839 6.12591 0.300394 5.46561 0.115637 4.73375C -0.0691191 4.00188 -0.0318219 3.23159 0.222777 2.52099C 0.477376 1.8104 0.93775 1.19169 1.54524 0.743685C 2.15274 0.295678 2.87985 0.0386595 3.63394 0.00537589C 4.12793 -0.0210471 4.62229 0.0501173 5.08878 0.214803C 5.55526 0.37949 5.98473 0.63447 6.35264 0.965179C 6.72055 1.29589 7.01971 1.69584 7.233 2.1422C 7.4463 2.58855 7.56957 3.07256 7.59576 3.56656L 7.59576 3.56656Z"/>
|
||||
<path id="path11_fill" d="M 2.25061 4.37943C 1.81886 4.39135 1.39322 4.27535 1.02722 4.04602C 0.661224 3.81668 0.371206 3.48424 0.193641 3.09052C 0.0160762 2.69679 -0.0411078 2.25935 0.0292804 1.83321C 0.0996686 1.40707 0.294486 1.01125 0.589233 0.695542C 0.883981 0.37983 1.2655 0.158316 1.68581 0.0588577C 2.10611 -0.0406005 2.54644 -0.0135622 2.95143 0.136572C 3.35641 0.286707 3.70796 0.553234 3.96186 0.902636C 4.21577 1.25204 4.3607 1.66872 4.37842 2.10027C 4.39529 2.6838 4.18131 3.25044 3.78293 3.67715C 3.38455 4.10387 2.83392 4.35623 2.25061 4.37943Z"/>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Install OpenAI and Dotenv for Python
|
||||
# TODO: Check why this can't be done in requirements.txt
|
||||
pip install python-dotenv
|
||||
pip install openai
|
||||
|
||||
# Install developer tooling for linting, formatting, type-checking, and tests.
|
||||
# These match the checks run in .github/workflows/code-quality.yml so
|
||||
# contributors can reproduce them locally before opening a pull request.
|
||||
pip install ruff black mypy pytest
|
||||
|
||||
# Install the OpenAI packages for Node.js
|
||||
# (Python related dependencies are covered in requirements.txt)
|
||||
# echo "Installing OpenAI For Node.js"
|
||||
# npm install --save openai
|
||||
@@ -0,0 +1,20 @@
|
||||
# OpenAI Provider
|
||||
OPENAI_API_KEY='<add your OpenAI API key here>'
|
||||
|
||||
## Azure OpenAI in Microsoft Foundry
|
||||
## (Azure OpenAI Service is now part of Microsoft Foundry: https://ai.azure.com?WT.mc_id=academic-105485-koreyst.
|
||||
## Create/manage your resource and deployments from the Foundry portal - the env var names below are unchanged.)
|
||||
AZURE_OPENAI_API_VERSION='2024-10-21' # Default is set! (current stable GA API version)
|
||||
AZURE_OPENAI_API_KEY='<add your Foundry resource key here>'
|
||||
AZURE_OPENAI_ENDPOINT='<add your Foundry resource endpoint here, e.g. https://<resource-name>.openai.azure.com>'
|
||||
AZURE_OPENAI_DEPLOYMENT='<add your chat completion model deployment name here, e.g. gpt-4o-mini>'
|
||||
AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT='<add your embeddings model deployment name here, e.g. text-embedding-3-small>'
|
||||
|
||||
## Microsoft Foundry Models
|
||||
## (Multi-provider model catalog - one endpoint/key for OpenAI, Meta, Mistral, Cohere, Microsoft, and more.
|
||||
## Replaces GitHub Models, which retires end of July 2026. Get these from your Foundry project's "Overview" page.)
|
||||
AZURE_INFERENCE_ENDPOINT='<add your Microsoft Foundry project endpoint here, e.g. https://<resource-name>.services.ai.azure.com/models>'
|
||||
AZURE_INFERENCE_CREDENTIAL='<add your Microsoft Foundry Models API key here>'
|
||||
|
||||
## Hugging Face
|
||||
HUGGING_FACE_API_KEY='<add your HuggingFace API or token here>'
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
"no-console": "off",
|
||||
"eqeqeq": ["error", "always"],
|
||||
"curly": ["error", "all"],
|
||||
"no-eval": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-new-func": "error",
|
||||
"no-script-url": "error",
|
||||
"prefer-const": "warn",
|
||||
"no-var": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-return-await": "warn",
|
||||
"require-await": "warn"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.ts", "**/*.tsx"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/explicit-function-return-type": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-non-null-assertion": "warn"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"node_modules/",
|
||||
"dist/",
|
||||
"build/",
|
||||
"*.min.js"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,19 @@
|
||||
version: 2
|
||||
updates:
|
||||
# For Python dependencies
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/" # Location of the requirements.txt file
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
# For Node.js dependencies
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/" # Location of the package.json file
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
# Optional: For GitHub Actions dependencies
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # Location of the .github/workflows directory
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -0,0 +1,161 @@
|
||||
---
|
||||
name: azure-openai-to-responses
|
||||
description: >-
|
||||
Migrate Python apps from Azure OpenAI Chat Completions to the Responses API.
|
||||
Covers AzureOpenAI/AsyncAzureOpenAI client migration to the v1 endpoint,
|
||||
streaming, tools, structured output, multi-turn, EntraID auth, and model
|
||||
compatibility checks. Python-focused, Azure OpenAI-specific.
|
||||
USE FOR: migrate to responses API, switch from chat completions, openai responses,
|
||||
upgrade openai SDK, responses API migration, move from completions to responses,
|
||||
gpt-5 migration, azure openai python migration, chat completions to responses,
|
||||
AzureOpenAI to OpenAI client, python azure openai upgrade.
|
||||
DO NOT USE FOR: building new apps from scratch (start with responses directly),
|
||||
Node/TypeScript/C#/Java/Go migrations (this skill is Python-only),
|
||||
Azure infrastructure setup (use azure-prepare), deploying models (use microsoft-foundry).
|
||||
license: MIT
|
||||
source: https://github.com/Azure-Samples/azure-openai-to-responses
|
||||
---
|
||||
|
||||
# Migrate Python Apps from Azure OpenAI Chat Completions to Responses API
|
||||
|
||||
> **AUTHORITATIVE GUIDANCE — FOLLOW EXACTLY**
|
||||
>
|
||||
> This skill migrates Python codebases using Azure OpenAI Chat Completions
|
||||
> to the unified Responses API. Follow these instructions precisely.
|
||||
> Do not improvise parameter mappings or invent API shapes.
|
||||
|
||||
Installed from [Azure-Samples/azure-openai-to-responses](https://github.com/Azure-Samples/azure-openai-to-responses) (MIT).
|
||||
|
||||
## Triggers
|
||||
|
||||
Activate this skill when the user wants to:
|
||||
- Migrate a Python app from Azure OpenAI Chat Completions to Responses API
|
||||
- Upgrade Python OpenAI SDK usage to the latest API shape against Azure OpenAI
|
||||
- Prepare Python code for GPT-5 or newer models that require Responses on Azure
|
||||
- Switch from `AzureOpenAI`/`AsyncAzureOpenAI` to standard `OpenAI`/`AsyncOpenAI` client with the v1 endpoint
|
||||
- Fix deprecation warnings related to `AzureOpenAI` constructors or `api_version`
|
||||
|
||||
## Why migrate
|
||||
|
||||
GPT-5 and newer models require the Responses API. The new `/openai/v1/` endpoint
|
||||
uses the standard `OpenAI()` client instead of `AzureOpenAI()`, requires no
|
||||
`api_version` parameter, and works identically across OpenAI and Azure OpenAI.
|
||||
|
||||
## What changes
|
||||
|
||||
| Chat Completions (before) | Responses API (after) |
|
||||
| --- | --- |
|
||||
| `AzureOpenAI()` / `AsyncAzureOpenAI()` | `OpenAI(base_url=...)` / `AsyncOpenAI(base_url=...)` |
|
||||
| `azure_endpoint=...` | `base_url=f"{endpoint.rstrip('/')}/openai/v1/"` |
|
||||
| `api_version="2024-..."` | Remove entirely — `/openai/v1/` is stable |
|
||||
| `azure_ad_token_provider=...` | `api_key=token_provider` |
|
||||
| `client.chat.completions.create(messages=...)` | `client.responses.create(input=...)` |
|
||||
| `resp.choices[0].message.content` | `resp.output_text` |
|
||||
| `max_tokens` | `max_output_tokens` (min 16 on Azure) |
|
||||
| `response_format` | `text={"format": {...}}` |
|
||||
| `seed` | **Remove** (not supported) |
|
||||
| tools nested `{"type":"function","function":{...}}` | flat `{"type":"function","name":...}` |
|
||||
| tool result `{"role":"tool","tool_call_id":...}` | `{"type":"function_call_output","call_id":...,"output":...}` |
|
||||
| `content[].type: "text"` | `content[].type: "input_text"` |
|
||||
| `content[].type: "image_url"` + `{"url": "..."}` | `content[].type: "input_image"` + flat `"image_url": "..."` |
|
||||
| streaming `chunk.choices[0].delta.content` | `event.type == "response.output_text.delta"` → `event.delta` |
|
||||
|
||||
## Model compatibility — CHECK FIRST
|
||||
|
||||
Verify the deployed model supports the Responses API before migrating. GPT-4o and
|
||||
GPT-4 support Responses for basic text/chat/streaming/tools but not all features.
|
||||
Newer models (gpt-4.1+, gpt-5.x) have full support. **GitHub Models
|
||||
(`models.github.ai`, `models.inference.ai.azure.com`) do NOT support the Responses
|
||||
API** — remove those code paths and switch to Azure OpenAI, OpenAI, or a compatible
|
||||
local endpoint.
|
||||
|
||||
Smoke test:
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
)
|
||||
resp = client.responses.create(
|
||||
model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
|
||||
input="ping",
|
||||
max_output_tokens=50,
|
||||
store=False,
|
||||
)
|
||||
print(resp.output_text)
|
||||
```
|
||||
|
||||
## Step 0: Client migration (prerequisite)
|
||||
|
||||
`AzureOpenAI`/`AsyncAzureOpenAI` constructors are deprecated in `openai>=1.108.1`.
|
||||
|
||||
Before:
|
||||
```python
|
||||
from openai import AzureOpenAI
|
||||
client = AzureOpenAI(
|
||||
api_version=os.environ["AZURE_OPENAI_API_VERSION"],
|
||||
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
|
||||
api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
||||
)
|
||||
```
|
||||
|
||||
After:
|
||||
```python
|
||||
from openai import OpenAI
|
||||
client = OpenAI(
|
||||
api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
)
|
||||
```
|
||||
|
||||
Cleanup: remove `api_version` args, remove `AZURE_OPENAI_API_VERSION` /
|
||||
`AZURE_OPENAI_VERSION` from `.env`/infra, rename `AZURE_OPENAI_CLIENT_ID` →
|
||||
`AZURE_CLIENT_ID`, ensure `openai>=1.108.1`.
|
||||
|
||||
## Step 1: Detect legacy call sites
|
||||
|
||||
```bash
|
||||
rg "chat\.completions\.create" # legacy API calls
|
||||
rg "ChatCompletion\.create|Completion\.create"
|
||||
rg "AzureOpenAI\(|AsyncAzureOpenAI\(" # deprecated constructors
|
||||
rg "choices\[0\]\.message\.content" # response access
|
||||
rg "choices\[0\]\.delta\.content" # streaming access
|
||||
rg "max_tokens\b" # rename to max_output_tokens
|
||||
rg "['\"]seed['\"]" # remove entirely
|
||||
rg "response_format" # → text.format
|
||||
rg "AZURE_OPENAI_API_VERSION|AZURE_OPENAI_VERSION"
|
||||
rg "models\.github\.ai|models\.inference\.ai\.azure" # GitHub Models: remove
|
||||
```
|
||||
|
||||
## Step 2: Apply migration
|
||||
|
||||
- `client.chat.completions.create(messages=...)` → `client.responses.create(input=...)`
|
||||
- `resp.choices[0].message.content` → `resp.output_text`
|
||||
- `max_tokens` → `max_output_tokens` (min 16); remove `seed`
|
||||
- `response_format` → `text={"format": {"type": "json_schema", "name": "Output", "strict": True, "schema": {...}}}`
|
||||
- Set `store=False` on every request (client-managed state)
|
||||
- Streaming: iterate events, handle `event.type == "response.output_text.delta"` (use `event.delta`) and `response.completed`
|
||||
- Tools: flat format, `tool_choice`, return results as `function_call_output` items; append `response.output` items for round-trips
|
||||
- Multi-turn: maintain conversation in an `input` array, or use `previous_response_id` (requires `store=True`)
|
||||
- O-series: `max_completion_tokens` → `max_output_tokens` (4096+), `reasoning_effort` → `reasoning={"effort": ...}`, `temperature` must be 1/omitted, remove `top_p`
|
||||
|
||||
## Acceptance criteria (all must pass)
|
||||
|
||||
- Zero matches for `chat.completions.create|ChatCompletion.create|Completion.create`
|
||||
- Zero matches for `AzureOpenAI(|AsyncAzureOpenAI(` — all use `OpenAI`/`AsyncOpenAI` + v1 endpoint
|
||||
- Zero matches for `models.github.ai|models.inference.ai.azure`
|
||||
- Zero matches for `choices[0]` — all access uses `resp.output_text` / Responses schema
|
||||
- No top-level `response_format`; structured output uses `text={"format": {...}}`
|
||||
- `openai>=1.108.1` in requirements; `store=False` on every call; no `api_version` in client construction
|
||||
- Tests updated (mocks use `kwargs.get("input")`, snapshots use Responses shape); `pytest` passes
|
||||
|
||||
See [references/cheat-sheet.md](./references/cheat-sheet.md) for complete before/after code examples.
|
||||
|
||||
## References
|
||||
|
||||
- [Azure OpenAI Responses API docs](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/responses)
|
||||
- [OpenAI Responses API reference](https://platform.openai.com/docs/api-reference/responses)
|
||||
- [Source skill: Azure-Samples/azure-openai-to-responses](https://github.com/Azure-Samples/azure-openai-to-responses)
|
||||
@@ -0,0 +1,246 @@
|
||||
# Responses API Cheat Sheet (Python + Azure OpenAI)
|
||||
|
||||
> All snippets assume `deployment = os.environ["AZURE_OPENAI_DEPLOYMENT"]` and `client` is already initialized.
|
||||
|
||||
Installed from [Azure-Samples/azure-openai-to-responses](https://github.com/Azure-Samples/azure-openai-to-responses) (MIT).
|
||||
|
||||
## Client setup — API key
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
)
|
||||
```
|
||||
|
||||
## Client setup — EntraID (recommended)
|
||||
|
||||
```python
|
||||
import os
|
||||
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
|
||||
from openai import OpenAI
|
||||
|
||||
token_provider = get_bearer_token_provider(
|
||||
DefaultAzureCredential(),
|
||||
"https://cognitiveservices.azure.com/.default",
|
||||
)
|
||||
client = OpenAI(
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
api_key=token_provider,
|
||||
)
|
||||
```
|
||||
|
||||
## Async client setup
|
||||
|
||||
```python
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
client = AsyncOpenAI(
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
api_key=token_provider, # or api_key=os.environ["AZURE_OPENAI_API_KEY"]
|
||||
)
|
||||
```
|
||||
|
||||
## Basic request
|
||||
|
||||
```python
|
||||
resp = client.responses.create(
|
||||
model=deployment,
|
||||
input="Hello",
|
||||
max_output_tokens=1000,
|
||||
store=False,
|
||||
)
|
||||
print(resp.output_text)
|
||||
```
|
||||
|
||||
## Full sync migration — before/after
|
||||
|
||||
Before (Chat Completions):
|
||||
```python
|
||||
from openai import AzureOpenAI
|
||||
client = AzureOpenAI(
|
||||
api_version=os.environ["AZURE_OPENAI_API_VERSION"],
|
||||
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
|
||||
api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
||||
)
|
||||
resp = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
max_tokens=500,
|
||||
)
|
||||
print(resp.choices[0].message.content)
|
||||
```
|
||||
|
||||
After (Responses API):
|
||||
```python
|
||||
from openai import OpenAI
|
||||
deployment = os.environ["AZURE_OPENAI_DEPLOYMENT"]
|
||||
client = OpenAI(
|
||||
api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
)
|
||||
resp = client.responses.create(
|
||||
model=deployment,
|
||||
input="Hello",
|
||||
max_output_tokens=1000,
|
||||
store=False,
|
||||
)
|
||||
print(resp.output_text)
|
||||
```
|
||||
|
||||
## Multi-turn conversation
|
||||
|
||||
```python
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful coding assistant."},
|
||||
{"role": "user", "content": "Write a Python function to calculate factorial"},
|
||||
]
|
||||
response = client.responses.create(model=deployment, input=messages, max_output_tokens=400)
|
||||
messages.append({"role": "assistant", "content": response.output_text})
|
||||
messages.append({"role": "user", "content": "Now optimize it with memoization"})
|
||||
response2 = client.responses.create(model=deployment, input=messages, max_output_tokens=400)
|
||||
print(response2.output_text)
|
||||
```
|
||||
|
||||
## Streaming (sync)
|
||||
|
||||
```python
|
||||
stream = client.responses.create(
|
||||
model=deployment,
|
||||
input="Explain streaming in simple terms",
|
||||
max_output_tokens=1000,
|
||||
stream=True,
|
||||
)
|
||||
for event in stream:
|
||||
if event.type == "response.output_text.delta":
|
||||
print(event.delta, end="", flush=True)
|
||||
elif event.type == "response.completed":
|
||||
print()
|
||||
```
|
||||
|
||||
## Streaming (async)
|
||||
|
||||
```python
|
||||
stream = await client.responses.create(
|
||||
model=deployment, input="...", max_output_tokens=1000, stream=True,
|
||||
)
|
||||
async for event in stream:
|
||||
if event.type == "response.output_text.delta":
|
||||
print(event.delta, end="", flush=True)
|
||||
elif event.type == "response.completed":
|
||||
print()
|
||||
```
|
||||
|
||||
## Structured output — JSON Schema
|
||||
|
||||
```python
|
||||
resp = client.responses.create(
|
||||
model=deployment,
|
||||
input="What is the capital of France?",
|
||||
max_output_tokens=500,
|
||||
text={
|
||||
"format": {
|
||||
"type": "json_schema",
|
||||
"name": "Output",
|
||||
"strict": True,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"answer": {"type": "string"}},
|
||||
"required": ["answer"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
}
|
||||
},
|
||||
store=False,
|
||||
)
|
||||
import json
|
||||
data = json.loads(resp.output_text)
|
||||
```
|
||||
|
||||
## Tools (flat Responses format)
|
||||
|
||||
```python
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "lookup_weather",
|
||||
"description": "Lookup the weather for a given city name.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"city_name": {"type": "string", "description": "The city name"}},
|
||||
"required": ["city_name"],
|
||||
"additionalProperties": False,
|
||||
},
|
||||
}
|
||||
]
|
||||
response = client.responses.create(
|
||||
model=deployment,
|
||||
input=[
|
||||
{"role": "system", "content": "You are a weather chatbot."},
|
||||
{"role": "user", "content": "What's the weather in Berkeley?"},
|
||||
],
|
||||
tools=tools,
|
||||
tool_choice="auto",
|
||||
store=False,
|
||||
)
|
||||
```
|
||||
|
||||
Tool call round-trip:
|
||||
```python
|
||||
import json
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a weather chatbot."},
|
||||
{"role": "user", "content": "Is it sunny in Berkeley?"},
|
||||
]
|
||||
response = client.responses.create(model=deployment, input=messages, tools=tools, store=False)
|
||||
tool_calls = [item for item in response.output if item.type == "function_call"]
|
||||
if tool_calls:
|
||||
messages.extend(response.output) # append the model's function_call items
|
||||
for tc in tool_calls:
|
||||
result = execute_tool(tc.name, json.loads(tc.arguments))
|
||||
messages.append({
|
||||
"type": "function_call_output",
|
||||
"call_id": tc.call_id,
|
||||
"output": json.dumps(result),
|
||||
})
|
||||
response = client.responses.create(model=deployment, input=messages, tools=tools, store=False)
|
||||
print(response.output_text)
|
||||
```
|
||||
|
||||
> `openai.pydantic_function_tool()` still generates the old nested format and is **not** compatible with `responses.create()`. Define tool schemas manually.
|
||||
|
||||
## Image input
|
||||
|
||||
Before (Chat): `{"type": "image_url", "image_url": {"url": "..."}}`
|
||||
After (Responses): `{"type": "input_image", "image_url": "..."}` (flat string — HTTPS URL or `data:image/...;base64,...`). Also `{"type": "text"}` → `{"type": "input_text"}`.
|
||||
|
||||
```python
|
||||
resp = client.responses.create(
|
||||
model=deployment,
|
||||
input=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "input_text", "text": "What's in this image?"},
|
||||
{"type": "input_image", "image_url": "https://example.com/image.jpg"},
|
||||
],
|
||||
}
|
||||
],
|
||||
max_output_tokens=500,
|
||||
store=False,
|
||||
)
|
||||
print(resp.output_text)
|
||||
```
|
||||
|
||||
## O-series (o1, o3-mini, o3, o4-mini)
|
||||
|
||||
| Chat Completions | Responses API | Notes |
|
||||
|---|---|---|
|
||||
| `max_completion_tokens` | `max_output_tokens` | Set high (4096+) |
|
||||
| `reasoning_effort` | `reasoning={"effort": ...}` | keep low/medium/high |
|
||||
| `temperature` | remove or set to `1` | o-series only accepts 1 |
|
||||
| `top_p` | remove | not supported |
|
||||
| `seed` | remove | not supported |
|
||||
@@ -0,0 +1,94 @@
|
||||
name: Code Quality
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '**.py'
|
||||
- '**.ts'
|
||||
- '**.js'
|
||||
- 'pyproject.toml'
|
||||
- '.eslintrc.json'
|
||||
- '.github/workflows/code-quality.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '**.py'
|
||||
- '**.ts'
|
||||
- '**.js'
|
||||
- 'pyproject.toml'
|
||||
- '.eslintrc.json'
|
||||
- '.github/workflows/code-quality.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
python-quality:
|
||||
name: Python Lint & Format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install linters
|
||||
run: python -m pip install --upgrade ruff black
|
||||
|
||||
# Enforced: the maintained shared utilities module must stay clean.
|
||||
- name: Ruff lint (shared utilities)
|
||||
run: ruff check shared/
|
||||
|
||||
- name: Black format check (shared utilities)
|
||||
run: black --check shared/
|
||||
|
||||
# Advisory: surface issues across the rest of the curriculum without
|
||||
# failing the build (lesson samples are intentionally kept simple).
|
||||
- name: Ruff lint (full repository, advisory)
|
||||
continue-on-error: true
|
||||
run: ruff check .
|
||||
|
||||
python-tests:
|
||||
name: Python Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install test dependencies
|
||||
run: python -m pip install pytest openai requests python-dotenv
|
||||
|
||||
- name: Run pytest (shared utilities)
|
||||
run: pytest tests/
|
||||
|
||||
|
||||
js-quality:
|
||||
name: JavaScript/TypeScript Lint (advisory)
|
||||
runs-on: ubuntu-latest
|
||||
# Advisory only: educational samples are not held to strict lint rules.
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install ESLint
|
||||
run: npm install --no-save eslint@8 @typescript-eslint/parser @typescript-eslint/eslint-plugin
|
||||
|
||||
- name: Run ESLint
|
||||
run: npx eslint . --ext .js,.ts
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Lock closed issue
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [closed]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: OSDKDev/lock-issues@v1.2
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Security Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
# Weekly scan every Monday at 06:00 UTC
|
||||
- cron: '0 6 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
codeql:
|
||||
name: CodeQL Analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
actions: read
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language:
|
||||
- javascript-typescript
|
||||
- python
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
|
||||
dependency-review:
|
||||
name: Dependency Review
|
||||
runs-on: ubuntu-latest
|
||||
# Dependency review only runs on pull requests.
|
||||
if: github.event_name == 'pull_request'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@v4
|
||||
with:
|
||||
comment-summary-in-pr: on-failure
|
||||
@@ -0,0 +1,30 @@
|
||||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '35 8 * * *'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue has not seen any action for a while! Closing for now, but it can be reopened at a later date.'
|
||||
stale-pr-message: 'This PR has not seen any action for a while! Closing for now, but it can be reopened at a later date.'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
||||
days-before-stale: 30 # Added parameter to control inactivity period
|
||||
days-before-close: 7 # Optional: Time after marking stale before closing
|
||||
@@ -0,0 +1,106 @@
|
||||
name: Validate Markdown
|
||||
|
||||
on:
|
||||
# Trigger the workflow on pull request
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '**.md'
|
||||
- '**.ipynb'
|
||||
- '!translations/**'
|
||||
- '!translated_images/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
check-broken-paths:
|
||||
name: Check Broken Relative Paths
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v7
|
||||
- name: Check broken Paths
|
||||
id: check-broken-paths
|
||||
uses: john0isaac/action-check-markdown@v1.3.1
|
||||
with:
|
||||
command: check_broken_paths
|
||||
directory: ./
|
||||
guide-url: 'https://github.com/microsoft/generative-ai-for-beginners/blob/main/CONTRIBUTING.md'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
check-paths-tracking:
|
||||
if: ${{ always() }}
|
||||
needs: check-broken-paths
|
||||
name: Check Paths Have Tracking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v7
|
||||
- name: Run Check paths tracking
|
||||
id: check-paths-tracking
|
||||
uses: john0isaac/action-check-markdown@v1.3.1
|
||||
with:
|
||||
command: check_paths_tracking
|
||||
directory: ./
|
||||
guide-url: 'https://github.com/microsoft/generative-ai-for-beginners/blob/main/CONTRIBUTING.md'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
check-urls-tracking:
|
||||
if: ${{ always() }}
|
||||
needs: check-paths-tracking
|
||||
name: Check URLs Have Tracking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Collect changed markdown files
|
||||
id: changed-markdown-files
|
||||
run: |
|
||||
mapfile -d '' -t files < <(git diff --name-only -z --diff-filter=ACMR "${{ github.event.pull_request.base.sha }}"..."${{ github.event.pull_request.head.sha }}" -- '**.md' '**.ipynb' ':(exclude)translations/**' ':(exclude)translated_images/**')
|
||||
if [ ${#files[@]} -eq 0 ]; then
|
||||
echo "has_files=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
printf '%s\0' "${files[@]}" > changed_markdown_files.txt
|
||||
echo "has_files=true" >> "$GITHUB_OUTPUT"
|
||||
- name: Run Check URLs tracking
|
||||
if: steps.changed-markdown-files.outputs.has_files == 'true'
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install markdown-checker
|
||||
mapfile -d '' -t files < changed_markdown_files.txt
|
||||
markdown-checker -f check_urls_tracking --guide-url 'https://github.com/microsoft/generative-ai-for-beginners/blob/main/CONTRIBUTING.md' "${files[@]}"
|
||||
check-urls-locale:
|
||||
if: ${{ always() }}
|
||||
needs: check-urls-tracking
|
||||
name: Check URLs Don't Have Locale
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v7
|
||||
- name: Run Check URLs Country Locale
|
||||
id: check-urls-locale
|
||||
uses: john0isaac/action-check-markdown@v1.3.1
|
||||
with:
|
||||
command: check_urls_locale
|
||||
directory: ./
|
||||
guide-url: 'https://github.com/microsoft/generative-ai-for-beginners/blob/main/CONTRIBUTING.md'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
check-broken-urls:
|
||||
if: ${{ always() }}
|
||||
name: Check Broken URLs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v7
|
||||
- name: Run Check Broken URLs
|
||||
id: check-broken-urls
|
||||
uses: john0isaac/action-check-markdown@v1.3.1
|
||||
with:
|
||||
command: check_broken_urls
|
||||
directory: ./
|
||||
guide-url: 'https://github.com/microsoft/generative-ai-for-beginners/blob/main/CONTRIBUTING.md'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Welcome to the Microsoft Generative AI
|
||||
on:
|
||||
# Trigger the workflow on new issue
|
||||
issues:
|
||||
types: [opened]
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
jobs:
|
||||
assess-issue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add Label and thanks comment to Issue
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const issueAuthor = context.payload.sender.login
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['needs-review']
|
||||
})
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `👋 Thanks for contributing @${ issueAuthor }! We will review the issue and get back to you soon.`
|
||||
})
|
||||
- name: Auto-assign issue
|
||||
uses: pozil/auto-assign-issue@v4
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
assignees: koreyspace
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Welcome to the Microsoft Generative AI
|
||||
on:
|
||||
# Trigger the workflow on pull request
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
jobs:
|
||||
assess-pull-request:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add Label and thanks comment to Pull request
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const issueAuthor = context.payload.sender.login
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['needs-review']
|
||||
})
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `👋 Thanks for contributing @${ issueAuthor }! We will review the pull request and get back to you soon.`
|
||||
})
|
||||
- name: Auto-assign pull request
|
||||
uses: pozil/auto-assign-issue@v4
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
assignees: koreyspace
|
||||
@@ -0,0 +1,168 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
node_modules/
|
||||
|
||||
.DS_Store
|
||||
|
||||
# downloaded stuff for 08-building-search-applications
|
||||
08-building-search-applications/scripts/transcripts_the_ai_show/
|
||||
.tool-versions
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
3.12.10
|
||||
@@ -0,0 +1 @@
|
||||
LLMs
|
||||
@@ -0,0 +1,47 @@
|
||||
# Cloud Setup ☁️ – GitHub Codespaces
|
||||
|
||||
**Use this guide if you don’t want to install anything locally.**
|
||||
Codespaces gives you a free, browser-based VS Code instance with all dependencies pre-installed.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why Codespaces?
|
||||
|
||||
| Benefit | What it means for you |
|
||||
|---------|----------------------|
|
||||
| ✅ Zero installs | Works on Chromebook, iPad, school lab PCs… |
|
||||
| ✅ Pre-built dev container | Python 3, Node.js, .NET, Java already inside |
|
||||
| ✅ Free quota | Personal accounts get **120 core-hours / 60 GB-hours per month** |
|
||||
|
||||
> 💡 **Tip**
|
||||
> Keep your quota healthy by **stopping** or **deleting** idle codespaces
|
||||
> (View ▸ Command Palette ▸ *Codespaces: Stop Codespace*).
|
||||
|
||||
---
|
||||
|
||||
## 2. Create a Codespace (one click)
|
||||
|
||||
1. **Fork** this repo (top-right **Fork** button).
|
||||
2. In your fork, click **Code ▸ Codespaces ▸ Create codespace on main**.
|
||||

|
||||
|
||||
✅ A browser VS Code window opens and the dev container starts building.
|
||||
This takes **~2 minutes** the first time.
|
||||
|
||||
## 3. Add your API key (the safe way)
|
||||
|
||||
### Option A Codespaces Secrets — Recommended
|
||||
|
||||
1. ⚙️ Gear icon -> Command Pallete-> Codespaces : Manage user secret -> Add a new secret.
|
||||
2. Name: OPENAI_API_KEY
|
||||
3. Value: paste your key → Add secret
|
||||
|
||||
That’s it—our code will pick it up automatically.
|
||||
|
||||
### Option B .env file (if you really need one)
|
||||
|
||||
```bash
|
||||
cp .env.copy .env
|
||||
code .env # fill in OPENAI_API_KEY=your_key_here
|
||||
```
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
# Local Setup 🖥️
|
||||
|
||||
**Use this guide if you prefer to run everything on your own laptop.**
|
||||
You have two paths: **(A) native Python + virtual-env** or **(B) VS Code Dev Container with Docker**.
|
||||
Choose whichever feels easier—both lead to the same lessons.
|
||||
|
||||
## 1. Prerequisites
|
||||
|
||||
| Tool | Version / Notes |
|
||||
|--------------------|--------------------------------------------------------------------------------------|
|
||||
| **Python** | 3.10 + (get it from <https://python.org>) |
|
||||
| **Git** | Latest (comes with Xcode / Git for Windows / Linux package manager) |
|
||||
| **VS Code** | Optional but recommended <https://code.visualstudio.com> |
|
||||
| **Docker Desktop** | *Only* for Option B. Free install: <https://docs.docker.com/desktop/> |
|
||||
|
||||
> 💡 **Tip** – Verify tools in a terminal:
|
||||
> `python --version`, `git --version`, `docker --version`, `code --version`
|
||||
|
||||
## 2. Option A – Native Python (quickest)
|
||||
|
||||
### Step 1 Clone this repo
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<your-github>/generative-ai-for-beginners
|
||||
cd generative-ai-for-beginners
|
||||
```
|
||||
|
||||
### Step 2 Create & activate a virtual environment
|
||||
|
||||
```bash
|
||||
python -m venv .venv # make one
|
||||
source .venv/bin/activate # macOS / Linux
|
||||
.\.venv\Scripts\activate # Windows PowerShell
|
||||
```
|
||||
|
||||
✅ Prompt should now start with (.venv)—that means you’re inside the env.
|
||||
|
||||
### Step 3 Install dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Skip to Section 3 on [API keys](#3-add-your-api-keys)
|
||||
|
||||
## 2. Option B – VS Code Dev Container (Docker)
|
||||
|
||||
We setup this repository and course with a [development container](https://containers.dev?WT.mc_id=academic-105485-koreyst) that has a Universal runtime that can support Python3, .NET, Node.js and Java development. The related configuration is defined in the `devcontainer.json` file located in the `.devcontainer/` folder at the root of this repository.
|
||||
|
||||
>**Why choose this?**
|
||||
>Identical environment to Codespaces; no dependency drift.
|
||||
|
||||
### Step 0 Install the extras
|
||||
|
||||
Docker Desktop – confirm ```docker --version``` works.
|
||||
VS Code Remote – Containers extension (ID: ms-vscode-remote.remote-containers).
|
||||
|
||||
### Step 1 Open the repo in VS Code
|
||||
|
||||
File ▸ Open Folder… → generative-ai-for-beginners
|
||||
|
||||
VS Code detects .devcontainer/ and pops a prompt.
|
||||
|
||||
### Step 2 Reopen in container
|
||||
|
||||
Click “Reopen in Container”. Docker builds the image (≈ 3 min first time).
|
||||
When the terminal prompt appears, you’re inside the container.
|
||||
|
||||
## 2. Option C – Miniconda
|
||||
|
||||
[Miniconda](https://conda.io/en/latest/miniconda.html?WT.mc_id=academic-105485-koreyst) is a lightweight installer for installing [Conda](https://docs.conda.io/en/latest?WT.mc_id=academic-105485-koreyst), Python, as well as a few packages.
|
||||
Conda itself is a package manager, that makes it easy to setup and switch between different Python [**virtual environments**](https://docs.python.org/3/tutorial/venv.html?WT.mc_id=academic-105485-koreyst) and packages. It also comes in handy for installing packages that are not available via `pip`.
|
||||
|
||||
### Step 0 Install Miniconda
|
||||
|
||||
Follow the [MiniConda installation guide](https://docs.anaconda.com/free/miniconda/#quick-command-line-install?WT.mc_id=academic-105485-koreyst) to set it up.
|
||||
|
||||
```bash
|
||||
conda --version
|
||||
```
|
||||
|
||||
### Step 1 Create a virtual environment
|
||||
|
||||
Create a new environment file (*environment.yml*). If you are following along using Codespaces, create this within the `.devcontainer` directory, thus `.devcontainer/environment.yml`.
|
||||
|
||||
### Step 2 Populate your environment file
|
||||
|
||||
Add the following snippet to your `environment.yml`
|
||||
|
||||
```yml
|
||||
name: <environment-name>
|
||||
channels:
|
||||
- defaults
|
||||
- microsoft
|
||||
dependencies:
|
||||
- python=<python-version>
|
||||
- openai
|
||||
- python-dotenv
|
||||
- pip
|
||||
- pip:
|
||||
- azure-ai-ml
|
||||
|
||||
```
|
||||
|
||||
### Step 3 Create your Conda environment
|
||||
|
||||
Run the commands below in your command line/terminal
|
||||
|
||||
```bash
|
||||
conda env create --name ai4beg --file .devcontainer/environment.yml # .devcontainer sub path applies to only Codespace setups
|
||||
conda activate ai4beg
|
||||
```
|
||||
|
||||
Refer to the [Conda environments guide](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html?WT.mc_id=academic-105485-koreyst) if you run into any issues.
|
||||
|
||||
## 2 Option D – Classic Jupyter / Jupyter Lab (in your browser)
|
||||
|
||||
> **Who’s this for?**
|
||||
> Anyone who loves the classic Jupyter interface or wants to run notebooks without VS Code.
|
||||
|
||||
### Step 1 Ensure Jupyter is installed
|
||||
|
||||
To start Jupyter locally, head over to the terminal/command line, navigate to the course directory, and execute:
|
||||
|
||||
```bash
|
||||
jupyter notebook
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
jupyterhub
|
||||
```
|
||||
|
||||
This will start a Jupyter instance and the URL to access it will be shown within the command line window.
|
||||
|
||||
Once you access the URL, you should see the course outline and be able to navigate to any `*.ipynb` file. For example, `08-building-search-applications/python/oai-solution.ipynb`.
|
||||
|
||||
## 3. Add Your API Keys
|
||||
|
||||
Keeping your API keys safe and secure is important when building any type of application. We recommend not to store any API keys directly in your code. Committing those details to a public repository could result in security issues and even unwanted costs if used by a bad actor.
|
||||
Here's a step-by-step guide on how to create a `.env` file for Python and add your Microsoft Foundry Models credentials:
|
||||
|
||||
> **Note:** GitHub Models (and its `GITHUB_TOKEN` variable) is retiring at the end of July 2026. This guide uses [Microsoft Foundry Models](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst) instead. Prefer to work fully offline? See [Foundry Local](https://foundrylocal.ai?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
1. **Navigate to Your Project Directory**: Open your terminal or command prompt and navigate to your project's root directory where you want to create the `.env` file.
|
||||
|
||||
```bash
|
||||
cd path/to/your/project
|
||||
```
|
||||
|
||||
2. **Create the `.env` File**: Use your preferred text editor to create a new file named `.env`. If you're using the command line, you can use `touch` (on Unix-based systems) or `echo` (on Windows):
|
||||
|
||||
Unix-based systems:
|
||||
|
||||
```bash
|
||||
touch .env
|
||||
```
|
||||
|
||||
Windows:
|
||||
|
||||
```cmd
|
||||
echo . > .env
|
||||
```
|
||||
|
||||
3. **Edit the `.env` File**: Open the `.env` file in a text editor (e.g., VS Code, Notepad++, or any other editor). Add the following lines to the file, replacing the placeholders with your actual Microsoft Foundry project endpoint and API key:
|
||||
|
||||
```env
|
||||
AZURE_INFERENCE_ENDPOINT=your_foundry_endpoint_here
|
||||
AZURE_INFERENCE_CREDENTIAL=your_foundry_api_key_here
|
||||
```
|
||||
|
||||
4. **Save the File**: Save the changes and close the text editor.
|
||||
|
||||
5. **Install `python-dotenv`**: If you haven't already, you'll need to install the `python-dotenv` package to load environment variables from the `.env` file into your Python application. You can install it using `pip`:
|
||||
|
||||
```bash
|
||||
pip install python-dotenv
|
||||
```
|
||||
|
||||
6. **Load Environment Variables in Your Python Script**: In your Python script, use the `python-dotenv` package to load the environment variables from the `.env` file:
|
||||
|
||||
```python
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# Access the Microsoft Foundry Models variables
|
||||
endpoint = os.getenv("AZURE_INFERENCE_ENDPOINT")
|
||||
token = os.getenv("AZURE_INFERENCE_CREDENTIAL")
|
||||
|
||||
print(endpoint)
|
||||
```
|
||||
|
||||
That's it! You've successfully created a `.env` file, added your Microsoft Foundry Models credentials, and loaded them into your Python application.
|
||||
|
||||
🔐 Never commit .env—it’s already in .gitignore.
|
||||
Full provider instructions live in [`providers.md`](03-providers.md).
|
||||
|
||||
## 4. What’s next?
|
||||
|
||||
| I want to… | Go to… |
|
||||
|---------------------|-------------------------------------------------------------------------|
|
||||
| Start Lesson 1 | [`01-introduction-to-genai`](../01-introduction-to-genai/README.md) |
|
||||
| Setup an LLM Provider | [`providers.md`](03-providers.md) |
|
||||
| Meet other learners | [Join our Discord](https://aka.ms/genai-discord?WT.mc_id=academic-105485-koreyst) |
|
||||
|
||||
## 5. Troubleshooting
|
||||
|
||||
| Symptom | Fix |
|
||||
|-------------------------------------------|-----------------------------------------------------------------|
|
||||
| `python not found` | Add Python to PATH or re-open terminal after install |
|
||||
| `pip` cannot build wheels (Windows) | `pip install --upgrade pip setuptools wheel` then retry. |
|
||||
| `ModuleNotFoundError: dotenv` | Run `pip install -r requirements.txt` (env wasn’t installed). |
|
||||
| Docker build fails *No space left* | Docker Desktop ▸ *Settings* ▸ *Resources* → increase disk size. |
|
||||
| VS Code keeps prompting to reopen | You may have both Options active; choose one (venv **or** container)|
|
||||
| OpenAI 401 / 429 errors | Check `OPENAI_API_KEY` value / request rate limits. |
|
||||
| Errors using Conda | Install Microsoft AI libraries using `conda install -c microsoft azure-ai-ml`|
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
# Choosing & Configuring an LLM Provider 🔑
|
||||
|
||||
Assignments **may** also be setup to work against one or more Large Language Model (LLM) deployments through a supported service provider like OpenAI, Azure or Hugging Face. These provide a _hosted endpoint_ (API) that we can access programmatically with the right credentials (API key or token). In this course, we discuss these providers:
|
||||
|
||||
- [OpenAI](https://platform.openai.com/docs/models?WT.mc_id=academic-105485-koreyst) with diverse models including the core GPT series.
|
||||
- [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/?WT.mc_id=academic-105485-koreyst) for OpenAI models with enterprise readiness in focus
|
||||
- [Microsoft Foundry Models](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst) for a single endpoint and API key to access hundreds of models from OpenAI, Meta, Mistral, Cohere, Microsoft and more (replaces GitHub Models, which is retiring at the end of July 2026)
|
||||
- [Hugging Face](https://huggingface.co/docs/hub/index?WT.mc_id=academic-105485-koreyst) for open-source models and inference server
|
||||
- [Foundry Local](https://foundrylocal.ai?WT.mc_id=academic-105485-koreyst) or [Ollama](https://ollama.com/?WT.mc_id=academic-105485-koreyst) if you'd rather run models fully offline on your own device, with no cloud subscription required
|
||||
|
||||
**You will need to use your own accounts for these exercises**. Assignments are optional so you can choose to setup one, all - or none - of the providers based on your interests. Some guidance for signup:
|
||||
|
||||
| Signup | Cost | API Key | Playground | Comments |
|
||||
|:---|:---|:---|:---|:---|
|
||||
| [OpenAI](https://platform.openai.com/signup?WT.mc_id=academic-105485-koreyst)| [Pricing](https://openai.com/pricing#language-models?WT.mc_id=academic-105485-koreyst)| [Project-based](https://platform.openai.com/api-keys?WT.mc_id=academic-105485-koreyst) | [No-Code, Web](https://platform.openai.com/playground?WT.mc_id=academic-105485-koreyst) | Multiple Models Available |
|
||||
| [Azure](https://aka.ms/azure/free?WT.mc_id=academic-105485-koreyst)| [Pricing](https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/?WT.mc_id=academic-105485-koreyst)| [SDK Quickstart](https://learn.microsoft.com/azure/ai-services/openai/quickstart?WT.mc_id=academic-105485-koreyst)| [Studio Quickstart](https://learn.microsoft.com/azure/ai-services/openai/quickstart?WT.mc_id=academic-105485-koreyst) | [Must Apply Ahead For Access](https://learn.microsoft.com/azure/ai-services/openai/?WT.mc_id=academic-105485-koreyst)|
|
||||
| [Microsoft Foundry](https://ai.azure.com?WT.mc_id=academic-105485-koreyst) | [Pricing](https://azure.microsoft.com/pricing/details/ai-foundry/?WT.mc_id=academic-105485-koreyst) | [Project Overview page](https://learn.microsoft.com/en-us/azure/ai-foundry/model-inference/overview?WT.mc_id=academic-105485-koreyst) | [Foundry Playground](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst) | Free tier available; one endpoint + key for many model providers |
|
||||
| [Hugging Face](https://huggingface.co/join?WT.mc_id=academic-105485-koreyst) | [Pricing](https://huggingface.co/pricing) | [Access Tokens](https://huggingface.co/docs/hub/security-tokens?WT.mc_id=academic-105485-koreyst) | [Hugging Chat](https://huggingface.co/chat/?WT.mc_id=academic-105485-koreyst)| [Hugging Chat has limited models](https://huggingface.co/chat/models?WT.mc_id=academic-105485-koreyst) |
|
||||
| [Foundry Local](https://foundrylocal.ai?WT.mc_id=academic-105485-koreyst) | Free (runs on your device) | Not required | [Local CLI/SDK](https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/get-started?WT.mc_id=academic-105485-koreyst) | Fully offline, OpenAI-compatible endpoint |
|
||||
| | | | | |
|
||||
|
||||
Follow the directions below to _configure_ this repository for use with different providers. Assignments that require a specific provider will contain one of these tags in their filename:
|
||||
|
||||
- `aoai` - requires Azure OpenAI endpoint, key
|
||||
- `oai` - requires OpenAI endpoint, key
|
||||
- `hf` - requires Hugging Face token
|
||||
- `githubmodels` - requires Microsoft Foundry Models endpoint, key (GitHub Models is retiring at the end of July 2026)
|
||||
|
||||
You can configure one, none, or all providers. Related assignments will simply error out on missing credentials.
|
||||
|
||||
## Create `.env` file
|
||||
|
||||
We assume that you have already read the guidance above and signed up with the relevant provider, and obtained the required authentication credentials (API_KEY or token). In the case of Azure OpenAI, we assume you also have a valid deployment of an Azure OpenAI Service (endpoint) with at least one GPT model deployed for chat completion.
|
||||
|
||||
The next step is to configure your **local environment variables** as follows:
|
||||
|
||||
1. Look in the root folder for a `.env.copy` file that should have contents like this:
|
||||
|
||||
```bash
|
||||
# OpenAI Provider
|
||||
OPENAI_API_KEY='<add your OpenAI API key here>'
|
||||
|
||||
## Azure OpenAI in Microsoft Foundry
|
||||
## (Azure OpenAI Service is now part of Microsoft Foundry: https://ai.azure.com)
|
||||
AZURE_OPENAI_API_VERSION='2024-10-21' # Default is set! (current stable GA API version)
|
||||
AZURE_OPENAI_API_KEY='<add your Foundry resource key here>'
|
||||
AZURE_OPENAI_ENDPOINT='<add your Foundry resource endpoint here, e.g. https://<resource-name>.openai.azure.com>'
|
||||
AZURE_OPENAI_DEPLOYMENT='<add your chat completion model deployment name here, e.g. gpt-4o-mini>'
|
||||
AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT='<add your embeddings model deployment name here, e.g. text-embedding-3-small>'
|
||||
|
||||
## Microsoft Foundry Models (multi-provider model catalog, replaces GitHub Models, which retires end of July 2026)
|
||||
AZURE_INFERENCE_ENDPOINT='<add your Microsoft Foundry project endpoint here>'
|
||||
AZURE_INFERENCE_CREDENTIAL='<add your Microsoft Foundry Models API key here>'
|
||||
|
||||
## Hugging Face
|
||||
HUGGING_FACE_API_KEY='<add your HuggingFace API or token here>'
|
||||
```
|
||||
|
||||
2. Copy that file to `.env` using the command below. This file is _gitignore-d_, keeping secrets safe.
|
||||
|
||||
```bash
|
||||
cp .env.copy .env
|
||||
```
|
||||
|
||||
3. Fill in the values (replace placeholders on right side of `=`) as described in the next section.
|
||||
|
||||
4. (Option) If you use GitHub Codespaces, you have the option to save environment variables as _Codespaces secrets_ associated with this repository. In that case, you won't need to setup a local .env file. **However, note that this option works only if you use GitHub Codespaces.** You will still need to setup the .env file if you use Docker Desktop instead.
|
||||
|
||||
## Populate `.env` file
|
||||
|
||||
Let's take a quick look at the variable names to understand what they represent:
|
||||
|
||||
| Variable | Description |
|
||||
| :--- | :--- |
|
||||
| HUGGING_FACE_API_KEY | This is the user access token you setup in your profile |
|
||||
| OPENAI_API_KEY | This is the authorization key for using the service for non-Azure OpenAI endpoints |
|
||||
| AZURE_OPENAI_API_KEY | This is the authorization key for using that service |
|
||||
| AZURE_OPENAI_ENDPOINT | This is the deployed endpoint for an Azure OpenAI resource |
|
||||
| AZURE_OPENAI_DEPLOYMENT | This is the _text generation_ model deployment endpoint |
|
||||
| AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT | This is the _text embeddings_ model deployment endpoint |
|
||||
| AZURE_INFERENCE_ENDPOINT | This is the endpoint for your Microsoft Foundry project, used for Microsoft Foundry Models |
|
||||
| AZURE_INFERENCE_CREDENTIAL | This is the API key for your Microsoft Foundry project |
|
||||
| | |
|
||||
|
||||
Note: The last two Azure OpenAI variables reflect a default model for chat completion (text generation) and vector search (embeddings) respectively. Instructions for setting them will be defined in relevant assignments.
|
||||
|
||||
## Configure Azure OpenAI: From Portal
|
||||
|
||||
> **Note:** Azure OpenAI Service is now part of [Microsoft Foundry](https://ai.azure.com?WT.mc_id=academic-105485-koreyst). Resources and deployments still show up in the Azure Portal, but day-to-day model management (deployments, playground, monitoring) now happens in the Foundry portal instead of the old standalone "Azure OpenAI Studio".
|
||||
|
||||
The Azure OpenAI endpoint and key values will be found in the [Azure Portal](https://portal.azure.com?WT.mc_id=academic-105485-koreyst) so let's start there.
|
||||
|
||||
1. Go to the [Azure Portal](https://portal.azure.com?WT.mc_id=academic-105485-koreyst)
|
||||
1. Click the **Keys and Endpoint** option in the sidebar (menu at left).
|
||||
1. Click **Show Keys** - you should see the following: KEY 1, KEY 2 and Endpoint.
|
||||
1. Use the KEY 1 value for AZURE_OPENAI_API_KEY
|
||||
1. Use the Endpoint value for AZURE_OPENAI_ENDPOINT
|
||||
|
||||
Next, we need the endpoints for the specific models we've deployed.
|
||||
|
||||
1. Click the **Model deployments** option in the sidebar (left menu) for Azure OpenAI resource.
|
||||
1. In the destination page, click **Go to Microsoft Foundry portal** (or **Manage Deployments**, depending on your resource type)
|
||||
|
||||
This will take you to the Microsoft Foundry portal, where we'll find the other values as described below.
|
||||
|
||||
## Configure Azure OpenAI: From Microsoft Foundry portal
|
||||
|
||||
1. Navigate to the [Microsoft Foundry portal](https://ai.azure.com?WT.mc_id=academic-105485-koreyst) **from your resource** as described above.
|
||||
1. Click the **Deployments** tab (sidebar, left) to view currently deployed models.
|
||||
1. If your desired model is not deployed, use **Deploy model** to deploy it from the [model catalog](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst).
|
||||
1. You will need a _text-generation_ model - we recommend: **gpt-4o-mini**
|
||||
1. You will need a _text-embedding_ model - we recommend **text-embedding-3-small**
|
||||
|
||||
Now update the environment variables to reflect the _Deployment name_ used. This will typically be the same as the model name unless you changed it explicitly. So, as an example, you might have:
|
||||
|
||||
```bash
|
||||
AZURE_OPENAI_DEPLOYMENT='gpt-4o-mini'
|
||||
AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT='text-embedding-3-small'
|
||||
```
|
||||
|
||||
**Don't forget to save the .env file when done**. You can now exit the file and return to the instructions for running the notebook.
|
||||
|
||||
## Configure OpenAI: From Profile
|
||||
|
||||
Your OpenAI API key can be found in your [OpenAI account](https://platform.openai.com/api-keys?WT.mc_id=academic-105485-koreyst). If you don't have one, you can sign up for an account and create an API key. Once you have the key, you can use it to populate the `OPENAI_API_KEY` variable in the `.env` file.
|
||||
|
||||
## Configure Hugging Face: From Profile
|
||||
|
||||
Your Hugging Face token can be found in your profile under [Access Tokens](https://huggingface.co/settings/tokens?WT.mc_id=academic-105485-koreyst). Don't post or share these publicly. Instead, create a new token for this project usage and copy that into the `.env` file under the `HUGGING_FACE_API_KEY` variable. _Note:_ This is technically not an API key but is used for authentication so we are keeping that naming convention for consistency.
|
||||
|
||||
## Configure Microsoft Foundry Models: From Portal
|
||||
|
||||
> **Note:** GitHub Models is retiring at the end of July 2026. Microsoft Foundry Models is the direct replacement, offering the same free-to-try model catalog and Azure AI Inference SDK / OpenAI SDK experience.
|
||||
|
||||
1. Go to [Microsoft Foundry](https://ai.azure.com?WT.mc_id=academic-105485-koreyst) and create (or open) a Foundry project.
|
||||
1. Browse the [model catalog](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst) and deploy a model, for example `gpt-4o-mini`.
|
||||
1. On the project's **Overview** page, copy the **endpoint** and **API key**.
|
||||
1. Use the endpoint value for `AZURE_INFERENCE_ENDPOINT` and the key value for `AZURE_INFERENCE_CREDENTIAL` in your `.env` file.
|
||||
|
||||
## Offline / Local Providers
|
||||
|
||||
If you'd rather not use a cloud subscription at all, you can run compatible open models directly on your own device:
|
||||
|
||||
- **[Foundry Local](https://foundrylocal.ai?WT.mc_id=academic-105485-koreyst)** - Microsoft's on-device runtime. It automatically selects the best execution provider (NPU, GPU, or CPU) and exposes an OpenAI-compatible endpoint, so you can reuse most of the sample code in this course with minimal changes. See the [Foundry Local documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/get-started?WT.mc_id=academic-105485-koreyst) to get started, or install with `winget install Microsoft.FoundryLocal` (Windows) / `brew install microsoft/foundrylocal/foundrylocal` (macOS).
|
||||
- **[Ollama](https://ollama.com/?WT.mc_id=academic-105485-koreyst)** - a popular alternative for running open models like Llama, Phi, Mistral, and Gemma locally.
|
||||
|
||||
See [Lesson 19: Building with SLMs](../19-slm/README.md?WT.mc_id=academic-105485-koreyst) for hands-on examples using both options.
|
||||
@@ -0,0 +1,232 @@
|
||||
# Getting Started with this course
|
||||
|
||||
We are very excited for you to start this course and see what you get inspired to build with Generative AI!
|
||||
|
||||
To ensure your success, this page outlines setup steps, technical requirements, and where to get help if needed.
|
||||
|
||||
## Setup Steps
|
||||
|
||||
To start taking this course, you will need to complete the following steps.
|
||||
|
||||
### 1. Fork this Repo
|
||||
|
||||
[Fork this entire repo](https://github.com/microsoft/generative-ai-for-beginners/fork?WT.mc_id=academic-105485-koreyst) to your own GitHub account to be able to change any code and complete the challenges. You can also [star (🌟) this repo](https://docs.github.com/en/get-started/exploring-projects-on-github/saving-repositories-with-stars?WT.mc_id=academic-105485-koreyst) to find it and related repos easier.
|
||||
|
||||
### 2. Create a codespace
|
||||
|
||||
To avoid any dependency issues when running the code, we recommend running this course in a [GitHub Codespaces](https://github.com/features/codespaces?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
In your fork: **Code -> Codespaces -> New on main**
|
||||
|
||||

|
||||
|
||||
#### 2.1 Add a secret
|
||||
|
||||
1. ⚙️ Gear icon -> Command Pallete-> Codespaces : Manage user secret -> Add a new secret.
|
||||
2. Name OPENAI_API_KEY, paste your key, Save.
|
||||
|
||||
### 3. What’s next?
|
||||
|
||||
| I want to… | Go to… |
|
||||
|---------------------|-------------------------------------------------------------------------|
|
||||
| Start Lesson 1 | [`01-introduction-to-genai`](../01-introduction-to-genai/README.md) |
|
||||
| Work offline | [`setup-local.md`](02-setup-local.md) |
|
||||
| Setup an LLM Provider | [`providers.md`](03-providers.md) |
|
||||
| Meet other learners | [Join our Discord](https://aka.ms/genai-discord?WT.mc_id=academic-105485-koreyst) |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
| Symptom | Fix |
|
||||
|-------------------------------------------|-----------------------------------------------------------------|
|
||||
| Container build stuck > 10 min | **Codespaces ➜ “Rebuild Container”** |
|
||||
| `python: command not found` | Terminal didn’t attach; click **+** ➜ *bash* |
|
||||
| `401 Unauthorized` from OpenAI | Wrong / expired `OPENAI_API_KEY` |
|
||||
| VS Code shows “Dev container mounting…” | Refresh the browser tab—Codespaces sometimes loses connection |
|
||||
| Notebook kernel missing | Notebook menu ➜ **Kernel ▸ Select Kernel ▸ Python 3** |
|
||||
|
||||
Unix-based systems:
|
||||
|
||||
```bash
|
||||
touch .env
|
||||
```
|
||||
|
||||
Windows:
|
||||
|
||||
```cmd
|
||||
echo . > .env
|
||||
```
|
||||
|
||||
3. **Edit the `.env` File**: Open the `.env` file in a text editor (e.g., VS Code, Notepad++, or any other editor). Add the following lines to the file, replacing the placeholders with your actual Microsoft Foundry Models endpoint and key (see [`providers.md`](03-providers.md) for how to get these):
|
||||
|
||||
> **Note:** GitHub Models (and its `GITHUB_TOKEN` variable) is retiring at the end of July 2026. Use [Microsoft Foundry Models](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst) instead.
|
||||
|
||||
```env
|
||||
AZURE_INFERENCE_ENDPOINT=your_foundry_endpoint_here
|
||||
AZURE_INFERENCE_CREDENTIAL=your_foundry_api_key_here
|
||||
```
|
||||
|
||||
4. **Save the File**: Save the changes and close the text editor.
|
||||
|
||||
5. **Install `python-dotenv`**: If you haven't already, you'll need to install the `python-dotenv` package to load environment variables from the `.env` file into your Python application. You can install it using `pip`:
|
||||
|
||||
```bash
|
||||
pip install python-dotenv
|
||||
```
|
||||
|
||||
6. **Load Environment Variables in Your Python Script**: In your Python script, use the `python-dotenv` package to load the environment variables from the `.env` file:
|
||||
|
||||
```python
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# Access the Microsoft Foundry Models variables
|
||||
endpoint = os.getenv("AZURE_INFERENCE_ENDPOINT")
|
||||
token = os.getenv("AZURE_INFERENCE_CREDENTIAL")
|
||||
|
||||
print(endpoint)
|
||||
```
|
||||
|
||||
That's it! You've successfully created a `.env` file, added your Microsoft Foundry Models credentials, and loaded them into your Python application.
|
||||
|
||||
## How to Run locally on your computer
|
||||
|
||||
To run the code locally on your computer, you would need to have some version of [Python installed](https://www.python.org/downloads/?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
To then use the repository, you need to clone it:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/microsoft/generative-ai-for-beginners
|
||||
cd generative-ai-for-beginners
|
||||
```
|
||||
|
||||
Once you have everything checked out, you can get started!
|
||||
|
||||
## Optional Steps
|
||||
|
||||
### Installing Miniconda
|
||||
|
||||
[Miniconda](https://conda.io/en/latest/miniconda.html?WT.mc_id=academic-105485-koreyst) is a lightweight installer for installing [Conda](https://docs.conda.io/en/latest?WT.mc_id=academic-105485-koreyst), Python, as well as a few packages.
|
||||
Conda itself is a package manager, that makes it easy to setup and switch between different Python [**virtual environments**](https://docs.python.org/3/tutorial/venv.html?WT.mc_id=academic-105485-koreyst) and packages. It also comes in handy for installing packages that are not available via `pip`.
|
||||
|
||||
You can follow the [MiniConda installation guide](https://docs.anaconda.com/free/miniconda/#quick-command-line-install?WT.mc_id=academic-105485-koreyst) to set it up.
|
||||
|
||||
With Miniconda installed, you need to clone the [repository](https://github.com/microsoft/generative-ai-for-beginners/fork?WT.mc_id=academic-105485-koreyst) (if you haven't already)
|
||||
|
||||
Next, you need to create a virtual environment. To do this with Conda, go ahead and create a new environment file (_environment.yml_). If you are following along using Codespaces, create this within the `.devcontainer` directory, thus `.devcontainer/environment.yml`.
|
||||
|
||||
Go ahead and populate your environment file with the snippet below:
|
||||
|
||||
```yml
|
||||
name: <environment-name>
|
||||
channels:
|
||||
- defaults
|
||||
- microsoft
|
||||
dependencies:
|
||||
- python=<python-version>
|
||||
- openai
|
||||
- python-dotenv
|
||||
- pip
|
||||
- pip:
|
||||
- azure-ai-ml
|
||||
```
|
||||
|
||||
If you find you are getting errors using conda you can manually install the Microsoft AI Libraries using the following command in a terminal.
|
||||
|
||||
```
|
||||
conda install -c microsoft azure-ai-ml
|
||||
```
|
||||
|
||||
The environment file specifies the dependencies we need. `<environment-name>` refers to the name you would like to use for your Conda environment, and `<python-version>` is the version of Python you would like to use, for example, `3` is the latest major version of Python.
|
||||
|
||||
With that done, you can go ahead and create your Conda environment by running the commands below in your command line/terminal
|
||||
|
||||
```bash
|
||||
conda env create --name ai4beg --file .devcontainer/environment.yml # .devcontainer sub path applies to only Codespace setups
|
||||
conda activate ai4beg
|
||||
```
|
||||
|
||||
Refer to the [Conda environments guide](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html?WT.mc_id=academic-105485-koreyst) if you run into any issues.
|
||||
|
||||
### Using Visual Studio Code with the Python support extension
|
||||
|
||||
We recommend using the [Visual Studio Code (VS Code)](https://code.visualstudio.com/?WT.mc_id=academic-105485-koreyst) editor with the [Python support extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python&WT.mc_id=academic-105485-koreyst) installed for this course. This is, however, more of a recommendation and not a definite requirement
|
||||
|
||||
> **Note**: By opening the course repository in VS Code, you have the option to set the project up within a container. This is because of the [special `.devcontainer`](https://code.visualstudio.com/docs/devcontainers/containers?itemName=ms-python.python&WT.mc_id=academic-105485-koreyst) directory found within the course repository. More on this later.
|
||||
|
||||
> **Note**: Once you clone and open the directory in VS Code, it will automatically suggest you install a Python support extension.
|
||||
|
||||
> **Note**: If VS Code suggests you re-open the repository in a container, decline this request in order to use the locally installed version of Python.
|
||||
|
||||
### Using Jupyter in the Browser
|
||||
|
||||
You can also work on the project using the [Jupyter environment](https://jupyter.org?WT.mc_id=academic-105485-koreyst) right within your browser. Both classic Jupyter and [Jupyter Hub](https://jupyter.org/hub?WT.mc_id=academic-105485-koreyst) provide quite a pleasant development environment with features such as auto-completion, code highlighting, etc.
|
||||
|
||||
To start Jupyter locally, head over to the terminal/command line, navigate to the course directory, and execute:
|
||||
|
||||
```bash
|
||||
jupyter notebook
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
jupyterhub
|
||||
```
|
||||
|
||||
This will start a Jupyter instance and the URL to access it will be shown within the command line window.
|
||||
|
||||
Once you access the URL, you should see the course outline and be able to navigate to any `*.ipynb` file. For example, `08-building-search-applications/python/oai-solution.ipynb`.
|
||||
|
||||
### Running in a container
|
||||
|
||||
An alternative to setting everything up on your computer or Codespace is to use a [container](<https://en.wikipedia.org/wiki/Containerization_(computing)?WT.mc_id=academic-105485-koreyst>). The special `.devcontainer` folder within the course repository makes it possible for VS Code to set up the project within a container. Outside of Codespaces, this will require the installation of Docker, and quite frankly, it involves a bit of work, so we recommend this only to those with experience working with containers.
|
||||
|
||||
One of the best ways to keep your API keys secure when using GitHub Codespaces is by using Codespace Secrets. Please follow the [Codespaces secrets management](https://docs.github.com/en/codespaces/managing-your-codespaces/managing-secrets-for-your-codespaces?WT.mc_id=academic-105485-koreyst) guide to learn more about this.
|
||||
|
||||
|
||||
## Lessons and Technical Requirements
|
||||
|
||||
The course has 6 concept lessons and 6 coding lessons.
|
||||
|
||||
For the coding lessons, we are using the Azure OpenAI Service. You will need access to the Azure OpenAI service and an API key to run this code. You can apply to get access by [completing this application](https://azure.microsoft.com/products/ai-services/openai-service?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
While you wait for your application to be processed, each coding lesson also includes a `README.md` file where you can view the code and outputs.
|
||||
|
||||
## Using the Azure OpenAI Service for the first time
|
||||
|
||||
If this is your first time working with the Azure OpenAI service, please follow this guide on how to [create and deploy an Azure OpenAI Service resource.](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource?pivots=web-portal&WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
## Using the OpenAI API for the first time
|
||||
|
||||
If this is your first time working with the OpenAI API, please follow the guide on how to [create and use the Interface.](https://platform.openai.com/docs/quickstart?context=pythont&WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
## Meet Other Learners
|
||||
|
||||
We have created channels in our official [AI Community Discord server](https://aka.ms/genai-discord?WT.mc_id=academic-105485-koreyst) for meeting other learners. This is a great way to network with other like-minded entrepreneurs, builders, students, and anyone looking to level up in Generative AI.
|
||||
|
||||
[](https://aka.ms/genai-discord?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
The project team will also be on this Discord server to help any learners.
|
||||
|
||||
## Contribute
|
||||
|
||||
This course is an open-source initiative. If you see areas of improvement or issues, please create a [Pull Request](https://github.com/microsoft/generative-ai-for-beginners/pulls?WT.mc_id=academic-105485-koreyst) or log a [GitHub issue](https://github.com/microsoft/generative-ai-for-beginners/issues?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
The project team will be tracking all contributions. Contributing to open source is an amazing way to build your career in Generative AI.
|
||||
|
||||
Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to and actually do, grant us the rights to use your contribution. For details, visit [CLA, Contributor License Agreement website](https://cla.microsoft.com?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
Important: when translating text in this repo, please ensure that you do not use machine translation. We will verify translations via the community, so please only volunteer for translations in languages where you are proficient.
|
||||
|
||||
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repositories using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/?WT.mc_id=academic-105485-koreyst). For more information read the Code of Conduct FAQ or contact [Email opencode](opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Let's Get Started
|
||||
|
||||
Now that you have completed the needed steps to complete this course, let's get started by getting an [introduction to Generative AI and LLMs](../01-introduction-to-genai/README.md?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,136 @@
|
||||
# Introduction to Generative AI and Large Language Models
|
||||
|
||||
[](https://youtu.be/lFXQkBvEe0o?si=6ZBcQTwLJJDpnX0K)
|
||||
|
||||
_(Click the image above to view video of this lesson)_
|
||||
|
||||
Generative AI is artificial intelligence capable of generating text, images and other types of content. What makes it a fantastic technology is that it democratizes AI, anyone can use it with as little as a text prompt, a sentence written in a natural language. There's no need for you to learn a language like Java or SQL to accomplish something worthwhile, all you need is to use your language, state what you want and out comes a suggestion from an AI model. The applications and impact for this are huge, you write or understand reports, write applications and much more, all in seconds.
|
||||
|
||||
In this curriculum, we’ll explore how our startup leverages generative AI to unlock new scenarios in the education world and how we address the inevitable challenges associated with the social implications of its application and the technology limitations.
|
||||
|
||||
## Introduction
|
||||
|
||||
This lesson will cover:
|
||||
|
||||
- Introduction to the business scenario: our startup idea and mission.
|
||||
- Generative AI and how we landed on the current technology landscape.
|
||||
- Inner working of a large language model.
|
||||
- Main capabilities and practical use cases of Large Language Models.
|
||||
|
||||
## Learning Goals
|
||||
|
||||
After completing this lesson, you will understand:
|
||||
|
||||
- What generative AI is and how Large Language Models work.
|
||||
- How you can leverage large language models for different use cases, with a focus on education scenarios.
|
||||
|
||||
## Scenario: our educational startup
|
||||
|
||||
Generative Artificial Intelligence (AI) represents the pinnacle of AI technology, pushing the boundaries of what was once thought impossible. Generative AI models have several capabilities and applications, but for this curriculum we'll explore how it's revolutionizing education through a fictional startup. We'll refer to this startup as _our startup_. Our startup works in the education domain with the ambitious mission statement of
|
||||
|
||||
> _improving accessibility in learning, on a global scale, ensuring equitable access to education and providing personalized learning experiences to every learner, according to their needs_.
|
||||
|
||||
Our startup team is aware we’ll not be able to achieve this goal without leveraging one of the most powerful tools of modern times – Large Language Models (LLMs).
|
||||
|
||||
Generative AI is expected to revolutionize the way we learn and teach today, with students having at their disposal virtual teachers 24 hours a day who provide vast amounts of information and examples, and teachers able to leverage innovative tools to assess their students and give feedback.
|
||||
|
||||

|
||||
|
||||
To start, let’s define some basic concepts and terminology we’ll be using throughout the curriculum.
|
||||
|
||||
## How did we get Generative AI?
|
||||
|
||||
Despite the extraordinary _hype_ created lately by the announcement of generative AI models, this technology is decades in the making, with the first research efforts dating back to the 60s. We're now at a point with AI having human cognitive capabilities, like conversation as shown by for example [OpenAI ChatGPT](https://openai.com/chatgpt) or [Microsoft Copilot](https://copilot.microsoft.com/?WT.mc_id=academic-105485-koreyst), which also uses a GPT model for its conversational web search experience.
|
||||
|
||||
Backing up a bit, the very first prototypes of AI consisted of typewritten chatbots, relying on a knowledge base extracted from a group of experts and represented into a computer. The answers in the knowledge base were triggered by keywords appearing in the input text.
|
||||
However, it soon became clear that such an approach, using typewritten chatbots, did not scale well.
|
||||
|
||||
### A statistical approach to AI: Machine Learning
|
||||
|
||||
A turning point arrived during the 90s, with the application of a statistical approach to text analysis. This led to the development of new algorithms – known as machine learning – capable of learning patterns from data without being explicitly programmed. This approach allows machines to simulate human language understanding: a statistical model is trained on text-label pairings, enabling the model to classify unknown input text with a pre-defined label representing the intention of the message.
|
||||
|
||||
### Neural networks and modern virtual assistants
|
||||
|
||||
In recent years, the technological evolution of hardware, capable of handling larger amounts of data and more complex computations, encouraged research in AI, leading to the development of advanced machine learning algorithms known as neural networks or deep learning algorithms.
|
||||
|
||||
Neural networks (and in particular Recurrent Neural Networks – RNNs) significantly enhanced natural language processing, enabling the representation of the meaning of text in a more meaningful way, valuing the context of a word in a sentence.
|
||||
|
||||
This is the technology that powered the virtual assistants born in the first decade of the new century, very proficient in interpreting human language, identifying a need, and performing an action to satisfy it – like answering with a pre-defined script or consuming a 3rd party service.
|
||||
|
||||
### Present day, Generative AI
|
||||
|
||||
So that’s how we came to Generative AI today, which can be seen as a subset of deep learning.
|
||||
|
||||

|
||||
|
||||
After decades of research in the AI field, a new model architecture – called _Transformer_ – overcame the limits of RNNs, being able to get much longer sequences of text as input. Transformers are based on the attention mechanism, enabling the model to give different weights to the inputs it receives, ‘paying more attention’ where the most relevant information is concentrated, regardless of their order in the text sequence.
|
||||
|
||||
Most of the recent generative AI models – also known as Large Language Models (LLMs), since they work with textual inputs and outputs – are indeed based on this architecture. What’s interesting about these models – trained on a huge amount of unlabeled data from diverse sources like books, articles and websites – is that they can be adapted to a wide variety of tasks and generate grammatically correct text with a semblance of creativity. So, not only did they incredibly enhance the capacity of a machine to ‘understand’ an input text, but they enabled their capacity to generate an original response in human language.
|
||||
|
||||
## How do large language models work?
|
||||
|
||||
In the next chapter we are going to explore different types of Generative AI models, but for now let’s have a look at how large language models work, with a focus on OpenAI GPT (Generative Pre-trained Transformer) models.
|
||||
|
||||
- **Tokenizer, text to numbers**: Large Language Models receive a text as input and generate a text as output. However, being statistical models, they work much better with numbers than text sequences. That’s why every input to the model is processed by a tokenizer, before being used by the core model. A token is a chunk of text – consisting of a variable number of characters, so the tokenizer's main task is splitting the input into an array of tokens. Then, each token is mapped with a token index, which is the integer encoding of the original text chunk.
|
||||
|
||||

|
||||
|
||||
- **Predicting output tokens**: Given n tokens as input (with max n varying from one model to another), the model is able to predict one token as output. This token is then incorporated into the input of the next iteration, in an expanding window pattern, enabling a better user experience of getting one (or multiple) sentence as an answer. This explains why, if you ever played with ChatGPT, you might have noticed that sometimes it looks like it stops in the middle of a sentence.
|
||||
|
||||
- **Selection process, probability distribution**: The output token is chosen by the model according to its probability of occurring after the current text sequence. This is because the model predicts a probability distribution over all possible ‘next tokens’, calculated based on its training. However, not always is the token with the highest probability chosen from the resulting distribution. A degree of randomness is added to this choice, in a way that the model acts in a non-deterministic fashion - we do not get the exact same output for the same input. This degree of randomness is added to simulate the process of creative thinking and it can be tuned using a model parameter called temperature.
|
||||
|
||||
## How can our startup leverage Large Language Models?
|
||||
|
||||
Now that we have a better understanding of the inner working of a large language model, let’s see some practical examples of the most common tasks they can perform pretty well, with an eye to our business scenario.
|
||||
We said that the main capability of a Large Language Model is _generating a text from scratch, starting from a textual input, written in natural language_.
|
||||
|
||||
But what kind of textual input and output?
|
||||
The input of a large language model is known as a prompt, while the output is known as a completion, term that refers to the model mechanism of generating the next token to complete the current input. We are going to dive deep into what is a prompt and how to design it in a way to get the most out of our model. But for now, let’s just say that a prompt may include:
|
||||
|
||||
- An **instruction** specifying the type of output we expect from the model. This instruction sometimes might embed some examples or some additional data.
|
||||
|
||||
1. Summarization of an article, book, product reviews and more, along with extraction of insights from unstructured data.
|
||||
|
||||

|
||||
|
||||
2. Creative ideation and design of an article, an essay, an assignment or more.
|
||||
|
||||

|
||||
|
||||
- A **question**, asked in the form of a conversation with an agent.
|
||||
|
||||

|
||||
|
||||
- A chunk of **text to complete**, which implicitly is an ask for writing assistance.
|
||||
|
||||

|
||||
|
||||
- A chunk of **code** together with the ask of explaining and documenting it, or a comment asking to generate a piece of code performing a specific task.
|
||||
|
||||

|
||||
|
||||
The examples above are quite simple and are not intended to be an exhaustive demonstration of Large Language Models' capabilities. They are meant to show the potential of using generative AI, in particular but not limited to educational contexts.
|
||||
|
||||
Also, the output of a generative AI model is not perfect and sometimes the creativity of the model can work against it, resulting in an output which is a combination of words that the human user can interpret as a mystification of reality, or it can be offensive. Generative AI is not intelligent - at least in the more comprehensive definition of intelligence, including critical and creative reasoning or emotional intelligence; it is not deterministic, and it is not trustworthy, since fabrications, such as erroneous references, content, and statements, may be combined with correct information, and presented in a persuasive and confident manner. In the following lessons, we’ll be dealing with all these limitations and we’ll see what we can do to mitigate them.
|
||||
|
||||
## Assignment
|
||||
|
||||
Your assignment is to read up more on [generative AI](https://en.wikipedia.org/wiki/Generative_artificial_intelligence?WT.mc_id=academic-105485-koreyst) and try to identify an area where you would add generative AI today that doesn't have it. How would the impact be different from doing it the "old way", can you do something you couldn't before, or are you faster? Write a 300 word summary on what your dream AI startup would look like and include headers like "Problem", "How I would use AI", "Impact" and optionally a business plan.
|
||||
|
||||
If you did this task, you might even be ready to apply to Microsoft's incubator, [Microsoft for Startups Founders Hub](https://www.microsoft.com/startups?WT.mc_id=academic-105485-koreyst) we offer credits for both Azure, OpenAI, mentoring and much more, check it out!
|
||||
|
||||
## Knowledge check
|
||||
|
||||
What's true about large language models?
|
||||
|
||||
1. You get the exact same response every time.
|
||||
1. It does things perfectly, great at adding numbers, produce working code etc.
|
||||
1. The response may vary despite using the same prompt. It's also great at giving you a first draft of something, be it text or code. But you need to improve on the results.
|
||||
|
||||
A: 3, an LLM is non-deterministic, the response varies, however, you can control its variance via a temperature setting. You also shouldn't expect it to do things perfectly, it's here to do the heavy-lifting for you which often means you get a good first attempt at something that you need to gradually improve.
|
||||
|
||||
## Great Work! Continue the Journey
|
||||
|
||||
After completing this lesson, check out our [Generative AI Learning collection](https://aka.ms/genai-collection?WT.mc_id=academic-105485-koreyst) to continue leveling up your Generative AI knowledge!
|
||||
|
||||
Head over to Lesson 2 where we will look at how to [explore and compare different LLM types](../02-exploring-and-comparing-different-llms/README.md?WT.mc_id=academic-105485-koreyst)!
|
||||
|
After Width: | Height: | Size: 720 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 299 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 311 KiB |
|
After Width: | Height: | Size: 311 KiB |
|
After Width: | Height: | Size: 320 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,203 @@
|
||||
# Exploring and comparing different LLMs
|
||||
|
||||
[](https://youtu.be/KIRUeDKscfI?si=8BHX1zvwzQBn-PlK)
|
||||
|
||||
> _Click the image above to view video of this lesson_
|
||||
|
||||
With the previous lesson, we have seen how Generative AI is changing the technology landscape, how Large Language Models (LLMs) work and how a business - like our startup - can apply them to their use cases and grow! In this chapter, we're looking to compare and contrast different types of large language models (LLMs) to understand their pros and cons.
|
||||
|
||||
The next step in our startup's journey is exploring the current landscape of LLMs and understanding which are suitable for our use case.
|
||||
|
||||
## Introduction
|
||||
|
||||
This lesson will cover:
|
||||
|
||||
- Different types of LLMs in the current landscape.
|
||||
- Testing, iterating, and comparing different models for your use case in Azure.
|
||||
- How to deploy an LLM.
|
||||
|
||||
## Learning Goals
|
||||
|
||||
After completing this lesson, you will be able to:
|
||||
|
||||
- Select the right model for your use case.
|
||||
- Understand how to test, iterate, and improve the performance of your model.
|
||||
- Know how businesses deploy models.
|
||||
|
||||
## Understand different types of LLMs
|
||||
|
||||
LLMs can have multiple categorizations based on their architecture, training data, and use case. Understanding these differences will help our startup select the right model for the scenario, and understand how to test, iterate, and improve performance.
|
||||
|
||||
There are many different types of LLM models, your choice of model depends on what you aim to use them for, your data, how much you're ready to pay and more.
|
||||
|
||||
Depending on if you aim to use the models for text, audio, video, image generation and so on, you might opt for a different type of model.
|
||||
|
||||
- **Audio and speech recognition**. Whisper-style models are still useful general-purpose speech recognition models, but production choices now also include newer speech-to-text models such as `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and diarization variants. Evaluate language coverage, diarization, real-time support, latency, and cost for your scenario. Learn more in the [OpenAI speech-to-text documentation](https://platform.openai.com/docs/guides/speech-to-text?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
- **Image generation**. DALL-E and Midjourney are well-known image generation options, but current OpenAI image APIs center on GPT Image models such as `gpt-image-2`, while Stable Diffusion, Imagen, Flux, and other model families are also common choices. Compare prompt adherence, editing support, style control, safety requirements, and licensing. Learn more in the [OpenAI image generation guide](https://platform.openai.com/docs/guides/images?WT.mc_id=academic-105485-koreyst) and Chapter 9 of this curriculum.
|
||||
|
||||
- **Text generation**. Text models now span frontier models, reasoning models, smaller low-latency models, and open-weight models. Current examples include OpenAI GPT-5.x models, Anthropic Claude 4.x models, Google Gemini 3.x models, Meta Llama 4 models, and Mistral models. Do not choose only by release date or price; compare task quality, latency, context window, tool use, safety behavior, regional availability, and total cost. The [Microsoft Foundry model catalog](https://ai.azure.com/catalog?WT.mc_id=academic-105485-koreyst) is a good place to compare models available on Azure.
|
||||
|
||||
- **Multi-modality**. Many current models can process more than text. Some accept image, audio, or video inputs; some can call tools; and specialized models can generate images, audio, or video. For example, current OpenAI models support text and image input, Gemini models can support text, code, image, audio, and video inputs depending on the variant, and Llama 4 Scout and Maverick are open-weight natively multimodal models. Always check each model card for supported input and output modalities before building a workflow around it.
|
||||
|
||||
Selecting a model means you get some basic capabilities, that might not be enough however. Often you have company specific data that you somehow need to tell the LLM about. There are a few different choices on how to approach that, more on that in the upcoming sections.
|
||||
|
||||
### Foundation Models versus LLMs
|
||||
|
||||
The term Foundation Model was [coined by Stanford researchers](https://arxiv.org/abs/2108.07258?WT.mc_id=academic-105485-koreyst) and defined as an AI model that follows some criteria, such as:
|
||||
|
||||
- **They are trained using unsupervised learning or self-supervised learning**, meaning they are trained on unlabeled multi-modal data, and they do not require human annotation or labeling of data for their training process.
|
||||
- **They are very large models**, based on very deep neural networks trained on billions of parameters.
|
||||
- **They are normally intended to serve as a ‘foundation’ for other models**, meaning they can be used as a starting point for other models to be built on top of, which can be done by fine-tuning.
|
||||
|
||||

|
||||
|
||||
Image source: [Essential Guide to Foundation Models and Large Language Models | by Babar M Bhatti | Medium
|
||||
](https://thebabar.medium.com/essential-guide-to-foundation-models-and-large-language-models-27dab58f7404)
|
||||
|
||||
To further clarify this distinction, let’s take ChatGPT as a historical example. Early versions of ChatGPT used GPT-3.5 as a foundation model. OpenAI then used chat-specific data and alignment techniques to create a tuned version that performed better in conversational scenarios, such as chatbots. Modern AI services often route between several model variants, so the service name and the underlying model name are not always the same thing.
|
||||
|
||||

|
||||
|
||||
Image source: [2108.07258.pdf (arxiv.org)](https://arxiv.org/pdf/2108.07258.pdf?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
### Open-Weight/Open-Source versus Proprietary Models
|
||||
|
||||
Another way to categorize LLMs is whether they are open-weight, open-source, or proprietary.
|
||||
|
||||
Open-source and open-weight models make model artifacts available for inspection, download, or customization, but their licenses differ. Some are fully open source, while others are open-weight models with usage restrictions. They can be useful when a business needs more control over deployment, data locality, cost, or customization. However, teams still need to review license terms, serving costs, maintenance, security updates, and evaluation quality before using them in production. Examples include [Meta Llama 4](https://ai.meta.com/blog/llama-4-multimodal-intelligence/?WT.mc_id=academic-105485-koreyst), some [Mistral models](https://docs.mistral.ai/models/overview?WT.mc_id=academic-105485-koreyst), and many models hosted on [Hugging Face](https://huggingface.co/models?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
Proprietary models are owned and hosted by a provider. These models are often optimized for managed production use and can offer strong support, safety systems, tool integration, and scale. However, customers usually cannot inspect or modify the model weights, and they must review provider terms for privacy, retention, compliance, and acceptable use. Examples include [OpenAI models](https://platform.openai.com/docs/models?WT.mc_id=academic-105485-koreyst), [Google Gemini](https://deepmind.google/models/gemini/pro/?WT.mc_id=academic-105485-koreyst), and [Anthropic Claude](https://platform.claude.com/docs/en/about-claude/models/overview?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
### Embedding versus Image generation versus Text and Code generation
|
||||
|
||||
LLMs can also be categorized by the output they generate.
|
||||
|
||||
Embeddings are a set of models that can convert text into a numerical form, called embedding, which is a numerical representation of the input text. Embeddings make it easier for machines to understand the relationships between words or sentences and can be consumed as inputs by other models, such as classification models, or clustering models that have better performance on numerical data. Embedding models are often used for transfer learning, where a model is built for a surrogate task for which there’s an abundance of data, and then the model weights (embeddings) are re-used for other downstream tasks. An example of this category is [OpenAI embeddings](https://platform.openai.com/docs/models/embeddings?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||

|
||||
|
||||
Image generation models are models that generate images. These models are often used for image editing, image synthesis, and image translation. Image generation models are often trained on large datasets of images, such as [LAION-5B](https://laion.ai/blog/laion-5b/?WT.mc_id=academic-105485-koreyst), and can be used to generate new images or to edit existing images with inpainting, super-resolution, and colorization techniques. Examples include [GPT Image models](https://platform.openai.com/docs/guides/images?WT.mc_id=academic-105485-koreyst), [Stable Diffusion models](https://github.com/Stability-AI/StableDiffusion?WT.mc_id=academic-105485-koreyst), and Imagen models.
|
||||
|
||||

|
||||
|
||||
Text and code generation models are models that generate text or code. These models are often used for text summarization, translation, and question answering. Text generation models are often trained on large datasets of text, such as [BookCorpus](https://www.cv-foundation.org/openaccess/content_iccv_2015/html/Zhu_Aligning_Books_and_ICCV_2015_paper.html?WT.mc_id=academic-105485-koreyst), and can be used to generate new text, or to answer questions. Code generation models, like [CodeParrot](https://huggingface.co/codeparrot?WT.mc_id=academic-105485-koreyst), are often trained on large datasets of code, such as GitHub, and can be used to generate new code, or to fix bugs in existing code.
|
||||
|
||||

|
||||
|
||||
### Encoder-Decoder versus Decoder-only
|
||||
|
||||
To talk about the different types of architectures of LLMs, let's use an analogy.
|
||||
|
||||
Imagine your manager gave you a task for writing a quiz for the students. You have two colleagues; one oversees creating the content and the other oversees reviewing them.
|
||||
|
||||
The content creator is like a decoder-only model: they can look at the topic, see what you already wrote, and then continue generating content based on that context. They are very good at writing engaging and informative content, but they are not always the best choice when the task is only to classify, retrieve, or encode information. Examples of decoder-only model families include GPT and Llama models.
|
||||
|
||||
The reviewer is like an Encoder only model, they look at the course written and the answers, noticing the relationship between them and understanding context, but they are not good at generating content. An example of Encoder only model would be BERT.
|
||||
|
||||
Imagine that we can have someone as well who could create and review the quiz, this is an Encoder-Decoder model. Some examples would be BART and T5.
|
||||
|
||||
### Service versus Model
|
||||
|
||||
Now, let's talk about the difference between a service and a model. A service is a product that is offered by a Cloud Service Provider, and is often a combination of models, data, and other components. A model is the core component of a service, and is often a foundation model, such as an LLM.
|
||||
|
||||
Services are often optimized for production use and are often easier to use than models, via a graphical user interface. However, services are not always available for free, and may require a subscription or payment to use, in exchange for leveraging the service owner’s equipment and resources, optimizing expenses and scaling easily. An example of a service is [Azure OpenAI Service](https://learn.microsoft.com/azure/ai-services/openai/overview?WT.mc_id=academic-105485-koreyst), which offers a pay-as-you-go rate plan, meaning users are charged proportionally to how much they use the service. Azure OpenAI Service also offers enterprise-grade security and a responsible AI framework on top of the models' capabilities.
|
||||
|
||||
Models are the neural network artifacts: parameters, weights, architecture, tokenizer, and supporting configuration. Running a model locally or in a private environment requires suitable hardware, serving infrastructure, monitoring, and either a compatible open-source/open-weight license or a commercial license. Open-weight models such as Llama 4 or Mistral models can be self-hosted, but they still require computational power and operational expertise.
|
||||
|
||||
## How to test and iterate with different models to understand performance on Azure
|
||||
|
||||
Once our team has explored the current LLMs landscape and identified some good candidates for their scenarios, the next step is testing them on their data and on their workload. This is an iterative process, done by experiments and measures.
|
||||
Most of the models we mentioned in previous paragraphs (OpenAI models, open-weight models like Llama 4 and Mistral, and Hugging Face models) are available in [Microsoft Foundry Models](https://learn.microsoft.com/azure/foundry/concepts/foundry-models-overview?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
[Microsoft Foundry](https://learn.microsoft.com/azure/foundry/what-is-foundry?WT.mc_id=academic-105485-koreyst), formerly Azure AI Studio/Azure AI Foundry, is a unified Azure platform for building AI apps and agents. It helps developers manage the lifecycle from experimentation and evaluation to deployment, monitoring, and governance. The model catalog in Microsoft Foundry enables the user to:
|
||||
|
||||
- Find the foundation model of interest in the catalog, including models sold by Azure and models from partners and community providers. Users can filter by task, provider, license, deployment option, or name.
|
||||
|
||||

|
||||
|
||||
- Review the model card, including a detailed description of intended use and training data, code samples and evaluation results on the internal evaluations library.
|
||||
|
||||

|
||||
|
||||
- Compare benchmarks across models and datasets available in the industry to assess which one meets the business scenario, through the [Model Benchmarks](https://learn.microsoft.com/azure/ai-studio/how-to/model-benchmarks?WT.mc_id=academic-105485-koreyst) pane.
|
||||
|
||||

|
||||
|
||||
- Fine-tune supported models on custom training data to improve model performance in a specific workload, leveraging the experimentation and tracking capabilities of Microsoft Foundry.
|
||||
|
||||

|
||||
|
||||
- Deploy the original pre-trained model or the fine-tuned version to a remote real-time inference endpoint, using managed compute or serverless deployment options, to enable applications to consume it.
|
||||
|
||||

|
||||
|
||||
> [!NOTE]
|
||||
> Not all models in the catalog are currently available for fine-tuning and/or pay-as-you-go deployment. Check the model card for details on the model's capabilities and limitations.
|
||||
|
||||
## Improving LLM results
|
||||
|
||||
We’ve explored with our startup team different kinds of LLMs and a cloud platform (Microsoft Foundry) that enables us to compare different models, evaluate them on test data, improve performance, and deploy them on inference endpoints.
|
||||
|
||||
But when shall they consider fine-tuning a model rather than using a pre-trained one? Are there other approaches to improve model performance on specific workloads?
|
||||
|
||||
There are several approaches a business can use to get the results they need from an LLM. You can select different types of models with different degrees of training when deploying an LLM in production, with different levels of complexity, cost, and quality. Here are some different approaches:
|
||||
|
||||
- **Prompt engineering with context**. The idea is to provide enough context when you prompt to ensure you get the responses you need.
|
||||
|
||||
- **Retrieval Augmented Generation, RAG**. Your data might exist in a database or web endpoint for example, to ensure this data, or a subset of it, is included at the time of prompting, you can fetch the relevant data and make that part of the user's prompt.
|
||||
|
||||
- **Fine-tuned model**. Here, you trained the model further on your own data which led to the model being more exact and responsive to your needs but might be costly.
|
||||
|
||||

|
||||
|
||||
Img source: [Four Ways that Enterprises Deploy LLMs | Fiddler AI Blog](https://www.fiddler.ai/blog/four-ways-that-enterprises-deploy-llms?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
### Prompt Engineering with Context
|
||||
|
||||
Pre-trained LLMs work very well on generalized natural language tasks, even by calling them with a short prompt, like a sentence to complete or a question – the so-called “zero-shot” learning.
|
||||
|
||||
However, the more the user can frame their query, with a detailed request and examples – the Context – the more accurate and closest to user’s expectations the answer will be. In this case, we talk about “one-shot” learning if the prompt includes only one example and “few shot learning” if it includes multiple examples.
|
||||
Prompt engineering with context is the most cost-effective approach to kick-off with.
|
||||
|
||||
### Retrieval Augmented Generation (RAG)
|
||||
|
||||
LLMs have the limitation that they can use only the data that has been used during their training to generate an answer. This means that they don’t know anything about the facts that happened after their training process, and they cannot access non-public information (like company data).
|
||||
This can be overcome through RAG, a technique that augments prompt with external data in the form of chunks of documents, considering prompt length limits. This is supported by Vector database tools (like [Azure Vector Search](https://learn.microsoft.com/azure/search/vector-search-overview?WT.mc_id=academic-105485-koreyst)) that retrieve the useful chunks from varied pre-defined data sources and add them to the prompt Context.
|
||||
|
||||
This technique is very helpful when a business doesn’t have enough data, enough time, or resources to fine-tune an LLM, but still wishes to improve performance on a specific workload and reduce risks of hallucinated, outdated, or unsupported answers.
|
||||
|
||||
### Fine-tuned model
|
||||
|
||||
Fine-tuning is a process that leverages transfer learning to ‘adapt’ the model to a downstream task or to solve a specific problem. Differently from few-shot learning and RAG, it results in a new model being generated, with updated weights and biases. It requires a set of training examples consisting of a single input (the prompt) and its associated output (the completion).
|
||||
This would be the preferred approach if:
|
||||
|
||||
- **Using smaller task-specific models**. A business would like to fine-tune a smaller model for a narrow task rather than repeatedly prompt a larger frontier model, resulting in a more cost-effective and faster solution.
|
||||
|
||||
- **Considering latency**. Latency is important for a specific use-case, so it’s not possible to use very long prompts or the number of examples that should be learned from the model doesn’t fit with the prompt length limit.
|
||||
|
||||
- **Adapting stable behavior**. A business has many high-quality examples and wants the model to consistently follow a task pattern, output format, tone, or domain-specific style. If the main problem is fresh facts or private knowledge that changes often, use RAG instead of relying on fine-tuning alone.
|
||||
|
||||
### Trained model
|
||||
|
||||
Training an LLM from scratch is without a doubt the most difficult and the most complex approach to adopt, requiring massive amounts of data, skilled resources, and appropriate computational power. This option should be considered only in a scenario where a business has a domain-specific use case and a large amount of domain-centric data.
|
||||
|
||||
## Knowledge check
|
||||
|
||||
What could be a good approach to improve LLM completion results?
|
||||
|
||||
1. Prompt engineering with context
|
||||
1. RAG
|
||||
1. Fine-tuned model
|
||||
|
||||
A: All three can help. Start with prompt engineering and context for quick improvements, and use RAG when the model needs current facts or private business data. Choose fine-tuning when you have enough high-quality examples and need the model to consistently follow a task, format, tone, or domain pattern.
|
||||
|
||||
## 🚀 Challenge
|
||||
|
||||
Read up more on how you can [use RAG](https://learn.microsoft.com/azure/search/retrieval-augmented-generation-overview?WT.mc_id=academic-105485-koreyst) for your business.
|
||||
|
||||
## Great Work, Continue Your Learning
|
||||
|
||||
After completing this lesson, check out our [Generative AI Learning collection](https://aka.ms/genai-collection?WT.mc_id=academic-105485-koreyst) to continue leveling up your Generative AI knowledge!
|
||||
|
||||
Head over to Lesson 3 where we will look at how to [build with Generative AI Responsibly](../03-using-generative-ai-responsibly/README.md?WT.mc_id=academic-105485-koreyst)!
|
||||
|
After Width: | Height: | Size: 831 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 209 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 72 KiB |
@@ -0,0 +1,133 @@
|
||||
# Using Generative AI Responsibly
|
||||
|
||||
[](https://youtu.be/YOp-e1GjZdA?si=7Wv4wu3x44L1DCVj)
|
||||
|
||||
> _Click the image above to view video of this lesson_
|
||||
|
||||
It's easy to be fascinated with AI and generative AI in particular, but you need to consider how you would use it responsibly. You need to consider things like how to ensure the output is fair, non-harmful and more. This chapter aims to provide you with the mentioned context, what to consider, and how to take active steps to improve your AI usage.
|
||||
|
||||
## Introduction
|
||||
|
||||
This lesson will cover:
|
||||
|
||||
- Why you should prioritize Responsible AI when building Generative AI applications.
|
||||
- Core principles of Responsible AI and how they relate to Generative AI.
|
||||
- How to put these Responsible AI principles into practice through strategy and tooling.
|
||||
|
||||
## Learning Goals
|
||||
|
||||
After completing this lesson you will know:
|
||||
|
||||
- The importance of Responsible AI when building Generative AI applications.
|
||||
- When to think and apply the core principles of Responsible AI when building Generative AI applications.
|
||||
- What tools and strategies are available to you to put the concept of Responsible AI into practice.
|
||||
|
||||
## Responsible AI Principles
|
||||
|
||||
The excitement of Generative AI has never been higher. This excitement has brought a lot of new developers, attention, and funding to this space. While this is very positive for anyone looking to build products and companies using Generative AI, it is also important we proceed responsibly.
|
||||
|
||||
Throughout this course, we are focusing on building our startup and our AI education product. We’ll use the principles of Responsible AI: Fairness, Inclusiveness, Reliability/Safety, Security & Privacy, Transparency and Accountability. With these principles, we will explore how they relate to our use of Generative AI in our products.
|
||||
|
||||
## Why Should You Prioritize Responsible AI
|
||||
|
||||
When building a product, taking a human-centric approach by keeping your user's best interest in mind leads to the best results.
|
||||
|
||||
The uniqueness of Generative AI is its power to create helpful answers, information, guidance, and content for users. This can be done without many manual steps which can lead to very impressive results. Without proper planning and strategies, it can also unfortunately lead to some harmful results for your users, your product, and society as a whole.
|
||||
|
||||
Let's look at some (but not all) of these potentially harmful results:
|
||||
|
||||
### Hallucinations
|
||||
|
||||
Hallucinations are a term used to describe when an LLM produces content that is either completely nonsensical or something we know is factually wrong based on other sources of information.
|
||||
|
||||
Let's take for example we build a feature for our startup that allows students to ask historical questions to a model. A student asks the question `Who was the sole survivor of Titanic?`
|
||||
|
||||
The model produces a response such as the one below:
|
||||
|
||||

|
||||
|
||||
> _(Source: [Flying bisons](https://flyingbisons.com?WT.mc_id=academic-105485-koreyst))_
|
||||
|
||||
This is a very confident and thorough answer. Unfortunately, it is incorrect. Even with a minimal amount of research, one would discover there was more than one survivor of the Titanic disaster. For a student who is just starting to research this topic, this answer can be persuasive enough to not be questioned and treated as fact. The consequences of this can lead to the AI system being unreliable and negatively impact the reputation of our startup.
|
||||
|
||||
With each iteration of any given LLM, we have seen performance improvements around minimizing hallucinations. Even with this improvement, we as application builders and users still need to remain aware of these limitations.
|
||||
|
||||
### Harmful Content
|
||||
|
||||
We covered in the earlier section when an LLM produces incorrect or nonsensical responses. Another risk we need to be aware of is when a model responds with harmful content.
|
||||
|
||||
Harmful content can be defined as:
|
||||
|
||||
- Providing instructions or encouraging self-harm or harm to certain groups.
|
||||
- Hateful or demeaning content.
|
||||
- Guiding the planning of any type of attack or violent acts.
|
||||
- Providing instructions on how to find illegal content or commit illegal acts.
|
||||
- Displaying sexually explicit content.
|
||||
|
||||
For our startup, we want to make sure we have the right tools and strategies in place to prevent this type of content from being seen by students.
|
||||
|
||||
### Lack of Fairness
|
||||
|
||||
Fairness is defined as “ensuring that an AI system is free from bias and discrimination and that they treat everyone fairly and equally.” In the world of Generative AI, we want to ensure that exclusionary worldviews of marginalized groups are not reinforced by the model’s output.
|
||||
|
||||
These types of outputs are not only destructive to building positive product experiences for our users, but they also cause further societal harm. As application builders, we should always keep a wide and diverse user base in mind when building solutions with Generative AI.
|
||||
|
||||
## How to Use Generative AI Responsibly
|
||||
|
||||
Now that we have identified the importance of Responsible Generative AI, let's look at 4 steps we can take to build our AI solutions responsibly:
|
||||
|
||||

|
||||
|
||||
### Measure Potential Harms
|
||||
|
||||
In software testing, we test the expected actions of a user on an application. Similarly, testing a diverse set of prompts users are most likely going to use is a good way to measure potential harm.
|
||||
|
||||
Since our startup is building an education product, it would be good to prepare a list of education-related prompts. This could be to cover a certain subject, historical facts, and prompts about student life.
|
||||
|
||||
### Mitigate Potential Harms
|
||||
|
||||
It is now time to find ways where we can prevent or limit the potential harm caused by the model and its responses. We can look at this in 4 different layers:
|
||||
|
||||

|
||||
|
||||
- **Model**. Choosing the right model for the right use case. Larger and more complex models like GPT-4 can cause more of a risk of harmful content when applied to smaller and more specific use cases. Using your training data to fine-tune also reduces the risk of harmful content.
|
||||
|
||||
- **Safety System**. A safety system is a set of tools and configurations on the platform serving the model that help mitigate harm. An example of this is the content filtering system on the Azure OpenAI service. Systems should also detect jailbreak attacks and unwanted activity like requests from bots.
|
||||
|
||||
- **Metaprompt**. Metaprompts and grounding are ways we can direct or limit the model based on certain behaviors and information. This could be using system inputs to define certain limits of the model. In addition, providing outputs that are more relevant to the scope or domain of the system.
|
||||
|
||||
It can also be using techniques like Retrieval Augmented Generation (RAG) to have the model only pull information from a selection of trusted sources. There is a lesson later in this course for [building search applications](../08-building-search-applications/README.md?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
- **User Experience**. The final layer is where the user interacts directly with the model through our application’s interface in some way. In this way we can design the UI/UX to limit the user on the types of inputs they can send to the model as well as text or images displayed to the user. When deploying the AI application, we also must be transparent about what our Generative AI application can and can’t do.
|
||||
|
||||
We have an entire lesson dedicated to [Designing UX for AI Applications](../12-designing-ux-for-ai-applications/README.md?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
- **Evaluate model**. Working with LLMs can be challenging because we don’t always have control over the data the model was trained on. Regardless, we should always evaluate the model’s performance and outputs. It’s still important to measure the model’s accuracy, similarity, groundedness, and relevance of the output. This helps provide transparency and trust to stakeholders and users.
|
||||
|
||||
### Operate a Responsible Generative AI solution
|
||||
|
||||
Building an operational practice around your AI applications is the final stage. This includes partnering with other parts of our startup like Legal and Security to ensure we are compliant with all regulatory policies. Before launching, we also want to build plans around delivery, handling incidents, and rollback to prevent any harm to our users from growing.
|
||||
|
||||
## Tools
|
||||
|
||||
While the work of developing Responsible AI solutions may seem like a lot, it is work well worth the effort. As the area of Generative AI grows, more tooling to help developers efficiently integrate responsibility into their workflows will mature. For example, the [Azure AI Content Safety](https://learn.microsoft.com/azure/ai-services/content-safety/overview?WT.mc_id=academic-105485-koreyst) can help detect harmful content and images via an API request.
|
||||
|
||||
## Knowledge check
|
||||
|
||||
What are some things you need to care about to ensure responsible AI usage?
|
||||
|
||||
1. That the answer is correct.
|
||||
1. Harmful usage, that AI isn't used for criminal purposes.
|
||||
1. Ensuring the AI is free from bias and discrimination.
|
||||
|
||||
A: 2 and 3 are correct. Responsible AI helps you consider how to mitigate harmful effects and biases and more.
|
||||
|
||||
## 🚀 Challenge
|
||||
|
||||
Read up on [Azure AI Content Safety](https://learn.microsoft.com/azure/ai-services/content-safety/overview?WT.mc_id=academic-105485-koreyst) and see what you can adopt for your usage.
|
||||
|
||||
## Great Work, Continue Your Learning
|
||||
|
||||
After completing this lesson, check out our [Generative AI Learning collection](https://aka.ms/genai-collection?WT.mc_id=academic-105485-koreyst) to continue leveling up your Generative AI knowledge!
|
||||
|
||||
Head over to Lesson 4 where we will look at [Prompt Engineering Fundamentals](../04-prompt-engineering-fundamentals/README.md?WT.mc_id=academic-105485-koreyst)!
|
||||
|
After Width: | Height: | Size: 710 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 314 KiB |
|
After Width: | Height: | Size: 398 KiB |
@@ -0,0 +1,404 @@
|
||||
# Prompt Engineering Fundamentals
|
||||
|
||||
[](https://youtu.be/GElCu2kUlRs?si=qrXsBvXnCW12epb8)
|
||||
|
||||
## Introduction
|
||||
This module covers essential concepts and techniques for creating effective prompts in generative AI models. The way you write your prompt to an LLM also matters. A carefully-crafted prompt can achieve a better quality of response. But what exactly do terms like _prompt_ and _prompt engineering_ mean? And how do I improve the prompt _input_ that I send to the LLM? These are the questions we'll try to answer within this chapter and the next.
|
||||
|
||||
_Generative AI_ is capable of creating new content (e.g., text, images, audio, code etc.) in response to user requests. It achieves this using _Large Language Models_ like OpenAI's GPT ("Generative Pre-trained Transformer") series that are trained for using natural language and code.
|
||||
|
||||
Users can now interact with these models using familiar paradigms like chat, without needing any technical expertise or training. The models are _prompt-based_ - users send a text input (prompt) and get back the AI response (completion). They can then "chat with the AI" iteratively, in multi-turn conversations, refining their prompt until the response matches their expectations.
|
||||
|
||||
"Prompts" now become the primary _programming interface_ for generative AI apps, telling the models what to do and influencing the quality of returned responses. "Prompt Engineering" is a fast-growing field of study that focuses on the _design and optimization_ of prompts to deliver consistent and quality responses at scale.
|
||||
|
||||
## Learning Goals
|
||||
|
||||
In this lesson, we learn what Prompt Engineering is, why it matters, and how we can craft more effective prompts for a given model and application objective. We'll understand core concepts and best practices for prompt engineering - and learn about an interactive Jupyter Notebooks "sandbox" environment where we can see these concepts applied to real examples.
|
||||
|
||||
By the end of this lesson we will be able to:
|
||||
|
||||
1. Explain what prompt engineering is and why it matters.
|
||||
2. Describe the components of a prompt and how they are used.
|
||||
3. Learn best practices and techniques for prompt engineering.
|
||||
4. Apply learned techniques to real examples, using an OpenAI endpoint.
|
||||
|
||||
## Key Terms
|
||||
|
||||
Prompt Engineering: The practice of designing and refining inputs to guide AI models toward producing desired outputs.
|
||||
Tokenization: The process of converting text into smaller units, called tokens, that a model can understand and process.
|
||||
Instruction-Tuned LLMs: Large Language Models (LLMs) that have been fine-tuned with specific instructions to improve their response accuracy and relevance.
|
||||
|
||||
## Learning Sandbox
|
||||
|
||||
Prompt engineering is currently more art than science. The best way to improve our intuition for it is to _practice more_ and adopt a trial-and-error approach that combines application domain expertise with recommended techniques and model-specific optimizations.
|
||||
|
||||
The Jupyter Notebook accompanying this lesson provides a _sandbox_ environment where you can try out what you learn - as you go or as part of the code challenge at the end. To execute the exercises, you will need:
|
||||
|
||||
1. **An Azure OpenAI API key** - the service endpoint for a deployed LLM.
|
||||
2. **A Python Runtime** - in which the Notebook can be executed.
|
||||
3. **Local Env Variables** - _complete the [SETUP](./../00-course-setup/02-setup-local.md?WT.mc_id=academic-105485-koreyst) steps now to get ready_.
|
||||
|
||||
The notebook comes with _starter_ exercises - but you are encouraged to add your own _Markdown_ (description) and _Code_ (prompt requests) sections to try out more examples or ideas - and build your intuition for prompt design.
|
||||
|
||||
## Illustrated Guide
|
||||
|
||||
Want to get the big picture of what this lesson covers before you dive in? Check out this illustrated guide, which gives you a sense of the main topics covered and the key takeaways for you to think about in each one. The lesson roadmap takes you from understanding the core concepts and challenges to addressing them with relevant prompt engineering techniques and best practices. Note that the "Advanced Techniques" section in this guide refers to content covered in the _next_ chapter of this curriculum.
|
||||
|
||||

|
||||
|
||||
## Our Startup
|
||||
|
||||
Now, let's talk about how _this topic_ relates to our startup mission to [bring AI innovation to education](https://educationblog.microsoft.com/2023/06/collaborating-to-bring-ai-innovation-to-education?WT.mc_id=academic-105485-koreyst). We want to build AI-powered applications of _personalized learning_ - so let's think about how different users of our application might "design" prompts:
|
||||
|
||||
- **Administrators** might ask the AI to _analyze curriculum data to identify gaps in coverage_. The AI can summarize results or visualize them with code.
|
||||
- **Educators** might ask the AI to _generate a lesson plan for a target audience and topic_. The AI can build the personalized plan in a specified format.
|
||||
- **Students** might ask the AI to _tutor them in a difficult subject_. The AI can now guide students with lessons, hints & examples tailored to their level.
|
||||
|
||||
That's just the tip of the iceberg. Check out [Prompts For Education](https://github.com/microsoft/prompts-for-edu/tree/main?WT.mc_id=academic-105485-koreyst) - an open-source prompts library curated by education experts - to get a broader sense of the possibilities! _Try running some of those prompts in the sandbox or using the OpenAI Playground to see what happens!_
|
||||
|
||||
<!--
|
||||
LESSON TEMPLATE:
|
||||
This unit should cover core concept #1.
|
||||
Reinforce the concept with examples and references.
|
||||
|
||||
CONCEPT #1:
|
||||
Prompt Engineering.
|
||||
Define it and explain why it is needed.
|
||||
-->
|
||||
|
||||
## What is Prompt Engineering?
|
||||
|
||||
We started this lesson by defining **Prompt Engineering** as the process of _designing and optimizing_ text inputs (prompts) to deliver consistent and quality responses (completions) for a given application objective and model. We can think of this as a 2-step process:
|
||||
|
||||
- _designing_ the initial prompt for a given model and objective
|
||||
- _refining_ the prompt iteratively to improve the quality of the response
|
||||
|
||||
This is necessarily a trial-and-error process that requires user intuition and effort to get optimal results. So why is it important? To answer that question, we first need to understand three concepts:
|
||||
|
||||
- _Tokenization_ = how the model "sees" the prompt
|
||||
- _Base LLMs_ = how the foundation model "processes" a prompt
|
||||
- _Instruction-Tuned LLMs_ = how the model can now see "tasks"
|
||||
|
||||
### Tokenization
|
||||
|
||||
An LLM sees prompts as a _sequence of tokens_ where different models (or versions of a model) can tokenize the same prompt in different ways. Since LLMs are trained on tokens (and not on raw text), the way prompts get tokenized has a direct impact on the quality of the generated response.
|
||||
|
||||
To get an intuition for how tokenization works, try tools like the [OpenAI Tokenizer](https://platform.openai.com/tokenizer?WT.mc_id=academic-105485-koreyst) shown below. Copy in your prompt - and see how that gets converted into tokens, paying attention to how whitespace characters and punctuation marks are handled. Note that this example shows an older LLM (GPT-3) - so trying this with a newer model may produce a different result.
|
||||
|
||||

|
||||
|
||||
### Concept: Foundation Models
|
||||
|
||||
Once a prompt is tokenized, the primary function of the ["Base LLM"](https://blog.gopenai.com/an-introduction-to-base-and-instruction-tuned-large-language-models-8de102c785a6?WT.mc_id=academic-105485-koreyst) (or Foundation model) is to predict the token in that sequence. Since LLMs are trained on massive text datasets, they have a good sense of the statistical relationships between tokens and can make that prediction with some confidence. Note that they don't understand the _meaning_ of the words in the prompt or token; they just see a pattern they can "complete" with their next prediction. They can continue predicting the sequence till terminated by user intervention or some pre-established condition.
|
||||
|
||||
Want to see how prompt-based completion works? Enter the above prompt into the [Microsoft Foundry playground](https://ai.azure.com?WT.mc_id=academic-105485-koreyst) with the default settings. The system is configured to treat prompts as requests for information - so you should see a completion that satisfies this context.
|
||||
|
||||
But what if the user wanted to see something specific that met some criteria or task objective? This is where _instruction-tuned_ LLMs come into the picture.
|
||||
|
||||

|
||||
|
||||
### Concept: Instruction Tuned LLMs
|
||||
|
||||
An [Instruction Tuned LLM](https://blog.gopenai.com/an-introduction-to-base-and-instruction-tuned-large-language-models-8de102c785a6?WT.mc_id=academic-105485-koreyst) starts with the foundation model and fine-tunes it with examples or input/output pairs (e.g., multi-turn "messages") that can contain clear instructions - and the response from the AI attempt to follow that instruction.
|
||||
|
||||
This uses techniques like Reinforcement Learning with Human Feedback (RLHF) that can train the model to _follow instructions_ and _learn from feedback_ so that it produces responses that are better-suited to practical applications and more relevant to user objectives.
|
||||
|
||||
Let's try it out - revisit the prompt above, but now change the _system message_ to provide the following instruction as context:
|
||||
|
||||
> _Summarize content you are provided with for a second-grade student. Keep the result to one paragraph with 3-5 bullet points._
|
||||
|
||||
See how the result is now tuned to reflect the desired goal and format? An educator can now directly use this response in their slides for that class.
|
||||
|
||||

|
||||
|
||||
## Why do we need Prompt Engineering?
|
||||
|
||||
Now that we know how prompts are processed by LLMs, let's talk about _why_ we need prompt engineering. The answer lies in the fact that current LLMs pose a number of challenges that make _reliable and consistent completions_ more challenging to achieve without putting effort into prompt construction and optimization. For instance:
|
||||
|
||||
1. **Model responses are stochastic.** The _same prompt_ will likely produce different responses with different models or model versions. And it may even produce different results with the _same model_ at different times. _Prompt engineering techniques can help us minimize these variations by providing better guardrails_.
|
||||
|
||||
1. **Models can fabricate responses.** Models are pre-trained with _large but finite_ datasets, meaning they lack knowledge about concepts outside that training scope. As a result, they can produce completions that are inaccurate, imaginary, or directly contradictory to known facts. _Prompt engineering techniques help users identify and mitigate such fabrications e.g., by asking AI for citations or reasoning_.
|
||||
|
||||
1. **Models capabilities will vary.** Newer models or model generations will have richer capabilities but also bring unique quirks and tradeoffs in cost & complexity. _Prompt engineering can help us develop best practices and workflows that abstract away differences and adapt to model-specific requirements in scalable, seamless ways_.
|
||||
|
||||
Let's see this in action in the OpenAI or Azure OpenAI Playground:
|
||||
|
||||
- Use the same prompt with different LLM deployments (e.g, OpenAI, Azure OpenAI, Hugging Face) - did you see the variations?
|
||||
- Use the same prompt repeatedly with the _same_ LLM deployment (e.g., Azure OpenAI playground) - how did these variations differ?
|
||||
|
||||
### Fabrications Example
|
||||
|
||||
In this course, we use the term **"fabrication"** to reference the phenomenon where LLMs sometimes generate factually incorrect information due to limitations in their training or other constraints. You may also have heard this referred to as _"hallucinations"_ in popular articles or research papers. However, we strongly recommend using _"fabrication"_ as the term so we don't accidentally anthropomorphize the behavior by attributing a human-like trait to a machine-driven outcome. This also reinforces [Responsible AI guidelines](https://www.microsoft.com/ai/responsible-ai?WT.mc_id=academic-105485-koreyst) from a terminology perspective, removing terms that may also be considered offensive or non-inclusive in some contexts.
|
||||
|
||||
Want to get a sense of how fabrications work? Think of a prompt that instructs the AI to generate content for a non-existent topic (to ensure it is not found in the training dataset). For example - I tried this prompt:
|
||||
|
||||
> **Prompt:** generate a lesson plan on the Martian War of 2076.
|
||||
|
||||
A web search showed me that there were fictional accounts (e.g., television series or books) on Martian wars - but none in 2076. Commonsense also tells us that 2076 is _in the future_ and thus, cannot be associated with a real event.
|
||||
|
||||
So what happens when we run this prompt with different LLM providers?
|
||||
|
||||
> **Response 1**: OpenAI Playground (GPT-35)
|
||||
|
||||

|
||||
|
||||
> **Response 2**: Azure OpenAI Playground (GPT-35)
|
||||
|
||||

|
||||
|
||||
> **Response 3**: : Hugging Face Chat Playground (LLama-2)
|
||||
|
||||

|
||||
|
||||
As expected, each model (or model version) produces slightly different responses thanks to stochastic behavior and model capability variations. For instance, one model targets an 8th grade audience while the other assumes a high-school student. But all three models did generate responses that could convince an uninformed user that the event was real.
|
||||
|
||||
Prompt engineering techniques like _metaprompting_ and _temperature configuration_ may reduce model fabrications to some extent. New prompt engineering _architectures_ also incorporate new tools and techniques seamlessly into the prompt flow, to mitigate or reduce some of these effects.
|
||||
|
||||
## Case Study: GitHub Copilot
|
||||
|
||||
Let's wrap this section by getting a sense of how prompt engineering is used in real-world solutions by looking at one Case Study: [GitHub Copilot](https://github.com/features/copilot?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
GitHub Copilot is your "AI Pair Programmer" - it converts text prompts into code completions and is integrated into your development environment (e.g., Visual Studio Code) for a seamless user experience. As documented in the series of blogs below, the earliest version was based on the OpenAI Codex model - with engineers quickly realizing the need to fine-tune the model and develop better prompt engineering techniques, to improve code quality. In July, they [debuted an improved AI model that goes beyond Codex](https://github.blog/2023-07-28-smarter-more-efficient-coding-github-copilot-goes-beyond-codex-with-improved-ai-model/?WT.mc_id=academic-105485-koreyst) for even faster suggestions.
|
||||
|
||||
Read the posts in order, to follow their learning journey.
|
||||
|
||||
- **May 2023** | [GitHub Copilot is Getting Better at Understanding Your Code](https://github.blog/2023-05-17-how-github-copilot-is-getting-better-at-understanding-your-code/?WT.mc_id=academic-105485-koreyst)
|
||||
- **May 2023** | [Inside GitHub: Working with the LLMs behind GitHub Copilot](https://github.blog/2023-05-17-inside-github-working-with-the-llms-behind-github-copilot/?WT.mc_id=academic-105485-koreyst).
|
||||
- **Jun 2023** | [How to write better prompts for GitHub Copilot](https://github.blog/2023-06-20-how-to-write-better-prompts-for-github-copilot/?WT.mc_id=academic-105485-koreyst).
|
||||
- **Jul 2023** | [.. GitHub Copilot goes beyond Codex with improved AI model](https://github.blog/2023-07-28-smarter-more-efficient-coding-github-copilot-goes-beyond-codex-with-improved-ai-model/?WT.mc_id=academic-105485-koreyst)
|
||||
- **Jul 2023** | [A Developer's Guide to Prompt Engineering and LLMs](https://github.blog/2023-07-17-prompt-engineering-guide-generative-ai-llms/?WT.mc_id=academic-105485-koreyst)
|
||||
- **Sep 2023** | [How to build an enterprise LLM app: Lessons from GitHub Copilot](https://github.blog/2023-09-06-how-to-build-an-enterprise-llm-application-lessons-from-github-copilot/?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
You can also browse their [Engineering blog](https://github.blog/category/engineering/?WT.mc_id=academic-105485-koreyst) for more posts like [this one](https://github.blog/2023-09-27-how-i-used-github-copilot-chat-to-build-a-reactjs-gallery-prototype/?WT.mc_id=academic-105485-koreyst) that shows how these models and techniques are _applied_ for driving real-world applications.
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
LESSON TEMPLATE:
|
||||
This unit should cover core concept #2.
|
||||
Reinforce the concept with examples and references.
|
||||
|
||||
CONCEPT #2:
|
||||
Prompt Design.
|
||||
Illustrated with examples.
|
||||
-->
|
||||
|
||||
## Prompt Construction
|
||||
|
||||
We've seen why prompt engineering is important - now let's understand how prompts are _constructed_ so we can evaluate different techniques for more effective prompt design.
|
||||
|
||||
### Basic Prompt
|
||||
|
||||
Let's start with the basic prompt: a text input sent to the model with no other context. Here's an example - when we send the first few words of the US national anthem to the OpenAI [Completion API](https://platform.openai.com/docs/api-reference/completions?WT.mc_id=academic-105485-koreyst) it instantly _completes_ the response with the next few lines, illustrating the basic prediction behavior.
|
||||
|
||||
| Prompt (Input) | Completion (Output) |
|
||||
| :----------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Oh say can you see | It sounds like you're starting the lyrics to "The Star-Spangled Banner," the national anthem of the United States. The full lyrics are ... |
|
||||
|
||||
### Complex Prompt
|
||||
|
||||
Now let's add context and instructions to that basic prompt. The [Chat Completion API](https://learn.microsoft.com/azure/ai-services/openai/how-to/chatgpt?WT.mc_id=academic-105485-koreyst) lets us construct a complex prompt as a collection of _messages_ with:
|
||||
|
||||
- Input/output pairs reflecting _user_ input and _assistant_ response.
|
||||
- System message setting the context for assistant behavior or personality.
|
||||
|
||||
The request is now in the form below, where the _tokenization_ effectively captures relevant information from context and conversation. Now, changing the system context can be as impactful on the quality of completions, as the user inputs provided.
|
||||
|
||||
```python
|
||||
response = client.responses.create(
|
||||
model="gpt-4o-mini",
|
||||
input=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Who won the world series in 2020?"},
|
||||
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
|
||||
{"role": "user", "content": "Where was it played?"}
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### Instruction Prompt
|
||||
|
||||
In the above examples, the user prompt was a simple text query that can be interpreted as a request for information. With _instruction_ prompts, we can use that text to specify a task in more detail, providing better guidance to the AI. Here's an example:
|
||||
|
||||
| Prompt (Input) | Completion (Output) | Instruction Type |
|
||||
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | :------------------ |
|
||||
| Write a description of the Civil War | _returned a simple paragraph_ | Simple |
|
||||
| Write a description of the Civil War. Provide key dates and events and describe their significance | _returned a paragraph followed by a list of key event dates with descriptions_ | Complex |
|
||||
| Write a description of the Civil War in 1 paragraph. Provide 3 bullet points with key dates and their significance. Provide 3 more bullet points with key historical figures and their contributions. Return the output as a JSON file | _returns more extensive details in a text box, formatted as JSON that you can copy-paste to a file and validate as needed_ | Complex. Formatted. |
|
||||
|
||||
## Primary Content
|
||||
|
||||
In the above examples, the prompt was still fairly open-ended, allowing the LLM to decide what part of its pre-trained dataset was relevant. With the _primary content_ design pattern, the input text is divided into two parts:
|
||||
|
||||
- an instruction (action)
|
||||
- relevant content (that influences action)
|
||||
|
||||
Here's an example where the instruction is to "summarize this in 2 sentences".
|
||||
|
||||
| Prompt (Input) | Completion (Output) |
|
||||
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Jupiter is the fifth planet from the Sun and the largest in the Solar System. It is a gas giant with a mass one-thousandth that of the Sun, but two-and-a-half times that of all the other planets in the Solar System combined. Jupiter is one of the brightest objects visible to the naked eye in the night sky, and has been known to ancient civilizations since before recorded history. It is named after the Roman god Jupiter.[19] When viewed from Earth, Jupiter can be bright enough for its reflected light to cast visible shadows,[20] and is on average the third-brightest natural object in the night sky after the Moon and Venus. <br/> **Summarize this in 2 short sentences** | Jupiter, the fifth planet from the Sun, is the largest in the Solar System and is known for being one of the brightest objects in the night sky. Named after the Roman god Jupiter, it's a gas giant whose mass is two-and-a-half times that of all other planets in the Solar System combined. |
|
||||
|
||||
The primary content segment can be used in various ways to drive more effective instructions:
|
||||
|
||||
- **Examples** - instead of telling the model what to do with an explicit instruction, give it examples of what to do and let it infer the pattern.
|
||||
- **Cues** - follow the instruction with a "cue" that primes the completion, guiding the model towards more relevant responses.
|
||||
- **Templates** - these are repeatable 'recipes' for prompts with placeholders (variables) that can be customized with data for specific use cases.
|
||||
|
||||
Let's explore these in action.
|
||||
|
||||
### Using Examples
|
||||
|
||||
This is an approach where you use the primary content to "feed the model" some examples of the desired output for a given instruction, and let it infer the pattern for the desired output. Based on the number of examples provided, we can have zero-shot prompting, one-shot prompting, few-shot prompting etc.
|
||||
|
||||
The prompt now consists of three components:
|
||||
|
||||
- A task description
|
||||
- A few examples of the desired output
|
||||
- The start of a new example (which becomes an implicit task description)
|
||||
|
||||
| Learning Type | Prompt (Input) | Completion (Output) |
|
||||
| :------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------- |
|
||||
| Zero-shot | "The Sun is Shining". Translate to Spanish | "El Sol está brillando". |
|
||||
| One-shot | "The Sun is Shining" => ""El Sol está brillando". <br> "It's a Cold and Windy Day" => | "Es un día frío y ventoso". |
|
||||
| Few-shot | The player ran the bases => Baseball <br/> The player hit an ace => Tennis <br/> The player hit a six => Cricket <br/> The player made a slam-dunk => | Basketball |
|
||||
| | | |
|
||||
|
||||
Note how we had to provide explicit instruction ("Translate to Spanish") in zero-shot prompting, but it gets inferred in the one-shot prompting example. The few-shot example shows how adding more examples allows models to make more accurate inferences with no added instructions.
|
||||
|
||||
### Prompt Cues
|
||||
|
||||
Another technique for using primary content is to provide _cues_ rather than examples. In this case, we are giving the model a nudge in the right direction by _starting it off_ with a snippet that reflects the desired response format. The model then "takes the cue" to continue in that vein.
|
||||
|
||||
| Number of Cues | Prompt (Input) | Completion (Output) |
|
||||
| :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 0 | Jupiter is the fifth planet from the Sun and the largest in the Solar System. It is a gas giant with a mass one-thousandth that of the Sun, but two-and-a-half times that of all the other planets in the Solar System combined. Jupiter is one of the brightest objects visible to the naked eye in the night sky, and has been known to ancient civilizations since before recorded history. <br/>**Summarize This** | Jupiter is the largest planet in our Solar System and the fifth one from the Sun. It is a gas giant with a mass 1/1000th of the Sun's, but it is heavier than all the other planets combined. Ancient civilizations have known about Jupiter for a long time, and it is easily visible in the night sky.. |
|
||||
| 1 | Jupiter is the fifth planet from the Sun and the largest in the Solar System. It is a gas giant with a mass one-thousandth that of the Sun, but two-and-a-half times that of all the other planets in the Solar System combined. Jupiter is one of the brightest objects visible to the naked eye in the night sky, and has been known to ancient civilizations since before recorded history. <br/>**Summarize This** <br/> What we learned is that Jupiter | is the fifth planet from the Sun and the largest in the Solar System. It is a gas giant with a mass one-thousandth that of the Sun, but two-and-a-half times that of all the other planets combined. It is easily visible to the naked eye and has been known since ancient times. |
|
||||
| 2 | Jupiter is the fifth planet from the Sun and the largest in the Solar System. It is a gas giant with a mass one-thousandth that of the Sun, but two-and-a-half times that of all the other planets in the Solar System combined. Jupiter is one of the brightest objects visible to the naked eye in the night sky, and has been known to ancient civilizations since before recorded history. <br/>**Summarize This** <br/> Top 3 Facts We Learned: | 1. Jupiter is the fifth planet from the Sun and the largest in the Solar System. <br/> 2. It is a gas giant with a mass one-thousandth that of the Sun...<br/> 3. Jupiter has been visible to the naked eye since ancient times ... |
|
||||
| | | |
|
||||
|
||||
### Prompt Templates
|
||||
|
||||
A prompt template is a _pre-defined recipe for a prompt_ that can be stored and reused as needed, to drive more consistent user experiences at scale. In its simplest form, it is simply a collection of prompt examples like [this one from OpenAI](https://cookbook.openai.com/examples/gpt4-1_prompting_guide?WT.mc_id=academic-105485-koreyst) that provides both the interactive prompt components (user and system messages) and the API-driven request format - to support reuse.
|
||||
|
||||
In it's more complex form like [this example from LangChain](https://python.langchain.com/docs/concepts/prompt_templates/?WT.mc_id=academic-105485-koreyst) it contains _placeholders_ that can be replaced with data from a variety of sources (user input, system context, external data sources etc.) to generate a prompt dynamically. This allows us to create a library of reusable prompts that can be used to drive consistent user experiences **programmatically** at scale.
|
||||
|
||||
Finally, the real value of templates lies in the ability to create and publish _prompt libraries_ for vertical application domains - where the prompt template is now _optimized_ to reflect application-specific context or examples that make the responses more relevant and accurate for the targeted user audience. The [Prompts For Edu](https://github.com/microsoft/prompts-for-edu?WT.mc_id=academic-105485-koreyst) repository is a great example of this approach, curating a library of prompts for the education domain with emphasis on key objectives like lesson planning, curriculum design, student tutoring etc.
|
||||
|
||||
## Supporting Content
|
||||
|
||||
If we think about prompt construction as having a instruction (task) and a target (primary content), then _secondary content_ is like additional context we provide to **influence the output in some way**. It could be tuning parameters, formatting instructions, topic taxonomies etc. that can help the model _tailor_ its response to be suit the desired user objectives or expectations.
|
||||
|
||||
For example: Given a course catalog with extensive metadata (name, description, level, metadata tags, instructor etc.) on all the available courses in the curriculum:
|
||||
|
||||
- we can define an instruction to "summarize the course catalog for Fall 2023"
|
||||
- we can use the primary content to provide a few examples of the desired output
|
||||
- we can use the secondary content to identify the top 5 "tags" of interest.
|
||||
|
||||
Now, the model can provide a summary in the format shown by the few examples - but if a result has multiple tags, it can prioritize the 5 tags identified in secondary content.
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
LESSON TEMPLATE:
|
||||
This unit should cover core concept #1.
|
||||
Reinforce the concept with examples and references.
|
||||
|
||||
CONCEPT #3:
|
||||
Prompt Engineering Techniques.
|
||||
What are some basic techniques for prompt engineering?
|
||||
Illustrate it with some exercises.
|
||||
-->
|
||||
|
||||
## Prompting Best Practices
|
||||
|
||||
Now that we know how prompts can be _constructed_, we can start thinking about how to _design_ them to reflect best practices. We can think about this in two parts - having the right _mindset_ and applying the right _techniques_.
|
||||
|
||||
### Prompt Engineering Mindset
|
||||
|
||||
Prompt Engineering is a trial-and-error process so keep three broad guiding factors in mind:
|
||||
|
||||
1. **Domain Understanding Matters.** Response accuracy and relevance is a function of the _domain_ in which that application or user operates. Apply your intuition and domain expertise to **customize techniques** further. For instance, define _domain-specific personalities_ in your system prompts, or use _domain-specific templates_ in your user prompts. Provide secondary content that reflects domain-specific contexts, or use _domain-specific cues and examples_ to guide the model towards familiar usage patterns.
|
||||
|
||||
2. **Model Understanding Matters.** We know models are stochastic by nature. But model implementations can also vary in terms of the training dataset they use (pre-trained knowledge), the capabilities they provide (e.g., via API or SDK) and the type of content they are optimized for (e.g, code vs. images vs. text). Understand the strengths and limitations of the model you are using, and use that knowledge to _prioritize tasks_ or build _customized templates_ that are optimized for the model's capabilities.
|
||||
|
||||
3. **Iteration & Validation Matters.** Models are evolving rapidly, and so are the techniques for prompt engineering. As a domain expert, you may have other context or criteria _your_ specific application, that may not apply to the broader community. Use prompt engineering tools & techniques to "jump start" prompt construction, then iterate and validate the results using your own intuition and domain expertise. Record your insights and create a **knowledge base** (e.g, prompt libraries) that can be used as a new baseline by others, for faster iterations in the future.
|
||||
|
||||
## Best Practices
|
||||
|
||||
Now let's look at common best practices that are recommended by [OpenAI](https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-openai-api?WT.mc_id=academic-105485-koreyst) and [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/concepts/prompt-engineering#best-practices?WT.mc_id=academic-105485-koreyst) practitioners.
|
||||
|
||||
| What | Why |
|
||||
| :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Evaluate the latest models. | New model generations are likely to have improved features and quality - but may also incur higher costs. Evaluate them for impact, then make migration decisions. |
|
||||
| Separate instructions & context | Check if your model/provider defines _delimiters_ to distinguish instructions, primary and secondary content more clearly. This can help models assign weights more accurately to tokens. |
|
||||
| Be specific and clear | Give more details about the desired context, outcome, length, format, style etc. This will improve both the quality and consistency of responses. Capture recipes in reusable templates. |
|
||||
| Be descriptive, use examples | Models may respond better to a "show and tell" approach. Start with a `zero-shot` approach where you give it an instruction (but no examples) then try `few-shot` as a refinement, providing a few examples of the desired output. Use analogies. |
|
||||
| Use cues to jumpstart completions | Nudge it towards a desired outcome by giving it some leading words or phrases that it can use as a starting point for the response. |
|
||||
| Double Down | Sometimes you may need to repeat yourself to the model. Give instructions before and after your primary content, use an instruction and a cue, etc. Iterate & validate to see what works. |
|
||||
| Order Matters | The order in which you present information to the model may impact the output, even in the learning examples, thanks to recency bias. Try different options to see what works best. |
|
||||
| Give the model an “out” | Give the model a _fallback_ completion response it can provide if it cannot complete the task for any reason. This can reduce chances of models generating false or fabricated responses. |
|
||||
| | |
|
||||
|
||||
As with any best practice, remember that _your mileage may vary_ based on the model, the task and the domain. Use these as a starting point, and iterate to find what works best for you. Constantly re-evaluate your prompt engineering process as new models and tools become available, with a focus on process scalability and response quality.
|
||||
|
||||
<!--
|
||||
LESSON TEMPLATE:
|
||||
This unit should provide a code challenge if applicable
|
||||
|
||||
CHALLENGE:
|
||||
Link to a Jupyter Notebook with only the code comments in the instructions (code sections are empty).
|
||||
|
||||
SOLUTION:
|
||||
Link to a copy of that Notebook with the prompts filled in and run, showing what one example could be.
|
||||
-->
|
||||
|
||||
## Assignment
|
||||
|
||||
Congratulations! You made it to the end of the lesson! It's time to put some of those concepts and techniques to the test with real examples!
|
||||
|
||||
For our assignment, we'll be using a Jupyter Notebook with exercises you can complete interactively. You can also extend the Notebook with your own Markdown and Code cells to explore ideas and techniques on your own.
|
||||
|
||||
### To get started, fork the repo, then
|
||||
|
||||
- (Recommended) Launch GitHub Codespaces
|
||||
- (Alternatively) Clone the repo to your local device and use it with Docker Desktop
|
||||
- (Alternatively) Open the Notebook with your preferred Notebook runtime environment.
|
||||
|
||||
### Next, configure your environment variables
|
||||
|
||||
- Copy the `.env.copy` file in repo root to `.env` and fill in the `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_DEPLOYMENT` values. Come back to [Learning Sandbox section](#learning-sandbox) to learn how.
|
||||
|
||||
### Next, open the Jupyter Notebook
|
||||
|
||||
- Select the runtime kernel. If using options 1 or 2, simply select the default Python 3.10.x kernel provided by the dev container.
|
||||
|
||||
You're all set to run the exercises. Note that there are no _right and wrong_ answers here - just exploring options by trial-and-error and building intuition for what works for a given model and application domain.
|
||||
|
||||
_For this reason there are no Code Solution segments in this lesson. Instead, the Notebook will have Markdown cells titled "My Solution:" that shows one example output for reference._
|
||||
|
||||
<!--
|
||||
LESSON TEMPLATE:
|
||||
Wrap the section with a summary and resources for self-guided learning.
|
||||
-->
|
||||
|
||||
## Knowledge check
|
||||
|
||||
Which of the following is a good prompt following some reasonable best practices?
|
||||
|
||||
1. Show me an image of red car
|
||||
2. Show me an image of red car of make Volvo and model XC90 parked by a cliff with the sun setting
|
||||
3. Show me an image of red car of make Volvo and model XC90
|
||||
|
||||
A: 2, it's the best prompt as it provides details on "what" and goes into specifics (not just any car but a specific make and model) and it also describes the overall setting. 3 is next best as it also contains a lot of description.
|
||||
|
||||
## 🚀 Challenge
|
||||
|
||||
See if you can leverage the "cue" technique with the prompt: Complete the sentence "Show me an image of red car of make Volvo and ". What does it respond with, and how would you improve it?
|
||||
|
||||
## Great Work! Continue Your Learning
|
||||
|
||||
Want to learn more about different Prompt Engineering concepts? Go to the [continued learning page](https://aka.ms/genai-collection?WT.mc_id=academic-105485-koreyst) to find other great resources on this topic.
|
||||
|
||||
Head over to Lesson 5 where we will look at [advanced prompting techniques](../05-advanced-prompts/README.md?WT.mc_id=academic-105485-koreyst)!
|
||||
|
||||
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 772 KiB |
|
After Width: | Height: | Size: 288 KiB |
|
After Width: | Height: | Size: 299 KiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 350 KiB |
@@ -0,0 +1,259 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The following notebook was auto-generated by GitHub Copilot Chat and is meant for initial setup only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Introduction to Prompt Engineering\n",
|
||||
"Prompt engineering is the process of designing and optimizing prompts for natural language processing tasks. It involves selecting the right prompts, tuning their parameters, and evaluating their performance. Prompt engineering is crucial for achieving high accuracy and efficiency in NLP models. In this section, we will explore the basics of prompt engineering using the OpenAI models for exploration."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 1: Tokenization\n",
|
||||
"Explore Tokenization using tiktoken, an open-source fast tokenizer from OpenAI\n",
|
||||
"See [OpenAI Cookbook](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb?WT.mc_id=academic-105485-koreyst) for more examples.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# EXERCISE:\n",
|
||||
"# 1. Run the exercise as is first\n",
|
||||
"# 2. Change the text to any prompt input you want to use & re-run to see tokens\n",
|
||||
"\n",
|
||||
"import tiktoken\n",
|
||||
"\n",
|
||||
"# Define the prompt you want tokenized\n",
|
||||
"text = f\"\"\"\n",
|
||||
"Jupiter is the fifth planet from the Sun and the \\\n",
|
||||
"largest in the Solar System. It is a gas giant with \\\n",
|
||||
"a mass one-thousandth that of the Sun, but two-and-a-half \\\n",
|
||||
"times that of all the other planets in the Solar System combined. \\\n",
|
||||
"Jupiter is one of the brightest objects visible to the naked eye \\\n",
|
||||
"in the night sky, and has been known to ancient civilizations since \\\n",
|
||||
"before recorded history. It is named after the Roman god Jupiter.[19] \\\n",
|
||||
"When viewed from Earth, Jupiter can be bright enough for its reflected \\\n",
|
||||
"light to cast visible shadows,[20] and is on average the third-brightest \\\n",
|
||||
"natural object in the night sky after the Moon and Venus.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"# Set the model you want encoding for\n",
|
||||
"encoding = tiktoken.encoding_for_model(\"gpt-4o\")\n",
|
||||
"\n",
|
||||
"# Encode the text - gives you the tokens in integer form\n",
|
||||
"tokens = encoding.encode(text)\n",
|
||||
"print(tokens);\n",
|
||||
"\n",
|
||||
"# Decode the integers to see what the text versions look like\n",
|
||||
"[encoding.decode_single_token_bytes(token) for token in tokens]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 2: Validate OpenAI API Key Setup\n",
|
||||
"\n",
|
||||
"Run the code below to verify that your OpenAI endpoint is set up correctly. The code just tries a simple basic prompt and validates the completion. Input `oh say can you see` should complete along the lines of `by the dawn's early light..`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Uses the OpenAI client against the Azure OpenAI (Microsoft Foundry) v1 endpoint\n",
|
||||
"# with the Responses API. See https://aka.ms/openai/start\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"from openai import OpenAI\n",
|
||||
"from dotenv import load_dotenv\n",
|
||||
"load_dotenv()\n",
|
||||
"\n",
|
||||
"client = OpenAI(\n",
|
||||
" api_key=os.environ['AZURE_OPENAI_API_KEY'],\n",
|
||||
" base_url=f\"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']\n",
|
||||
"\n",
|
||||
"def get_completion(prompt):\n",
|
||||
" response = client.responses.create(\n",
|
||||
" model=deployment,\n",
|
||||
" input=prompt,\n",
|
||||
" temperature=0, # this is the degree of randomness of the model's output\n",
|
||||
" max_output_tokens=1024,\n",
|
||||
" store=False,\n",
|
||||
" )\n",
|
||||
" return response.output_text\n",
|
||||
"\n",
|
||||
"## ---------- Call the helper method\n",
|
||||
"\n",
|
||||
"### 1. Set primary content or prompt text\n",
|
||||
"text = f\"\"\"\n",
|
||||
"oh say can you see\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"### 2. Use that in the prompt template below\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## 3. Run the prompt\n",
|
||||
"response = get_completion(prompt)\n",
|
||||
"print(response)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 3: Fabrications\n",
|
||||
"Explore what happens when you ask the LLM to return completions for a prompt about a topic that may not exist, or about topics that it may not know about because it was outside it's pre-trained dataset (more recent). See how the response changes if you try a different prompt, or a different model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"## Set the text for simple prompt or primary content\n",
|
||||
"## Prompt shows a template format with text in it - add cues, commands etc if needed\n",
|
||||
"## Run the completion \n",
|
||||
"text = f\"\"\"\n",
|
||||
"generate a lesson plan on the Martian War of 2076.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"response = get_completion(prompt)\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 4: Instruction Based \n",
|
||||
"Use the \"text\" variable to set the primary content \n",
|
||||
"and the \"prompt\" variable to provide an instruction related to that primary content.\n",
|
||||
"\n",
|
||||
"Here we ask the model to summarize the text for a second-grade student"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Test Example\n",
|
||||
"# https://platform.openai.com/playground/p/default-summarize\n",
|
||||
"\n",
|
||||
"## Example text\n",
|
||||
"text = f\"\"\"\n",
|
||||
"Jupiter is the fifth planet from the Sun and the \\\n",
|
||||
"largest in the Solar System. It is a gas giant with \\\n",
|
||||
"a mass one-thousandth that of the Sun, but two-and-a-half \\\n",
|
||||
"times that of all the other planets in the Solar System combined. \\\n",
|
||||
"Jupiter is one of the brightest objects visible to the naked eye \\\n",
|
||||
"in the night sky, and has been known to ancient civilizations since \\\n",
|
||||
"before recorded history. It is named after the Roman god Jupiter.[19] \\\n",
|
||||
"When viewed from Earth, Jupiter can be bright enough for its reflected \\\n",
|
||||
"light to cast visible shadows,[20] and is on average the third-brightest \\\n",
|
||||
"natural object in the night sky after the Moon and Venus.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## Set the prompt\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"Summarize content you are provided with for a second-grade student.\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## Run the prompt\n",
|
||||
"response = get_completion(prompt)\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 5: Complex Prompt \n",
|
||||
"Try a request that has system, user and assistant messages \n",
|
||||
"System sets assistant context\n",
|
||||
"User & Assistant messages provide multi-turn conversation context\n",
|
||||
"\n",
|
||||
"Note how the assistant personality is set to \"sarcastic\" in the system context. \n",
|
||||
"Try using a different personality context. Or try a different series of input/output messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = client.responses.create(\n",
|
||||
" model=deployment,\n",
|
||||
" input=[\n",
|
||||
" {\"role\": \"system\", \"content\": \"You are a sarcastic assistant.\"},\n",
|
||||
" {\"role\": \"user\", \"content\": \"Who won the world series in 2020?\"},\n",
|
||||
" {\"role\": \"assistant\", \"content\": \"Who do you think won? The Los Angeles Dodgers of course.\"},\n",
|
||||
" {\"role\": \"user\", \"content\": \"Where was it played?\"}\n",
|
||||
" ],\n",
|
||||
" store=False,\n",
|
||||
")\n",
|
||||
"print(response.output_text)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise: Explore Your Intuition\n",
|
||||
"The above examples give you patterns that you can use to create new prompts (simple, complex, instruction etc.) - try creating other exercises to explore some of the other ideas we've talked about like examples, cues and more."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.13"
|
||||
},
|
||||
"orig_nbformat": 4
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Introduction to Prompt Engineering\n",
|
||||
"Prompt engineering is the process of designing and optimizing prompts for natural language processing tasks. It involves selecting the right prompts, tuning their parameters, and evaluating their performance. Prompt engineering is crucial for achieving high accuracy and efficiency in NLP models. In this section, we will explore the basics of prompt engineering using the OpenAI models for exploration."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 1: Tokenization\n",
|
||||
"Explore Tokenization using tiktoken, an open-source fast tokenizer from OpenAI\n",
|
||||
"See [OpenAI Cookbook](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb?WT.mc_id=academic-105485-koreyst) for more examples.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# EXERCISE:\n",
|
||||
"# 1. Run the exercise as is first\n",
|
||||
"# 2. Change the text to any prompt input you want to use & re-run to see tokens\n",
|
||||
"\n",
|
||||
"import tiktoken\n",
|
||||
"\n",
|
||||
"# Define the prompt you want tokenized\n",
|
||||
"text = f\"\"\"\n",
|
||||
"Jupiter is the fifth planet from the Sun and the \\\n",
|
||||
"largest in the Solar System. It is a gas giant with \\\n",
|
||||
"a mass one-thousandth that of the Sun, but two-and-a-half \\\n",
|
||||
"times that of all the other planets in the Solar System combined. \\\n",
|
||||
"Jupiter is one of the brightest objects visible to the naked eye \\\n",
|
||||
"in the night sky, and has been known to ancient civilizations since \\\n",
|
||||
"before recorded history. It is named after the Roman god Jupiter.[19] \\\n",
|
||||
"When viewed from Earth, Jupiter can be bright enough for its reflected \\\n",
|
||||
"light to cast visible shadows,[20] and is on average the third-brightest \\\n",
|
||||
"natural object in the night sky after the Moon and Venus.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"# Set the model you want encoding for\n",
|
||||
"encoding = tiktoken.encoding_for_model(\"gpt-4o\")\n",
|
||||
"\n",
|
||||
"# Encode the text - gives you the tokens in integer form\n",
|
||||
"tokens = encoding.encode(text)\n",
|
||||
"print(tokens);\n",
|
||||
"\n",
|
||||
"# Decode the integers to see what the text versions look like\n",
|
||||
"[encoding.decode_single_token_bytes(token) for token in tokens]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 2: Validate Microsoft Foundry Models Key Setup\n",
|
||||
"\n",
|
||||
"> **Note:** GitHub Models is retiring at the end of July 2026. This exercise uses [Microsoft Foundry Models](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst) instead, which offers the same free-to-try model catalog and Azure AI Inference SDK experience.\n",
|
||||
"\n",
|
||||
"Run the code below to verify that your Microsoft Foundry Models endpoint is set up correctly. The code just tries a simple basic prompt and validates the completion. Input `oh say can you see` should complete along the lines of `by the dawn's early light..`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"That line is the opening lyric of \"The Star-Spangled Banner,\" the national anthem of the United States, written by Francis Scott Key. If you'd like more information or analysis, feel free to ask!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from azure.ai.inference import ChatCompletionsClient\n",
|
||||
"from azure.ai.inference.models import SystemMessage, UserMessage\n",
|
||||
"from azure.core.credentials import AzureKeyCredential\n",
|
||||
"\n",
|
||||
"# Get these from your Microsoft Foundry project's \"Overview\" page\n",
|
||||
"token = os.environ[\"AZURE_INFERENCE_CREDENTIAL\"]\n",
|
||||
"endpoint = os.environ[\"AZURE_INFERENCE_ENDPOINT\"]\n",
|
||||
"\n",
|
||||
"model_name = \"gpt-4o-mini\"\n",
|
||||
"\n",
|
||||
"client = ChatCompletionsClient(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" credential=AzureKeyCredential(token),\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"def get_completion(prompt, client, model_name, temperature=1.0, max_tokens=1000, top_p=1.0):\n",
|
||||
" response = client.complete(\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": \"You are a helpful assistant.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": prompt,\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" model=model_name,\n",
|
||||
" temperature=temperature,\n",
|
||||
" max_tokens=max_tokens,\n",
|
||||
" top_p=top_p\n",
|
||||
" )\n",
|
||||
" return response.choices[0].message.content\n",
|
||||
"\n",
|
||||
"## ---------- Call the helper method\n",
|
||||
"\n",
|
||||
"### 1. Set primary content or prompt text\n",
|
||||
"text = f\"\"\"\n",
|
||||
"oh say can you see\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"### 2. Use that in the prompt template below\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## 3. Run the prompt\n",
|
||||
"response = get_completion(prompt, client, model_name)\n",
|
||||
"print(response)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 3: Fabrications\n",
|
||||
"Explore what happens when you ask the LLM to return completions for a prompt about a topic that may not exist, or about topics that it may not know about because it was outside it's pre-trained dataset (more recent). See how the response changes if you try a different prompt, or a different model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"## Set the text for simple prompt or primary content\n",
|
||||
"## Prompt shows a template format with text in it - add cues, commands etc if needed\n",
|
||||
"## Run the completion \n",
|
||||
"text = f\"\"\"\n",
|
||||
"generate a lesson plan on the Martian War of 2076.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"response = get_completion(prompt, client, model_name)\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 4: Instruction Based \n",
|
||||
"Use the \"text\" variable to set the primary content \n",
|
||||
"and the \"prompt\" variable to provide an instruction related to that primary content.\n",
|
||||
"\n",
|
||||
"Here we ask the model to summarize the text for a second-grade student"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Jupiter is the fifth planet from the Sun and the biggest one in our Solar System. It's made of gas and is much bigger than all the other planets put together! You can see Jupiter in the night sky because it's very bright. People have noticed it for a really long time and named it after a Roman god.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Test Example\n",
|
||||
"# https://platform.openai.com/playground/p/default-summarize\n",
|
||||
"\n",
|
||||
"## Example text\n",
|
||||
"text = f\"\"\"\n",
|
||||
"Jupiter is the fifth planet from the Sun and the \\\n",
|
||||
"largest in the Solar System. It is a gas giant with \\\n",
|
||||
"a mass one-thousandth that of the Sun, but two-and-a-half \\\n",
|
||||
"times that of all the other planets in the Solar System combined. \\\n",
|
||||
"Jupiter is one of the brightest objects visible to the naked eye \\\n",
|
||||
"in the night sky, and has been known to ancient civilizations since \\\n",
|
||||
"before recorded history. It is named after the Roman god Jupiter.[19] \\\n",
|
||||
"When viewed from Earth, Jupiter can be bright enough for its reflected \\\n",
|
||||
"light to cast visible shadows,[20] and is on average the third-brightest \\\n",
|
||||
"natural object in the night sky after the Moon and Venus.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## Set the prompt\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"Summarize content you are provided with for a second-grade student.\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## Run the prompt\n",
|
||||
"response = get_completion(prompt, client, model_name)\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 5: Complex Prompt \n",
|
||||
"Try a request that has system, user and assistant messages \n",
|
||||
"System sets assistant context\n",
|
||||
"User & Assistant messages provide multi-turn conversation context\n",
|
||||
"\n",
|
||||
"Note how the assistant personality is set to \"sarcastic\" in the system context. \n",
|
||||
"Try using a different personality context. Or try a different series of input/output messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Oh, you mean the famous 2020 World Series that wasn’t in a regular location? That was the year they played in the glamorous Arlington, Texas, at Globe Life Field.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = client.complete(\n",
|
||||
" model=model_name,\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"system\", \"content\": \"You are a sarcastic assistant.\"},\n",
|
||||
" {\"role\": \"user\", \"content\": \"Who won the world series in 2020?\"},\n",
|
||||
" {\"role\": \"assistant\", \"content\": \"Who do you think won? The Los Angeles Dodgers of course.\"},\n",
|
||||
" {\"role\": \"user\", \"content\": \"Where was it played?\"}\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"print(response.choices[0].message.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise: Explore Your Intuition\n",
|
||||
"The above examples give you patterns that you can use to create new prompts (simple, complex, instruction etc.) - try creating other exercises to explore some of the other ideas we've talked about like examples, cues and more."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "ai4beg",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The following notebook was auto-generated by GitHub Copilot Chat and is meant for initial setup only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Introduction to Prompt Engineering\n",
|
||||
"Prompt engineering is the process of designing and optimizing prompts for natural language processing tasks. It involves selecting the right prompts, tuning their parameters, and evaluating their performance. Prompt engineering is crucial for achieving high accuracy and efficiency in NLP models. In this section, we will explore the basics of prompt engineering using the OpenAI models for exploration."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 1: Tokenization\n",
|
||||
"Explore Tokenization using tiktoken, an open-source fast tokenizer from OpenAI\n",
|
||||
"See [OpenAI Cookbook](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb?WT.mc_id=academic-105485-koreyst) for more examples.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# EXERCISE:\n",
|
||||
"# 1. Run the exercise as is first\n",
|
||||
"# 2. Change the text to any prompt input you want to use & re-run to see tokens\n",
|
||||
"\n",
|
||||
"import tiktoken\n",
|
||||
"\n",
|
||||
"# Define the prompt you want tokenized\n",
|
||||
"text = f\"\"\"\n",
|
||||
"Jupiter is the fifth planet from the Sun and the \\\n",
|
||||
"largest in the Solar System. It is a gas giant with \\\n",
|
||||
"a mass one-thousandth that of the Sun, but two-and-a-half \\\n",
|
||||
"times that of all the other planets in the Solar System combined. \\\n",
|
||||
"Jupiter is one of the brightest objects visible to the naked eye \\\n",
|
||||
"in the night sky, and has been known to ancient civilizations since \\\n",
|
||||
"before recorded history. It is named after the Roman god Jupiter.[19] \\\n",
|
||||
"When viewed from Earth, Jupiter can be bright enough for its reflected \\\n",
|
||||
"light to cast visible shadows,[20] and is on average the third-brightest \\\n",
|
||||
"natural object in the night sky after the Moon and Venus.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"# Set the model you want encoding for\n",
|
||||
"encoding = tiktoken.encoding_for_model(\"gpt-4o\")\n",
|
||||
"\n",
|
||||
"# Encode the text - gives you the tokens in integer form\n",
|
||||
"tokens = encoding.encode(text)\n",
|
||||
"print(tokens);\n",
|
||||
"\n",
|
||||
"# Decode the integers to see what the text versions look like\n",
|
||||
"[encoding.decode_single_token_bytes(token) for token in tokens]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 2: Validate OpenAI API Key Setup\n",
|
||||
"\n",
|
||||
"Run the code below to verify that your OpenAI endpoint is set up correctly. The code just tries a simple basic prompt and validates the completion. Input `oh say can you see` should complete along the lines of `by the dawn's early light..`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Uses the OpenAI client with the Responses API.\n",
|
||||
"# See https://platform.openai.com/docs/api-reference/responses\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"from openai import OpenAI\n",
|
||||
"from dotenv import load_dotenv\n",
|
||||
"load_dotenv()\n",
|
||||
"\n",
|
||||
"client = OpenAI()\n",
|
||||
"\n",
|
||||
"deployment=\"gpt-4o-mini\"\n",
|
||||
"\n",
|
||||
"def get_completion(prompt):\n",
|
||||
" response = client.responses.create(\n",
|
||||
" model=deployment,\n",
|
||||
" input=prompt,\n",
|
||||
" temperature=0, # this is the degree of randomness of the model's output\n",
|
||||
" max_output_tokens=1024,\n",
|
||||
" store=False,\n",
|
||||
" )\n",
|
||||
" return response.output_text\n",
|
||||
"\n",
|
||||
"## ---------- Call the helper method\n",
|
||||
"\n",
|
||||
"### 1. Set primary content or prompt text\n",
|
||||
"text = f\"\"\"\n",
|
||||
"oh say can you see\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"### 2. Use that in the prompt template below\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## 3. Run the prompt\n",
|
||||
"response = get_completion(prompt)\n",
|
||||
"print(response)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 3: Fabrications\n",
|
||||
"Explore what happens when you ask the LLM to return completions for a prompt about a topic that may not exist, or about topics that it may not know about because it was outside it's pre-trained dataset (more recent). See how the response changes if you try a different prompt, or a different model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"## Set the text for simple prompt or primary content\n",
|
||||
"## Prompt shows a template format with text in it - add cues, commands etc if needed\n",
|
||||
"## Run the completion \n",
|
||||
"text = f\"\"\"\n",
|
||||
"generate a lesson plan on the Martian War of 2076.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"response = get_completion(prompt)\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 4: Instruction Based \n",
|
||||
"Use the \"text\" variable to set the primary content \n",
|
||||
"and the \"prompt\" variable to provide an instruction related to that primary content.\n",
|
||||
"\n",
|
||||
"Here we ask the model to summarize the text for a second-grade student"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Test Example\n",
|
||||
"# https://platform.openai.com/playground/p/default-summarize\n",
|
||||
"\n",
|
||||
"## Example text\n",
|
||||
"text = f\"\"\"\n",
|
||||
"Jupiter is the fifth planet from the Sun and the \\\n",
|
||||
"largest in the Solar System. It is a gas giant with \\\n",
|
||||
"a mass one-thousandth that of the Sun, but two-and-a-half \\\n",
|
||||
"times that of all the other planets in the Solar System combined. \\\n",
|
||||
"Jupiter is one of the brightest objects visible to the naked eye \\\n",
|
||||
"in the night sky, and has been known to ancient civilizations since \\\n",
|
||||
"before recorded history. It is named after the Roman god Jupiter.[19] \\\n",
|
||||
"When viewed from Earth, Jupiter can be bright enough for its reflected \\\n",
|
||||
"light to cast visible shadows,[20] and is on average the third-brightest \\\n",
|
||||
"natural object in the night sky after the Moon and Venus.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## Set the prompt\n",
|
||||
"prompt = f\"\"\"\n",
|
||||
"Summarize content you are provided with for a second-grade student.\n",
|
||||
"```{text}```\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"## Run the prompt\n",
|
||||
"response = get_completion(prompt)\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise 5: Complex Prompt \n",
|
||||
"Try a request that has system, user and assistant messages \n",
|
||||
"System sets assistant context\n",
|
||||
"User & Assistant messages provide multi-turn conversation context\n",
|
||||
"\n",
|
||||
"Note how the assistant personality is set to \"sarcastic\" in the system context. \n",
|
||||
"Try using a different personality context. Or try a different series of input/output messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = client.responses.create(\n",
|
||||
" model=deployment,\n",
|
||||
" input=[\n",
|
||||
" {\"role\": \"system\", \"content\": \"You are a sarcastic assistant.\"},\n",
|
||||
" {\"role\": \"user\", \"content\": \"Who won the world series in 2020?\"},\n",
|
||||
" {\"role\": \"assistant\", \"content\": \"Who do you think won? The Los Angeles Dodgers of course.\"},\n",
|
||||
" {\"role\": \"user\", \"content\": \"Where was it played?\"}\n",
|
||||
" ],\n",
|
||||
" store=False,\n",
|
||||
")\n",
|
||||
"print(response.output_text)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Exercise: Explore Your Intuition\n",
|
||||
"The above examples give you patterns that you can use to create new prompts (simple, complex, instruction etc.) - try creating other exercises to explore some of the other ideas we've talked about like examples, cues and more."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,636 @@
|
||||
# Creating Advanced prompts
|
||||
|
||||
[](https://youtu.be/BAjzkaCdRok?si=NmUIyRf7-cDgbjtt)
|
||||
|
||||
Let's recap some learnings from the previous chapter:
|
||||
|
||||
> Prompt _engineering_ is the process by which we **guide the model towards more relevant responses** by providing more useful instructions or context.
|
||||
|
||||
There are also two steps to writing prompts: constructing the prompt, by providing relevant context, and _optimization_, how to gradually improve the prompt.
|
||||
|
||||
At this point, we have some basic understanding of how to write prompts, but we need to go deeper. In this chapter, you will go from trying out various prompts to understanding why one prompt is better than another. You will learn how to construct prompts following some basic techniques that can be applied to any LLM.
|
||||
|
||||
## Introduction
|
||||
|
||||
In this chapter, we will cover the following topics:
|
||||
|
||||
- Extend your knowledge of prompt engineering by applying different techniques to your prompts.
|
||||
- Configuring your prompts to vary the output.
|
||||
|
||||
## Learning goals
|
||||
|
||||
After completing this lesson, you'll be able to:
|
||||
|
||||
- Apply prompt engineering techniques that improve the outcome of your prompts.
|
||||
- Perform prompting that is either varied or deterministic.
|
||||
|
||||
## Prompt engineering
|
||||
|
||||
Prompt engineering is the process of creating prompts that will produce the desired outcome. There's more to prompt engineering than just writing a text prompt. Prompt engineering is not an engineering discipline, it's more a set of techniques that you can apply to get the desired outcome.
|
||||
|
||||
### An example of a prompt
|
||||
|
||||
Let's take a basic prompt like this one:
|
||||
|
||||
> Generate 10 questions on geography.
|
||||
|
||||
In this prompt, you are actually applying a set of different prompt techniques.
|
||||
|
||||
Let's break this down.
|
||||
|
||||
- **Context**, you specify it should be about "geography".
|
||||
- **Limiting the output**, you want no more than 10 questions.
|
||||
|
||||
### Limitations of simple prompting
|
||||
|
||||
You may or may not get the desired outcome. You will get your questions generated, but geography is a big topic and you may not get what you want to due the following reasons:
|
||||
|
||||
- **Big topic**, you don't know if it's going to be about countries, capitals, rivers and so on.
|
||||
- **Format**, what if you wanted the questions to be formatted in a certain way?
|
||||
|
||||
As you can see, there's a lot to consider when creating prompts.
|
||||
|
||||
So far, we've seen a simple prompt example, but generative AI is capable of much more to help people in a variety of roles and industries. Let's explore some basic techniques next.
|
||||
|
||||
### Techniques for prompting
|
||||
|
||||
First, we need to understand that prompting is an _emergent_ property of an LLM meaning that this is not a feature that is built into the model but rather something we discover as we use the model.
|
||||
|
||||
There are some basic techniques that we can use to prompt an LLM. Let's explore them.
|
||||
|
||||
- **Zero-shot prompting**, this is the most basic form of prompting. It's a single prompt requesting a response from the LLM based solely on its training data.
|
||||
- **Few-shot prompting**, this type of prompting guides the LLM by providing 1 or more examples it can rely on to generate its response.
|
||||
- **Chain-of-thought**, this type of prompting tells the LLM how to break down a problem into steps.
|
||||
- **Generated knowledge**, to improve the response of a prompt, you can provide generated facts or knowledge additionally to your prompt.
|
||||
- **Least to most**, like chain-of-thought, this technique is about breaking down a problem into a series of steps and then ask these steps to be performed in order.
|
||||
- **Self-refine**, this technique is about critiquing the LLM's output and then asking it to improve.
|
||||
- **Maieutic prompting**. What you want here is to ensure the LLM answer is correct and you ask it to explain various parts of the answer. This is a form of self-refine.
|
||||
|
||||
### Zero-shot prompting
|
||||
|
||||
This style of prompting is very simple, it consists of a single prompt. This technique is probably what you're using as you're starting to learn about LLMs. Here's an example:
|
||||
|
||||
- Prompt: "What is Algebra?"
|
||||
- Answer: "Algebra is a branch of mathematics that studies mathematical symbols and the rules for manipulating these symbols."
|
||||
|
||||
### Few-shot prompting
|
||||
|
||||
This style of prompting helps the model by providing a few examples along with the request. It consists of a single prompt with additional task-specific data. Here's an example:
|
||||
|
||||
- Prompt: "Write a poem in the style of Shakespeare. Here are a few examples of Shakespearean sonnets.:
|
||||
Sonnet 18: 'Shall I compare thee to a summer's day? Thou art more lovely and more temperate...'
|
||||
Sonnet 116: 'Let me not to the marriage of true minds Admit impediments. Love is not love Which alters when it alteration finds...'
|
||||
Sonnet 132: 'Thine eyes I love, and they, as pitying me, Knowing thy heart torment me with disdain,...'
|
||||
Now, write a sonnet about the beauty of the moon."
|
||||
- Answer: "Upon the sky, the moon doth softly gleam, In silv'ry light that casts its gentle grace,..."
|
||||
|
||||
Examples provide the LLM with the context, format or style of the desired output. They help the model understand the specific task and generate more accurate and relevant responses.
|
||||
|
||||
### Chain-of-thought
|
||||
|
||||
Chain-of-thought is a very interesting technique as it's about taking the LLM through a series of steps. The idea is to instruct the LLM in such a way that it understands how to do something. Consider the following example, with and without chain-of-thought:
|
||||
|
||||
- Prompt: "Alice has 5 apples, throws 3 apples, gives 2 to Bob and Bob gives one back, how many apples does Alice have?"
|
||||
- Answer: 5
|
||||
|
||||
LLM answers with 5, which is incorrect. Correct answer is 1 apple, given the calculation (5 -3 -2 + 1 = 1).
|
||||
|
||||
So how can we teach the LLM to do this correctly?
|
||||
|
||||
Let's try chain-of-thought. Applying chain-of-thought means:
|
||||
|
||||
1. Give the LLM a similar example.
|
||||
1. Show the calculation, and how to calculate it correctly.
|
||||
1. Provide the original prompt.
|
||||
|
||||
Here's how:
|
||||
|
||||
- Prompt: "Lisa has 7 apples, throws 1 apple, gives 4 apples to Bart and Bart gives one back:
|
||||
7 -1 = 6
|
||||
6 -4 = 2
|
||||
2 +1 = 3
|
||||
Alice has 5 apples, throws 3 apples, gives 2 to Bob and Bob gives one back, how many apples does Alice have?"
|
||||
Answer: 1
|
||||
|
||||
Note how we write substantially longer prompts with another example, a calculation and then the original prompt and we arrive at the correct answer 1.
|
||||
|
||||
As you can see chain-of-thought is a very powerful technique.
|
||||
|
||||
### Generated knowledge
|
||||
|
||||
Many times when you want to construct a prompt, you want to do so using your own company's data. You want part of the prompt to be from the company and the other part should be the actual prompt you're interested in.
|
||||
|
||||
As an example, this is what your prompt then can look like if you're in the insurance business:
|
||||
|
||||
```text
|
||||
{{company}}: {{company_name}}
|
||||
{{products}}:
|
||||
{{products_list}}
|
||||
Please suggest an insurance given the following budget and requirements:
|
||||
Budget: {{budget}}
|
||||
Requirements: {{requirements}}
|
||||
```
|
||||
|
||||
Above, you see how the prompt is constructed using a template. In the template there's a number of variables, denoted by `{{variable}}`, that will be replaced with actual values from a company API.
|
||||
|
||||
Here's an example of how the prompt could look like once the variables have been replaced by content from your company:
|
||||
|
||||
```text
|
||||
Insurance company: ACME Insurance
|
||||
Insurance products (cost per month):
|
||||
- Car, cheap, 500 USD
|
||||
- Car, expensive, 1100 USD
|
||||
- Home, cheap, 600 USD
|
||||
- Home, expensive, 1200 USD
|
||||
- Life, cheap, 100 USD
|
||||
|
||||
Please suggest an insurance given the following budget and requirements:
|
||||
Budget: $1000
|
||||
Requirements: Car, Home, and Life insurance
|
||||
```
|
||||
|
||||
Running this prompt through an LLM will produce a response like this:
|
||||
|
||||
```output
|
||||
Given the budget and requirements, we suggest the following insurance package from ACME Insurance:
|
||||
- Car, cheap, 500 USD
|
||||
- Home, cheap, 600 USD
|
||||
- Life, cheap, 100 USD
|
||||
Total cost: $1,200 USD
|
||||
```
|
||||
|
||||
As you can see, it also suggests the Life insurance, which it shouldn't. This result is an indication that we need to optimize the prompt by changing the prompt to be clearer on what it can allow. After some _trial and error_, we arrive at the following prompt:
|
||||
|
||||
```text
|
||||
Insurance company: ACME Insurance
|
||||
Insurance products (cost per month):
|
||||
- type: Car, cheap, cost: 500 USD
|
||||
- type: Car, expensive, cost: 1100 USD
|
||||
- type: Home, cheap, cost: 600 USD
|
||||
- type: Home, expensive, cost: 1200 USD
|
||||
- type: Life, cheap, cost: 100 USD
|
||||
|
||||
Please suggest an insurance given the following budget and requirements:
|
||||
Budget: $1000 restrict choice to types: Car, Home
|
||||
```
|
||||
|
||||
Note how adding _type_ and _cost_ and also using the keyword _restrict_ helps the LLM to understand what we want.
|
||||
|
||||
Now we get the following response:
|
||||
|
||||
```output
|
||||
Given the budget and requirements, we suggest the Car, Cheap insurance product which costs 500 USD per month.
|
||||
```
|
||||
|
||||
The point of this example was to show that even though we're using a basic technique like _generated knowledge_, we still need to optimize the prompt in most cases to get the desired outcome.
|
||||
|
||||
### Least-to-most
|
||||
|
||||
The idea with Least-to-most prompting is to break down a bigger problem into subproblems. That way, you help guide the LLM on how to "conquer" the bigger problem. A good example could be for data science where you can ask the LLM to divide up a problem like so:
|
||||
|
||||
> Prompt: How to perform data science in 5 steps?
|
||||
|
||||
With your AI assistant answering with:
|
||||
|
||||
1. Collect data
|
||||
1. Clean data
|
||||
1. Analyze data
|
||||
1. Plot data
|
||||
1. Present data
|
||||
|
||||
### Self-refine, critique the results
|
||||
|
||||
With generative AIs and LLMs, you can't trust the output. You need to verify it. After all, the LLM is just presenting you what's the next most likely thing to say, not what's correct. Therefore, a good idea is to ask the LLM to critique itself, which leads us to the self-refine technique.
|
||||
|
||||
How it works is that you follow the following steps:
|
||||
|
||||
1. Initial prompt asking the LLM to solve a problem
|
||||
1. LLM answers
|
||||
1. You critique the answer and ask the AI to improve
|
||||
1. LLM answers again, this time considering the critique and suggest solutions it came up with
|
||||
|
||||
You can repeat this process as many times as you want.
|
||||
|
||||
Here's an example using this technique:
|
||||
|
||||
> Prompt: "Create a Python Web API with routes products and customers"
|
||||
|
||||
AI Answer:
|
||||
|
||||
```python
|
||||
import flask
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/products')
|
||||
def products():
|
||||
return 'Products'
|
||||
|
||||
@app.route('/customers')
|
||||
def customers():
|
||||
return 'Customers'
|
||||
```
|
||||
|
||||
> Prompt: suggest 3 improvements of the above code
|
||||
|
||||
AI Answer:
|
||||
|
||||
```text
|
||||
Sure, here are three suggestions to improve the code:
|
||||
|
||||
Import the correct module: The first line should import Flask instead of flask. The correct import statement should be from flask import Flask.
|
||||
|
||||
Add a main function: Add a if __name__ == '__main__': block to the end of the file to run the application when the file is executed.
|
||||
|
||||
Use f-strings: Use f-strings to format the return statements in the route functions. For example, return 'Products' can be changed to return f'This is the {request.path} page'.
|
||||
```
|
||||
|
||||
```python
|
||||
from flask import Flask, request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/products')
|
||||
def products():
|
||||
return f'This is the {request.path} page'
|
||||
|
||||
@app.route('/customers')
|
||||
def customers():
|
||||
return f'This is the {request.path} page'
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
```
|
||||
|
||||
As you can see, the above AI response is improving the first suggested code thanks to critiquing the first answer.
|
||||
|
||||
### Maieutic prompting
|
||||
|
||||
Maieutic prompting is a technique that is similar to self-refine but it's more about asking the LLM to explain itself. The goal is to reduce inconsistencies in the LLM's output so to ensure it arrives at the correct answer. The workflow to follow is:
|
||||
|
||||
1. Ask the LLM to answer a question
|
||||
1. For each part of the answer, ask the LLM to explain it more in depth.
|
||||
1. If there are inconsistencies, discard the parts that are inconsistent.
|
||||
|
||||
Repeat 2 and 3 until you've gone through all the parts and you're satisfied with the answer.
|
||||
|
||||
Here's an example prompt:
|
||||
|
||||
> prompt: How can I create a crisis plan to mitigate a pandemic in 5 steps?
|
||||
> LLM answer:
|
||||
|
||||
```text
|
||||
1. Identify the risks
|
||||
1. Identify the stakeholders
|
||||
1. Identify the resources
|
||||
1. Identify the actions
|
||||
1. Identify the communication channels
|
||||
```
|
||||
|
||||
It's identified 5 steps, but can we determine if this is correct? Let's ask the LLM to explain each step:
|
||||
|
||||
> prompt: Explain the first step in more detail, what are the risks in detail with a pandemic?
|
||||
|
||||
```text
|
||||
The risks are the following:
|
||||
|
||||
- Loss of life
|
||||
- Loss of business
|
||||
- Loss of reputation
|
||||
- Loss of customers
|
||||
- Loss of employees
|
||||
```
|
||||
|
||||
At this point, we want to make sure the LLM is correct so we ask it to explain the first risk in more detail and hope it's consistent with the answer above:
|
||||
|
||||
> prompt: In a Pandemic, which is the biggest risk and why?
|
||||
|
||||
```text
|
||||
The biggest risk is loss of life because it's the most important thing to protect.
|
||||
```
|
||||
|
||||
> What are the two biggest risks in a pandemic?
|
||||
|
||||
```text
|
||||
The two biggest risks are loss of life and loss of business.
|
||||
```
|
||||
|
||||
At this point, the LLM is consistent and mentions "life" and "business" as the top two risks. We can now continue to the next step and feel fairly confident. However, we shouldn't trust the LLM blindly, we should always verify the output.
|
||||
|
||||
## Vary your output
|
||||
|
||||
LLMs are nondeterministic by nature, meaning that you will get different results each time you run the same prompt. Try the following prompt for example:
|
||||
|
||||
> "Generate code for a Python Web API"
|
||||
|
||||
```python
|
||||
# Import necessary modules
|
||||
import flask
|
||||
from flask import request, jsonify
|
||||
|
||||
# Create a Flask app
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
# Create a route for the API
|
||||
@app.route('/api', methods=['GET'])
|
||||
def api():
|
||||
# Get the data from the request
|
||||
data = request.args
|
||||
|
||||
# Process the data
|
||||
result = process_data(data)
|
||||
|
||||
# Return the result as JSON
|
||||
return jsonify(result)
|
||||
|
||||
# Function to process the data
|
||||
def process_data(data):
|
||||
# Do something with the data
|
||||
result = data + 1
|
||||
|
||||
# Return the result
|
||||
return result
|
||||
|
||||
# Run the app
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
```
|
||||
|
||||
Running the same prompt again generates a slightly different response:
|
||||
|
||||
```python
|
||||
#import necessary packages
|
||||
import flask
|
||||
from flask import request, jsonify
|
||||
|
||||
#create the Flask application
|
||||
app = flask.Flask(__name__)
|
||||
app.config["DEBUG"] = True
|
||||
|
||||
#create a list of books
|
||||
books = [
|
||||
{'id': 0,
|
||||
'title': 'A Fire Upon the Deep',
|
||||
'author': 'Vernor Vinge',
|
||||
'first_sentence': 'The coldsleep itself was dreamless.',
|
||||
'year_published': '1992'},
|
||||
{'id': 1,
|
||||
'title': 'The Ones Who Walk Away From Omelas',
|
||||
'author': 'Ursula K. Le Guin',
|
||||
'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.',
|
||||
'published': '1973'},
|
||||
{'id': 2,
|
||||
'title': 'Dhalgren',
|
||||
'author': 'Samuel R. Delany',
|
||||
'first_sentence': 'to wound the autumnal city.',
|
||||
'published': '1975'}
|
||||
]
|
||||
|
||||
#create an endpoint for the API
|
||||
@app.route('/', methods=['GET'])
|
||||
def home():
|
||||
return '''<h1>Books API</h1>
|
||||
<p>A prototype API for retrieving books.</p>'''
|
||||
|
||||
#create an endpoint to return all books
|
||||
@app.route('/api/v1/resources/books/all', methods=['GET'])
|
||||
def api_all():
|
||||
return jsonify(books)
|
||||
|
||||
#create an endpoint to return a single book
|
||||
@app.route('/api/v1/resources/books', methods=['GET'])
|
||||
def api_id():
|
||||
#check if an ID was provided as part of the URL
|
||||
#if ID is provided, assign it to a variable
|
||||
#if no ID is provided, display an error
|
||||
if 'id' in request.args:
|
||||
id = int(request.args['id'])
|
||||
else:
|
||||
return "Error: No id field provided. Please specify an id."
|
||||
|
||||
#create an empty list for our results
|
||||
results = []
|
||||
|
||||
#loop through the data and match results that fit the requested ID
|
||||
#IDs are unique, but other fields might return many results
|
||||
for book in books:
|
||||
if book['id'] == id:
|
||||
results.append(book)
|
||||
|
||||
#use the jsonify function from Flask to convert our list of
|
||||
#Python dictionaries to the JSON format
|
||||
return jsonify(results)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
> So is the varied output a problem?
|
||||
|
||||
Depends on what you're trying to do. If you want a specific response then it's a problem. If you're ok with a varied output like "Generate any 3 questions on geography", then it's not a problem.
|
||||
|
||||
### Using temperature to vary your output
|
||||
|
||||
Ok, so we've decided we want to limit the output to be more predictable, that is more deterministic. How do we do that?
|
||||
|
||||
Temperature is a value between 0 and 1, where 0 is the most deterministic and 1 is the most varied. The default value is 0.7. Let's see what happens with two runs of the same prompt with temperature set to 0.1:
|
||||
|
||||
> "Generate code for a Python Web API"
|
||||
|
||||
```python
|
||||
#import necessary libraries
|
||||
import flask
|
||||
from flask import request, jsonify
|
||||
|
||||
#create an instance of the Flask class
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
#create an endpoint for the API
|
||||
@app.route('/api/v1/endpoint', methods=['GET'])
|
||||
def api_endpoint():
|
||||
#get the request data
|
||||
data = request.get_json()
|
||||
|
||||
#process the data
|
||||
result = process_data(data)
|
||||
|
||||
#return the result
|
||||
return jsonify(result)
|
||||
|
||||
#function to process the data
|
||||
def process_data(data):
|
||||
#process the data
|
||||
result = {'result': 'success'}
|
||||
|
||||
#return the result
|
||||
return result
|
||||
|
||||
#run the Flask app
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
```
|
||||
|
||||
Running the prompt again gives us this outcome:
|
||||
|
||||
```python
|
||||
#import necessary libraries
|
||||
import flask
|
||||
from flask import request, jsonify
|
||||
|
||||
#create a Flask app
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
#create a route for the API
|
||||
@app.route('/api', methods=['GET'])
|
||||
def api_call():
|
||||
#get the data from the request
|
||||
data = request.get_json()
|
||||
|
||||
#process the data
|
||||
result = process_data(data)
|
||||
|
||||
#return the result
|
||||
return jsonify(result)
|
||||
|
||||
#define the process_data function
|
||||
def process_data(data):
|
||||
#do something with the data
|
||||
result = data + 1
|
||||
|
||||
#return the result
|
||||
return result
|
||||
|
||||
#run the app
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
```
|
||||
|
||||
There's only a tiny difference between these two outputs. Let's do the opposite this time, let's set the temperature to 0.9:
|
||||
|
||||
```python
|
||||
# Import necessary libraries
|
||||
import flask
|
||||
from flask import request, jsonify
|
||||
|
||||
# Create a Flask app
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
# Create a route for the API
|
||||
@app.route('/api', methods=['GET'])
|
||||
def api_call():
|
||||
# Get the data from the request
|
||||
data = request.args
|
||||
|
||||
# Process the data
|
||||
result = process_data(data)
|
||||
|
||||
# Return the result
|
||||
return jsonify(result)
|
||||
|
||||
# Function to process the data
|
||||
def process_data(data):
|
||||
# Do something with the data
|
||||
result = data + 1
|
||||
|
||||
# Return the result
|
||||
return result
|
||||
|
||||
# Run the app
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
```
|
||||
|
||||
and the second attempt at 0.9 as temperature value:
|
||||
|
||||
```python
|
||||
import flask
|
||||
from flask import request, jsonify
|
||||
|
||||
# create the Flask app
|
||||
app = flask.Flask(__name__)
|
||||
app.config['DEBUG'] = True
|
||||
|
||||
# create some test data
|
||||
books = [
|
||||
{'id': 0, 'title': 'A Fire Upon The Deep', 'author': 'Vernor Vinge', 'first_sentence': 'The coldsleep itself was dreamless.', 'year_published': '1992'},
|
||||
{'id': 1, 'title': 'The Ones Who Walk Away From Omelas', 'author': 'Ursula K. Le Guin', 'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.', 'published': '1973'},
|
||||
{'id': 2, 'title': 'Dhalgren', 'author': 'Samuel R. Delany', 'first_sentence': 'to wound the autumnal city.', 'published': '1975'}
|
||||
]
|
||||
|
||||
# create an endpoint
|
||||
@app.route('/', methods=['GET'])
|
||||
def home():
|
||||
return '''<h1>Welcome to our book API!</h1>'''
|
||||
|
||||
@app.route('/api/v1/resources/books
|
||||
|
||||
```
|
||||
|
||||
As you can see, the results couldn't be more varied.
|
||||
|
||||
> Note, that there are more parameters you can change to vary the output, like top-k, top-p, repetition penalty, length penalty and diversity penalty but these are outside the scope of this curriculum.
|
||||
|
||||
## Good practices
|
||||
|
||||
There are many practices you can apply to try to get what you want. You will find your own style as you use prompting more and more.
|
||||
|
||||
Additionally to the techniques we've covered, there are some good practices to consider when prompting an LLM.
|
||||
|
||||
Here are some good practices to consider:
|
||||
|
||||
- **Specify context**. Context matters, the more you can specify like domain, topic, etc. the better.
|
||||
- Limit the output. If you want a specific number of items or a specific length, specify it.
|
||||
- **Specify both what and how**. Remember to mention both what you want and how you want it, for example "Create a Python Web API with routes products and customers, divide it into 3 files".
|
||||
- **Use templates**. Often, you will want to enrich your prompts with data from your company. Use templates to do this. Templates can have variables that you replace with actual data.
|
||||
- **Spell correctly**. LLMs might provide you with a correct response, but if you spell correctly, you will get a better response.
|
||||
|
||||
## Assignment
|
||||
|
||||
Here's code in Python showing how to build a simple API using Flask:
|
||||
|
||||
```python
|
||||
from flask import Flask, request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def hello():
|
||||
name = request.args.get('name', 'World')
|
||||
return f'Hello, {name}!'
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
```
|
||||
|
||||
Use an AI assistant like GitHub Copilot or ChatGPT and apply the "self-refine" technique to improve the code.
|
||||
|
||||
## Solution
|
||||
|
||||
Please attempt to solve the assignment by adding suitable prompts to the code.
|
||||
|
||||
> [!TIP]
|
||||
> Phrase a prompt to ask it to improve, it's a good idea to limit how many improvements. You can also ask to improve it in a certain way, for example architecture, performance, security, etc.
|
||||
|
||||
[Solution](./python/aoai-solution.py?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
## Knowledge check
|
||||
|
||||
Why would I use chain-of-thought prompting? Show me 1 correct response and 2 incorrect responses.
|
||||
|
||||
1. To teach the LLM how to solve a problem.
|
||||
1. B, To teach the LLM to find errors in code.
|
||||
1. C, To instruct the LLM to come up with different solutions.
|
||||
|
||||
A: 1, because chain-of-thought is about showing the LLM how to solve a problem by providing it with a series of steps, and similar problems and how they were solved.
|
||||
|
||||
## 🚀 Challenge
|
||||
|
||||
You just used the self-refine technique in the assignment. Take any program you built and consider what improvements you would want to apply to it. Now use the self-refine technique to apply the proposed changes. What did you think the result, better or worse?
|
||||
|
||||
## Great Work! Continue Your Learning
|
||||
|
||||
After completing this lesson, check out our [Generative AI Learning collection](https://aka.ms/genai-collection?WT.mc_id=academic-105485-koreyst) to continue leveling up your Generative AI knowledge!
|
||||
|
||||
Head over to Lesson 6 where we will apply our knowledge of Prompt Engineering by [building text generation apps](../06-text-generation-apps/README.md?WT.mc_id=academic-105485-koreyst)
|
||||
|
After Width: | Height: | Size: 702 KiB |
@@ -0,0 +1,11 @@
|
||||
const express = require('express')
|
||||
|
||||
const app = express()
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World!')
|
||||
})
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Example app listening on port 3000!')
|
||||
})
|
||||
@@ -0,0 +1,121 @@
|
||||
// starter code
|
||||
const express = require('express')
|
||||
|
||||
const app = express()
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World!')
|
||||
})
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Example app listening on port 3000!')
|
||||
})
|
||||
|
||||
|
||||
// prompt in Copilot Chat, find 3 improvements to the code and apply
|
||||
// results:
|
||||
|
||||
/*
|
||||
Sure, here are three improvements that can be made to the code:
|
||||
|
||||
1. The warning "File is a CommonJS module; it may be converted to an ES module" can be resolved by adding "type": "module" to the package.json file. This will allow the use of ES modules in the project.
|
||||
|
||||
1. The warning "'req' is declared but its value is never read" can be resolved by removing the req parameter from the callback function of the app.get() method. Since the req parameter is not used in the function, it can be safely removed.
|
||||
|
||||
1. The console.log() statement in the app.listen() method can be replaced with a more descriptive message. For example, "Server is running on port 3000" would be a more informative message.
|
||||
|
||||
Here is the updated code with the suggested improvements:
|
||||
*/
|
||||
import express from 'express';
|
||||
|
||||
const app = express();
|
||||
|
||||
app.get('/', (_, res) => {
|
||||
res.send('Hello World!');
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Server is running on port 3000');
|
||||
});
|
||||
|
||||
// Prompt: improve the code and make it more secure
|
||||
|
||||
/*
|
||||
Sure, here are some suggestions to improve the code and make it more secure:
|
||||
|
||||
1. Use environment variables to store sensitive information such as database credentials, API keys, and other secrets. This will prevent the information from being hard-coded in the code and exposed in case of a security breach.
|
||||
|
||||
1. Implement input validation to prevent malicious input from being processed by the server. For example, you can use a library like express-validator to validate user input and sanitize it before processing.
|
||||
|
||||
1. Use HTTPS instead of HTTP to encrypt data in transit and prevent eavesdropping and man-in-the-middle attacks. You can use a library like https to create an HTTPS server and obtain a TLS/SSL certificate from a trusted certificate authority.
|
||||
|
||||
Here is the updated code with the suggested improvements:
|
||||
*/
|
||||
import express from 'express';
|
||||
import https from 'https';
|
||||
import fs from 'fs';
|
||||
import { check, validationResult } from 'express-validator';
|
||||
|
||||
const app = express();
|
||||
|
||||
// Use environment variables to store sensitive information
|
||||
const PORT = process.env.PORT || 3000;
|
||||
const DB_USER = process.env.DB_USER;
|
||||
const DB_PASS = process.env.DB_PASS;
|
||||
|
||||
// Implement input validation
|
||||
app.get('/', [
|
||||
check('name').isLength({ min: 3 }).withMessage('Name must be at least 3 characters'),
|
||||
check('email').isEmail().withMessage('Invalid email address'),
|
||||
], (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
const { name, email } = req.query;
|
||||
res.send(`Hello ${name} (${email})!`);
|
||||
});
|
||||
|
||||
// Use HTTPS instead of HTTP
|
||||
// SECURITY: Use absolute paths and proper error handling for certificates
|
||||
const path = await import('path');
|
||||
const certDir = process.env.CERT_DIR || process.cwd();
|
||||
|
||||
// SECURITY: Validate certificate directory is within allowed path
|
||||
const resolvedCertDir = path.resolve(certDir);
|
||||
if (!resolvedCertDir.startsWith(process.cwd())) {
|
||||
throw new Error('Certificate directory must be within the application directory');
|
||||
}
|
||||
|
||||
let options;
|
||||
try {
|
||||
options = {
|
||||
key: fs.readFileSync(path.join(resolvedCertDir, 'server.key')),
|
||||
cert: fs.readFileSync(path.join(resolvedCertDir, 'server.cert'))
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to load SSL certificates:', error.message);
|
||||
console.error('Please ensure server.key and server.cert files exist in the certificate directory');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
https.createServer(options, app).listen(PORT, () => {
|
||||
console.log(`Server is running securely on port ${PORT}`);
|
||||
});
|
||||
|
||||
/*
|
||||
Note that the server.key and server.cert files are required to create an HTTPS server. You can generate a self-signed certificate using the following command:
|
||||
*/
|
||||
|
||||
```bash
|
||||
openssl req -nodes -new -x509 -keyout server.key -out server.cert
|
||||
```
|
||||
|
||||
/*
|
||||
This will generate a `server.key` and `server.cert` file in the current directory. However, for production use, you should obtain a TLS/SSL certificate from a trusted certificate authority.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
from flask import Flask, request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def hello():
|
||||
name = request.args.get('name', 'World')
|
||||
return f'Hello, {name}!'
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
@@ -0,0 +1,59 @@
|
||||
# here are some suggestions to improve the code:
|
||||
|
||||
# - Add input validation to prevent malicious input from being processed by the server. You can use a library like flask-wtf to validate user input and sanitize it before processing.
|
||||
|
||||
# - Use environment variables to store sensitive information such as database credentials, API keys, and other secrets. This will prevent the information from being hard-coded in the code and exposed in case of a security breach.
|
||||
|
||||
# - Implement error handling to provide meaningful error messages to the user in case of errors. You can use the @app.errorhandler() decorator to handle exceptions and return an error response.
|
||||
|
||||
import os
|
||||
from flask import Flask, render_template_string
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, SubmitField
|
||||
from wtforms.validators import DataRequired, Length, Email
|
||||
from markupsafe import escape
|
||||
|
||||
app = Flask(__name__)
|
||||
# SECURITY: Load secret key from environment variable instead of hardcoding
|
||||
app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', os.urandom(32))
|
||||
|
||||
class HelloForm(FlaskForm):
|
||||
name = StringField('Name', validators=[DataRequired(), Length(min=3)])
|
||||
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||
submit = SubmitField('Submit')
|
||||
|
||||
# Form template with proper CSRF protection and escaping
|
||||
FORM_TEMPLATE = '''
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Hello Form</title></head>
|
||||
<body>
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
<p>{{ form.name.label }} {{ form.name(size=32) }}
|
||||
{% for error in form.name.errors %}<span style="color: red;">[{{ error }}]</span>{% endfor %}</p>
|
||||
<p>{{ form.email.label }} {{ form.email(size=32) }}
|
||||
{% for error in form.email.errors %}<span style="color: red;">[{{ error }}]</span>{% endfor %}</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def hello():
|
||||
form = HelloForm()
|
||||
if form.validate_on_submit():
|
||||
# SECURITY: Use escape() to prevent XSS attacks
|
||||
safe_name = escape(form.name.data)
|
||||
safe_email = escape(form.email.data)
|
||||
return f'Hello, {safe_name} ({safe_email})!'
|
||||
# SECURITY: Use Flask's render_template_string for proper escaping
|
||||
return render_template_string(FORM_TEMPLATE, form=form)
|
||||
|
||||
@app.errorhandler(400)
|
||||
def bad_request(error):
|
||||
return 'Bad request', 400
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
@@ -0,0 +1,660 @@
|
||||
# Building Text Generation Applications
|
||||
|
||||
[](https://youtu.be/0Y5Luf5sRQA?si=t_xVg0clnAI4oUFZ)
|
||||
|
||||
> _(Click the image above to view video of this lesson)_
|
||||
|
||||
You've seen so far through this curriculum that there are core concepts like prompts and even a whole discipline called "prompt engineering". Many tools you can interact with like ChatGPT, Office 365, Microsoft Power Platform and more, support you using prompts to accomplish something.
|
||||
|
||||
For you to add such an experience to an app, you need to understand concepts like prompts, completions and choose a library to work with. That's exactly what you'll learn in this chapter.
|
||||
|
||||
## Introduction
|
||||
|
||||
In this chapter, you will:
|
||||
|
||||
- Learn about the openai library and it's core concepts.
|
||||
- Build a text generation app using openai.
|
||||
- Understand how to use concepts like prompt, temperature, and tokens to build a text generation app.
|
||||
|
||||
## Learning goals
|
||||
|
||||
At the end of this lesson, you'll be able to:
|
||||
|
||||
- Explain what a text generation app is.
|
||||
- Build a text generation app using openai.
|
||||
- Configure your app to use more or less tokens and also change the temperature, for a varied output.
|
||||
|
||||
## What is a text generation app?
|
||||
|
||||
Normally when you build an app it has some kind of interface like the following:
|
||||
|
||||
- Command-based. Console apps are typical apps where you type a command and it carries out a task. For example, `git` is a command-based app.
|
||||
- User interface (UI). Some apps have graphical user interfaces (GUIs) where you click buttons, input text, select options and more.
|
||||
|
||||
### Console and UI apps are limited
|
||||
|
||||
Compare it to a command-based app where you type a command:
|
||||
|
||||
- **It's limited**. You can't just type any command, only the ones that the app supports.
|
||||
- **Language specific**. Some apps support many languages, but by default the app is built for a specific language, even if you can add more language support.
|
||||
|
||||
### Benefits of text generation apps
|
||||
|
||||
So how is a text generation app different?
|
||||
|
||||
In a text generation app, you have more flexibility, you're not limited to a set of commands or a specific input language. Instead, you can use natural language to interact with the app. Another benefit is that you're already interacting with a data source that has been trained on a vast corpus of information, whereas a traditional app might be limited on what's in a database.
|
||||
|
||||
### What can I build with a text generation app?
|
||||
|
||||
There are many things you can build. For example:
|
||||
|
||||
- **A chatbot**. A chatbot answering questions about topics, like your company and its products could be a good match.
|
||||
- **Helper**. LLMs are great at things like summarizing text, getting insights from text, producing text like resumes and more.
|
||||
- **Code assistant**. Depending on the language model you use, you can build a code assistant that helps you write code. For example, you can use a product like GitHub Copilot as well as ChatGPT to help you write code.
|
||||
|
||||
## How can I get started?
|
||||
|
||||
Well, you need to find a way to integrate with an LLM which usually entails the following two approaches:
|
||||
|
||||
- Use an API. Here you're constructing web requests with your prompt and get generated text back.
|
||||
- Use a library. Libraries help encapsulate the API calls and make them easier to use.
|
||||
|
||||
## Libraries/SDKs
|
||||
|
||||
There are a few well known libraries for working with LLMs like:
|
||||
|
||||
- **openai**, this library makes it easy to connect to your model and send in prompts.
|
||||
|
||||
Then there are libraries that operate on a higher level like:
|
||||
|
||||
- **Langchain**. Langchain is well known and supports Python.
|
||||
- **Semantic Kernel**. Semantic Kernel is a library by Microsoft supporting the languages C#, Python, and Java.
|
||||
|
||||
## First app using openai
|
||||
|
||||
Let's see how we can build our first app, what libraries we need, how much is required and so on.
|
||||
|
||||
### Install openai
|
||||
|
||||
There are many libraries out there for interacting with OpenAI or Azure OpenAI. It's possible to use numerous programming languages as well like C#, Python, JavaScript, Java and more. We've chosen to use the `openai` Python library, so we'll use `pip` to install it.
|
||||
|
||||
```bash
|
||||
pip install openai
|
||||
```
|
||||
|
||||
### Create a resource
|
||||
|
||||
You need to carry out the following steps:
|
||||
|
||||
- Create an account on Azure [https://azure.microsoft.com/free/](https://azure.microsoft.com/free/?WT.mc_id=academic-105485-koreyst).
|
||||
- Gain access to Azure OpenAI. Go to [https://learn.microsoft.com/azure/ai-services/openai/overview#how-do-i-get-access-to-azure-openai](https://learn.microsoft.com/azure/ai-services/openai/overview#how-do-i-get-access-to-azure-openai?WT.mc_id=academic-105485-koreyst) and request access.
|
||||
|
||||
> [!NOTE]
|
||||
> At the time of writing, you need to apply for access to Azure OpenAI.
|
||||
|
||||
- Install Python <https://www.python.org/>
|
||||
- Have created an Azure OpenAI Service resource. See this guide for how to [create a resource](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource?pivots=web-portal?WT.mc_id=academic-105485-koreyst).
|
||||
|
||||
### Locate API key and endpoint
|
||||
|
||||
At this point, you need to tell your `openai` library what API key to use. To find your API key, go to "Keys and Endpoint" section of your Azure OpenAI resource and copy the "Key 1" value.
|
||||
|
||||

|
||||
|
||||
Now that you have this information copied, let's instruct the libraries to use it.
|
||||
|
||||
> [!NOTE]
|
||||
> It's worth separating your API key from your code. You can do so by using environment variables.
|
||||
>
|
||||
> - Set the environment variable `OPENAI_API_KEY` to your API key.
|
||||
> `export OPENAI_API_KEY='sk-...'`
|
||||
|
||||
### Setup configuration Azure
|
||||
|
||||
If you're using Azure OpenAI (now part of Microsoft Foundry), here's how you setup configuration. We use the standard `OpenAI` client pointed at the Azure OpenAI `/openai/v1/` endpoint, which works with the Responses API and needs no `api_version`:
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key=os.environ["AZURE_OPENAI_API_KEY"],
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
)
|
||||
```
|
||||
|
||||
Above we're setting the following:
|
||||
|
||||
- `api_key`, this is your API key found in the Azure Portal or Microsoft Foundry portal.
|
||||
- `base_url`, this is your Foundry resource endpoint with `/openai/v1/` appended. The stable v1 endpoint works across OpenAI and Azure OpenAI with no `api_version` management.
|
||||
|
||||
> [!NOTE] > `os.environ` reads environment variables. You can use it to read environment variables like `AZURE_OPENAI_API_KEY` and `AZURE_OPENAI_ENDPOINT`. Set these environment variables in your terminal or by using a library like `dotenv`.
|
||||
|
||||
## Generate text
|
||||
|
||||
The way to generate text is to use the Responses API via the `responses.create` method. Here's an example:
|
||||
|
||||
```python
|
||||
prompt = "Complete the following: Once upon a time there was a"
|
||||
|
||||
response = client.responses.create(
|
||||
model="gpt-4o-mini", # this is your model deployment name
|
||||
input=prompt,
|
||||
store=False,
|
||||
)
|
||||
print(response.output_text)
|
||||
```
|
||||
|
||||
In the above code, we create a response and pass in the model we want to use and the prompt. Then we print the generated text via `response.output_text`.
|
||||
|
||||
### Multi-turn conversations
|
||||
|
||||
The Responses API is well suited for both single-turn text generation and multi-turn chatbots - you provide a list of messages in `input` to build up a conversation:
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(api_key="sk-...")
|
||||
|
||||
response = client.responses.create(model="gpt-4o-mini", input="Hello world", store=False)
|
||||
print(response.output_text)
|
||||
```
|
||||
|
||||
More on this functionality in an upcoming chapter.
|
||||
|
||||
## Exercise - your first text generation app
|
||||
|
||||
Now that we learned how to set up and configure openai, it's time to build your first text generation app. To build your app, follow these steps:
|
||||
|
||||
1. Create a virtual environment and install openai:
|
||||
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install openai
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If you're using Windows type `venv\Scripts\activate` instead of `source venv/bin/activate`.
|
||||
|
||||
> [!NOTE]
|
||||
> Locate your Azure OpenAI key by going to [https://portal.azure.com/](https://portal.azure.com/?WT.mc_id=academic-105485-koreyst) and search for `Open AI` and select the `Open AI resource` and then select `Keys and Endpoint` and copy the `Key 1` value.
|
||||
|
||||
1. Create an _app.py_ file and give it the following code:
|
||||
|
||||
```python
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key="<replace this value with your Azure OpenAI key>",
|
||||
base_url="<endpoint found in Azure Portal>/openai/v1/",
|
||||
)
|
||||
deployment_name = "<deployment name>"
|
||||
|
||||
# add your completion code
|
||||
prompt = "Complete the following: Once upon a time there was a"
|
||||
|
||||
# make a request using the Responses API
|
||||
response = client.responses.create(model=deployment_name, input=prompt, store=False)
|
||||
|
||||
# print response
|
||||
print(response.output_text)
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If you're using plain OpenAI (not Azure), use `client = OpenAI(api_key="<replace this value with your OpenAI key>")` (no `base_url`) and pass a model name like `gpt-4o-mini` instead of a deployment name.
|
||||
|
||||
You should see an output like the following:
|
||||
|
||||
```output
|
||||
very unhappy _____.
|
||||
|
||||
Once upon a time there was a very unhappy mermaid.
|
||||
```
|
||||
|
||||
## Different types of prompts, for different things
|
||||
|
||||
Now you've seen how to generate text using a prompt. You even have a program up and running that you can modify and change to generate different types of text.
|
||||
|
||||
Prompts can be used for all sorts of tasks. For example:
|
||||
|
||||
- **Generate a type of text**. For example, you can generate a poem, questions for a quiz etc.
|
||||
- **Lookup information**. You can use prompts to look for information like the following example 'What does CORS mean in web development?'.
|
||||
- **Generate code**. You can use prompts to generate code, for example developing a regular expression used to validate emails or why not generate an entire program, like a web app?
|
||||
|
||||
## A more practical use case: a recipe generator
|
||||
|
||||
Imagine you have ingredients at home and you want to cook something. For that, you need a recipe. A way to find recipes is to use a search engine or you could use an LLM to do so.
|
||||
|
||||
You could write a prompt like so:
|
||||
|
||||
> "Show me 5 recipes for a dish with the following ingredients: chicken, potatoes, and carrots. Per recipe, list all the ingredients used"
|
||||
|
||||
Given the above prompt, you might get a response similar to:
|
||||
|
||||
```output
|
||||
1. Roasted Chicken and Vegetables:
|
||||
Ingredients:
|
||||
- 4 chicken thighs
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 2 tablespoons olive oil
|
||||
- 2 cloves garlic, minced
|
||||
- 1 teaspoon dried thyme
|
||||
- 1 teaspoon dried oregano
|
||||
- Salt and pepper, to taste
|
||||
|
||||
2. Chicken and Potato Stew:
|
||||
Ingredients:
|
||||
- 2 tablespoons olive oil
|
||||
- 1 onion, diced
|
||||
- 2 cloves garlic, minced
|
||||
- 2 chicken breasts, cut into cubes
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 1 teaspoon dried oregano
|
||||
- 1 teaspoon dried thyme
|
||||
- 1 cup chicken broth
|
||||
- Salt and pepper, to taste
|
||||
|
||||
3. Chicken and Potato Bake:
|
||||
Ingredients:
|
||||
- 2 tablespoons olive oil
|
||||
- 2 chicken breasts, cut into cubes
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 1 onion, diced
|
||||
- 2 cloves garlic, minced
|
||||
- 1 teaspoon dried oregano
|
||||
- 1 teaspoon dried thyme
|
||||
- 1 cup chicken broth
|
||||
- Salt and pepper, to taste
|
||||
|
||||
4. Chicken and Potato Soup:
|
||||
Ingredients:
|
||||
- 2 tablespoons olive oil
|
||||
- 1 onion, diced
|
||||
- 2 cloves garlic, minced
|
||||
- 2 chicken breasts, cut into cubes
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 1 teaspoon dried oregano
|
||||
- 1 teaspoon dried thyme
|
||||
- 4 cups chicken broth
|
||||
- Salt and pepper, to taste
|
||||
|
||||
5. Chicken and Potato Hash:
|
||||
Ingredients:
|
||||
- 2 tablespoons olive oil
|
||||
- 2 chicken breasts, cut into cubes
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 1 onion, diced
|
||||
- 2 cloves garlic, minced
|
||||
- 1 teaspoon dried oregano
|
||||
```
|
||||
|
||||
This outcome is great, I know what to cook. At this point, what could be useful improvements are:
|
||||
|
||||
- Filtering out ingredients I don't like or am allergic to.
|
||||
- Produce a shopping list, in case I don't have all the ingredients at home.
|
||||
|
||||
For the above cases, let's add an additional prompt:
|
||||
|
||||
> "Please remove recipes with garlic as I'm allergic and replace it with something else. Also, please produce a shopping list for the recipes, considering I already have chicken, potatoes and carrots at home."
|
||||
|
||||
Now you have a new result, namely:
|
||||
|
||||
```output
|
||||
1. Roasted Chicken and Vegetables:
|
||||
Ingredients:
|
||||
- 4 chicken thighs
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 2 tablespoons olive oil
|
||||
- 1 teaspoon dried thyme
|
||||
- 1 teaspoon dried oregano
|
||||
- Salt and pepper, to taste
|
||||
|
||||
2. Chicken and Potato Stew:
|
||||
Ingredients:
|
||||
- 2 tablespoons olive oil
|
||||
- 1 onion, diced
|
||||
- 2 chicken breasts, cut into cubes
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 1 teaspoon dried oregano
|
||||
- 1 teaspoon dried thyme
|
||||
- 1 cup chicken broth
|
||||
- Salt and pepper, to taste
|
||||
|
||||
3. Chicken and Potato Bake:
|
||||
Ingredients:
|
||||
- 2 tablespoons olive oil
|
||||
- 2 chicken breasts, cut into cubes
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 1 onion, diced
|
||||
- 1 teaspoon dried oregano
|
||||
- 1 teaspoon dried thyme
|
||||
- 1 cup chicken broth
|
||||
- Salt and pepper, to taste
|
||||
|
||||
4. Chicken and Potato Soup:
|
||||
Ingredients:
|
||||
- 2 tablespoons olive oil
|
||||
- 1 onion, diced
|
||||
- 2 chicken breasts, cut into cubes
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 1 teaspoon dried oregano
|
||||
- 1 teaspoon dried thyme
|
||||
- 4 cups chicken broth
|
||||
- Salt and pepper, to taste
|
||||
|
||||
5. Chicken and Potato Hash:
|
||||
Ingredients:
|
||||
- 2 tablespoons olive oil
|
||||
- 2 chicken breasts, cut into cubes
|
||||
- 2 potatoes, cut into cubes
|
||||
- 2 carrots, cut into cubes
|
||||
- 1 onion, diced
|
||||
- 1 teaspoon dried oregano
|
||||
|
||||
Shopping List:
|
||||
- Olive oil
|
||||
- Onion
|
||||
- Thyme
|
||||
- Oregano
|
||||
- Salt
|
||||
- Pepper
|
||||
```
|
||||
|
||||
That's your five recipes, with no garlic mentioned and you also have a shopping list considering what you already have at home.
|
||||
|
||||
## Exercise - build a recipe generator
|
||||
|
||||
Now that we have played out a scenario, let's write code to match the demonstrated scenario. To do so, follow these steps:
|
||||
|
||||
1. Use the existing _app.py_ file as a starting point
|
||||
1. Locate the `prompt` variable and change its code to the following:
|
||||
|
||||
```python
|
||||
prompt = "Show me 5 recipes for a dish with the following ingredients: chicken, potatoes, and carrots. Per recipe, list all the ingredients used"
|
||||
```
|
||||
|
||||
If you now run the code, you should see an output similar to:
|
||||
|
||||
```output
|
||||
-Chicken Stew with Potatoes and Carrots: 3 tablespoons oil, 1 onion, chopped, 2 cloves garlic, minced, 1 carrot, peeled and chopped, 1 potato, peeled and chopped, 1 bay leaf, 1 thyme sprig, 1/2 teaspoon salt, 1/4 teaspoon black pepper, 1 1/2 cups chicken broth, 1/2 cup dry white wine, 2 tablespoons chopped fresh parsley, 2 tablespoons unsalted butter, 1 1/2 pounds boneless, skinless chicken thighs, cut into 1-inch pieces
|
||||
-Oven-Roasted Chicken with Potatoes and Carrots: 3 tablespoons extra-virgin olive oil, 1 tablespoon Dijon mustard, 1 tablespoon chopped fresh rosemary, 1 tablespoon chopped fresh thyme, 4 cloves garlic, minced, 1 1/2 pounds small red potatoes, quartered, 1 1/2 pounds carrots, quartered lengthwise, 1/2 teaspoon salt, 1/4 teaspoon black pepper, 1 (4-pound) whole chicken
|
||||
-Chicken, Potato, and Carrot Casserole: cooking spray, 1 large onion, chopped, 2 cloves garlic, minced, 1 carrot, peeled and shredded, 1 potato, peeled and shredded, 1/2 teaspoon dried thyme leaves, 1/4 teaspoon salt, 1/4 teaspoon black pepper, 2 cups fat-free, low-sodium chicken broth, 1 cup frozen peas, 1/4 cup all-purpose flour, 1 cup 2% reduced-fat milk, 1/4 cup grated Parmesan cheese
|
||||
|
||||
-One Pot Chicken and Potato Dinner: 2 tablespoons olive oil, 1 pound boneless, skinless chicken thighs, cut into 1-inch pieces, 1 large onion, chopped, 3 cloves garlic, minced, 1 carrot, peeled and chopped, 1 potato, peeled and chopped, 1 bay leaf, 1 thyme sprig, 1/2 teaspoon salt, 1/4 teaspoon black pepper, 2 cups chicken broth, 1/2 cup dry white wine
|
||||
|
||||
-Chicken, Potato, and Carrot Curry: 1 tablespoon vegetable oil, 1 large onion, chopped, 2 cloves garlic, minced, 1 carrot, peeled and chopped, 1 potato, peeled and chopped, 1 teaspoon ground coriander, 1 teaspoon ground cumin, 1/2 teaspoon ground turmeric, 1/2 teaspoon ground ginger, 1/4 teaspoon cayenne pepper, 2 cups chicken broth, 1/2 cup dry white wine, 1 (15-ounce) can chickpeas, drained and rinsed, 1/2 cup raisins, 1/2 cup chopped fresh cilantro
|
||||
```
|
||||
|
||||
> NOTE, your LLM is nondeterministic, so you might get different results every time you run the program.
|
||||
|
||||
Great, let's see how we can improve things. To improve things, we want to make sure the code is flexible, so ingredients and number of recipes can be improved and changed.
|
||||
|
||||
1. Let's change the code in the following way:
|
||||
|
||||
```python
|
||||
no_recipes = input("No of recipes (for example, 5): ")
|
||||
|
||||
ingredients = input("List of ingredients (for example, chicken, potatoes, and carrots): ")
|
||||
|
||||
# interpolate the number of recipes into the prompt an ingredients
|
||||
prompt = f"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used"
|
||||
```
|
||||
|
||||
Taking the code for a test run, could look like this:
|
||||
|
||||
```output
|
||||
No of recipes (for example, 5): 3
|
||||
List of ingredients (for example, chicken, potatoes, and carrots): milk,strawberries
|
||||
|
||||
-Strawberry milk shake: milk, strawberries, sugar, vanilla extract, ice cubes
|
||||
-Strawberry shortcake: milk, flour, baking powder, sugar, salt, unsalted butter, strawberries, whipped cream
|
||||
-Strawberry milk: milk, strawberries, sugar, vanilla extract
|
||||
```
|
||||
|
||||
### Improve by adding filter and shopping list
|
||||
|
||||
We now have a working app capable of producing recipes and it's flexible as it relies on inputs from the user, both on the number of recipes but also the ingredients used.
|
||||
|
||||
To further improve it, we want to add the following:
|
||||
|
||||
- **Filter out ingredients**. We want to be able to filter out ingredients we don't like or are allergic to. To accomplish this change, we can edit our existing prompt and add a filter condition to the end of it like so:
|
||||
|
||||
```python
|
||||
filter = input("Filter (for example, vegetarian, vegan, or gluten-free): ")
|
||||
|
||||
prompt = f"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used, no {filter}"
|
||||
```
|
||||
|
||||
Above, we add `{filter}` to the end of the prompt and we also capture the filter value from the user.
|
||||
|
||||
An example input of running the program can now look like so:
|
||||
|
||||
```output
|
||||
No of recipes (for example, 5): 3
|
||||
List of ingredients (for example, chicken, potatoes, and carrots): onion,milk
|
||||
Filter (for example, vegetarian, vegan, or gluten-free): no milk
|
||||
|
||||
1. French Onion Soup
|
||||
|
||||
Ingredients:
|
||||
|
||||
-1 large onion, sliced
|
||||
-3 cups beef broth
|
||||
-1 cup milk
|
||||
-6 slices french bread
|
||||
-1/4 cup shredded Parmesan cheese
|
||||
-1 tablespoon butter
|
||||
-1 teaspoon dried thyme
|
||||
-1/4 teaspoon salt
|
||||
-1/4 teaspoon black pepper
|
||||
|
||||
Instructions:
|
||||
|
||||
1. In a large pot, sauté onions in butter until golden brown.
|
||||
2. Add beef broth, milk, thyme, salt, and pepper. Bring to a boil.
|
||||
3. Reduce heat and simmer for 10 minutes.
|
||||
4. Place french bread slices on soup bowls.
|
||||
5. Ladle soup over bread.
|
||||
6. Sprinkle with Parmesan cheese.
|
||||
|
||||
2. Onion and Potato Soup
|
||||
|
||||
Ingredients:
|
||||
|
||||
-1 large onion, chopped
|
||||
-2 cups potatoes, diced
|
||||
-3 cups vegetable broth
|
||||
-1 cup milk
|
||||
-1/4 teaspoon black pepper
|
||||
|
||||
Instructions:
|
||||
|
||||
1. In a large pot, sauté onions in butter until golden brown.
|
||||
2. Add potatoes, vegetable broth, milk, and pepper. Bring to a boil.
|
||||
3. Reduce heat and simmer for 10 minutes.
|
||||
4. Serve hot.
|
||||
|
||||
3. Creamy Onion Soup
|
||||
|
||||
Ingredients:
|
||||
|
||||
-1 large onion, chopped
|
||||
-3 cups vegetable broth
|
||||
-1 cup milk
|
||||
-1/4 teaspoon black pepper
|
||||
-1/4 cup all-purpose flour
|
||||
-1/2 cup shredded Parmesan cheese
|
||||
|
||||
Instructions:
|
||||
|
||||
1. In a large pot, sauté onions in butter until golden brown.
|
||||
2. Add vegetable broth, milk, and pepper. Bring to a boil.
|
||||
3. Reduce heat and simmer for 10 minutes.
|
||||
4. In a small bowl, whisk together flour and Parmesan cheese until smooth.
|
||||
5. Add to soup and simmer for an additional 5 minutes, or until soup has thickened.
|
||||
```
|
||||
|
||||
As you can see, any recipes with milk in it has been filtered out. But, if you're lactose intolerant, you might want to filter out recipes with cheese in them as well, so there's a need to be clear.
|
||||
|
||||
- **Produce a shopping list**. We want to produce a shopping list, considering what we already have at home.
|
||||
|
||||
For this functionality, we could either try to solve everything in one prompt or we could split it up into two prompts. Let's try the latter approach. Here we're suggesting adding an additional prompt, but for that to work, we need to add the result of the former prompt as context to the latter prompt.
|
||||
|
||||
Locate the part in the code that prints out the result from the first prompt and add the following code below:
|
||||
|
||||
```python
|
||||
old_prompt_result = response.output_text
|
||||
prompt = "Produce a shopping list for the generated recipes and please don't include ingredients that I already have."
|
||||
|
||||
new_prompt = f"{old_prompt_result} {prompt}"
|
||||
response = client.responses.create(model=deployment_name, input=new_prompt, max_output_tokens=1200, store=False)
|
||||
|
||||
# print response
|
||||
print("Shopping list:")
|
||||
print(response.output_text)
|
||||
```
|
||||
|
||||
Note the following:
|
||||
|
||||
1. We're constructing a new prompt by adding the result from the first prompt to the new prompt:
|
||||
|
||||
```python
|
||||
new_prompt = f"{old_prompt_result} {prompt}"
|
||||
```
|
||||
|
||||
1. We make a new request, but also considering the number of tokens we asked for in the first prompt, so this time we say `max_output_tokens` is 1200.
|
||||
|
||||
```python
|
||||
response = client.responses.create(model=deployment_name, input=new_prompt, max_output_tokens=1200, store=False)
|
||||
```
|
||||
|
||||
Taking this code for a spin, we now arrive at the following output:
|
||||
|
||||
```output
|
||||
No of recipes (for example, 5): 2
|
||||
List of ingredients (for example, chicken, potatoes, and carrots): apple,flour
|
||||
Filter (for example, vegetarian, vegan, or gluten-free): sugar
|
||||
|
||||
|
||||
-Apple and flour pancakes: 1 cup flour, 1/2 tsp baking powder, 1/2 tsp baking soda, 1/4 tsp salt, 1 tbsp sugar, 1 egg, 1 cup buttermilk or sour milk, 1/4 cup melted butter, 1 Granny Smith apple, peeled and grated
|
||||
-Apple fritters: 1-1/2 cups flour, 1 tsp baking powder, 1/4 tsp salt, 1/4 tsp baking soda, 1/4 tsp nutmeg, 1/4 tsp cinnamon, 1/4 tsp allspice, 1/4 cup sugar, 1/4 cup vegetable shortening, 1/4 cup milk, 1 egg, 2 cups shredded, peeled apples
|
||||
Shopping list:
|
||||
-Flour, baking powder, baking soda, salt, sugar, egg, buttermilk, butter, apple, nutmeg, cinnamon, allspice
|
||||
```
|
||||
|
||||
## Improve your setup
|
||||
|
||||
What we have so far is code that works, but there are some tweaks we should be doing to improve things further. Some things we should do are:
|
||||
|
||||
- **Separate secrets from code**, like the API key. Secrets do not belong in code and should be stored in a secure location. To separate secrets from code, we can use environment variables and libraries like `python-dotenv` to load them from a file. Here's how that would look like in code:
|
||||
|
||||
1. Create a `.env` file with the following content:
|
||||
|
||||
```bash
|
||||
OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
> Note, for Azure OpenAI in Microsoft Foundry, you need to set the following environment variables instead:
|
||||
|
||||
```bash
|
||||
AZURE_OPENAI_API_KEY=<replace>
|
||||
AZURE_OPENAI_ENDPOINT=<replace>
|
||||
AZURE_OPENAI_API_VERSION=2024-10-21
|
||||
```
|
||||
|
||||
In code, you would load the environment variables like so:
|
||||
|
||||
```python
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from openai import OpenAI
|
||||
|
||||
load_dotenv()
|
||||
|
||||
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
||||
```
|
||||
|
||||
- **A word on token length**. We should consider how many tokens we need to generate the text we want. Tokens cost money, so where possible, we should try to be economical with the number of tokens we use. For example, can we phrase the prompt so that we can use less tokens?
|
||||
|
||||
To change the tokens used, you can use the `max_output_tokens` parameter. For example, if you want to use 100 tokens, you would do:
|
||||
|
||||
```python
|
||||
response = client.responses.create(model=deployment, input=prompt, max_output_tokens=100, store=False)
|
||||
```
|
||||
|
||||
- **Experimenting with temperature**. Temperature is something we haven't mentioned so far but is an important context for how our program performs. The higher the temperature value the more random the output will be. Conversely the lower the temperature value the more predictable the output will be. Consider whether you want variation in your output or not.
|
||||
|
||||
To alter the temperature, you can use the `temperature` parameter. For example, if you want to use a temperature of 0.5, you would do:
|
||||
|
||||
```python
|
||||
response = client.responses.create(model=deployment, input=prompt, temperature=0.5, store=False)
|
||||
```
|
||||
|
||||
> Note, the closer to 1.0, the more varied the output.
|
||||
|
||||
## Assignment
|
||||
|
||||
For this assignment, you can choose what to build.
|
||||
|
||||
Here are some suggestions:
|
||||
|
||||
- Tweak the recipe generator app to improve it further. Play around with temperature values, and the prompts to see what you can come up with.
|
||||
- Build a "study buddy". This app should be able to answer questions about a topic for example Python, you could have prompts like "What is a certain topic in Python?", or you could have a prompt that says, show me code for a certain topic etc.
|
||||
- History bot, make history come alive, instruct the bot to play a certain historical character and ask it questions about its life and times.
|
||||
|
||||
## Solution
|
||||
|
||||
### Study buddy
|
||||
|
||||
Below is a starter prompt, see how you can use it and tweak it to your liking.
|
||||
|
||||
```text
|
||||
- "You're an expert on the Python language
|
||||
|
||||
Suggest a beginner lesson for Python in the following format:
|
||||
|
||||
Format:
|
||||
- concepts:
|
||||
- brief explanation of the lesson:
|
||||
- exercise in code with solutions"
|
||||
```
|
||||
|
||||
### History bot
|
||||
|
||||
Here are some prompts you could be using:
|
||||
|
||||
```text
|
||||
- "You are Abe Lincoln, tell me about yourself in 3 sentences, and respond using grammar and words like Abe would have used"
|
||||
- "You are Abe Lincoln, respond using grammar and words like Abe would have used:
|
||||
|
||||
Tell me about your greatest accomplishments, in 300 words"
|
||||
```
|
||||
|
||||
## Knowledge check
|
||||
|
||||
What does the concept temperature do?
|
||||
|
||||
1. It controls how random the output is.
|
||||
1. It controls how big the response is.
|
||||
1. It controls how many tokens are used.
|
||||
|
||||
## 🚀 Challenge
|
||||
|
||||
When working on the assignment, try to vary the temperature, try setting it to 0, 0.5, and 1. Remember that 0 is the least varied and 1 is the most. What value works best for your app?
|
||||
|
||||
## Great Work! Continue Your Learning
|
||||
|
||||
After completing this lesson, check out our [Generative AI Learning collection](https://aka.ms/genai-collection?WT.mc_id=academic-105485-koreyst) to continue leveling up your Generative AI knowledge!
|
||||
|
||||
Head over to Lesson 7 where we will look at how to [build chat applications](../07-building-chat-applications/README.md?WT.mc_id=academic-105485-koreyst)!
|
||||
@@ -0,0 +1,59 @@
|
||||
#!meta
|
||||
|
||||
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}}
|
||||
|
||||
#!csharp
|
||||
|
||||
#r "nuget: Azure.AI.OpenAI, 2.1.0"
|
||||
|
||||
#!csharp
|
||||
|
||||
using Azure;
|
||||
using Azure.AI.OpenAI;
|
||||
using OpenAI.Chat;
|
||||
using System.ClientModel;
|
||||
using static System.Environment;
|
||||
|
||||
string endpoint = "<replace with endpoint>";
|
||||
string key = "<replace with API key>";
|
||||
|
||||
// Enter the deployment name you chose when you deployed the model (for example: gpt-4o-mini).
|
||||
string deployment = "<replace with deployment>";
|
||||
|
||||
AzureOpenAIClient azureClient = new(new Uri(endpoint), new ApiKeyCredential(key));
|
||||
ChatClient chatClient = azureClient.GetChatClient(deployment);
|
||||
|
||||
string prompt = "Complete the following: Once upon a time there was a";
|
||||
Console.Write($"Input: {prompt}\n");
|
||||
|
||||
ChatCompletion completion = chatClient.CompleteChat(new UserChatMessage(prompt));
|
||||
Console.WriteLine($"Chatbot: {completion.Content[0].Text}");
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.Write("No of recipes (for example, 5): ");
|
||||
string no_recipes = "2";
|
||||
|
||||
Console.Write("List of ingredients (for example, chicken, potatoes, and carrots): ");
|
||||
string ingredients = "chocolate";
|
||||
|
||||
Console.Write("Filter (for example, vegetarian, vegan, or gluten-free): ");
|
||||
string filter = "peanuts";
|
||||
|
||||
// interpolate the number of recipes into the prompt an ingredients
|
||||
string prompt = $"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used, no {filter}: ";
|
||||
|
||||
#!csharp
|
||||
|
||||
// deployment, prompt, max output tokens, temperature
|
||||
ChatCompletionOptions options = new()
|
||||
{
|
||||
MaxOutputTokenCount = 150,
|
||||
Temperature = 0.1f,
|
||||
};
|
||||
|
||||
ChatCompletion completion = chatClient.CompleteChat(
|
||||
new[] { new UserChatMessage(prompt) },
|
||||
options);
|
||||
Console.WriteLine("==== Recipes ====");
|
||||
Console.WriteLine($"Chatbot: {completion.Content[0].Text}");
|
||||
|
After Width: | Height: | Size: 738 KiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 246 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 329 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 376 KiB |
@@ -0,0 +1,90 @@
|
||||
import ModelClient from "@azure-rest/ai-inference";
|
||||
import { AzureKeyCredential } from "@azure/core-auth";
|
||||
|
||||
// SECURITY: Validate required environment variables
|
||||
// Get these from your Microsoft Foundry project's "Overview" page
|
||||
// (GitHub Models is retiring end of July 2026 - see https://ai.azure.com/catalog/models)
|
||||
const token = process.env["AZURE_INFERENCE_CREDENTIAL"];
|
||||
if (!token) {
|
||||
throw new Error("AZURE_INFERENCE_CREDENTIAL environment variable is required. Please set it before running this application.");
|
||||
}
|
||||
|
||||
const endpoint = process.env["AZURE_INFERENCE_ENDPOINT"];
|
||||
if (!endpoint) {
|
||||
throw new Error("AZURE_INFERENCE_ENDPOINT environment variable is required. Please set it before running this application.");
|
||||
}
|
||||
const modelName = "gpt-4o-mini";
|
||||
|
||||
export async function main() {
|
||||
|
||||
console.log("== Recipe Recommendation App ==");
|
||||
|
||||
console.log("Number of recipes: (for example: 5): ");
|
||||
const numRecipes = "3";
|
||||
|
||||
console.log("List of ingredients: (for example: chicken, potatoes, and carrots): ");
|
||||
const ingredients = "chocolate";
|
||||
|
||||
console.log("Filter (for example: vegetarian, vegan, or gluten-free): ");
|
||||
const filter = "peanuts";
|
||||
|
||||
const promptText = `Show me ${numRecipes} recipes for a dish with the following ingredients: ${ingredients}. Per recipe, list all the ingredients used, no ${filter}: `;
|
||||
|
||||
|
||||
const client = new ModelClient(endpoint, new AzureKeyCredential(token));
|
||||
|
||||
const response = await client.path("/chat/completions").post({
|
||||
body: {
|
||||
messages: [
|
||||
{ role: "system", content: "You are a helpful assistant." },
|
||||
{ role: "user", content: promptText }
|
||||
],
|
||||
model: modelName,
|
||||
temperature: 1.0,
|
||||
max_tokens: 1000,
|
||||
top_p: 1.0
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
|
||||
if (response.status !== "200") {
|
||||
throw response.body.error;
|
||||
}
|
||||
console.log(response.body.choices[0].message.content);
|
||||
|
||||
|
||||
const oldPromptResult = response.body.choices[0].message.content;
|
||||
|
||||
const promptShoppingList = 'Produce a shopping list, and please do not include the following ingredients that I already have at home: ';
|
||||
|
||||
const newPrompt = `Given ingredients at home: ${ingredients} and these generated recipes: ${oldPromptResult}, ${promptShoppingList}`;
|
||||
|
||||
const shoppingListMessage =
|
||||
await client.path("/chat/completions").post({
|
||||
body: {
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'Here is your shopping list:'
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: newPrompt
|
||||
},
|
||||
],
|
||||
model: modelName,
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.log('The sample encountered an error: ', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("The sample encountered an error:", err);
|
||||
});
|
||||
@@ -0,0 +1,285 @@
|
||||
{
|
||||
"name": "js-ghmodels",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@azure-rest/ai-inference": "latest",
|
||||
"@azure/core-auth": "latest",
|
||||
"@azure/core-sse": "latest"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure-rest/ai-inference": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure-rest/ai-inference/-/ai-inference-1.0.0-beta.2.tgz",
|
||||
"integrity": "sha512-xPnXJmldQ7vufTT1GtoHPk+korWFnl72whoDB34tb9DALTGGBKIAWobDQBMGYdV2ce0/KdhRFPwnfeZjNyfVsw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure-rest/core-client": "^2.1.0",
|
||||
"@azure/abort-controller": "^1.0.0",
|
||||
"@azure/core-auth": "^1.7.2",
|
||||
"@azure/core-lro": "^2.6.0",
|
||||
"@azure/core-rest-pipeline": "^1.14.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure-rest/core-client": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.2.0.tgz",
|
||||
"integrity": "sha512-2uPSZPRb2TRyYONl5IcsWhX7C1xbm6Gof/tcLlDnYg7fCVcEfASfySKZ9OTHxhNeFgo79LhrT6juEoxTHvrXkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-rest-pipeline": "^1.5.0",
|
||||
"@azure/core-tracing": "^1.0.1",
|
||||
"@azure/core-util": "^1.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure-rest/core-client/node_modules/@azure/abort-controller": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
|
||||
"integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/abort-controller": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
|
||||
"integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-auth": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.8.0.tgz",
|
||||
"integrity": "sha512-YvFMowkXzLbXNM11yZtVLhUCmuG0ex7JKOH366ipjmHBhL3vpDcPAeWF+jf0X+jVXwFqo3UhsWUq4kH0ZPdu/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-util": "^1.1.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-auth/node_modules/@azure/abort-controller": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
|
||||
"integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-lro": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz",
|
||||
"integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-util": "^1.2.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-lro/node_modules/@azure/abort-controller": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
|
||||
"integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.17.0.tgz",
|
||||
"integrity": "sha512-62Vv8nC+uPId3j86XJ0WI+sBf0jlqTqPUFCBNrGtlaUeQUIXWV/D8GE5A1d+Qx8H7OQojn2WguC8kChD6v0shA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-auth": "^1.8.0",
|
||||
"@azure/core-tracing": "^1.0.1",
|
||||
"@azure/core-util": "^1.9.0",
|
||||
"@azure/logger": "^1.0.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
|
||||
"integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-sse": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-sse/-/core-sse-2.1.3.tgz",
|
||||
"integrity": "sha512-KSSdIKy8kvWCpYr8Hzpu22j3wcXsVTYE0IlgmI1T/aHvBDsLgV91y90UTfVWnuiuApRLCCVC4gS09ApBGOmYQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-tracing": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz",
|
||||
"integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-util": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.10.0.tgz",
|
||||
"integrity": "sha512-dqLWQsh9Nro1YQU+405POVtXnwrIVqPyfUzc4zXCbThTg7+vNNaiMkwbX9AMXKyoFYFClxmB3s25ZFr3+jZkww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-util/node_modules/@azure/abort-controller": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
|
||||
"integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/logger": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz",
|
||||
"integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
|
||||
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
|
||||
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.0.2",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
|
||||
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
|
||||
"license": "0BSD"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@azure-rest/ai-inference": "latest",
|
||||
"@azure/core-auth": "latest",
|
||||
"@azure/core-sse": "latest"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
from openai import OpenAI
|
||||
import os
|
||||
import re
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# SECURITY: Validate environment variables with helpful error messages
|
||||
def get_required_env(var_name: str) -> str:
|
||||
"""Get a required environment variable or raise an error with helpful message."""
|
||||
value = os.getenv(var_name)
|
||||
if not value:
|
||||
raise ValueError(f"Missing required environment variable: {var_name}. Please set it in your .env file.")
|
||||
return value
|
||||
|
||||
# SECURITY: Input validation functions
|
||||
def validate_number_input(value: str, min_val: int = 1, max_val: int = 20) -> int:
|
||||
"""Validate and sanitize numeric input."""
|
||||
try:
|
||||
num = int(value)
|
||||
if num < min_val or num > max_val:
|
||||
raise ValueError(f"Number must be between {min_val} and {max_val}")
|
||||
return num
|
||||
except ValueError:
|
||||
raise ValueError(f"Please enter a valid number between {min_val} and {max_val}")
|
||||
|
||||
def validate_text_input(value: str, max_length: int = 500) -> str:
|
||||
"""Validate and sanitize text input to prevent prompt injection."""
|
||||
if len(value) > max_length:
|
||||
raise ValueError(f"Input too long. Maximum {max_length} characters allowed.")
|
||||
# Remove potentially dangerous characters/patterns
|
||||
sanitized = re.sub(r'[<>{}[\]|\\`]', '', value)
|
||||
# Limit to alphanumeric, spaces, commas, and basic punctuation
|
||||
if not re.match(r'^[\w\s,.\'-]+$', sanitized, re.UNICODE):
|
||||
raise ValueError("Input contains invalid characters")
|
||||
return sanitized.strip()
|
||||
|
||||
# configure the OpenAI client against the Azure OpenAI (Microsoft Foundry) v1 endpoint
|
||||
client = OpenAI(
|
||||
api_key=get_required_env('AZURE_OPENAI_API_KEY'),
|
||||
base_url=f"{get_required_env('AZURE_OPENAI_ENDPOINT').rstrip('/')}/openai/v1/",
|
||||
)
|
||||
|
||||
deployment = get_required_env('AZURE_OPENAI_DEPLOYMENT')
|
||||
|
||||
# SECURITY: Validate all user inputs
|
||||
try:
|
||||
no_recipes_input = input("No of recipes (for example, 5): ")
|
||||
no_recipes = validate_number_input(no_recipes_input, 1, 20)
|
||||
|
||||
ingredients_input = input("List of ingredients (for example, chicken, potatoes, and carrots): ")
|
||||
ingredients = validate_text_input(ingredients_input, 500)
|
||||
|
||||
filter_input = input("Filter (for example, vegetarian, vegan, or gluten-free): ")
|
||||
filter_value = validate_text_input(filter_input, 100) if filter_input.strip() else "none"
|
||||
except ValueError as e:
|
||||
print(f"Input validation error: {e}")
|
||||
exit(1)
|
||||
|
||||
# interpolate the number of recipes into the prompt and ingredients
|
||||
# Note: Using validated and sanitized inputs
|
||||
prompt = f"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used, no {filter_value}: "
|
||||
|
||||
response = client.responses.create(model=deployment, input=prompt, max_output_tokens=600, temperature=0.1, store=False)
|
||||
|
||||
|
||||
# print response
|
||||
print("Recipes:")
|
||||
old_prompt_result = response.output_text
|
||||
if not old_prompt_result:
|
||||
print("No response received.")
|
||||
else:
|
||||
print(old_prompt_result)
|
||||
|
||||
prompt_shopping = "Produce a shopping list, and please don't include ingredients that I already have at home: "
|
||||
new_prompt = f"Given ingredients at home {ingredients} and these generated recipes: {old_prompt_result}, {prompt_shopping}"
|
||||
response = client.responses.create(model=deployment, input=new_prompt, max_output_tokens=600, temperature=0, store=False)
|
||||
|
||||
# print response
|
||||
print("\n=====Shopping list ======= \n")
|
||||
if response.output_text:
|
||||
print(response.output_text)
|
||||
else:
|
||||
print("No response received.")
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# pylint: disable=all
|
||||
from openai import OpenAI
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# configure the OpenAI client against the Azure OpenAI (Microsoft Foundry) v1 endpoint
|
||||
client = OpenAI(
|
||||
api_key=os.environ['AZURE_OPENAI_API_KEY'],
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
)
|
||||
|
||||
deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']
|
||||
|
||||
# add your completion code
|
||||
prompt = "Complete the following: Once upon a time there was a"
|
||||
# make a request using the Responses API
|
||||
response = client.responses.create(model=deployment, input=prompt, store=False)
|
||||
|
||||
# print response
|
||||
print(response.output_text)
|
||||
|
||||
# very unhappy _____.
|
||||
|
||||
# Once upon a time there was a very unhappy mermaid.
|
||||
@@ -0,0 +1,760 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Build text generation apps\n",
|
||||
"\n",
|
||||
"You've seen so far through this curriculum that there are core concepts like prompts and even a whole discipline called \"prompt engineering\". Many tools you can interact with like ChatGPT, Office 365, Microsoft Power Platform and more, support you using prompts to accomplish something.\n",
|
||||
"\n",
|
||||
"For you to add such an experience to an app, you need to understand concepts like prompts, completions and choose a library to work with. That's exactly what you'll learn in this chapter.\n",
|
||||
"\n",
|
||||
"## Introduction\n",
|
||||
"\n",
|
||||
"In this chapter, you will:\n",
|
||||
"\n",
|
||||
"- Learn about the openai library and its core concepts.\n",
|
||||
"- Build a text generation app using openai.\n",
|
||||
"- Understand how to use concepts like prompt, temperature, and tokens to build a text generation app.\n",
|
||||
"\n",
|
||||
"## Learning goals\n",
|
||||
"\n",
|
||||
"At the end of this lesson, you'll be able to:\n",
|
||||
"\n",
|
||||
"- Explain what a text generation app is.\n",
|
||||
"- Build a text generation app using openai.\n",
|
||||
"- Configure your app to use more or less tokens and also change the temperature, for a varied output.\n",
|
||||
"\n",
|
||||
"## What is a text generation app?\n",
|
||||
"\n",
|
||||
"Normally when you build an app it has some kind of interface like the following:\n",
|
||||
"\n",
|
||||
"- Command-based. Console apps are typical apps where you type a command and it carries out a task. For example, `git` is a command-based app.\n",
|
||||
"- User interface (UI). Some apps have graphical user interfaces (GUIs) where you click buttons, input text, select options and more.\n",
|
||||
"\n",
|
||||
"### Console and UI apps are limited\n",
|
||||
"\n",
|
||||
"Compare it to a command-based app where you type a command: \n",
|
||||
"\n",
|
||||
"- **It's limited**. You can't just type any command, only the ones that the app supports.\n",
|
||||
"- **Language specific**. Some apps support many languages, but by default the app is built for a specific language, even if you can add more language support. \n",
|
||||
"\n",
|
||||
"### Benefits of text generation apps\n",
|
||||
"\n",
|
||||
"So how is a text generation app different?\n",
|
||||
"\n",
|
||||
"In a text generation app, you have more flexibility, you're not limited to a set of commands or a specific input language. Instead, you can use natural language to interact with the app. Another benefit is that because you're already interacting with a data source that has been trained on a vast corpus of information, whereas a traditional app might be limited on what's in a database. \n",
|
||||
"\n",
|
||||
"### What can I build with a text generation app?\n",
|
||||
"\n",
|
||||
"There are many things you can build. For example:\n",
|
||||
"\n",
|
||||
"- **A chatbot**. A chatbot answering questions about topics, like your company and its products could be a good match.\n",
|
||||
"- **Helper**. LLMs are great at things like summarizing text, getting insights from text, producing text like resumes and more.\n",
|
||||
"- **Code assistant**. Depending on the language model you use, you can build a code assistant that helps you write code. For example, you can use a product like GitHub Copilot as well as ChatGPT to help you write code.\n",
|
||||
"\n",
|
||||
"## How can I get started?\n",
|
||||
"\n",
|
||||
"Well, you need to find a way to integrate with an LLM which usually entails the following two approaches:\n",
|
||||
"\n",
|
||||
"- Use an API. Here you're constructing web requests with your prompt and get generated text back.\n",
|
||||
"- Use a library. Libraries help encapsulate the API calls and make them easier to use.\n",
|
||||
"\n",
|
||||
"## Libraries/SDKs\n",
|
||||
"\n",
|
||||
"There are a few well known libraries for working with LLMs like:\n",
|
||||
"\n",
|
||||
"- **openai**, this library makes it easy to connect to your model and send in prompts.\n",
|
||||
"\n",
|
||||
"Then there are libraries that operate on a higher level like:\n",
|
||||
"\n",
|
||||
"- **Langchain**. Langchain is well known and supports Python.\n",
|
||||
"- **Semantic Kernel**. Semantic Kernel is a library by Microsoft supporting the languages C#, Python, and Java.\n",
|
||||
"\n",
|
||||
"## First app using openai\n",
|
||||
"\n",
|
||||
"Let's see how we can build our first app, what libraries we need, how much is required and so on.\n",
|
||||
"\n",
|
||||
"### Install openai\n",
|
||||
"\n",
|
||||
" > [!NOTE] This step is not necessary if run this notebook on Codespaces or within a Devcontainer\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"There are many libraries out there for interacting with OpenAI or Azure OpenAI. It's possible to use numerous programming languages as well like C#, Python, JavaScript, Java and more. \n",
|
||||
"We've chosen to use the `openai` Python library, so we'll use `pip` to install it.\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"pip install openai\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"If you aren't running this notebook in a Codespaces or a Dev Container, you also need to install [Python](https://www.python.org/) on your machine.\n",
|
||||
"\n",
|
||||
"### Create a resource\n",
|
||||
"\n",
|
||||
"In case you didn't already, you need to carry out the following steps:\n",
|
||||
"\n",
|
||||
"- Create an account on Azure <https://azure.microsoft.com/free/>.\n",
|
||||
"- Create an Azure OpenAI resource in [Microsoft Foundry](https://ai.azure.com?WT.mc_id=academic-105485-koreyst). See this guide for how to [create a resource](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource?pivots=web-portal&WT.mc_id=academic-105485-koreyst).\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Locate API key and endpoint\n",
|
||||
"\n",
|
||||
"At this point, you need to tell your `openai` library what API key to use. To find your API key, go to the \"Keys and Endpoint\" section of your Azure OpenAI resource and copy the \"Key 1\" value.\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"Now that you have this information copied, let's instruct the libraries to use it.\n",
|
||||
"\n",
|
||||
"> [!NOTE]\n",
|
||||
"> It's worth separating your API key from your code. You can do so by using environment variables.\n",
|
||||
"> - Set the environment variable `AZURE_OPENAI_API_KEY` to your API key in your .env file. If you already completed the previous exercises of this course, you are all set up.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Setup configuration Azure\n",
|
||||
"\n",
|
||||
"If you're using Azure OpenAI, here's how you set up configuration. The Responses API is served from the Azure OpenAI **v1 endpoint**, so we point the `OpenAI` client at `<your-endpoint>/openai/v1/`:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"client = OpenAI(\n",
|
||||
" api_key=os.environ['AZURE_OPENAI_API_KEY'],\n",
|
||||
" base_url=f\"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"deployment = os.environ['AZURE_OPENAI_DEPLOYMENT']\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Above we're setting the following:\n",
|
||||
"\n",
|
||||
"- `api_key`, this is your API key found in the Azure Portal.\n",
|
||||
"- `base_url`, this is your Azure OpenAI endpoint with `/openai/v1/` appended. You can find the endpoint in the Azure Portal next to your API key. Using the v1 endpoint means you no longer need to pass an `api_version`.\n",
|
||||
"- `deployment`, this is the name of the model deployment you created in the Foundry portal.\n",
|
||||
"\n",
|
||||
"> [!NOTE]\n",
|
||||
"> `os.environ` is a mapping that reads environment variables. You can use it to read environment variables like `AZURE_OPENAI_API_KEY` and `AZURE_OPENAI_ENDPOINT`.\n",
|
||||
"\n",
|
||||
"## Generate text\n",
|
||||
"\n",
|
||||
"The way to generate text is to use the `responses.create` method. Here's an example:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"prompt = \"Complete the following: Once upon a time there was a\"\n",
|
||||
"\n",
|
||||
"response = client.responses.create(model=deployment, input=prompt, store=False)\n",
|
||||
"print(response.output_text)\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"In the above code, we create a response object and pass in the model we want to use and the prompt. Then we print the generated text.\n",
|
||||
"\n",
|
||||
"### Chat responses\n",
|
||||
"\n",
|
||||
"The Responses API is well suited for both single-turn text generation and multi-turn chatbots - you simply provide more messages in the `input` list to build up a conversation:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"client = OpenAI(\n",
|
||||
" api_key=os.environ['AZURE_OPENAI_API_KEY'],\n",
|
||||
" base_url=f\"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"deployment = os.environ['AZURE_OPENAI_DEPLOYMENT']\n",
|
||||
"\n",
|
||||
"response = client.responses.create(model=deployment, input=[{\"role\": \"user\", \"content\": \"Hello world\"}], store=False)\n",
|
||||
"print(response.output_text)\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"More on this functionality in a coming chapter.\n",
|
||||
"\n",
|
||||
"## Exercise - your first text generation app\n",
|
||||
"\n",
|
||||
"Now that we learned how to set up and configure Azure OpenAI service, it's time to build your first text generation app. To build your app, follow these steps:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"1. Create a virtual environment and install openai:\n",
|
||||
"\n",
|
||||
" > [!NOTE] This step is not necessary if you run this notebook on Codespaces or within a Devcontainer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create virtual environment\n",
|
||||
"! python -m venv venv\n",
|
||||
"# Activate virtual environment\n",
|
||||
"! source venv/bin/activate\n",
|
||||
"# Install openai package\n",
|
||||
"! pip install openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> [!NOTE]\n",
|
||||
"> If you're using Windows type `venv\\Scripts\\activate` instead of `source venv/bin/activate`. \n",
|
||||
"\n",
|
||||
"> [!NOTE]\n",
|
||||
"> Locate your Azure OpenAI key by going to https://portal.azure.com/ and search for `Open AI` and select the `Open AI resource` and then select `Keys and Endpoint` and copy the `Key 1` value."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"1. Create a *app.py* file and give it the following code:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from openai import OpenAI\n",
|
||||
"from dotenv import load_dotenv\n",
|
||||
"load_dotenv()\n",
|
||||
"\n",
|
||||
"client = OpenAI(\n",
|
||||
" api_key=os.environ['AZURE_OPENAI_API_KEY'],\n",
|
||||
" base_url=f\"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']\n",
|
||||
"\n",
|
||||
"# add your completion code\n",
|
||||
"prompt = \"Complete the following: Once upon a time there was a\"\n",
|
||||
"\n",
|
||||
"# make a request using the Responses API\n",
|
||||
"response = client.responses.create(model=deployment, input=prompt, store=False)\n",
|
||||
"\n",
|
||||
"# print response\n",
|
||||
"print(response.output_text)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
" You should see an output like the following:\n",
|
||||
"\n",
|
||||
" ```output\n",
|
||||
" very unhappy _____.\n",
|
||||
"\n",
|
||||
" Once upon a time there was a very unhappy mermaid.\n",
|
||||
" ```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Different types of prompts, for different things\n",
|
||||
"\n",
|
||||
"Now you've seen how to generate text using a prompt. You even have a program up and running that you can modify and change to generate different types of text. \n",
|
||||
"\n",
|
||||
"Prompts can be used for all sorts of tasks. For example:\n",
|
||||
"\n",
|
||||
"- **Generate a type of text**. For example, you can generate a poem, questions for a quiz etc.\n",
|
||||
"- **Lookup information**. You can use prompts to look for information like the following example 'What does CORS mean in web development?'.\n",
|
||||
"- **Generate code**. You can use prompts to generate code, for example developing a regular expression used to validate emails or why not generate an entire program, like a web app? \n",
|
||||
"\n",
|
||||
"## A more practical use case: a recipe generator\n",
|
||||
"\n",
|
||||
"Imagine you have ingredients at home and you want to cook something. For that, you need a recipe. A way to find recipes is to use a search engine or you could use an LLM to do so.\n",
|
||||
"\n",
|
||||
"You could write a prompt like so:\n",
|
||||
"\n",
|
||||
"> \"Show me 5 recipes for a dish with the following ingredients: chicken, potatoes, and carrots. Per recipe, list all the ingredients used\"\n",
|
||||
"\n",
|
||||
"Given the above prompt, you might get a response similar to:\n",
|
||||
"\n",
|
||||
"```output\n",
|
||||
"1. Roasted Chicken and Vegetables: \n",
|
||||
"Ingredients: \n",
|
||||
"- 4 chicken thighs\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"2. Chicken and Potato Stew: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 cup chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"3. Chicken and Potato Bake: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 cup chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"4. Chicken and Potato Soup: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 4 cups chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"5. Chicken and Potato Hash: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"This outcome is great, I know what to cook. At this point, what could be useful improvements are:\n",
|
||||
"\n",
|
||||
"- Filtering out ingredients I don't like or am allergic to.\n",
|
||||
"- Produce a shopping list, in case I don't have all the ingredients at home.\n",
|
||||
"\n",
|
||||
"For the above cases, let's add an additional prompt:\n",
|
||||
"\n",
|
||||
"> \"Please remove recipes with garlic as I'm allergic and replace it with something else. Also, please produce a shopping list for the recipes, considering I already have chicken, potatoes and carrots at home.\"\n",
|
||||
"\n",
|
||||
"Now you have a new result, namely:\n",
|
||||
"\n",
|
||||
"```output\n",
|
||||
"1. Roasted Chicken and Vegetables: \n",
|
||||
"Ingredients: \n",
|
||||
"- 4 chicken thighs\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"2. Chicken and Potato Stew: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 cup chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"3. Chicken and Potato Bake: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 cup chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"4. Chicken and Potato Soup: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 4 cups chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"5. Chicken and Potato Hash: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"\n",
|
||||
"Shopping List: \n",
|
||||
"- Olive oil\n",
|
||||
"- Onion\n",
|
||||
"- Thyme\n",
|
||||
"- Oregano\n",
|
||||
"- Salt\n",
|
||||
"- Pepper\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"That's your five recipes, with no garlic mentioned and you also have a shopping list considering what you already have at home. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercise - build a recipe generator\n",
|
||||
"\n",
|
||||
"Now that we have played out a scenario, let's write code to match the demonstrated scenario. To do so, follow these steps:\n",
|
||||
"\n",
|
||||
"1. Use the existing *app.py* file as a starting point\n",
|
||||
"1. Locate the `prompt` variable and change its code to the following:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from openai import OpenAI\n",
|
||||
"from dotenv import load_dotenv\n",
|
||||
"\n",
|
||||
"# load environment variables from .env file\n",
|
||||
"load_dotenv()\n",
|
||||
"\n",
|
||||
"client = OpenAI(\n",
|
||||
" api_key=os.environ['AZURE_OPENAI_API_KEY'],\n",
|
||||
" base_url=f\"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"deployment = os.environ['AZURE_OPENAI_DEPLOYMENT']\n",
|
||||
"\n",
|
||||
"prompt = \"Show me 5 recipes for a dish with the following ingredients: chicken, potatoes, and carrots. Per recipe, list all the ingredients used\"\n",
|
||||
"\n",
|
||||
"# make a request using the Responses API\n",
|
||||
"response = client.responses.create(model=deployment, input=prompt, max_output_tokens=600, store=False)\n",
|
||||
"\n",
|
||||
"# print response\n",
|
||||
"print(response.output_text)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If you now run the code, you should see an output similar to:\n",
|
||||
"\n",
|
||||
"```output\n",
|
||||
"-Chicken Stew with Potatoes and Carrots: 3 tablespoons oil, 1 onion, chopped, 2 cloves garlic, minced, 1 carrot, peeled and chopped, 1 potato, peeled and chopped, 1 bay leaf, 1 thyme sprig, 1/2 teaspoon salt, 1/4 teaspoon black pepper, 1 1/2 cups chicken broth, 1/2 cup dry white wine, 2 tablespoons chopped fresh parsley, 2 tablespoons unsalted butter, 1 1/2 pounds boneless, skinless chicken thighs, cut into 1-inch pieces\n",
|
||||
"-Oven-Roasted Chicken with Potatoes and Carrots: 3 tablespoons extra-virgin olive oil, 1 tablespoon Dijon mustard, 1 tablespoon chopped fresh rosemary, 1 tablespoon chopped fresh thyme, 4 cloves garlic, minced, 1 1/2 pounds small red potatoes, quartered, 1 1/2 pounds carrots, quartered lengthwise, 1/2 teaspoon salt, 1/4 teaspoon black pepper, 1 (4-pound) whole chicken\n",
|
||||
"-Chicken, Potato, and Carrot Casserole: cooking spray, 1 large onion, chopped, 2 cloves garlic, minced, 1 carrot, peeled and shredded, 1 potato, peeled and shredded, 1/2 teaspoon dried thyme leaves, 1/4 teaspoon salt, 1/4 teaspoon black pepper, 2 cups fat-free, low-sodium chicken broth, 1 cup frozen peas, 1/4 cup all-purpose flour, 1 cup 2% reduced-fat milk, 1/4 cup grated Parmesan cheese\n",
|
||||
"\n",
|
||||
"-One Pot Chicken and Potato Dinner: 2 tablespoons olive oil, 1 pound boneless, skinless chicken thighs, cut into 1-inch pieces, 1 large onion, chopped, 3 cloves garlic, minced, 1 carrot, peeled and chopped, 1 potato, peeled and chopped, 1 bay leaf, 1 thyme sprig, 1/2 teaspoon salt, 1/4 teaspoon black pepper, 2 cups chicken broth, 1/2 cup dry white wine\n",
|
||||
"\n",
|
||||
"-Chicken, Potato, and Carrot Curry: 1 tablespoon vegetable oil, 1 large onion, chopped, 2 cloves garlic, minced, 1 carrot, peeled and chopped, 1 potato, peeled and chopped, 1 teaspoon ground coriander, 1 teaspoon ground cumin, 1/2 teaspoon ground turmeric, 1/2 teaspoon ground ginger, 1/4 teaspoon cayenne pepper, 2 cups chicken broth, 1/2 cup dry white wine, 1 (15-ounce) can chickpeas, drained and rinsed, 1/2 cup raisins, 1/2 cup chopped fresh cilantro\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"> NOTE, your LLM is nondeterministic, so you might get different results every time you run the program.\n",
|
||||
"\n",
|
||||
"Great, let's see how we can improve things. To improve things, we want to make sure the code is flexible, so ingredients and number of recipes can be improved and changed. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"1. Let's change the code in the following way:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from openai import OpenAI\n",
|
||||
"from dotenv import load_dotenv\n",
|
||||
"\n",
|
||||
"# load environment variables from .env file\n",
|
||||
"load_dotenv()\n",
|
||||
"\n",
|
||||
"client = OpenAI(\n",
|
||||
" api_key=os.environ['AZURE_OPENAI_API_KEY'],\n",
|
||||
" base_url=f\"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"deployment = os.environ['AZURE_OPENAI_DEPLOYMENT']\n",
|
||||
"\n",
|
||||
"no_recipes = input(\"No of recipes (for example, 5: \")\n",
|
||||
"\n",
|
||||
"ingredients = input(\"List of ingredients (for example, chicken, potatoes, and carrots: \")\n",
|
||||
"\n",
|
||||
"# interpolate the number of recipes into the prompt an ingredients\n",
|
||||
"prompt = f\"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used\"\n",
|
||||
"\n",
|
||||
"# make a request using the Responses API\n",
|
||||
"response = client.responses.create(model=deployment, input=prompt, max_output_tokens=600, store=False)\n",
|
||||
"\n",
|
||||
"# print response\n",
|
||||
"print(response.output_text)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"-Strawberry shortcake: milk, flour, baking powder, sugar, salt, unsalted butter, strawberries, whipped cream \n",
|
||||
"-Strawberry milk: milk, strawberries, sugar, vanilla extract\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"### Improve by adding filter and shopping list\n",
|
||||
"\n",
|
||||
"We now have a working app capable of producing recipes and it's flexible as it relies on inputs from the user, both on the number of recipes but also the ingredients used.\n",
|
||||
"\n",
|
||||
"To further improve it, we want to add the following:\n",
|
||||
"\n",
|
||||
"- **Filter out ingredients**. We want to be able to filter out ingredients we don't like or are allergic to. To accomplish this change, we can edit our existing prompt and add a filter condition to the end of it like so:\n",
|
||||
"\n",
|
||||
" ```python\n",
|
||||
" filter = input(\"Filter (for example, vegetarian, vegan, or gluten-free: \")\n",
|
||||
"\n",
|
||||
" prompt = f\"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used, no {filter}\"\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
" Above, we add `{filter}` to the end of the prompt and we also capture the filter value from the user.\n",
|
||||
"\n",
|
||||
" An example input of running the program can now look like so:\n",
|
||||
" \n",
|
||||
" ```output \n",
|
||||
" No of recipes (for example, 5: 3\n",
|
||||
" List of ingredients (for example, chicken, potatoes, and carrots: onion,milk\n",
|
||||
" Filter (for example, vegetarian, vegan, or gluten-free: no milk\n",
|
||||
"\n",
|
||||
" 1. French Onion Soup\n",
|
||||
"\n",
|
||||
" Ingredients:\n",
|
||||
" \n",
|
||||
" -1 large onion, sliced\n",
|
||||
" -3 cups beef broth\n",
|
||||
" -1 cup milk\n",
|
||||
" -6 slices french bread\n",
|
||||
" -1/4 cup shredded Parmesan cheese\n",
|
||||
" -1 tablespoon butter\n",
|
||||
" -1 teaspoon dried thyme\n",
|
||||
" -1/4 teaspoon salt\n",
|
||||
" -1/4 teaspoon black pepper\n",
|
||||
" \n",
|
||||
" Instructions:\n",
|
||||
" \n",
|
||||
" 1. In a large pot, sauté onions in butter until golden brown.\n",
|
||||
" 2. Add beef broth, milk, thyme, salt, and pepper. Bring to a boil.\n",
|
||||
" 3. Reduce heat and simmer for 10 minutes.\n",
|
||||
" 4. Place french bread slices on soup bowls.\n",
|
||||
" 5. Ladle soup over bread.\n",
|
||||
" 6. Sprinkle with Parmesan cheese.\n",
|
||||
" \n",
|
||||
" 2. Onion and Potato Soup\n",
|
||||
" \n",
|
||||
" Ingredients:\n",
|
||||
" \n",
|
||||
" -1 large onion, chopped\n",
|
||||
" -2 cups potatoes, diced\n",
|
||||
" -3 cups vegetable broth\n",
|
||||
" -1 cup milk\n",
|
||||
" -1/4 teaspoon black pepper\n",
|
||||
" \n",
|
||||
" Instructions:\n",
|
||||
" \n",
|
||||
" 1. In a large pot, sauté onions in butter until golden brown.\n",
|
||||
" 2. Add potatoes, vegetable broth, milk, and pepper. Bring to a boil.\n",
|
||||
" 3. Reduce heat and simmer for 10 minutes.\n",
|
||||
" 4. Serve hot.\n",
|
||||
" \n",
|
||||
" 3. Creamy Onion Soup\n",
|
||||
" \n",
|
||||
" Ingredients:\n",
|
||||
" \n",
|
||||
" -1 large onion, chopped\n",
|
||||
" -3 cups vegetable broth\n",
|
||||
" -1 cup milk\n",
|
||||
" -1/4 teaspoon black pepper\n",
|
||||
" -1/4 cup all-purpose flour\n",
|
||||
" -1/2 cup shredded Parmesan cheese\n",
|
||||
" \n",
|
||||
" Instructions:\n",
|
||||
" \n",
|
||||
" 1. In a large pot, sauté onions in butter until golden brown.\n",
|
||||
" 2. Add vegetable broth, milk, and pepper. Bring to a boil.\n",
|
||||
" 3. Reduce heat and simmer for 10 minutes.\n",
|
||||
" 4. In a small bowl, whisk together flour and Parmesan cheese until smooth.\n",
|
||||
" 5. Add to soup and simmer for an additional 5 minutes, or until soup has thickened.\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
" As you can see, any recipes with milk in it has been filtered out. But, if you're lactose intolerant, you might want to filter out recipes with cheese in them as well, so there's a need to be clear.\n",
|
||||
"\n",
|
||||
"- **Produce a shopping list**. We want to produce a shopping list, considering what we already have at home.\n",
|
||||
"\n",
|
||||
" For this functionality, we could either try to solve everything in one prompt or we could split it up into two prompts. Let's try the latter approach. Here we're suggesting adding an additional prompt, but for that to work, we need to add the result of the former prompt as context to the latter prompt. \n",
|
||||
"\n",
|
||||
" Locate the part in the code that prints out the result from the first prompt and add the following code below:\n",
|
||||
" \n",
|
||||
" ```python\n",
|
||||
" old_prompt_result = response.output_text\n",
|
||||
" prompt = \"Produce a shopping list for the generated recipes and please don't include ingredients that I already have.\"\n",
|
||||
" \n",
|
||||
" new_prompt = f\"{old_prompt_result} {prompt}\"\n",
|
||||
" messages = [{\"role\": \"user\", \"content\": new_prompt}]\n",
|
||||
" response = client.responses.create(model=deployment, input=messages, max_output_tokens=1200, store=False)\n",
|
||||
" \n",
|
||||
" # print response\n",
|
||||
" print(\"Shopping list:\")\n",
|
||||
" print(response.output_text)\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
" Note the following:\n",
|
||||
"\n",
|
||||
" - We're constructing a new prompt by adding the result from the first prompt to the new prompt: \n",
|
||||
" \n",
|
||||
" ```python\n",
|
||||
" new_prompt = f\"{old_prompt_result} {prompt}\"\n",
|
||||
" messages = [{\"role\": \"user\", \"content\": new_prompt}]\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
" - We make a new request, but also considering the number of tokens we asked for in the first prompt, so this time we say `max_output_tokens` is 1200. \n",
|
||||
"\n",
|
||||
" ```python\n",
|
||||
" response = client.responses.create(model=deployment, input=messages, max_output_tokens=1200, store=False)\n",
|
||||
" ``` \n",
|
||||
"\n",
|
||||
" Taking this code for a spin, we now arrive at the following output:\n",
|
||||
"\n",
|
||||
" ```output\n",
|
||||
" No of recipes (for example, 5: 2\n",
|
||||
" List of ingredients (for example, chicken, potatoes, and carrots: apple,flour\n",
|
||||
" Filter (for example, vegetarian, vegan, or gluten-free: sugar\n",
|
||||
" Recipes:\n",
|
||||
" or milk.\n",
|
||||
" \n",
|
||||
" -Apple and flour pancakes: 1 cup flour, 1/2 tsp baking powder, 1/2 tsp baking soda, 1/4 tsp salt, 1 tbsp sugar, 1 egg, 1 cup buttermilk or sour milk, 1/4 cup melted butter, 1 Granny Smith apple, peeled and grated\n",
|
||||
" -Apple fritters: 1-1/2 cups flour, 1 tsp baking powder, 1/4 tsp salt, 1/4 tsp baking soda, 1/4 tsp nutmeg, 1/4 tsp cinnamon, 1/4 tsp allspice, 1/4 cup sugar, 1/4 cup vegetable shortening, 1/4 cup milk, 1 egg, 2 cups shredded, peeled apples\n",
|
||||
" Shopping list:\n",
|
||||
" -Flour, baking powder, baking soda, salt, sugar, egg, buttermilk, butter, apple, nutmeg, cinnamon, allspice \n",
|
||||
" ```\n",
|
||||
" \n",
|
||||
"- **A word on token length**. We should consider how many tokens we need to generate the text we want. Tokens cost money, so where possible, we should try to be economical with the number of tokens we use. For example, can we phrase the prompt so that we can use less tokens?\n",
|
||||
"\n",
|
||||
" To change tokens used, you can use the `max_output_tokens` parameter. For example, if you want to use 100 tokens, you would do:\n",
|
||||
"\n",
|
||||
" ```python\n",
|
||||
" response = client.responses.create(model=deployment, input=messages, max_output_tokens=100, store=False)\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
"- **Experimenting with temperature**. Temperature is something we haven't mentioned so far but is an important context for how our program performs. The higher the temperature value the more random the output will be. Conversely the lower the temperature value the more predictable the output will be. Consider whether you want variation in your output or not.\n",
|
||||
"\n",
|
||||
" To alter the temperature, you can use the `temperature` parameter. For example, if you want to use a temperature of 0.5, you would do:\n",
|
||||
"\n",
|
||||
" ```python\n",
|
||||
" response = client.responses.create(model=deployment, input=messages, temperature=0.5, store=False)\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
" > Note, the closer to 1.0, the more varied the output.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Assignment\n",
|
||||
"\n",
|
||||
"For this assignment, you can choose what to build.\n",
|
||||
"\n",
|
||||
"Here are some suggestions:\n",
|
||||
"\n",
|
||||
"- Tweak the recipe generator app to improve it further. Play around with temperature values, and the prompts to see what you can come up with.\n",
|
||||
"- Build a \"study buddy\". This app should be able to answer questions about a topic for example Python, you could have prompts like \"What is a certain topic in Python?\", or you could have a prompt that says, show me code for a certain topic etc.\n",
|
||||
"- History bot, make history come alive, instruct the bot to play a certain historical character and ask it questions about its life and times. \n",
|
||||
"\n",
|
||||
"## Solution\n",
|
||||
"\n",
|
||||
"### Study buddy\n",
|
||||
"\n",
|
||||
"- \"You're an expert on the Python language\n",
|
||||
"\n",
|
||||
" Suggest a beginner lesson for Python in the following format:\n",
|
||||
" \n",
|
||||
" Format:\n",
|
||||
" - concepts:\n",
|
||||
" - brief explanation of the lesson:\n",
|
||||
" - exercise in code with solutions\"\n",
|
||||
"\n",
|
||||
"Above is a starter prompt, see how you can use it and tweak it to your liking.\n",
|
||||
"\n",
|
||||
"### History bot\n",
|
||||
"\n",
|
||||
"Here's some prompts you could be using:\n",
|
||||
"\n",
|
||||
"- \"You are Abe Lincoln, tell me about yourself in 3 sentences, and respond using grammar and words like Abe would have used\"\n",
|
||||
"- \"You are Abe Lincoln, respond using grammar and words like Abe would have used:\n",
|
||||
"\n",
|
||||
" Tell me about your greatest accomplishments, in 300 words:\"\n",
|
||||
"\n",
|
||||
"## Knowledge check\n",
|
||||
"\n",
|
||||
"What does the concept temperature do?\n",
|
||||
"\n",
|
||||
"1. It controls how random the output is.\n",
|
||||
"1. It controls how big the response is.\n",
|
||||
"1. It controls how many tokens are used.\n",
|
||||
"\n",
|
||||
"A: 1\n",
|
||||
"\n",
|
||||
"What's a good way to store secrets like API keys?\n",
|
||||
"\n",
|
||||
"1. In code.\n",
|
||||
"1. In a file.\n",
|
||||
"1. In environment variables.\n",
|
||||
"\n",
|
||||
"A: 3, because environment variables are not stored in code and can be loaded from the code. \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
from openai import OpenAI
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# configure the OpenAI client against the Azure OpenAI (Microsoft Foundry) v1 endpoint
|
||||
client = OpenAI(
|
||||
api_key=os.environ['AZURE_OPENAI_API_KEY'],
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
)
|
||||
|
||||
deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']
|
||||
|
||||
# add your completion code
|
||||
persona = input("Tell me the historical character I want to be: ")
|
||||
question = input("Ask your question about the historical character: ")
|
||||
prompt = f"""
|
||||
You are going to play as a historical character {persona}.
|
||||
|
||||
Whenever certain questions are asked, you need to remember facts about the timelines and incidents and respond the accurate answer only. Don't create content yourself. If you don't know something, tell that you don't remember.
|
||||
|
||||
Provide answer for the question: {question}
|
||||
"""
|
||||
# make a request using the Responses API
|
||||
response = client.responses.create(model=deployment, input=prompt, store=False)
|
||||
|
||||
# print response
|
||||
print(response.output_text)
|
||||
|
||||
# very unhappy _____.
|
||||
|
||||
# Once upon a time there was a very unhappy mermaid.
|
||||
@@ -0,0 +1,37 @@
|
||||
from openai import OpenAI
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# configure the OpenAI client against the Azure OpenAI (Microsoft Foundry) v1 endpoint
|
||||
client = OpenAI(
|
||||
api_key=os.environ['AZURE_OPENAI_API_KEY'],
|
||||
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
|
||||
)
|
||||
|
||||
deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']
|
||||
|
||||
# add your completion code
|
||||
question = input("Ask your questions on python language to your study buddy: ")
|
||||
prompt = f"""
|
||||
You are an expert on the python language.
|
||||
|
||||
Whenever certain questions are asked, you need to provide response in below format.
|
||||
|
||||
- Concept
|
||||
- Example code showing the concept implementation
|
||||
- explanation of the example and how the concept is done for the user to understand better.
|
||||
|
||||
Provide answer for the question: {question}
|
||||
"""
|
||||
# make a request using the Responses API
|
||||
response = client.responses.create(model=deployment, input=prompt, store=False)
|
||||
|
||||
# print response
|
||||
print(response.output_text)
|
||||
|
||||
# very unhappy _____.
|
||||
|
||||
# Once upon a time there was a very unhappy mermaid.
|
||||
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
from azure.ai.inference import ChatCompletionsClient
|
||||
from azure.ai.inference.models import SystemMessage, UserMessage
|
||||
from azure.core.credentials import AzureKeyCredential
|
||||
|
||||
# Get these from your Microsoft Foundry project's "Overview" page
|
||||
# (GitHub Models is retiring end of July 2026 - see https://ai.azure.com/catalog/models)
|
||||
token = os.environ["AZURE_INFERENCE_CREDENTIAL"]
|
||||
endpoint = os.environ["AZURE_INFERENCE_ENDPOINT"]
|
||||
|
||||
model_name = "gpt-4o-mini"
|
||||
|
||||
client = ChatCompletionsClient(
|
||||
endpoint=endpoint,
|
||||
credential=AzureKeyCredential(token),
|
||||
)
|
||||
|
||||
prompt = "Show me 5 recipes for a dish with the following ingredients: chicken, potatoes, and carrots. Per recipe, list all the ingredients used"
|
||||
|
||||
response = client.complete(
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": prompt,
|
||||
},
|
||||
],
|
||||
model=model_name,
|
||||
# Optional parameters
|
||||
temperature=1.,
|
||||
max_tokens=1000,
|
||||
top_p=1.
|
||||
)
|
||||
|
||||
if response.choices and response.choices[0].message is not None:
|
||||
print(response.choices[0].message.content)
|
||||
@@ -0,0 +1,853 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Build text generation apps\n",
|
||||
"\n",
|
||||
"You've seen so far through this curriculum that there are core concepts like prompts and even a whole discipline called \"prompt engineering\". Many tools you can interact with like ChatGPT, Office 365, Microsoft Power Platform and more, support you using prompts to accomplish something.\n",
|
||||
"\n",
|
||||
"For you to add such an experience to an app, you need to understand concepts like prompts, completions and choose a library to work with. That's exactly what you'll learn in this chapter.\n",
|
||||
"\n",
|
||||
"## Introduction\n",
|
||||
"\n",
|
||||
"In this chapter, you will:\n",
|
||||
"\n",
|
||||
"- Learn about the openai library and its core concepts.\n",
|
||||
"- Build a text generation app using openai.\n",
|
||||
"- Understand how to use concepts like prompt, temperature, and tokens to build a text generation app.\n",
|
||||
"\n",
|
||||
"## Learning goals\n",
|
||||
"\n",
|
||||
"At the end of this lesson, you'll be able to:\n",
|
||||
"\n",
|
||||
"- Explain what a text generation app is.\n",
|
||||
"- Build a text generation app using openai.\n",
|
||||
"- Configure your app to use more or less tokens and also change the temperature, for a varied output.\n",
|
||||
"\n",
|
||||
"## What is a text generation app?\n",
|
||||
"\n",
|
||||
"Normally when you build an app it has some kind of interface like the following:\n",
|
||||
"\n",
|
||||
"- Command-based. Console apps are typical apps where you type a command and it carries out a task. For example, `git` is a command-based app.\n",
|
||||
"- User interface (UI). Some apps have graphical user interfaces (GUIs) where you click buttons, input text, select options and more.\n",
|
||||
"\n",
|
||||
"### Console and UI apps are limited\n",
|
||||
"\n",
|
||||
"Compare it to a command-based app where you type a command: \n",
|
||||
"\n",
|
||||
"- **It's limited**. You can't just type any command, only the ones that the app supports.\n",
|
||||
"- **Language specific**. Some apps support many languages, but by default the app is built for a specific language, even if you can add more language support. \n",
|
||||
"\n",
|
||||
"### Benefits of text generation apps\n",
|
||||
"\n",
|
||||
"So how is a text generation app different?\n",
|
||||
"\n",
|
||||
"In a text generation app, you have more flexibility, you're not limited to a set of commands or a specific input language. Instead, you can use natural language to interact with the app. Another benefit is that because you're already interacting with a data source that has been trained on a vast corpus of information, whereas a traditional app might be limited on what's in a database. \n",
|
||||
"\n",
|
||||
"### What can I build with a text generation app?\n",
|
||||
"\n",
|
||||
"There are many things you can build. For example:\n",
|
||||
"\n",
|
||||
"- **A chatbot**. A chatbot answering questions about topics, like your company and its products could be a good match.\n",
|
||||
"- **Helper**. LLMs are great at things like summarizing text, getting insights from text, producing text like resumes and more.\n",
|
||||
"- **Code assistant**. Depending on the language model you use, you can build a code assistant that helps you write code. For example, you can use a product like GitHub Copilot as well as ChatGPT to help you write code.\n",
|
||||
"\n",
|
||||
"## How can I get started?\n",
|
||||
"\n",
|
||||
"Well, you need to find a way to integrate with an LLM which usually entails the following two approaches:\n",
|
||||
"\n",
|
||||
"- Use an API. Here you're constructing web requests with your prompt and get generated text back.\n",
|
||||
"- Use a library. Libraries help encapsulate the API calls and make them easier to use.\n",
|
||||
"\n",
|
||||
"## Libraries/SDKs\n",
|
||||
"\n",
|
||||
"There are a few well known libraries for working with LLMs like:\n",
|
||||
"\n",
|
||||
"- **openai**, this library makes it easy to connect to your model and send in prompts.\n",
|
||||
"\n",
|
||||
"Then there are libraries that operate on a higher level like:\n",
|
||||
"\n",
|
||||
"- **Langchain**. Langchain is well known and supports Python.\n",
|
||||
"- **Semantic Kernel**. Semantic Kernel is a library by Microsoft supporting the languages C#, Python, and Java.\n",
|
||||
"\n",
|
||||
"## First app using Microsoft Foundry Models Playground and Azure AI Inference SDK\n",
|
||||
"\n",
|
||||
"Let's see how we can build our first app, what libraries we need, how much is required and so on.\n",
|
||||
"\n",
|
||||
"> **Note:** GitHub Models is retiring at the end of July 2026. The steps below have been updated to use [Microsoft Foundry Models](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst), which offers the same \"explore, try in a playground, and call with an API key\" experience GitHub Models offered, now hosted directly in the Microsoft Foundry portal. Prefer to work fully offline instead? See [Foundry Local](https://foundrylocal.ai?WT.mc_id=academic-105485-koreyst) for running models on your own device, no cloud subscription required.\n",
|
||||
"\n",
|
||||
"### What is Microsoft Foundry Models?\n",
|
||||
"\n",
|
||||
"Welcome to [Microsoft Foundry Models](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst)! The Foundry model catalog gives you a single place to explore and try hundreds of AI models - from OpenAI, Meta, Mistral, Cohere, Microsoft, and more - hosted on Azure, all accessible via a free playground and, once deployed, callable from your favorite code IDE.\n",
|
||||
"\n",
|
||||
"### What do I need?\n",
|
||||
"\n",
|
||||
"* A Microsoft account and an [Azure subscription](https://aka.ms/azure/free?WT.mc_id=academic-105485-koreyst) (a free account works)\n",
|
||||
"* A [Microsoft Foundry project](https://ai.azure.com?WT.mc_id=academic-105485-koreyst)\n",
|
||||
"\n",
|
||||
"Lets get started!\n",
|
||||
"\n",
|
||||
"### Find a model and test it\n",
|
||||
"\n",
|
||||
"Navigate to the [Microsoft Foundry Models catalog](https://ai.azure.com/catalog/models?WT.mc_id=academic-105485-koreyst)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Choose a model - for example OpenAI GPT-4o mini\n",
|
||||
"\n",
|
||||
"Here you will see the model card. You can:\n",
|
||||
"* Read details about the model in the readme, benchmarks, and license tabs\n",
|
||||
"* Deploy the model to your Foundry project\n",
|
||||
"* Once deployed, interact with the model directly in the playground\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Open the **Playground** to interact with the model, add system prompts and change parameter details - and also get all the code you need to run this from anywhere, in Python, JavaScript, C#, and REST.\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Lets use the model in our own IDE\n",
|
||||
"\n",
|
||||
"Two options here:\n",
|
||||
"1. **GitHub Codespaces** - seamless integration with Codespaces\n",
|
||||
"2. **VS Code (or any favorite IDE)** - you'll need the **endpoint** and **API key** from your Foundry project\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Either way, you'll find these values on the **Overview** page of your Foundry project.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### 1.Codespaces \n",
|
||||
"\n",
|
||||
"* Create a new codespace (or use an existing)\n",
|
||||
"* VS Code will open in your browser with a set of sample notebooks in multiple languages you can try\n",
|
||||
"* Run the sample ```./githubmodels-app.py```. \n",
|
||||
"\n",
|
||||
"> Note: In codespaces, save your Foundry endpoint and key as Codespaces secrets so you don't have to set them locally.\n",
|
||||
"\n",
|
||||
"**Now move to 'Generate Text' section below to continue this assignment**\n",
|
||||
"\n",
|
||||
"### 2. VS Code (or any favorite IDE)\n",
|
||||
"\n",
|
||||
"From your Microsoft Foundry project you have all the information you need to run in your favorite IDE. This example will show VS Code\n",
|
||||
"\n",
|
||||
"* Deploy a chat model such as `gpt-4o-mini` in your Foundry project\n",
|
||||
"* Copy the project's **endpoint** and **API key** from the Overview page\n",
|
||||
"* Create environment variables to store them: `AZURE_INFERENCE_ENDPOINT` and `AZURE_INFERENCE_CREDENTIAL` - samples available in bash, powershell and windows command prompt\n",
|
||||
"* Install dependencies: ```pip install azure-ai-inference```\n",
|
||||
"* Copy basic sample code into a .py file\n",
|
||||
"* navigate to where your code is saved and run the file: ```python filename.py```\n",
|
||||
"\n",
|
||||
"Don't forget by using the Azure AI Inference SDK, you can easily experiment with different models by modifying the value of `model_name` in the code. \n",
|
||||
"\n",
|
||||
"The following models are available in the Microsoft Foundry Models catalog:\n",
|
||||
"\n",
|
||||
"* OpenAI: gpt-4o, gpt-4o-mini, gpt-4.1, gpt-4.1-mini, o3-mini, text-embedding-3-large, text-embedding-3-small\n",
|
||||
"* Meta: Meta-Llama-3.1-405B-Instruct, Meta-Llama-3.1-70B-Instruct, Meta-Llama-3.1-8B-Instruct, Llama-3.2-11B-Vision-Instruct, Llama-3.2-90B-Vision-Instruct, Llama-4-Scout-17B-16E-Instruct\n",
|
||||
"* Mistral AI: Mistral-large-2411, Mistral-small-2503, Codestral-2501, Ministral-3B\n",
|
||||
"* Microsoft: Phi-4, Phi-4-mini-instruct, Phi-4-multimodal-instruct, Phi-4-reasoning\n",
|
||||
"* Cohere: Cohere-command-r-plus-08-2024, Cohere-embed-v3-multilingual, Cohere-embed-v3-english\n",
|
||||
"* DeepSeek: DeepSeek-V3, DeepSeek-R1\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"**Now move to 'Generate Text' section below to continue this assignment**\n",
|
||||
"\n",
|
||||
"## Generate text with ChatCompletions\n",
|
||||
"\n",
|
||||
"The way to generate text is to use the `ChatCompletionsClient` class. \n",
|
||||
"In `samples/python/azure_ai_inference/basic.py`, in the response section of code, update the code the user role by changing the content parameter to below:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"\n",
|
||||
"response = client.complete(\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": \"You are a helpful assistant.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": \"Complete the following: Once upon a time there was a\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" model=model_name,\n",
|
||||
" # Optional parameters\n",
|
||||
" temperature=1.,\n",
|
||||
" max_tokens=1000,\n",
|
||||
" top_p=1. \n",
|
||||
")\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Run the updated file to see the output\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Different types of prompts, for different things\n",
|
||||
"\n",
|
||||
"Now you've seen how to generate text using a prompt. You even have a program up and running that you can modify and change to generate different types of text. \n",
|
||||
"\n",
|
||||
"Prompts can be used for all sorts of tasks. For example:\n",
|
||||
"\n",
|
||||
"- **Generate a type of text**. For example, you can generate a poem, questions for a quiz etc.\n",
|
||||
"- **Lookup information**. You can use prompts to look for information like the following example 'What does CORS mean in web development?'.\n",
|
||||
"- **Generate code**. You can use prompts to generate code, for example developing a regular expression used to validate emails or why not generate an entire program, like a web app? \n",
|
||||
"\n",
|
||||
"## Exercise: a recipe generator\n",
|
||||
"\n",
|
||||
"Imagine you have ingredients at home and you want to cook something. For that, you need a recipe. A way to find recipes is to use a search engine or you could use an LLM to do so.\n",
|
||||
"\n",
|
||||
"You could write a prompt like so:\n",
|
||||
"\n",
|
||||
"> \"Show me 5 recipes for a dish with the following ingredients: chicken, potatoes, and carrots. Per recipe, list all the ingredients used\"\n",
|
||||
"\n",
|
||||
"Given the above prompt, you might get a response similar to:\n",
|
||||
"\n",
|
||||
"```output\n",
|
||||
"1. Roasted Chicken and Vegetables: \n",
|
||||
"Ingredients: \n",
|
||||
"- 4 chicken thighs\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"2. Chicken and Potato Stew: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 cup chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"3. Chicken and Potato Bake: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 cup chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"4. Chicken and Potato Soup: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 4 cups chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"5. Chicken and Potato Hash: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"This outcome is great, I know what to cook. At this point, what could be useful improvements are:\n",
|
||||
"\n",
|
||||
"- Filtering out ingredients I don't like or am allergic to.\n",
|
||||
"- Produce a shopping list, in case I don't have all the ingredients at home.\n",
|
||||
"\n",
|
||||
"For the above cases, let's add an additional prompt:\n",
|
||||
"\n",
|
||||
"> \"Please remove recipes with garlic as I'm allergic and replace it with something else. Also, please produce a shopping list for the recipes, considering I already have chicken, potatoes and carrots at home.\"\n",
|
||||
"\n",
|
||||
"Now you have a new result, namely:\n",
|
||||
"\n",
|
||||
"```output\n",
|
||||
"1. Roasted Chicken and Vegetables: \n",
|
||||
"Ingredients: \n",
|
||||
"- 4 chicken thighs\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"2. Chicken and Potato Stew: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 cup chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"3. Chicken and Potato Bake: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 1 cup chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"4. Chicken and Potato Soup: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"- 1 teaspoon dried thyme\n",
|
||||
"- 4 cups chicken broth\n",
|
||||
"- Salt and pepper, to taste\n",
|
||||
"\n",
|
||||
"5. Chicken and Potato Hash: \n",
|
||||
"Ingredients: \n",
|
||||
"- 2 tablespoons olive oil\n",
|
||||
"- 2 chicken breasts, cut into cubes\n",
|
||||
"- 2 potatoes, cut into cubes\n",
|
||||
"- 2 carrots, cut into cubes\n",
|
||||
"- 1 onion, diced\n",
|
||||
"- 1 teaspoon dried oregano\n",
|
||||
"\n",
|
||||
"Shopping List: \n",
|
||||
"- Olive oil\n",
|
||||
"- Onion\n",
|
||||
"- Thyme\n",
|
||||
"- Oregano\n",
|
||||
"- Salt\n",
|
||||
"- Pepper\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"That's your five recipes, with no garlic mentioned and you also have a shopping list considering what you already have at home. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercise - build a recipe generator\n",
|
||||
"\n",
|
||||
"Now that we have played out a scenario, let's write code to match the demonstrated scenario. To do so, follow these steps:\n",
|
||||
"\n",
|
||||
"1. Use the existing file as a starting point\n",
|
||||
"1. Create a `prompt` variable and change the sample code as below:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from azure.ai.inference import ChatCompletionsClient\n",
|
||||
"from azure.ai.inference.models import SystemMessage, UserMessage\n",
|
||||
"from azure.core.credentials import AzureKeyCredential\n",
|
||||
"\n",
|
||||
"# Get these from your Microsoft Foundry project's \"Overview\" page\n",
|
||||
"token = os.environ[\"AZURE_INFERENCE_CREDENTIAL\"]\n",
|
||||
"endpoint = os.environ[\"AZURE_INFERENCE_ENDPOINT\"]\n",
|
||||
"\n",
|
||||
"model_name = \"gpt-4o-mini\"\n",
|
||||
"\n",
|
||||
"client = ChatCompletionsClient(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" credential=AzureKeyCredential(token),\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"prompt = \"Show me 5 recipes for a dish with the following ingredients: chicken, potatoes, and carrots. Per recipe, list all the ingredients used\"\n",
|
||||
"\n",
|
||||
"response = client.complete(\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": \"You are a helpful assistant.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": prompt,\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" model=model_name,\n",
|
||||
" # Optional parameters\n",
|
||||
" temperature=1.,\n",
|
||||
" max_tokens=1000,\n",
|
||||
" top_p=1. \n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(response.choices[0].message.content)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If you now run the code, you should see an output similar to:\n",
|
||||
"\n",
|
||||
"```output\n",
|
||||
"### Recipe 1: Classic Chicken Stew\n",
|
||||
"#### Ingredients:\n",
|
||||
"- 2 lbs chicken thighs or drumsticks, skinless\n",
|
||||
"- 4 cups chicken broth\n",
|
||||
"- 4 medium potatoes, peeled and diced\n",
|
||||
"- 4 large carrots, peeled and sliced\n",
|
||||
"- 1 large onion, chopped\n",
|
||||
"- 2 cloves garlic, minced\n",
|
||||
"- 2 celery stalks, sliced\n",
|
||||
"- 1 tsp dried thyme\n",
|
||||
"- 1 tsp dried rosemary\n",
|
||||
"- Salt and pepper to taste\n",
|
||||
"- 2 tbsp olive oil\n",
|
||||
"- 2 tbsp flour (optional, for thickening)\n",
|
||||
"\n",
|
||||
"### Recipe 2: Chicken and Vegetable Roast\n",
|
||||
"#### Ingredients:\n",
|
||||
"- 4 chicken breasts or thighs\n",
|
||||
"- 4 medium potatoes, cut into wedges\n",
|
||||
"- 4 large carrots, cut into sticks\n",
|
||||
"- 1 large onion, cut into wedges\n",
|
||||
"- 3 cloves garlic, minced\n",
|
||||
"- 1/4 cup olive oil \n",
|
||||
"- 1 tsp paprika\n",
|
||||
"- 1 tsp dried oregano\n",
|
||||
"- Salt and pepper to taste\n",
|
||||
"- Juice of 1 lemon\n",
|
||||
"- Fresh parsley, chopped (for garnish)\n",
|
||||
"(continued ...)\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"> NOTE, your LLM is nondeterministic, so you might get different results every time you run the program.\n",
|
||||
"\n",
|
||||
"Great, let's see how we can improve things. To improve things, we want to make sure the code is flexible, so ingredients and number of recipes can be improved and changed. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"1. Let's change the code in the following way:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from azure.ai.inference import ChatCompletionsClient\n",
|
||||
"from azure.ai.inference.models import SystemMessage, UserMessage\n",
|
||||
"from azure.core.credentials import AzureKeyCredential\n",
|
||||
"\n",
|
||||
"# Get these from your Microsoft Foundry project's \"Overview\" page\n",
|
||||
"token = os.environ[\"AZURE_INFERENCE_CREDENTIAL\"]\n",
|
||||
"endpoint = os.environ[\"AZURE_INFERENCE_ENDPOINT\"]\n",
|
||||
"\n",
|
||||
"model_name = \"gpt-4o-mini\"\n",
|
||||
"\n",
|
||||
"client = ChatCompletionsClient(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" credential=AzureKeyCredential(token),\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"no_recipes = input(\"No of recipes (for example, 5): \")\n",
|
||||
"\n",
|
||||
"ingredients = input(\"List of ingredients (for example, chicken, potatoes, and carrots): \")\n",
|
||||
"\n",
|
||||
"# interpolate the number of recipes into the prompt an ingredients\n",
|
||||
"prompt = f\"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used\"\n",
|
||||
"\n",
|
||||
"response = client.complete(\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": \"You are a helpful assistant.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": prompt,\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" model=model_name,\n",
|
||||
" # Optional parameters\n",
|
||||
" temperature=1.,\n",
|
||||
" max_tokens=1000,\n",
|
||||
" top_p=1. \n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(response.choices[0].message.content)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Taking the code for a test run, could look like this:\n",
|
||||
" \n",
|
||||
"```output\n",
|
||||
"No of recipes (for example, 5): 2\n",
|
||||
"List of ingredients (for example, chicken, potatoes, and carrots): milk, strawberries\n",
|
||||
"\n",
|
||||
"Sure! Here are two recipes featuring milk and strawberries:\n",
|
||||
"\n",
|
||||
"### Recipe 1: Strawberry Milkshake\n",
|
||||
"\n",
|
||||
"#### Ingredients:\n",
|
||||
"- 1 cup milk\n",
|
||||
"- 1 cup strawberries, hulled and sliced\n",
|
||||
"- 2 tablespoons sugar (optional, to taste)\n",
|
||||
"- 1/2 teaspoon vanilla extract\n",
|
||||
"- 5-6 ice cubes\n",
|
||||
"\n",
|
||||
"#### Instructions:\n",
|
||||
"1. Combine the milk, strawberries, sugar (if using), and vanilla extract in a blender.\n",
|
||||
"2. Blend on high until smooth and creamy.\n",
|
||||
"3. Add the ice cubes and blend again until the ice is fully crushed and the milkshake is frothy.\n",
|
||||
"4. Pour into a glass and serve immediately.\n",
|
||||
"\n",
|
||||
"### Recipe 2: Strawberry Panna Cotta\n",
|
||||
"\n",
|
||||
"#### Ingredients:\n",
|
||||
"- 1 cup milk\n",
|
||||
"- 1 cup strawberries, hulled and pureed\n",
|
||||
"- 1/4 cup sugar\n",
|
||||
"- 1 teaspoon vanilla extract\n",
|
||||
"- 1 envelope unflavored gelatin (about 2 1/2 teaspoons)\n",
|
||||
"- 2 tablespoons cold water\n",
|
||||
"- 1 cup heavy cream\n",
|
||||
"\n",
|
||||
"#### Instructions:\n",
|
||||
"1. Sprinkle the gelatin over the cold water in a small bowl and let it stand for about 5-10 minutes to soften.\n",
|
||||
"2. In a saucepan, combine the milk, heavy cream, and sugar. Cook over medium heat, stirring frequently until the sugar is dissolved and the mixture begins to simmer. Do not let it boil.\n",
|
||||
"3. Remove the saucepan from the heat and stir in the softened gelatin until completely dissolved.\n",
|
||||
"4. Stir in the vanilla extract and allow the mixture to cool slightly.\n",
|
||||
"5. Divide the mixture evenly into serving cups or molds and refrigerate for at least 4 hours or until set.\n",
|
||||
"6. To prepare the strawberry puree, blend the strawberries until smooth.\n",
|
||||
"7. Once the panna cotta is set, spoon the strawberry puree over the top of each panna cotta.\n",
|
||||
"8. Serve chilled.\n",
|
||||
"\n",
|
||||
"Enjoy these delightful recipes!\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"### Improve by adding filter and shopping list\n",
|
||||
"\n",
|
||||
"We now have a working app capable of producing recipes and it's flexible as it relies on inputs from the user, both on the number of recipes but also the ingredients used.\n",
|
||||
"\n",
|
||||
"To further improve it, we want to add the following:\n",
|
||||
"\n",
|
||||
"- **Filter out ingredients**. We want to be able to filter out ingredients we don't like or are allergic to. To accomplish this change, we can edit our existing prompt and add a filter condition to the end of it like so:\n",
|
||||
"\n",
|
||||
" ```python\n",
|
||||
" filter = input(\"Filter (for example, vegetarian, vegan, or gluten-free: \")\n",
|
||||
"\n",
|
||||
" prompt = f\"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used, no {filter}\"\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
" Above, we add `{filter}` to the end of the prompt and we also capture the filter value from the user.\n",
|
||||
"\n",
|
||||
" An example input of running the program can now look like so:\n",
|
||||
" \n",
|
||||
" ```output \n",
|
||||
" No of recipes (for example, 5): 2\n",
|
||||
" List of ingredients (for example, chicken, potatoes, and carrots): onion, milk\n",
|
||||
" Filter (for example, vegetarian, vegan, or gluten-free: no milk\n",
|
||||
" Certainly! Here are two recipes using onion but omitting milk:\n",
|
||||
" \n",
|
||||
" ### Recipe 1: Caramelized Onions\n",
|
||||
" \n",
|
||||
" #### Ingredients:\n",
|
||||
" - 4 large onions, thinly sliced\n",
|
||||
" - 2 tablespoons olive oil\n",
|
||||
" - 1 tablespoon butter\n",
|
||||
" - 1 teaspoon salt\n",
|
||||
" - 1 teaspoon sugar (optional)\n",
|
||||
" - 1 tablespoon balsamic vinegar (optional)\n",
|
||||
" \n",
|
||||
" #### Instructions:\n",
|
||||
" 1. Heat the olive oil and butter in a large skillet over medium heat until the butter is melted.\n",
|
||||
" 2. Add the onions and stir to coat them with the oil and butter mixture.\n",
|
||||
" 3. Add salt (and sugar if using) to the onions.\n",
|
||||
" 4. Cook the onions, stirring occasionally, for about 45 minutes to an hour until they are golden brown and caramelized.\n",
|
||||
" 5. If using, add balsamic vinegar during the last 5 minutes of cooking.\n",
|
||||
" 6. Remove from heat and serve as a topping for burgers, steak, or as a side dish.\n",
|
||||
" \n",
|
||||
" ### Recipe 2: French Onion Soup\n",
|
||||
" \n",
|
||||
" #### Ingredients:\n",
|
||||
" - 4 large onions, thinly sliced\n",
|
||||
" - 3 tablespoons unsalted butter\n",
|
||||
" - 2 cloves garlic, minced\n",
|
||||
" - 1 teaspoon sugar\n",
|
||||
" - 1 teaspoon salt\n",
|
||||
" - 1/4 cup dry white wine (optional)\n",
|
||||
" - 4 cups beef broth\n",
|
||||
" - 4 cups chicken broth\n",
|
||||
" - 1 bay leaf\n",
|
||||
" - 1 teaspoon fresh thyme, chopped (or 1/2 teaspoon dried thyme)\n",
|
||||
" - 1 baguette, sliced\n",
|
||||
" - 2 cups Gruyère cheese, grated\n",
|
||||
" \n",
|
||||
" #### Instructions:\n",
|
||||
" 1. Melt the butter in a large pot over medium heat.\n",
|
||||
" 2. Add the onions, garlic, sugar, and salt, and cook, stirring frequently, until the onions are deeply caramelized (about 30-35 minutes).\n",
|
||||
" 3. If using, add the white wine and cook until it evaporates, about 3-5 minutes.\n",
|
||||
" 4. Add the beef and chicken broths, bay leaf, and thyme. Bring to a simmer and cook for another 30 minutes. Remove the bay leaf.\n",
|
||||
" 5. Preheat the oven to 400°F (200°C).\n",
|
||||
" 6. Place the baguette slices on a baking sheet and toast them in the preheated oven until golden brown, about 5 minutes.\n",
|
||||
" 7. Ladle the soup into oven-safe bowls and place a slice of toasted baguette on top of each bowl.\n",
|
||||
" 8. Sprinkle the grated Gruyère cheese generously over the baguette slices.\n",
|
||||
" 9. Place the bowls under the broiler until the cheese is melted and bubbly, about 3-5 minutes.\n",
|
||||
" 10. Serve hot.\n",
|
||||
" \n",
|
||||
" Enjoy your delicious onion dishes!\n",
|
||||
" ```\n",
|
||||
" \n",
|
||||
"- **Produce a shopping list**. We want to produce a shopping list, considering what we already have at home.\n",
|
||||
"\n",
|
||||
" For this functionality, we could either try to solve everything in one prompt or we could split it up into two prompts. Let's try the latter approach. Here we're suggesting adding an additional prompt, but for that to work, we need to add the result of the former prompt as context to the latter prompt. \n",
|
||||
"\n",
|
||||
" Locate the part in the code that prints out the result from the first prompt and add the following code below:\n",
|
||||
" \n",
|
||||
" ```python\n",
|
||||
" old_prompt_result = response.choices[0].message.content\n",
|
||||
" prompt = \"Produce a shopping list for the generated recipes and please don't include ingredients that I already have.\"\n",
|
||||
" \n",
|
||||
" new_prompt = f\"{old_prompt_result} {prompt}\"\n",
|
||||
" \n",
|
||||
" response = client.complete(\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": \"You are a helpful assistant.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": new_prompt,\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" model=model_name,\n",
|
||||
" # Optional parameters\n",
|
||||
" temperature=1.,\n",
|
||||
" max_tokens=1200,\n",
|
||||
" top_p=1. \n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # print response\n",
|
||||
" print(\"Shopping list:\")\n",
|
||||
" print(response.choices[0].message.content)\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" Note the following:\n",
|
||||
"\n",
|
||||
" - We're constructing a new prompt by adding the result from the first prompt to the new prompt: \n",
|
||||
" \n",
|
||||
" ```python\n",
|
||||
" new_prompt = f\"{old_prompt_result} {prompt}\"\n",
|
||||
" messages = [{\"role\": \"user\", \"content\": new_prompt}]\n",
|
||||
" ```\n",
|
||||
"\n",
|
||||
" - We make a new request, but also considering the number of tokens we asked for in the first prompt, so this time we say `max_tokens` is 1200. **A word on token length**. We should consider how many tokens we need to generate the text we want. Tokens cost money, so where possible, we should try to be economical with the number of tokens we use. For example, can we phrase the prompt so that we can use less tokens?\n",
|
||||
"\n",
|
||||
" ```python\n",
|
||||
" response = client.complete(\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": \"You are a helpful assistant.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": new_prompt,\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" model=model_name,\n",
|
||||
" # Optional parameters\n",
|
||||
" temperature=1.,\n",
|
||||
" max_tokens=1200,\n",
|
||||
" top_p=1. \n",
|
||||
" ) \n",
|
||||
" ``` \n",
|
||||
"\n",
|
||||
" Taking this code for a spin, we now arrive at the following output:\n",
|
||||
"\n",
|
||||
" ```output\n",
|
||||
" No of recipes (for example, 5): 1\n",
|
||||
" List of ingredients (for example, chicken, potatoes, and carrots): strawberry, milk\n",
|
||||
" Filter (for example, vegetarian, vegan, or gluten-free): nuts\n",
|
||||
" \n",
|
||||
" Certainly! Here's a simple and delicious recipe for a strawberry milkshake using strawberry and milk as primary ingredients:\n",
|
||||
" \n",
|
||||
" ### Strawberry Milkshake\n",
|
||||
" \n",
|
||||
" #### Ingredients:\n",
|
||||
" - 1 cup fresh strawberries, hulled\n",
|
||||
" - 1 cup cold milk\n",
|
||||
" - 1 tablespoon honey or sugar (optional, to taste)\n",
|
||||
" - 1/2 teaspoon vanilla extract (optional)\n",
|
||||
" - 3-4 ice cubes\n",
|
||||
" \n",
|
||||
" #### Instructions:\n",
|
||||
" 1. Wash and hull the strawberries, then slice them in half.\n",
|
||||
" 2. In a blender, combine the strawberries, cold milk, honey or sugar (if using), vanilla extract (if using), and ice cubes.\n",
|
||||
" 3. Blend until smooth and frothy.\n",
|
||||
" 4. Pour the milkshake into a glass.\n",
|
||||
" 5. Serve immediately and enjoy your refreshing strawberry milkshake!\n",
|
||||
" \n",
|
||||
" This recipe is nut-free and makes for a delightful and quick treat!\n",
|
||||
" Shopping list:\n",
|
||||
" Sure! Here’s the shopping list for the Strawberry Milkshake recipe based on the ingredients provided. Please adjust based on what you already have at home:\n",
|
||||
" \n",
|
||||
" ### Shopping List:\n",
|
||||
" - Fresh strawberries (1 cup)\n",
|
||||
" - Milk (1 cup)\n",
|
||||
" \n",
|
||||
" Optional:\n",
|
||||
" - Honey or sugar (1 tablespoon)\n",
|
||||
" - Vanilla extract (1/2 teaspoon)\n",
|
||||
" - Ice cubes (3-4)\n",
|
||||
" \n",
|
||||
" Feel free to omit the optional ingredients if you prefer or if you already have them on hand. Enjoy your delicious strawberry milkshake!\n",
|
||||
" ```\n",
|
||||
" \n",
|
||||
"- **Experimenting with temperature**. Temperature is something we haven't mentioned so far but is an important context for how our program performs. The higher the temperature value the more random the output will be. Conversely the lower the temperature value the more predictable the output will be. Consider whether you want variation in your output or not.\n",
|
||||
"\n",
|
||||
" To alter the temperature, you can use the `temperature` parameter. For example, if you want to use a temperature of 0.5, you would do:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
" response = client.complete(\n",
|
||||
" messages=[\n",
|
||||
" {\n",
|
||||
" \"role\": \"system\",\n",
|
||||
" \"content\": \"You are a helpful assistant.\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"role\": \"user\",\n",
|
||||
" \"content\": new_prompt,\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" model=model_name,\n",
|
||||
" # Optional parameters\n",
|
||||
" temperature=0.5,\n",
|
||||
" max_tokens=1200,\n",
|
||||
" top_p=1. \n",
|
||||
" )\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
" > Note, the closer to 1.0, the more varied the output.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Assignment\n",
|
||||
"\n",
|
||||
"For this assignment, you can choose what to build.\n",
|
||||
"\n",
|
||||
"Here are some suggestions:\n",
|
||||
"\n",
|
||||
"- Tweak the recipe generator app to improve it further. Play around with temperature values, and the prompts to see what you can come up with.\n",
|
||||
"- Build a \"study buddy\". This app should be able to answer questions about a topic for example Python, you could have prompts like \"What is a certain topic in Python?\", or you could have a prompt that says, show me code for a certain topic etc.\n",
|
||||
"- History bot, make history come alive, instruct the bot to play a certain historical character and ask it questions about its life and times. \n",
|
||||
"\n",
|
||||
"## Solution\n",
|
||||
"\n",
|
||||
"### Study buddy\n",
|
||||
"\n",
|
||||
"- \"You're an expert on the Python language\n",
|
||||
"\n",
|
||||
" Suggest a beginner lesson for Python in the following format:\n",
|
||||
" \n",
|
||||
" Format:\n",
|
||||
" - concepts:\n",
|
||||
" - brief explanation of the lesson:\n",
|
||||
" - exercise in code with solutions\"\n",
|
||||
"\n",
|
||||
"Above is a starter prompt, see how you can use it and tweak it to your liking.\n",
|
||||
"\n",
|
||||
"### History bot\n",
|
||||
"\n",
|
||||
"Here's some prompts you could be using:\n",
|
||||
"\n",
|
||||
"- \"You are Abe Lincoln, tell me about yourself in 3 sentences, and respond using grammar and words like Abe would have used\"\n",
|
||||
"- \"You are Abe Lincoln, respond using grammar and words like Abe would have used:\n",
|
||||
"\n",
|
||||
" Tell me about your greatest accomplishments, in 300 words:\"\n",
|
||||
"\n",
|
||||
"## Knowledge check\n",
|
||||
"\n",
|
||||
"What does the concept temperature do?\n",
|
||||
"\n",
|
||||
"1. It controls how random the output is.\n",
|
||||
"1. It controls how big the response is.\n",
|
||||
"1. It controls how many tokens are used.\n",
|
||||
"\n",
|
||||
"A: 1\n",
|
||||
"\n",
|
||||
"What's a good way to store secrets like API keys?\n",
|
||||
"\n",
|
||||
"1. In code.\n",
|
||||
"1. In a file.\n",
|
||||
"1. In environment variables.\n",
|
||||
"\n",
|
||||
"A: 3, because environment variables are not stored in code and can be loaded from the code. "
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
from openai import OpenAI
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# configure Azure OpenAI service client
|
||||
client = OpenAI()
|
||||
deployment = "gpt-4o-mini"
|
||||
|
||||
no_recipes = input("No of recipes (for example, 5: ")
|
||||
|
||||
ingredients = input("List of ingredients (for example, chicken, potatoes, and carrots: ")
|
||||
|
||||
filter = input("Filter (for example, vegetarian, vegan, or gluten-free: ")
|
||||
|
||||
# interpolate the number of recipes into the prompt an ingredients
|
||||
prompt = f"Show me {no_recipes} recipes for a dish with the following ingredients: {ingredients}. Per recipe, list all the ingredients used, no {filter}: "
|
||||
|
||||
response = client.responses.create(model=deployment, input=prompt, max_output_tokens=600, temperature=0.1, store=False)
|
||||
|
||||
|
||||
# print response
|
||||
print("Recipes:")
|
||||
old_prompt_result = response.output_text
|
||||
if not old_prompt_result:
|
||||
print("No response received.")
|
||||
else:
|
||||
print(old_prompt_result)
|
||||
|
||||
prompt_shopping = "Produce a shopping list, and please don't include ingredients that I already have at home: "
|
||||
new_prompt = f"Given ingredients at home {ingredients} and these generated recipes: {old_prompt_result}, {prompt_shopping}"
|
||||
response = client.responses.create(model=deployment, input=new_prompt, max_output_tokens=600, temperature=0, store=False)
|
||||
|
||||
# print response
|
||||
print("\n=====Shopping list ======= \n")
|
||||
if response.output_text:
|
||||
print(response.output_text)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
from openai import OpenAI
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# configure OpenAI service client
|
||||
client = OpenAI()
|
||||
deployment = "gpt-4o-mini"
|
||||
|
||||
# add your completion code
|
||||
prompt = "Complete the following: Once upon a time there was a"
|
||||
# make a request using the Responses API
|
||||
response = client.responses.create(model=deployment, input=prompt, store=False)
|
||||
|
||||
# print response
|
||||
print(response.output_text)
|
||||
|
||||
# very unhappy _____.
|
||||
|
||||
# Once upon a time there was a very unhappy mermaid.
|
||||