From becb53973e710ec4236a5a1735c49a4a0d172da0 Mon Sep 17 00:00:00 2001 From: Corrado Cavalli Date: Thu, 16 Nov 2023 15:12:09 +0100 Subject: [PATCH] fixed ch06 --- .../1-introduction.ipynb | 18 +- 04-prompt-engineering-fundamentals/README.md | 793 +++++++++--------- .../notebook-azure-openai.ipynb | 325 +++---- 3 files changed, 535 insertions(+), 601 deletions(-) diff --git a/04-prompt-engineering-fundamentals/1-introduction.ipynb b/04-prompt-engineering-fundamentals/1-introduction.ipynb index b55170471..f3b860d68 100644 --- a/04-prompt-engineering-fundamentals/1-introduction.ipynb +++ b/04-prompt-engineering-fundamentals/1-introduction.ipynb @@ -72,29 +72,35 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "By the dawn's early light,\n" + ] + } + ], "source": [ "# The OpenAI SDK was updated on Nov 8, 2023 with new guidance for migration\n", "# See: https://github.com/openai/openai-python/discussions/742\n", "\n", "## Updated\n", "import os\n", - "import openai\n", "from openai import AzureOpenAI\n", "\n", "client = AzureOpenAI(\n", " api_key=os.environ['AZURE_OPENAI_KEY'], # this is also the default, it can be omitted\n", - " api_version = \"2023-05-15\",\n", - " azure_endpoint = os.environ['AZURE_OPENAI_ENDPOINT']\n", + " api_version = \"2023-05-15\"\n", " )\n", "\n", "deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']\n", "\n", "## Updated\n", "def get_completion(prompt):\n", - " messages = [{\"role\": \"user\", \"content\": prompt}]\n", + " messages = [{\"role\": \"user\", \"content\": prompt}] \n", " response = client.chat.completions.create( \n", " model=deployment, \n", " messages=messages,\n", diff --git a/04-prompt-engineering-fundamentals/README.md b/04-prompt-engineering-fundamentals/README.md index 067fe5153..f1b9640bf 100644 --- a/04-prompt-engineering-fundamentals/README.md +++ b/04-prompt-engineering-fundamentals/README.md @@ -1,399 +1,394 @@ -# Prompt Engineering Fundamentals - -[![Prompt Engineering Fundamentals](./images/04-lesson-banner.png?WT.mc_id=academic-105485-koreyst)](https://youtu.be/r2ItK3UMVTk?WT.mc_id=academic-105485-koreyst) - - -How you write your prompt to the LLM matters, a carefully crafted prompt can achieve a better result than one that isn't. But what even are these concepts, prompt, prompt engineering and how do I improve what I send to the LLM? Questions like these are what this chapter and the upcoming chapter are looking to answer. - -_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_ (LLMs) 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 till 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. - -## 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 OpenAI API key - the service endpoint for a deployed LLM. - -2. A Python Runtime - in which the Notebook can be executed. - -We have instrumented this repository with a _dev container_ that comes with a Python 3 runtime. Simply open the repo in GitHub Codespaces or on your local Docker Desktop, to activate the runtime automatically. Then open the notebook and select the Python 3.x kernel to prepare the Notebook for execution. - -The default notebook is set up for use with an OpenAI API Key. Simply copy the `.env.copy` file in the root of the folder to `.env` and update the `OPENAI_API_KEY=` line with your API key - and you're all set. - -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. - -## 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!_ - - - -## 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. - -![Tokenization](./images/04-tokenizer-example.png?WT.mc_id=academic-105485-koreyst) - -### 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 Azure OpenAI Studio [_Chat Playground_](https://oai.azure.com/playground?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. - -![Base LLM Chat Completion](./images/04-playground-chat-base.png?WT.mc_id=academic-105485-koreyst) - -### 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. - -![Instruction Tuned LLM Chat Completion](./images/04-playground-chat-instructions.png?WT.mc_id=academic-105485-koreyst) - -## 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 1](./images/04-fabrication-oai.png?WT.mc_id=academic-105485-koreyst) - -> **Response 2**: Azure OpenAI Playground (GPT-35) - -![Response 2](./images/04-fabrication-aoai.png?WT.mc_id=academic-105485-koreyst) - -> **Response 3**: : Hugging Face Chat Playground (LLama-2) - -![Response 3](./images/04-fabrication-huggingchat.png?WT.mc_id=academic-105485-koreyst) - -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. - ---- - - - -## 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 = openai.ChatCompletion.create( - model="gpt-3.5-turbo", - messages=[ - {"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.
**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".
"It's a Cold and Windy Day" => | "Es un día frío y ventoso". | -| Few-shot | The player ran the bases => Baseball
The player hit an ace => Tennis
The player hit a six => Cricket
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.
**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.
**Summarize This**
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.
**Summarize This**
Top 3 Facts We Learned: | 1. Jupiter is the fifth planet from the Sun and the largest in the Solar System.
2. It is a gas giant with a mass one-thousandth that of the Sun...
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://platform.openai.com/examples?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/modules/model_io/prompts/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. - ---- - - - -## 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 [Open AI](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. - - - -## 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 - -- Copt the `.env.copy` file in repo root to `.env` and fill in the `OPENAI_API_KEY` value. You can find your API Key in your [OpenAI Dashboard](https://beta.openai.com/account/api-keys?WT.mc_id=academic-105485-koreyst). - -### 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._ - - - - -## 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 [contiuned learning page](../13-continued-learning/README.md?WT.mc_id=academic-105485-koreyst) to find other great resources on this topic. - -Head over to Lesson 5 where we will look at [advance prompting techniques](../05-advanced-prompts/README.md?WT.mc_id=academic-105485-koreyst)! +# Prompt Engineering Fundamentals + +[![Prompt Engineering Fundamentals](./images/04-lesson-banner.png?WT.mc_id=academic-105485-koreyst)](https://youtu.be/r2ItK3UMVTk?WT.mc_id=academic-105485-koreyst) + +How you write your prompt to the LLM matters, a carefully crafted prompt can achieve a better result than one that isn't. But what even are these concepts, prompt, prompt engineering and how do I improve what I send to the LLM? Questions like these are what this chapter and the upcoming chapter are looking to answer. + +_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_ (LLMs) 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 till 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. + +## 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 OpenAI API key - the service endpoint for a deployed LLM. + +2. A Python Runtime - in which the Notebook can be executed. + +We have instrumented this repository with a _dev container_ that comes with a Python 3 runtime. Simply open the repo in GitHub Codespaces or on your local Docker Desktop, to activate the runtime automatically. Then open the notebook and select the Python 3.x kernel to prepare the Notebook for execution. + +The default notebook is set up for use with an OpenAI API Key. Simply copy the `.env.copy` file in the root of the folder to `.env` and update the `OPENAI_API_KEY=` line with your API key - and you're all set. + +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. + +## 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!_ + + + +## 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. + +![Tokenization](./images/04-tokenizer-example.png?WT.mc_id=academic-105485-koreyst) + +### 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 Azure OpenAI Studio [_Chat Playground_](https://oai.azure.com/playground?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. + +![Base LLM Chat Completion](./images/04-playground-chat-base.png?WT.mc_id=academic-105485-koreyst) + +### 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. + +![Instruction Tuned LLM Chat Completion](./images/04-playground-chat-instructions.png?WT.mc_id=academic-105485-koreyst) + +## 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 1](./images/04-fabrication-oai.png?WT.mc_id=academic-105485-koreyst) + +> **Response 2**: Azure OpenAI Playground (GPT-35) + +![Response 2](./images/04-fabrication-aoai.png?WT.mc_id=academic-105485-koreyst) + +> **Response 3**: : Hugging Face Chat Playground (LLama-2) + +![Response 3](./images/04-fabrication-huggingchat.png?WT.mc_id=academic-105485-koreyst) + +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. + +--- + + + +## 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 = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=[ + {"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.
**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".
"It's a Cold and Windy Day" => | "Es un día frío y ventoso". | +| Few-shot | The player ran the bases => Baseball
The player hit an ace => Tennis
The player hit a six => Cricket
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.
**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.
**Summarize This**
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.
**Summarize This**
Top 3 Facts We Learned: | 1. Jupiter is the fifth planet from the Sun and the largest in the Solar System.
2. It is a gas giant with a mass one-thousandth that of the Sun...
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://platform.openai.com/examples?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/modules/model_io/prompts/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. + +--- + + + +## 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 [Open AI](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. + + + +## 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 + +- Copt the `.env.copy` file in repo root to `.env` and fill in the `AZURE_OPENAI_KEY`, `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_DEPLOYMENT` values. You can find your API Key in your [OpenAI Dashboard](https://beta.openai.com/account/api-keys?WT.mc_id=academic-105485-koreyst). + +### 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._ + + + +## 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 [contiuned learning page](../13-continued-learning/README.md?WT.mc_id=academic-105485-koreyst) to find other great resources on this topic. + +Head over to Lesson 5 where we will look at [advance prompting techniques](../05-advanced-prompts/README.md?WT.mc_id=academic-105485-koreyst)! diff --git a/06-text-generation-apps/notebook-azure-openai.ipynb b/06-text-generation-apps/notebook-azure-openai.ipynb index 90004b980..371f9d6a1 100644 --- a/06-text-generation-apps/notebook-azure-openai.ipynb +++ b/06-text-generation-apps/notebook-azure-openai.ipynb @@ -78,7 +78,12 @@ "\n", "### Install openai\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. We've chosen to use the `openai` Python library, so we'll use `pip` to install it.\n", + "[!NOTE]\n", + " > 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", @@ -86,7 +91,7 @@ "\n", "### Create a resource\n", "\n", - "You need to carry out the following steps:\n", + "In case you didn't alrady, you need to carry out the following steps:\n", "\n", "- Create an account on Azure .\n", "- Gain access to Azure Open AI. Go to and request access.\n", @@ -108,8 +113,8 @@ "\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 `OPENAI_API_KEY` to your API key.\n", - "> `export OPENAI_API_KEY='sk-...'`\n", + "> - Set the environment variable `AZURE_OPENAI_KEY` to your API key.\n", + "> `export AZURE_OPENAI_KEY='sk-...'`\n", "\n", "\n", "### Setup configuration Azure\n", @@ -117,21 +122,22 @@ "If you're using Azure Open AI, here's how you setup configuration:\n", "\n", "```python\n", - "openai.api_type = 'azure'\n", - "openai.api_key = os.environ[\"OPENAI_API_KEY\"]\n", - "openai.api_version = '2023-05-15'\n", - "openai.api_base = os.getenv(\"API_BASE\")\n", + "client = AzureOpenAI(\n", + " api_key=os.environ['AZURE_OPENAI_KEY'], \n", + " api_version = \"2023-05-15\"\n", + " )\n", + "\n", + "deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']\n", "```\n", "\n", "Above we're setting the following:\n", "\n", - "- `api_type` to `azure`. This tells the library to use Azure Open AI and not OpenAI.\n", "- `api_key`, this is your API key found in the Azure Portal.\n", "- `api_version`, this is the version of the API you want to use. At the time of writing, the latest version is `2023-05-15`.\n", - "- `api_base`, this is the endpoint of the API. You can find it in the Azure Portal next to your API key. \n", + "- `deployment`, this is the endpoint of the API. You can find it in the Azure Portal next to your API key. \n", "\n", "> [!NOTE]\n", - "> `os.getenv` is a function that reads environment variables. You can use it to read environment variables like `OPENAI_API_KEY` and `API_BASE`. Set these environment variables in your terminal or by using a library like `dotenv`.\n", + "> `os.environ` is a function that reads environment variables. You can use it to read environment variables like `AZURE_OPENAI_KEY` and `AZURE_OPENAI_ENDPOINT`. Set these environment variables in your .env file as indicated [here](../04-prompt-engineering-fundamentals/README.md)\n", "\n", "## Generate text\n", "\n", @@ -140,7 +146,7 @@ "```python\n", "prompt = \"Complete the following: Once upon a time there was a\"\n", "\n", - "completion = openai.Completion.create(model=\"davinci-002\", prompt=prompt)\n", + "completion = client.completions.create.create(model=deployment, prompt=prompt)\n", "print(completion.choices[0].text)\n", "```\n", "\n", @@ -151,11 +157,14 @@ "So far, you've seen how we've been using `Completion` to generate text. But there's another class called `ChatCompletion` that is more suited for chatbots. Here's an example of using it:\n", "\n", "```python\n", - "import openai\n", + "client = AzureOpenAI(\n", + " api_key=os.environ['AZURE_OPENAI_KEY'], \n", + " api_version = \"2023-05-15\"\n", + " )\n", "\n", - "openai.api_key = \"sk-...\"\n", + "deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']\n", "\n", - "completion = openai.ChatCompletion.create(model=\"gpt-3.5-turbo\", messages=[{\"role\": \"user\", \"content\": \"Hello world\"}])\n", + "completion = client.chat.completions.create(model=deployment, messages=[{\"role\": \"user\", \"content\": \"Hello world\"}])\n", "print(completion.choices[0].message.content)\n", "```\n", "\n", @@ -171,63 +180,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "1. Create a virtual environment and install openai:" + "1. Create a virtual environment and install openai:\n", + "\n", + "[!NOTE]\n", + " > This step is not necessary if run this notebook on Codespaces or within a Devcontainer" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "'source' is not recognized as an internal or external command,\n", - "operable program or batch file.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting openai\n", - " Using cached openai-0.28.1-py3-none-any.whl (76 kB)\n", - "Collecting tqdm\n", - " Using cached tqdm-4.66.1-py3-none-any.whl (78 kB)\n", - "Requirement already satisfied: requests>=2.20 in c:\\users\\chnoring\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\\localcache\\local-packages\\python310\\site-packages (from openai) (2.31.0)\n", - "Collecting aiohttp\n", - " Using cached aiohttp-3.8.6-cp310-cp310-win_amd64.whl (325 kB)\n", - "Requirement already satisfied: idna<4,>=2.5 in c:\\users\\chnoring\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\\localcache\\local-packages\\python310\\site-packages (from requests>=2.20->openai) (3.3)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in c:\\users\\chnoring\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\\localcache\\local-packages\\python310\\site-packages (from requests>=2.20->openai) (3.1.0)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\users\\chnoring\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\\localcache\\local-packages\\python310\\site-packages (from requests>=2.20->openai) (2.0.3)\n", - "Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\chnoring\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\\localcache\\local-packages\\python310\\site-packages (from requests>=2.20->openai) (2023.5.7)\n", - "Collecting aiosignal>=1.1.2\n", - " Using cached aiosignal-1.3.1-py3-none-any.whl (7.6 kB)\n", - "Collecting frozenlist>=1.1.1\n", - " Using cached frozenlist-1.4.0-cp310-cp310-win_amd64.whl (44 kB)\n", - "Collecting multidict<7.0,>=4.5\n", - " Using cached multidict-6.0.4-cp310-cp310-win_amd64.whl (28 kB)\n", - "Collecting async-timeout<5.0,>=4.0.0a3\n", - " Using cached async_timeout-4.0.3-py3-none-any.whl (5.7 kB)\n", - "Collecting yarl<2.0,>=1.0\n", - " Using cached yarl-1.9.2-cp310-cp310-win_amd64.whl (61 kB)\n", - "Requirement already satisfied: attrs>=17.3.0 in c:\\users\\chnoring\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\\localcache\\local-packages\\python310\\site-packages (from aiohttp->openai) (23.1.0)\n", - "Requirement already satisfied: colorama in c:\\users\\chnoring\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\\localcache\\local-packages\\python310\\site-packages (from tqdm->openai) (0.4.5)\n", - "Installing collected packages: tqdm, multidict, frozenlist, async-timeout, yarl, aiosignal, aiohttp, openai\n", - "Successfully installed aiohttp-3.8.6 aiosignal-1.3.1 async-timeout-4.0.3 frozenlist-1.4.0 multidict-6.0.4 openai-0.28.1 tqdm-4.66.1 yarl-1.9.2\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "[notice] A new release of pip is available: 23.0.1 -> 23.3.1\n", - "[notice] To update, run: C:\\Users\\chnoring\\AppData\\Local\\Microsoft\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\\python.exe -m pip install --upgrade pip\n" - ] - } - ], + "outputs": [], "source": [ "# Create virtual environment\n", "! python -m venv venv\n", @@ -265,29 +228,26 @@ "name": "stdout", "output_type": "stream", "text": [ - " _____\n", - "\n", - "There was a princess who loved to sing and dance.\n" + " girl named xxxx. She was dying because of some dangerous disease. She knew that\n" ] } ], "source": [ - "import openai\n", + "import os\n", + "from openai import AzureOpenAI\n", "\n", - "openai.api_key = \"\"\n", - "# azure\n", + "client = AzureOpenAI(\n", + " api_key=os.environ['AZURE_OPENAI_KEY'], \n", + " api_version = \"2023-05-15\"\n", + " )\n", "\n", - "openai.api_type = 'azure' \n", - "openai.api_version = '2023-05-15'\n", - "openai.api_base = \"\"\n", - "engine = \"davinci-001\"\n", - "deployment_name = \"\" \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 completion\n", - "completion = openai.Completion.create(engine= deployment_name, model=\"davinci-002\", prompt=prompt)\n", + "completion = client.completions.create(model=deployment, prompt=prompt)\n", "\n", "# print response\n", "print(completion.choices[0].text)" @@ -297,8 +257,6 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "> [!NOTE] \n", - "> If you're using Azure Open AI, you need to set the `api_type` to `azure` and set the `api_key` to your Azure Open AI key.\n", "\n", " You should see an output like the following:\n", "\n", @@ -488,39 +446,98 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - ".\n", + " and the amount. Goal is 5 unique and different recipes.\n", + "One Pot Chicken and Potato Bake:\n", + "Ingredients:\n", + "1 pound Chicken Breast\n", + "10-12 small potatoes, cut in half\n", + "1 cup Carrots, sliced\n", + "2 tablespoons Olive Oil\n", + "2 tablespoons Dijon Mustard\n", + "2 tablespoons Honey\n", + "1/2 teaspoon Paprika\n", + "1/2 teaspoon Salt\n", + "1/4 teaspoon Black Pepper\n", + "\n", + "Chicken and Potato Kebabs:\n", + "Ingredients:\n", + "1 pound chicken pieces\n", + "5 medium-sized potatoes\n", + "10 baby carrots\n", + "1/2 medium-sized onion, cut into chunks\n", + "1/4 cup olive oil\n", + "2 tablespoons oregano\n", + "1 tablespoon paprika\n", + "1 tablespoon cumin\n", + "1 tablespoon black pepper\n", + "1 tablespoon salt\n", + "\n", + "Roasted Chicken with Potatoes and Carrots:\n", + "Ingredients:\n", + "1 whole chicken, cut into 8 pieces\n", + "4-6 potatoes, peeled and quartered\n", + "2-3 cups carrots, peeled and sliced\n", + "2 tablespoons olive oil\n", + "2 tablespoons minced garlic\n", + "2 tablespoons dried herbs (like thyme, rosemary, and oregano)\n", + "1 teaspoon salt\n", + "1/2 teaspoon black pepper\n", "\n", - "-Slow Cooker Garlic Honey Chicken and Potatoes\n", - "-One Pan Roasted Chicken and Potatoes with Carrots\n", - "-Chicken, Potato, and Carrot Stir-Fry\n", - "-Chicken Pot Pie with Carrots\n", - "-Roasted Chicken and Carrots with Potatoes\n" + "Chicken, Potato and Carrot Stew:\n", + "Ingredients:\n", + "1 pound chicken thighs\n", + "1 pound potatoes, peeled and chopped\n", + "1 cup carrots, peeled and chopped\n", + "1 medium onion, chopped\n", + "4 cups chicken broth\n", + "2 tablespoons olive oil\n", + "1 tablespoon tomato paste\n", + "1 tablespoon dried thyme\n", + "1 teaspoon salt\n", + "1/2 teaspoon black pepper\n", + "\n", + "Chicken and Vegetable Curry:\n", + "Ingredients:\n", + "1lb chicken, diced\n", + "1 lb potato, diced\n", + "1 cup carrots, diced\n", + "1/2 tsp salt\n", + "1/2 tsp black pepper\n", + "2 tbsp olive oil\n", + "1 onion, chopped finely\n", + "3 garlic cloves, minced\n", + "1 tbsp curry powder\n", + "1 tsp ground cumin\n", + "1 tsp ground coriander\n", + "1 tsp ground ginger\n", + "1 can of coconut milk (13.5 oz)\n", + "1 cup chicken broth\n", + "1/4 cup chopped cilantro (optional)<|im_end|>\n" ] } ], "source": [ - "import openai\n", + "import os\n", + "from openai import AzureOpenAI\n", "\n", - "openai.api_key = \"\"\n", - "# azure\n", + "client = AzureOpenAI(\n", + " api_key=os.environ['AZURE_OPENAI_KEY'], \n", + " api_version = \"2023-05-15\"\n", + " )\n", "\n", - "openai.api_type = 'azure' \n", - "openai.api_version = '2023-05-15'\n", - "openai.api_base = \"\"\n", - "engine = \"davinci-001\"\n", - "deployment_name = \"\" \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 completion\n", - "completion = openai.Completion.create(engine= deployment_name, model=\"davinci-002\", prompt=prompt, max_tokens=600)\n", + "completion = client.completions.create(model=deployment, prompt=prompt, max_tokens=600)\n", "\n", "# print response\n", "print(completion.choices[0].text)" @@ -556,73 +573,19 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 12, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "Leeks are a type of onion that can be eaten raw or cooked. In this recipe, leeks are cooked and mixed with a creamy yogurt sauce.\n", - "\n", - "Ingredients:\n", - "-1 leek\n", - "-1/4 cup plain yogurt\n", - "-1 tablespoon olive oil\n", - "-1 clove garlic, minced\n", - "-Salt and pepper\n", - "\n", - "Instructions:\n", - "\n", - "1. Cut the leek in half lengthwise and then thinly slice.\n", - "\n", - "2. In a large skillet, heat the olive oil over medium heat. Add the garlic and leeks and cook until soft, about 5 minutes.\n", - "\n", - "3. Add the yogurt and salt and pepper to taste. Cook until heated through, about 5 more minutes.\n", - "\n", - "This recipe uses leeks in a savory pie.\n", - "\n", - "Ingredients:\n", - "-1 leek\n", - "-2 tablespoons butter\n", - "-2 tablespoons flour\n", - "-1 cup milk\n", - "-Salt and pepper\n", - "-1/2 cup grated cheddar cheese\n", - "-1/4 cup chopped parsley\n", - "-1 pie crust\n", - "\n", - "Instructions:\n", - "\n", - "1. Preheat the oven to 400 degrees F.\n", - "\n", - "2. Slice the leek in half lengthwise and then thinly slice.\n", - "\n", - "3. In a medium saucepan, melt the butter over medium heat. Stir in the flour and cook for 1 minute.\n", - "\n", - "4. Add the milk and salt and pepper to taste. Stir until the mixture thickens.\n", - "\n", - "5. Stir in the cheddar cheese and parsley.\n", - "\n", - "6. Pour the mixture into the pie crust.\n", - "\n", - "7. Bake for 15 minutes, or until the cheese is melted and bubbly.\n" - ] - } - ], + "outputs": [], "source": [ - "import openai\n", + "import os\n", + "from openai import AzureOpenAI\n", "\n", - "openai.api_key = \"\"\n", - "# azure\n", + "client = AzureOpenAI(\n", + " api_key=os.environ['AZURE_OPENAI_KEY'], \n", + " api_version = \"2023-05-15\"\n", + " )\n", "\n", - "openai.api_type = 'azure' \n", - "openai.api_version = '2023-05-15'\n", - "openai.api_base = \"\"\n", - "engine = \"davinci-001\"\n", - "deployment_name = \"\" \n", + "deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']\n", "\n", "no_recipes = input(\"No of recipes (for example, 5: \")\n", "\n", @@ -632,7 +595,7 @@ "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 completion\n", - "completion = openai.Completion.create(engine= deployment_name, model=\"davinci-002\", prompt=prompt, max_tokens=600)\n", + "completion = client.completions.create(model=deployment, prompt=prompt, max_tokens=600)\n", "\n", "# print response\n", "print(completion.choices[0].text)" @@ -752,7 +715,7 @@ " 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", - " completion = openai.Completion.create(engine=deployment_name, prompt=new_prompt, max_tokens=1200)\n", + " completion = client.completion.create(model=deployment, prompt=new_prompt, max_tokens=1200)\n", " \n", " # print response\n", " print(\"Shopping list:\")\n", @@ -770,7 +733,7 @@ " - 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. \n", "\n", " ```python\n", - " completion = openai.Completion.create(engine=deployment_name, prompt=new_prompt, max_tokens=1200)\n", + " completion = client.completion.create(model=deployment, prompt=new_prompt, max_tokens=1200)\n", " ``` \n", "\n", " Taking this code for a spin, we now arrive at the following output:\n", @@ -787,43 +750,13 @@ " Shopping list:\n", " -Flour, baking powder, baking soda, salt, sugar, egg, buttermilk, butter, apple, nutmeg, cinnamon, allspice \n", " ```\n", - "\n", - "## Improve your setup \n", - "\n", - "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 is:\n", - "\n", - "- **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:\n", - "\n", - " - Create a `.env` file with the following content:\n", - "\n", - " ```bash\n", - " OPENAI_API_KEY=sk-...\n", - " ``` \n", - "\n", - " > Note, for Azure, you need to set the following environment variables:\n", - "\n", - " ```bash\n", - " OPENAI_API_TYPE=azure\n", - " OPENAI_API_VERSION=2023-05-15\n", - " OPENAI_API_BASE=\n", - " ```\n", - "\n", - " In code, you would then load the environment variables like so:\n", - "\n", - " ```python\n", - " from dotenv import load_dotenv\n", - "\n", - " load_dotenv()\n", - "\n", - " openai.api_key = os.environ[\"OPENAI_API_KEY\"]\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_tokens` parameter. For example, if you want to use 100 tokens, you would do:\n", "\n", " ```python\n", - " completion = openai.Completion.create(model=\"davinci-002\", prompt=prompt, max_tokens=100)\n", + " completion = client.completions.create(model=deployment, prompt=prompt, max_tokens=100)\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", @@ -831,7 +764,7 @@ " 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", - " completion = openai.Completion.create(model=\"davinci-002\", prompt=prompt, temperature=0.5)\n", + " completion = client.completions.create(model=deployment, prompt=prompt, temperature=0.5)\n", " ```\n", "\n", " > Note, the closer to 1.0, the more varied the output.\n", @@ -908,7 +841,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.10.8" }, "orig_nbformat": 4 },