
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>GauravMantri.com</title>
	<atom:link href="http://gauravmantri.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://gauravmantri.com</link>
	<description>Gaurav Mantri&#039;s Personal Blog</description>
	<lastBuildDate>Fri, 15 Nov 2024 16:24:09 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.4.1</generator>
	<item>
		<title>Keeping Your Azure OpenAI App Running Smoothly During Service Interruptions</title>
		<link>https://gauravmantri.com/2024/11/15/keeping-your-azure-openai-app-running-smoothly-during-service-interruptions/</link>
					<comments>https://gauravmantri.com/2024/11/15/keeping-your-azure-openai-app-running-smoothly-during-service-interruptions/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Fri, 15 Nov 2024 16:24:08 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Azure OpenAI]]></category>
		<category><![CDATA[Failover]]></category>
		<category><![CDATA[Geo-Redundancy]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1978</guid>

					<description><![CDATA[<p>In this post, I’ll walk you through a simple yet effective approach we use at Purple Leaf to ensure our application stay online, even when Azure OpenAI service faces throttling or downtime. By deploying Azure OpenAI in multiple regions and implementing a smart failover strategy, we’re able to provide a seamless experience for our users, [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/11/15/keeping-your-azure-openai-app-running-smoothly-during-service-interruptions/">Keeping Your Azure OpenAI App Running Smoothly During Service Interruptions</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this post, I’ll walk you through a simple yet effective approach we use at <a rel="noreferrer noopener" href="https://purpleleaf.ai" target="_blank">Purple Leaf</a> to ensure our application stay online, even when Azure OpenAI service faces throttling or downtime. By deploying Azure OpenAI in multiple regions and implementing a smart failover strategy, we’re able to provide a seamless experience for our users, regardless of unexpected service disruptions.</p>



<p>Here’s how it works:</p>



<h3>1. Deploy Azure OpenAI Across Multiple Regions </h3>



<p>We deploy Azure OpenAI service in at least two regions — one as the primary and another as the secondary.</p>



<h3>2. Failover Logic</h3>



<p>With both regions set up, our application needs a way to switch smoothly between them. For example, if primary region fails to process a request, the system should automatically retry the request with the secondary region. This approach ensures that, even if one service instance is down or throttled, your application can continue running without significant interruptions.</p>



<p>By deploying Azure OpenAI service across multiple regions and integrating this failover strategy, we protect our application from unexpected downtime and keep our users happy. </p>



<p>In the next sections, I’ll provide sample code to demonstrate how to implement this approach effectively.</p>



<h3>Code</h3>


<pre class="brush: jscript; title: ; notranslate">
export default class AzureOpenAIHelper {
  private readonly _azureOpenAIPrimaryClient: AzureOpenAI;
  private readonly _azureOpenAISecondaryClient: AzureOpenAI;

  constructor() {
    this._azureOpenAIPrimaryClient = new AzureOpenAI({
      endpoint: &quot;primary-end-point&quot;,
      apiKey: &quot;primary-end-point-api-key&quot;,
      deployment: &quot;primary-deployment-id&quot;,
      apiVersion: &quot;primary-api-version&quot;,
    });
    this._azureOpenAISecondaryClient = new AzureOpenAI({
      endpoint: &quot;secondary-end-point&quot;,
      apiKey: &quot;secondary-end-point-api-key&quot;,
      deployment: &quot;secondary-deployment-id&quot;,
      apiVersion: &quot;secondary-api-version&quot;,
    });
  }

  async processChatRequest(systemMessage: string, userMessage: string, isPrimary: boolean = true, attempt: number = 1) {
    try {
      const client = isPrimary
        ? this._azureOpenAIPrimaryClient
        : this._azureOpenAISecondaryClient;
      const messages = [
        { role: 'system', content: systemMessage },
        { role: 'user', content: userMessage}
      ]  
      const result = await client.chat.completions.create({
          messages,
          model: model,
          response_format: { type: 'text' },
        },
        {},
      );        
      return result;
    } catch (error: unknown) {
      if (attempt &lt; 4) {
        return await this.processChatRequest(
          systemMessage,
          userMessage,
          !isPrimary,
          attempt + 1,
        );
      } else {
        throw err;
      }
    }
  }
}
</pre>


<p>In the class constructor, we create instances of both primary and secondary Azure OpenAI services. </p>



<p><code>processChatRequest</code> method makes the call to the Azure OpenAI service. By default this method connects to the primary service. </p>



<p>If the call fails for whatever reason, we execute the same method but this time we are connecting to the secondary service. If the call fails again, we fall back to primary. </p>



<p>We do this a limited number of times (4) before we give up and throw an exception.</p>



<h3>Summary</h3>



<p>When working with services like Azure OpenAI, throttling and downtime can potentially bring your entire application down. That’s why having a plan for redundancy isn’t just nice to have — it’s essential.</p>



<p>By deploying Azure OpenAI in multiple regions and using a simple failover strategy, you’re not only adding a layer of resilience but also ensuring your app keeps running smoothly, even when things go wrong.</p>



<p>This setup has been a super helpful for us at Purple Leaf, helping us deliver a stable, reliable experience without leaving our users hanging.</p>



<p>I hope you have found this blog post useful. Please do share your thoughts on how you are handling Azure OpenAI service issues.</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/11/15/keeping-your-azure-openai-app-running-smoothly-during-service-interruptions/">Keeping Your Azure OpenAI App Running Smoothly During Service Interruptions</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/11/15/keeping-your-azure-openai-app-running-smoothly-during-service-interruptions/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Is SaaS Dead? I Don’t Think So!</title>
		<link>https://gauravmantri.com/2024/10/02/is-saas-dead-i-dont-think-so/</link>
					<comments>https://gauravmantri.com/2024/10/02/is-saas-dead-i-dont-think-so/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Wed, 02 Oct 2024 03:14:39 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[SaaS]]></category>
		<category><![CDATA[Software-as-a-Service]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1975</guid>

					<description><![CDATA[<p>Writing software has become incredibly easy today, thanks to the proliferation of AI-powered code-generation tools like GitHub Copilot, Kursor, Vercel&#8217;s V0, and GPT Engineer. Combine that with the rise of low-code/no-code platforms, and it&#8217;s no surprise that debates about the future of Software-as-a-Service (SaaS) have emerged. Many pundits are now proclaiming the death of the [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/10/02/is-saas-dead-i-dont-think-so/">Is SaaS Dead? I Don’t Think So!</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Writing software has become incredibly easy today, thanks to the proliferation of AI-powered code-generation tools like GitHub Copilot, Kursor, Vercel&#8217;s V0, and GPT Engineer. Combine that with the rise of low-code/no-code platforms, and it&#8217;s no surprise that debates about the future of Software-as-a-Service (SaaS) have emerged.</p>



<p>Many pundits are now proclaiming the death of the SaaS industry, fueled by the idea of &#8220;<strong>why rent when you can build?</strong>&#8221; They argue that businesses, armed with these new technologies, can build and customize software in-house with fewer resources, bypassing the need for external SaaS providers.</p>



<p>High-profile examples, like <strong>Klarna decommissioning some SaaS services from its enterprise</strong>, only add fuel to this fire.</p>



<p>So, is SaaS dead?</p>



<p><strong>I don’t think so.</strong></p>



<p>While it’s true that AI and no-code platforms have made development more accessible, they haven’t eliminated the need for comprehensive, secure, and continuously evolving services. </p>



<p>SaaS isn’t just about the software—it’s an ecosystem that offers businesses instant access to powerful tools, enterprise-grade security, and seamless scalability, without the heavy lifting required to build, maintain, and update those solutions in-house.</p>



<h2>Why Did You Choose SaaS in the First Place?</h2>



<p>Let me ask you this: Why did you choose a SaaS service in the first place? My guess is that the following were some of the primary reasons you considered:</p>



<h3>Focus on Core Competencies</h3>



<p>Unless you&#8217;re a Payment Tech SaaS company, it makes zero sense for you to write your own payment processing solution—simply because <strong>that’s not your core competency</strong>. Choosing a payment processing SaaS solution (like Stripe) allows you to <strong>focus on what you do best</strong>.</p>



<p>Instead of sinking time and resources into building, maintaining, and securing software, you can <strong>focus on your core competencies, driving innovation and growth where it matters most</strong>. SaaS takes the burden of software management off your shoulders, letting you focus on strategy and execution.</p>



<p>While AI tools can undeniably assist with software development, they don’t replace the ongoing responsibility of maintaining, securing, and evolving these systems. The time and effort saved by AI in development can quickly be negated by the resources still required to manage infrastructure, updates, and security. SaaS providers continue to handle all of this for you, allowing businesses to truly focus on their core mission.</p>



<h3>Move Fast and Avoid the Complexities of Deployment and Infrastructure Management</h3>



<p>When you chose SaaS, a major factor was likely the need to move fast and avoid the technical complexities that come with deployment and infrastructure management. SaaS provides immediate access to powerful, industry-standard tools without the need for lengthy development cycles or complex setups. Whether onboarding a small team or scaling across multiple departments, SaaS solutions enable rapid deployment with minimal disruption, allowing your business to hit the ground running.</p>



<p>While AI can accelerate coding and development, it doesn’t simplify the challenges of integrating custom-built solutions into existing ecosystems. Deployment is often fraught with testing, troubleshooting, and infrastructure configuration. SaaS solutions, by contrast, come pre-configured and battle-tested, offering businesses a faster, hassle-free path to productivity without the infrastructure headaches.</p>



<h3>Security and Compliance? No Problem.</h3>



<p>When you chose SaaS, it wasn’t just about convenience—it was about trusting experts with the critical aspects of security and compliance. You were fully aware of the complexities involved but didn’t have the resources, expertise, or desire to manage them in-house. SaaS providers invest heavily in enterprise-grade security, regulatory compliance, and continuous monitoring, offering a level of protection that would be difficult and costly to replicate internally.</p>



<p>While AI can certainly assist with certain security tasks, it doesn’t provide the full-scale infrastructure, 24/7 monitoring, and regulatory expertise needed to ensure compliance and prevent breaches. SaaS solutions come with these built-in safeguards, giving you peace of mind that your business is secure and compliant with the latest industry standards.</p>



<h3>Cost-Efficiency and Scalability</h3>



<p>One of the main drivers behind your decision to choose SaaS was the need for a <strong>cost-efficient and scalable solution</strong>. SaaS provides a flexible pricing model—allowing you to pay only for what you use—while avoiding the large upfront costs associated with building and maintaining custom software. As your business grows, SaaS effortlessly scales with you, ensuring that you have the right amount of resources at any given time without the headache of managing infrastructure.</p>



<p>While AI can help streamline development, it doesn’t automatically lower the costs associated with infrastructure, scaling, and maintenance. Custom-built software, even AI-assisted, still requires continuous monitoring and resource allocation to handle growth. SaaS, by contrast, offers seamless scalability without the hidden expenses, making it an ideal long-term solution for businesses looking to grow efficiently.</p>



<p>Some folks may bring up the famous <strong>“Bell Curve,”</strong> where once you reach a certain scale, it becomes more efficient to do things in-house—and quite honestly, I will really look forward to that.</p>



<p>However, for most businesses, reaching that point is a long way off. Until then, the cost-efficiency and scalability that SaaS offers provide the perfect balance of affordability, flexibility, and convenience, allowing companies to grow without unnecessary complications. SaaS gives you the tools to thrive now, and when that scale comes, you&#8217;ll be in a strong position to evaluate whether it’s time to take things in-house or continue reaping the benefits of SaaS solutions.</p>



<h3>Conclusion</h3>



<p>In a world where AI and low-code/no-code platforms are rapidly transforming how we build software, it’s tempting to question the future of SaaS. Yet, while these technologies have made development more accessible, they haven&#8217;t replaced the need for SaaS solutions. From allowing businesses to focus on their core competencies, to avoiding the headaches of deployment and infrastructure management, to providing security and compliance at scale, SaaS continues to deliver unmatched value.</p>



<p>Moreover, SaaS offers the cost-efficiency and scalability that growing businesses need to thrive. While some may eventually reach the point where in-house solutions become more practical, for the majority of organizations, SaaS remains the most efficient and flexible path forward. AI may accelerate innovation, but SaaS is the proven foundation that allows businesses to move fast, stay secure, and scale with confidence.</p>



<p>SaaS isn’t dead—far from it. It’s evolving alongside new technologies and continues to be the backbone of countless business operations.</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/10/02/is-saas-dead-i-dont-think-so/">Is SaaS Dead? I Don’t Think So!</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/10/02/is-saas-dead-i-dont-think-so/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Smart To-Do Creator: Combining the ease of Azure Logic Apps and the power of Generative AI</title>
		<link>https://gauravmantri.com/2024/03/18/smart-to-do-creator-combining-the-ease-of-azure-logic-apps-and-the-power-of-generative-ai/</link>
					<comments>https://gauravmantri.com/2024/03/18/smart-to-do-creator-combining-the-ease-of-azure-logic-apps-and-the-power-of-generative-ai/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Sun, 17 Mar 2024 18:52:42 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Azure Logic Apps]]></category>
		<category><![CDATA[Prompt Engineering]]></category>
		<category><![CDATA[Semantic Kernel]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1964</guid>

					<description><![CDATA[<p>In this post, I am going to talk about how I built a smart to-do creator using Azure Logic Apps and Generative AI (Azure OpenAI service). I recently took a course of LinkedIn Learning about Azure Logic Apps (called Azure Logic Apps &#8211; Building solutions for data and integration) and thought I would put my [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/03/18/smart-to-do-creator-combining-the-ease-of-azure-logic-apps-and-the-power-of-generative-ai/">Smart To-Do Creator: Combining the ease of Azure Logic Apps and the power of Generative AI</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this post, I am going to talk about how I built a smart to-do creator using Azure Logic Apps and Generative AI (Azure OpenAI service). I recently took a course of LinkedIn Learning about Azure Logic Apps (called <a rel="noreferrer noopener" href="https://www.linkedin.com/learning/azure-logic-apps/building-solutions-for-data-and-integration" target="_blank">Azure Logic Apps &#8211; Building solutions for data and integration</a>) and thought I would put my learning to a good use by building something small yet useful (best way to learn new is by building something using it, right?) and that&#8217;s how I ended up creating this tool.</p>



<h2>What is Smart To-Do Creator?</h2>



<p>Simply put, <strong>this tool basically creates tasks out of your emails</strong>. It reads the contents of your email and then somehow infers that the sender wants you to do some task for them, creates a task, and then saves it in Microsoft To-Do application.</p>



<h2>Architecture</h2>



<p>Architecture for this application is fairly simple and is shown in the picture below.</p>



<figure class="wp-block-image size-large"><img src="https://gauravmantri.com/wp-content/uploads/2024/03/Untitled-Diagram.drawio-2.png" alt="" class="wp-image-1967" srcset="https://gauravmantri.com/wp-content/uploads/2024/03/Untitled-Diagram.drawio-2.png 602w, https://gauravmantri.com/wp-content/uploads/2024/03/Untitled-Diagram.drawio-2-300x269.png 300w" sizes="(max-width: 602px) 100vw, 602px" /></figure>



<p>It makes use of Azure Logic Apps and Azure OpenAI Service.</p>



<p>Azure Logic App is connected to your Office 365 account using <strong>Outlook connector</strong>. As soon as an email comes in, it extracts metadata about the email (like sender, subject and body) and send it to an HTTP connector.</p>



<p><strong>HTTP connector</strong> is nothing but an HTTP triggered Azure Function. When it is triggered, it sends the input data to Azure OpenAI and asks it to understand the email and see if a task can be created using the email. </p>



<p>It relies on a Large Language Model (LLM) text comprehension and reasoning capabilities. Through clever prompt engineering, it outputs a JSON object containing the task details like the task subject, description and due date and feeds that to a To-Do connector.</p>



<p><strong>To-Do connector</strong> basically takes this data and creates a task for the user.</p>



<h2>Test</h2>



<p>To test it, I sent myself an email asking me to get some stuff (for me <img src="https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f602.png" alt="😂" class="wp-smiley" style="height: 1em; max-height: 1em;" />) during my upcoming trip to India and surprisingly it worked really well. When the workflow finished, I had a task assigned to me with a meaningful title, a concise description of the task and an expected end date (even though I did not specify an exact date).</p>



<figure class="wp-block-image size-large"><img src="https://gauravmantri.com/wp-content/uploads/2024/03/Screenshot-2024-03-17-at-1.26.50 PM.png" alt="" class="wp-image-1968" srcset="https://gauravmantri.com/wp-content/uploads/2024/03/Screenshot-2024-03-17-at-1.26.50 PM.png 861w, https://gauravmantri.com/wp-content/uploads/2024/03/Screenshot-2024-03-17-at-1.26.50 PM-300x114.png 300w, https://gauravmantri.com/wp-content/uploads/2024/03/Screenshot-2024-03-17-at-1.26.50 PM-768x292.png 768w" sizes="(max-width: 861px) 100vw, 861px" /></figure>



<figure class="wp-block-image size-large"><img src="https://gauravmantri.com/wp-content/uploads/2024/03/Screenshot-2024-03-17-at-1.28.01 PM.png" alt="" class="wp-image-1969" srcset="https://gauravmantri.com/wp-content/uploads/2024/03/Screenshot-2024-03-17-at-1.28.01 PM.png 341w, https://gauravmantri.com/wp-content/uploads/2024/03/Screenshot-2024-03-17-at-1.28.01 PM-173x300.png 173w" sizes="(max-width: 341px) 100vw, 341px" /></figure>



<p>Pretty neat, huh!</p>



<h2>Code</h2>



<p>I stitched together the whole solution in less than 4 hours (out of which I spent about an hour getting Logic App service configured correctly). I am pretty sure that this code can be improved considerably but sharing it nonetheless.</p>



<h3>Workflow Code</h3>


<pre class="brush: plain; title: ; notranslate">
{
    &quot;definition&quot;: {
        &quot;$schema&quot;: &quot;https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#&quot;,
        &quot;actions&quot;: {
            &quot;Add_a_to-do_(V3)&quot;: {
                &quot;inputs&quot;: {
                    &quot;body&quot;: {
                        &quot;body&quot;: {
                            &quot;content&quot;: &quot;&lt;p&gt;@{body('Parse_JSON')['task']}&lt;/p&gt;&quot;,
                            &quot;contentType&quot;: &quot;html&quot;
                        },
                        &quot;dueDateTime&quot;: {
                            &quot;dateTime&quot;: &quot;@body('Parse_JSON')['dueDate']&quot;,
                            &quot;timeZone&quot;: &quot;UTC&quot;
                        },
                        &quot;reminderDateTime&quot;: {
                            &quot;timeZone&quot;: &quot;UTC&quot;
                        },
                        &quot;title&quot;: &quot;@body('Parse_JSON')['title']&quot;
                    },
                    &quot;host&quot;: {
                        &quot;connection&quot;: {
                            &quot;referenceName&quot;: &quot;todo&quot;
                        }
                    },
                    &quot;method&quot;: &quot;post&quot;,
                    &quot;path&quot;: &quot;/lists/@{encodeURIComponent('Some Base64 encoded string')}/tasks&quot;
                },
                &quot;runAfter&quot;: {
                    &quot;Parse_JSON&quot;: [
                        &quot;SUCCEEDED&quot;
                    ]
                },
                &quot;type&quot;: &quot;ApiConnection&quot;
            },
            &quot;HTTP&quot;: {
                &quot;inputs&quot;: {
                    &quot;body&quot;: {
                        &quot;body&quot;: &quot;@{triggerBody()}&quot;,
                        &quot;from&quot;: &quot;@{triggerBody()?['from']}&quot;,
                        &quot;subject&quot;: &quot;@{triggerBody()?['subject']}&quot;
                    },
                    &quot;method&quot;: &quot;POST&quot;,
                    &quot;uri&quot;: &quot;https://myfunctionapp.azurewebsites.net/api/HttpTrigger1&quot;
                },
                &quot;runAfter&quot;: {},
                &quot;runtimeConfiguration&quot;: {
                    &quot;contentTransfer&quot;: {
                        &quot;transferMode&quot;: &quot;Chunked&quot;
                    }
                },
                &quot;type&quot;: &quot;Http&quot;
            },
            &quot;Parse_JSON&quot;: {
                &quot;inputs&quot;: {
                    &quot;content&quot;: &quot;@body('HTTP')&quot;,
                    &quot;schema&quot;: {
                        &quot;$schema&quot;: &quot;http://json-schema.org/draft-04/schema#&quot;,
                        &quot;properties&quot;: {
                            &quot;dueDate&quot;: {
                                &quot;type&quot;: &quot;string&quot;
                            },
                            &quot;task&quot;: {
                                &quot;type&quot;: &quot;string&quot;
                            },
                            &quot;title&quot;: {
                                &quot;type&quot;: &quot;string&quot;
                            }
                        },
                        &quot;required&quot;: [
                            &quot;title&quot;,
                            &quot;task&quot;,
                            &quot;dueDate&quot;
                        ],
                        &quot;type&quot;: &quot;object&quot;
                    }
                },
                &quot;runAfter&quot;: {
                    &quot;HTTP&quot;: [
                        &quot;SUCCEEDED&quot;
                    ]
                },
                &quot;type&quot;: &quot;ParseJson&quot;
            }
        },
        &quot;contentVersion&quot;: &quot;1.0.0.0&quot;,
        &quot;outputs&quot;: {},
        &quot;triggers&quot;: {
            &quot;When_a_new_email_arrives_(V3)&quot;: {
                &quot;inputs&quot;: {
                    &quot;fetch&quot;: {
                        &quot;method&quot;: &quot;get&quot;,
                        &quot;pathTemplate&quot;: {
                            &quot;template&quot;: &quot;/v3/Mail/OnNewEmail&quot;
                        },
                        &quot;queries&quot;: {
                            &quot;fetchOnlyWithAttachment&quot;: false,
                            &quot;folderPath&quot;: &quot;Inbox&quot;,
                            &quot;from&quot;: &quot;email@domain.com&quot;,
                            &quot;importance&quot;: &quot;Any&quot;,
                            &quot;includeAttachments&quot;: false
                        }
                    },
                    &quot;host&quot;: {
                        &quot;connection&quot;: {
                            &quot;referenceName&quot;: &quot;office365&quot;
                        }
                    },
                    &quot;subscribe&quot;: {
                        &quot;body&quot;: {
                            &quot;NotificationUrl&quot;: &quot;@{listCallbackUrl()}&quot;
                        },
                        &quot;method&quot;: &quot;post&quot;,
                        &quot;pathTemplate&quot;: {
                            &quot;template&quot;: &quot;/GraphMailSubscriptionPoke/$subscriptions&quot;
                        },
                        &quot;queries&quot;: {
                            &quot;fetchOnlyWithAttachment&quot;: false,
                            &quot;folderPath&quot;: &quot;Inbox&quot;,
                            &quot;importance&quot;: &quot;Any&quot;
                        }
                    }
                },
                &quot;splitOn&quot;: &quot;@triggerBody()?['value']&quot;,
                &quot;type&quot;: &quot;ApiConnectionNotification&quot;
            }
        }
    },
    &quot;kind&quot;: &quot;Stateful&quot;
}
</pre>


<h3>Function Code</h3>



<p>Here&#8217;s the code for Azure Function. It&#8217;s really crappy code so please do not use it as is <img src="https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" />.</p>


<pre class="brush: plain; title: ; notranslate">
using System.Collections.Generic;
using System.Net;
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
using Newtonsoft.Json;

namespace ToDoListFunctionApp;

public static class HttpTrigger1
{
    private const string _azureOpenAIEndpoint = &quot;https://xxx.openai.azure.com/&quot;;
    private const string _azureOpenAIKey = &quot;aff9ad587352c904832fe6ed932ab30f&quot;;
    private const string _azureOpenAIDeploymentId = &quot;gpt-4-32k&quot;;
    
    [Function(&quot;HttpTrigger1&quot;)]
    public static async Task&lt;HttpResponseData&gt; Run([HttpTrigger(AuthorizationLevel.Function, &quot;get&quot;, &quot;post&quot;)] HttpRequestData req,
        FunctionContext executionContext)
    {
        var logger = executionContext.GetLogger(&quot;HttpTrigger1&quot;);
        logger.LogInformation(&quot;C# HTTP trigger function processed a request.&quot;);

        IncomingMessage message = null;
        using (var streamReader = new StreamReader(req.Body))
        {
            var requestBody = await streamReader.ReadToEndAsync();
            message = JsonConvert.DeserializeObject&lt;IncomingMessage&gt;(requestBody);
        }

        if (string.IsNullOrWhiteSpace(message.From) || string.IsNullOrWhiteSpace(message.Subject) ||
            string.IsNullOrWhiteSpace(message.Body))
        {
            throw new InvalidOperationException();
        }

        var kernel = GetKernel();
        var path = Path.Combine(Directory.GetCurrentDirectory(), &quot;Prompt.yaml&quot;);
        var function = kernel.CreateFunctionFromPromptYaml(await File.ReadAllTextAsync(path),
            new HandlebarsPromptTemplateFactory());
        var openAIPromptSettings = new OpenAIPromptExecutionSettings()
        {
            Temperature = 0
        };        
        var kernelArguments = new KernelArguments(openAIPromptSettings)
        {
            [&quot;current_date&quot;] = DateTime.UtcNow.Date.ToString(&quot;yyyy-MM-ddTHH:mm:ssZ&quot;),
            [&quot;sender&quot;] = message.From,
            [&quot;subject&quot;] = message.Subject,
            [&quot;body&quot;] = message.Body,
        };
        var result = (await kernel.InvokeAsync(function, kernelArguments)).ToString();
        var response = req.CreateResponse(HttpStatusCode.OK);
        response.Headers.Add(&quot;Content-Type&quot;, &quot;text/plain; charset=utf-8&quot;);

        response.WriteString(result);

        return response;
        
    }
    
    private static Kernel GetKernel()
    {
        var azureOpenAIClient =
            new OpenAIClient(new Uri(_azureOpenAIEndpoint), new AzureKeyCredential(_azureOpenAIKey));
        var kernelBuilder = Kernel.CreateBuilder();
        kernelBuilder.AddAzureOpenAIChatCompletion(_azureOpenAIDeploymentId, azureOpenAIClient);
        var kernel = kernelBuilder.Build();
        return kernel;
    }
}

class IncomingMessage
{
    [JsonProperty(PropertyName = &quot;from&quot;)]
    public string From { get; set; }

    [JsonProperty(PropertyName = &quot;subject&quot;)]
    public string Subject { get; set; }
    
    [JsonProperty(PropertyName = &quot;body&quot;)]
    public string Body { get; set; }
}

class TaskDetails
{
    [JsonProperty(PropertyName = &quot;title&quot;)]
    public string Title { get; set; }
    
    [JsonProperty(PropertyName = &quot;task&quot;)]
    public string Task { get; set; }
    
    [JsonProperty(PropertyName = &quot;dueDate&quot;)]
    public string DueDate { get; set; }
}
</pre>


<p>It makes use of Microsoft Semantic Kernel.</p>



<h3>Prompt</h3>



<p>This is the heart and soul of this entire application.</p>


<pre class="brush: plain; title: ; notranslate">
name: ToDoCreator
description: Use this function to analyze user's ask and create a task for the user .
template_format: handlebars
template: |
  &lt;message role=&quot;system&quot;&gt;
  Grounding Rules:
  ================
  - You are an AI assistant specializing in comprehending text and extract meaningful information from the text based on user's ask.
  - Maintain honesty. If uncertain of an answer, respond with, &quot;I apologize, but I currently lack sufficient information to accurately answer your question.&quot;.
  - Uphold user privacy. Do not ask for, store, or share personal data without explicit permission.  
  - Promote inclusivity and respect. Do not engage in or tolerate hate speech, discrimination, or bigotry of any form. Treat all users equally, irrespective of race, ethnicity, religion, gender, age, nationality, or disability.  
  - Respect copyright laws and intellectual property rights. Do not share, reproduce, or distribute copyrighted material without the appropriate authorization.  
  - Provide precise and concise responses. Maintain a respectful and professional tone in all interactions. 
  - Wait for the user's question before providing information. Stay within your domain of expertise - text comprehension and extracting meaningful information from that text.  
  - Ensure responses are up-to-date and accessible. Avoid unnecessary jargon and technical language when possible.
  
  Rules of Engagement:
  ====================
  - User has provided you the information from the email they have received. The information contains three things:
    - 1. Sender information: Name of the person who has sent the email.
    - 2. Subject: Subject of the email.
    - 3. Body: Body of the email.
  - User is expecting you to analyze the body and the subject of the email and identify if the sender wants the user to do something in that email (task).
  - Your job is to create a task based on the information provided to you.
  - Please do not assume anything and only consider the information provided to you below.
  - ALWAYS produce the output in JSON format. The output JSON format should be: {&quot;title&quot;: &quot;task title&quot;, &quot;task&quot;: &quot;task details&quot;, &quot;dueDate&quot;: &quot;task due date&quot;}.
  - You will be provided today's date. Based on that and from the task, infer a due date for the task. If due date cannot be inferred from the task, use a date one week from the today's date as task due date.
  - Task due date must always be outputted in ISO-8601 format (yyyy-MM-ddTHH:mm:ssZ). For example, 2024-01-01T15:00:00Z.
  - MOST IMPORTANTLY, If no task can be created based on the information provided, you must return an empty response. Do not make up the task.
  &lt;/message&gt;
  
  &lt;message role=&quot;user&quot;&gt;
  Today's Date
  ============
  {{current_date}}
  
  Sender
  ======
  {{sender}}
  
  Subject
  =======
  {{subject}}
  
  Body
  ====
  {{body}}
  &lt;/message&gt;
  
  &lt;message role=&quot;system&quot;&gt;
  Considering the information provided to you, please create a task and output it in the following format: {&quot;title&quot;: &quot;task title&quot;, &quot;task&quot;: &quot;task details&quot;, &quot;dueDate&quot;: &quot;task due date&quot;}. If no task can be created, simply return an empty response.
  &lt;/message&gt;
input_variables:
  - name: current_date
    description: current date in yyyy-MM-ddTHH:mm:ssZ format.
    is_required: true
  - name: sender
    description: email sender information.
    is_required: true
  - name: subject
    description: email subject
    is_required: true
  - name: body
    description: email body
    is_required: true
execution_settings:
  default:
    temperature: 0
</pre>


<h2>Summary</h2>



<p>That&#8217;s it for this post. This is the first time I used Azure Logic Apps and I am genuinely impressed! For the first time, I have realized the convenience of No Code/Low Code platform. Combine that with the power of an LLM, we got ourselves a winner!</p>



<p>I will continue to explore this more and share my learnings.</p>



<p>Till then, be well and happy (low/no) coding! </p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/03/18/smart-to-do-creator-combining-the-ease-of-azure-logic-apps-and-the-power-of-generative-ai/">Smart To-Do Creator: Combining the ease of Azure Logic Apps and the power of Generative AI</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/03/18/smart-to-do-creator-combining-the-ease-of-azure-logic-apps-and-the-power-of-generative-ai/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Azure Sidekick – An AI Assistant to Answer Questions About Your Azure Resources (Part III – Lessons Learned)</title>
		<link>https://gauravmantri.com/2024/02/16/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-iii-lessons-learned/</link>
					<comments>https://gauravmantri.com/2024/02/16/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-iii-lessons-learned/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Fri, 16 Feb 2024 00:28:44 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Azure Sidekick]]></category>
		<category><![CDATA[Prompt Engineering]]></category>
		<category><![CDATA[Prompt Patterns]]></category>
		<category><![CDATA[Semantic Kernel]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1960</guid>

					<description><![CDATA[<p>Best way to learn a new technology is by building something (regardless of how big or small it is) with it. This was my primary intent behind building Azure Sidekick. I had so much fun building this and learnt a lot of things along the way. Not only I learnt more about Microsoft Semantic Kernel [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/16/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-iii-lessons-learned/">Azure Sidekick – An AI Assistant to Answer Questions About Your Azure Resources (Part III – Lessons Learned)</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Best way to learn a new technology is by building something (regardless of how big or small it is) with it. This was my primary intent behind building Azure Sidekick.</p>



<p>I had so much fun building this and learnt a lot of things along the way. Not only I learnt more about Microsoft Semantic Kernel and Prompt Engineering, I also picked up a thing or two in .Net as well (<code><a rel="noreferrer noopener" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addkeyedsingleton?view=dotnet-plat-ext-8.0" target="_blank">AddKeyedSingleton</a></code> and <code><a rel="noreferrer noopener" href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1?view=net-7.0" target="_blank">IAsyncEnumerable&lt;T></a></code> to name a few).</p>



<p>In my previous posts about this, I talked about the features and how I built it. You can read those posts <a rel="noreferrer noopener" href="https://gauravmantri.com/2024/02/11/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-introduction/" target="_blank">here</a> and <a rel="noreferrer noopener" href="https://gauravmantri.com/2024/02/14/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-ii-prompt-patterns-more/" target="_blank">here</a>.</p>



<p>In this post I will talk about the lessons learned.</p>



<p>So let&#8217;s begin!</p>



<h2>Lessons Learned</h2>



<p>Here are some of the lessons I learned while developing this tool.</p>



<h3>Writing prompts is hard</h3>



<p>Writing great prompts is really-really hard. An LLM is a black box and we do not know how it is going to respond to our queries. Only solution to this problem is to write really good prompts. That requires one to have subject matter expertise as well as the ability to clearly communicate the ask, which unfortunately not many of us (including yours truly) possess.</p>



<p>Luckily, we can make use of LLMs to help us write great prompts. I wrote about how I used it building this tool in one of my recent posts that you can read <a href="https://gauravmantri.com/2024/01/29/writing-prompts-is-hard-luckily-theres-an-easy-way-out/" target="_blank" rel="noreferrer noopener">here</a>.</p>



<p> You really need to work with the LLM to get the right prompt. I spent countless hours just to get a single prompt working. </p>



<p>For example, when I was working on intent recognition prompt, just to get &#8220;Storage&#8221; intent returned correctly, I started by asking the question like &#8220;Given a question about Azure, how would you know that the question is about Azure Storage?&#8221;. LLM gave me some answer and then I asked follow up questions about its answer like &#8220;what about this&#8221; or &#8220;what about that&#8221; etc. until it gave me something I felt really comfortable with.</p>



<p>This is what it finally looks like:</p>


<pre class="brush: plain; title: ; notranslate">
&lt;intent&gt;
    &lt;name&gt;Storage&lt;/name&gt;
    &lt;description&gt;
    Return this intent if the question:
    - Keywords: Has keywords such as 'Azure Storage', 'Blob Storage', 'Queue Storage', 'File Storage', 'Table Storage', 'Disk Storage', 'Data Lake Storage', 'Backup', or 'Azure Storage Explorer'.
    - Context: Has context regarding Azure's storage capabilities, types of storage, data management, or other aspects specifically related to Azure Storage.
    - Features: Mention of specific features such as security, scalability, redundancy, data replication, or pricing within the Azure Storage ecosystem.
    - Operation or Commands: Asking for details about operations or commands in Azure Storage, like creating a storage account, uploading or downloading data, managing data, etc.
    - Troubleshooting: Looking for troubleshooting tips, specifically seeking solutions for issues or errors encountered while using Azure Storage.
    - Comparisons: Seeking comparison between Azure Storage and other cloud storage services like AWS S3, Google Cloud Storage, etc. and also comparison between various Azure Storage services.
    - Rules: Looking for rules, restrictions and naming conventions applicable to storage accounts, blob containers &amp;amp; blobs, queues &amp;amp; messages, tables &amp;amp; entities and file shares, directories &amp;amp; files.
    - Storage resources: Finding information about storage accounts, blob containers &amp;amp; blobs, queues &amp;amp; messages, tables &amp;amp; entities and file shares, directories &amp;amp; files in an Azure subscription.
    - &quot;How to&quot; questions related to Azure Storage.
    &lt;/description&gt;
&lt;/intent&gt;
</pre>


<h3>Stop thinking like a developer</h3>



<p>Us developers are very rational in our thinking. Somehow (through our experience or otherwise) it is hardwired in our brain that if we write code to add two numbers (and wrote it correctly), it will always give us the correct result. This is probably because we know how the programming languages work we kind of implicitly trust them.</p>



<p>Unfortunately, it is not the case when you are building an AI application. We do not anything about underlying LLM and how it would process our question and that creates a problem for us developers.</p>



<p>To solve this dilemma, stop thinking like a developer. </p>



<p>Do not make any assumptions like if I can write code that adds two numbers and give me proper result, LLM can also do the same. </p>



<p>Provide as much details as possible in the prompt even for the most trivial tasks. Do not leave anything behind.</p>



<p>When you are writing prompts, put on your &#8220;Business Analyst&#8221; hat on and write down each and everything. Think of prompts as writing requirements document for another developer who is going to write an application for you.</p>



<h3>Streaming responses are always better</h3>



<p>After I built the very first version, I made a small video about the application to share it with some folks. At that time, the application waited to receive complete response from the LLM before sending it to the user. As I was recording the video, I immediately realized that it is a very bad user experience (UX).</p>



<p>My first priority after that was to return the responses in a streaming way. It was much-much better UX as the user is immediately seeing the response from the LLM.</p>



<p>If you are building a chatbot kind of an application, please opt for streamed responses wherever possible. Find a hack if you have to, in order to achieve this. You will make your users very happy!</p>



<h3>Don&#8217;t make it a chore</h3>



<p>It&#8217;s more of an advise rather than my learning, but have loads of fun building what you are building (I know, I did). Don&#8217;t make it a chore!</p>



<p>Remember, you are building something because you do not have to but because you want to! There is a huge difference between &#8220;have to&#8221; and &#8220;want to&#8221;.</p>



<p>Because you are learning along the way, don&#8217;t be afraid of making mistakes. But keep on correcting your mistakes and keep on improving as you learn. For example, when I first implemented streaming response, I was not aware of &#8220;IAsyncEnumerable&#8221; and implemented it using events. When I came to know about it, I changed the code to make use of it. The code was much cleaner that way.</p>



<h2>Summary</h2>



<p>That&#8217;s it for this post and this series about Azure Sidekick. By no means I am done with this project (will be implementing planners shortly). As I make significant changes or learn new things, I will share them here.</p>



<p>In the next post, I am going to talk about a nifty little trick with Semantic Kernel I learned recently that could potentially lower your Azure OpenAI bill.</p>



<p>Keep learning and keep building!</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/16/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-iii-lessons-learned/">Azure Sidekick – An AI Assistant to Answer Questions About Your Azure Resources (Part III – Lessons Learned)</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/02/16/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-iii-lessons-learned/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Azure Sidekick – An AI Assistant to Answer Questions About Your Azure Resources (Part II &#8211; Prompt Patterns &#038; More)</title>
		<link>https://gauravmantri.com/2024/02/14/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-ii-prompt-patterns-more/</link>
					<comments>https://gauravmantri.com/2024/02/14/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-ii-prompt-patterns-more/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Wed, 14 Feb 2024 01:47:46 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Azure Sidekick]]></category>
		<category><![CDATA[Prompt Engineering]]></category>
		<category><![CDATA[Prompt Patterns]]></category>
		<category><![CDATA[Semantic Kernel]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1951</guid>

					<description><![CDATA[<p>In my previous post about Azure Sidekick, I gave a general introduction about the tool and its capabilities. If you have not read that post so far, I would strongly encourage you to read that first. You can read that post here. In this post, I will talk about the general flow of the application, [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/14/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-ii-prompt-patterns-more/">Azure Sidekick – An AI Assistant to Answer Questions About Your Azure Resources (Part II &#8211; Prompt Patterns &#038; More)</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In my previous post about Azure Sidekick, I gave a general introduction about the tool and its capabilities. If you have not read that post so far, I would strongly encourage you to read that first. You can read that post <a rel="noreferrer noopener" href="https://gauravmantri.com/2024/02/11/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-introduction/" target="_blank">here</a>.</p>



<p>In this post, I will talk about the general flow of the application, prompt patterns used there and some other things.</p>



<p>So, let&#8217;s start!</p>



<h2>Application Flow</h2>



<p>From the outside, it looks like a very simple application: you ask it a question (about Azure or Storage accounts in your subscription) and then it answers it. However, a lot is happening behind the scenes and the application goes through multiple steps to answer your question.</p>



<p><strong>1. Rephrase/Reword Step</strong>: When you ask a question, first thing the application does is that it tries to rephrase/reword the question by sending your question to LLM. LLM takes in the grounding rules, context and chat history (if available) into consideration and replies back with a rephrased question. </p>



<p><strong>2. Intent Recognition Step</strong>: Next, the rephrased question is sent to the LLM for recognizing the intent of the question. It is a very important step because the routing happens based on the result of this and it also kind of serve as &#8220;input&#8221; guardrails.</p>



<p><strong>3. Answering Step</strong>: This is the final step where based on the intent, appropriate plugins and tools are loaded and LLM provides and answer to the question. </p>



<p>However, depending on the intent there could be multiple steps involved in this step. </p>



<p>For example, if the question is about storage accounts, then the application will fetch the list of storage accounts from the selected subscription and then send that data to the LLM to generate an answer.</p>



<p>Similarly, if the question is about a specific storage account, then the application will first try to extract the storage account name from the question, fetch its properties and then send that data to the LLM to generate  an answer.</p>



<p>I must confess that the code currently does not utilize the full potential of the LLMs especially its reasoning and planning capabilities. This is something I have started looking into recently and hopefully in next few releases, I will incorporate those.</p>



<h2>Prompt Patterns</h2>



<p>Now that you know how the application works, let&#8217;s talk about some of the prompt patterns I have used to build this application. I wrote a long post about prompt patterns sometime back which you can read <a rel="noreferrer noopener" href="https://gauravmantri.com/2023/10/05/prompt-patterns-every-generative-ai-developer-must-know/" target="_blank">here</a>.</p>



<p>Here are the prompt patterns I have used so far:</p>



<h3>Rephrase/Reword</h3>



<h4>What it does?</h4>



<p>As the name suggests, it rephrases or rewords a user&#8217;s question so that it becomes easier for an LLM to understand and process. It makes use of grounding rules, context and chat history (if available) to rephrase a question.</p>



<h4>Why it is needed?</h4>



<p>During the course of development, I found many reasons as to why it is needed:</p>



<ul><li><strong>Incomplete question</strong>: A user may have asked an incomplete question. For example, the user may just write &#8220;Lighthouse&#8221; (when they wanted to ask about Azure Lighthouse). Rephrase prompt will produce an output like &#8220;Please tell me about Azure Lighthouse and its features&#8221;.</li></ul>



<ul><li><strong>Lacking context</strong>: When we converse amongst ourselves, we can somehow understand the context of the question based on the previous questions however LLMs lack that capability. Chat history helps but relying on chat history alone is just not sufficient. Using this prompt pattern helps. For example, let&#8217;s say the user has first asked a question like &#8220;How many storage accounts are there in my subscription?&#8221; and then asks &#8220;Can you name them?&#8221;. Rephrase prompt will produce an output like &#8220;Please give me the names of the storage accounts that are there in my subscription?&#8221;.</li></ul>



<h3>Intent Recognition</h3>



<h4>What it does?</h4>



<p>This prompt pattern is used to recognize the intent of the question i.e. to find out what the question is about. Is it about Azure, Azure Storage or something else, that&#8217;s what is determined using this prompt pattern.</p>



<h4>Why it is needed?</h4>



<p>There are many use cases for using this pattern. In context of this application, this pattern is used for the following things:</p>



<ul><li><strong>Routing</strong>: A user may be asking a general question about Azure, or a question concerning their storage accounts, or a question related to a specific storage account. The user&#8217;s question may be ambiguous or may not even be related to Azure. Using this prompt pattern will help the application identify the intent of the question and route it accordingly.</li><li><strong>Input Guardrails</strong>: This pattern also help us enforce input guardrails. For example, let&#8217;s say you ask a general question like &#8220;What is the capital of India?&#8221;. Even though the LLM knows the answer to the question, because of this pattern, user will be informed that the purpose of this tool is to answer questions about Azure only. This becomes really important if you are building an AI application for your enterprise. You would want to constrain the application only to answer the questions related to your enterprise. If you need a proof of how bad it could be, please see this news about UK based parcel delivery company &#8211; DPD: <a rel="noreferrer noopener" href="https://www.bbc.com/news/technology-68025677" target="_blank">https://www.bbc.com/news/technology-68025677</a>. </li><li><strong>Relevant chat history</strong>: Another use case we found very useful in our application is that we filter the chat history based on the intent of the question and pass that to the prompt.</li></ul>



<h3>Named Entity Recognition/Extraction</h3>



<h4>What it does?</h4>



<p>This pattern is used to recognize and extract the named entities in a question. For example, if the user asks a question like &#8220;Can I host a static website in xyz storage account?&#8221;, this pattern will recognize that the question is about &#8220;xyz&#8221; storage account and extract &#8220;xyz&#8221; it from there.</p>



<h4>Why it is needed?</h4>



<ul><li><strong>Native code execution</strong>: In context of this application, we need this information to execute native code (get the properties of a storage account). </li></ul>



<h2>Grounding Rules</h2>



<p>Next, I want to talk about setting the grounding rules. Grounding rules are very important in any AI application, as they set the overall constraints for your application. You can use these grounding rules to:</p>



<ul><li>Give your application a persona;</li><li>Set the boundaries;</li><li>Enforce legal restrictions;</li><li>Overall tone of the responses etc. </li></ul>



<p>For example, here are the grounding rules we use in the application:</p>


<pre class="brush: plain; title: ; notranslate">
- You are Azure Sidekick, an AI assistant specializing in Azure, tasked with providing accurate and knowledgeable responses to user inquiries about Azure.
- Maintain honesty. If uncertain of an answer, respond with, &quot;I apologize, but I currently lack sufficient information to accurately answer your question.
- Uphold user privacy. Do not ask for, store, or share personal data without explicit permission. 
- Promote inclusivity and respect. Do not engage in or tolerate hate speech, discrimination, or bigotry of any form. Treat all users equally, irrespective of race, ethnicity, religion, gender, age, nationality, or disability.  
- Respect copyright laws and intellectual property rights. Do not share, reproduce, or distribute copyrighted material without the appropriate authorization.  
- Provide precise and concise responses. Maintain a respectful and professional tone in all interactions. 
- Wait for the user's question before providing information. Stay within your domain of expertise - Azure and related services.
- Ensure responses are up-to-date and accessible. Avoid unnecessary jargon and technical language when possible.
</pre>


<h2>Chat History</h2>



<p>Now, let&#8217;s talk about chat history. Simply put, chat history is the history of the conversation between the user and the AI application. Usually, it contains the question asked by the user and the answer given by the application.</p>



<p>Chat history is an important part of any LLM application as it helps setting a proper context for the question being asked and thus it increases the chances of getting better answer to the question.</p>



<p>There are a few things to keep in mind when using chat history in the prompts:</p>



<ul><li><strong>Use context-sensitive chat history</strong>: What I mean by that is use only that portion of chat history which is relevant to the question being asked. For example, if the user is asking a question about storage accounts, only include chat history related to storage in the prompt.</li><li><strong>Avoid unnecessary chat history</strong>: Even with context-sensitive chat history, you may end up with many items in the chat history relevant to that context. However, that does not mean you pass everything to your prompt. Just pick last 3, 5 or 10 context-sensitive chat history items to the prompt.</li></ul>



<p>Following these will ensure that you are not unnecessarily bloating your prompts and thus save on input prompt tokens :), without compromising on the functionality.</p>



<h2>Goal Reminder</h2>



<p>This is an interesting thing I have found. I noticed that when I reminded LLM about their goal, I got better results. The best way to do so is include a summarized goal at the end of the prompt.</p>



<p>For example, this is how I am ending the rephrase prompt:</p>


<pre class="brush: plain; title: ; notranslate">
Considering the information provided to you, 
please rephrase or reword the current question 
to increase its clarity and specificity for a 
language model. Consider identifying the 
key elements or concepts within the question, 
ensuring the language is precise, and avoiding 
any ambiguity or overly complex language. 
Remember to incorporate the context provided by 
the previous questions and answers. 
Your goal is to create a revised question that 
maintains the original intent, but is more 
easily understood by an LLM when considering 
the conversation history.
</pre>


<h2>Summary</h2>



<p>That&#8217;s it for this post. It turned out to be somewhat lengthy but I couldn&#8217;t help it as I had so much to share :). I hope you have enjoyed this post.</p>



<p>In the next (and concluding post about this), I will talk about the lessons learned while building this. Stay tuned for that.</p>



<p>Until then, be well!</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/14/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-ii-prompt-patterns-more/">Azure Sidekick – An AI Assistant to Answer Questions About Your Azure Resources (Part II &#8211; Prompt Patterns &#038; More)</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/02/14/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-part-ii-prompt-patterns-more/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Azure Sidekick &#8211; An AI Assistant to Answer Questions About Your Azure Resources (Introduction)</title>
		<link>https://gauravmantri.com/2024/02/11/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-introduction/</link>
					<comments>https://gauravmantri.com/2024/02/11/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-introduction/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Sun, 11 Feb 2024 01:55:06 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Azure Sidekick]]></category>
		<category><![CDATA[Azure Storage]]></category>
		<category><![CDATA[Semantic Kernel]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1947</guid>

					<description><![CDATA[<p>I am pleased to present to you Azure Sidekick, an AI assistant that can answer questions about the resources running in your Azure Subscriptions. Unlike my other posts which are quite code heavy, in these series of posts about this tool, I will take you through the journey I undertook while building this. I will [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/11/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-introduction/">Azure Sidekick &#8211; An AI Assistant to Answer Questions About Your Azure Resources (Introduction)</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>I am pleased to present to you <strong>Azure Sidekick</strong>, an AI assistant that can answer questions about the resources running in your Azure Subscriptions.</p>



<p>Unlike my other posts which are quite code heavy, in these series of posts about this tool, I will take you through the journey I undertook while building this. I will talk about my motivations behind building this and some things I learned along the way.</p>



<p>In this post, I will talk about what this AI assistant is and what can it do today. I will talk about why I built it and then the tools and technology used to build this.</p>



<p>So, let&#8217;s begin. </p>



<h2>What is Azure Sidekick?</h2>



<p>As mentioned above, Azure Sidekick is an AI assistant that can (<strong>eventually</strong>) answer questions about the resources running in your Azure Subscriptions using natural language.</p>



<p>Currently following scenarios are supported:</p>



<ul><li><strong>General Azure Questions</strong>: It can answer your general Azure questions. For example, you can ask it to explain Azure Lighthouse to you or you can ask it to compare different messaging services available in Azure. For this, it relies on the knowledge of the LLM (so the information may be outdated). In future, I might integrate Bing search so that most current information is available when answering general Azure questions.</li><li><strong>Questions about Storage Accounts</strong>: It can answer questions about storage accounts in an Azure Subscription. For example, you ask it to find storage accounts that are tagged/not-tagged/tagged with specific name/value pairs, or find storage accounts of certain kind etc. Basically, you can ask questions about storage accounts in your Azure Subscription based on its properties.</li><li><strong>Questions about a specific Storage Account</strong>: It can answer questions about a specific storage account in an Azure Subscription. For example, you can ask it to find if a specific storage account can host static website, or find its location etc. Basically, you can ask questions about a specific storage account in your Azure Subscription based on its properties.</li><li><strong>General Azure Storage Questions</strong>: Lastly, you can use it to get answers to general questions related to Azure Storage. For example, you can ask it to provide naming rules for a storage account, or limitations on the number of blob containers you can create etc.</li></ul>



<p>Currently, the scope of this assistant is very limited but one thing to understand is that Azure is very vast with over 200+ services and support for more services will come in due course of time.</p>



<p>I prepared a small video (nothing fancy by any means) about it, that you can watch.</p>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe title="Azure Sidekick - An AI assistant to answer questions about your Azure resources." width="500" height="281" src="https://www.youtube.com/embed/LcUdn8bNCng?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div></figure>



<h2>Why did I build it?</h2>



<p>Simple answer, <strong>to scratch the itch to build an AI assistant from start to end</strong> :). I have been reading a lot about Generative AI and wanted to go through the &#8220;pain&#8221; of building an AI assistant to apply my learnings and to learn new things.</p>



<p>Before building this tool, I had some beliefs (like, what&#8217;s the big deal with prompts) and I was happy to see that some of them were a lot misplaced :). I also got an opportunity to test some of my hypothesis.</p>



<p>Building this tool taught me a lot and those were reflected in some of my recent posts and I will share those in my future posts as well.</p>



<h2>Tools &amp; Technologies </h2>



<p>In its current form, Azure Sidekick is a console application written using <strong>.Net 8</strong>. For Generative AI, it makes use of <strong>Azure OpenAI</strong> (I used a GPT-4 model) and makes use of <strong>Microsoft Semantic Kernel</strong> for AI orchestration.</p>



<p>Sometime back I wrote an introductory post about Microsoft Semantic Kernel and at that time I thought what is the need for it when you have more robust and feature rich orchestration engines like LangChain available (one of my beliefs that got shattered :)). </p>



<p>After using it for some time now, I am pleasantly surprised with the progress it has made and how robust it is. If you are building a Gen AI application using .Net, please give it a serious try. I think you will not be disappointed.</p>



<h2>Source Code</h2>



<p>I am very happy to say that the tool is open source and is available on GitHub: </p>



<p><a rel="noreferrer noopener" href="https://github.com/gmantri/azure-sidekick" target="_blank">https://github.com/gmantri/azure-sidekick</a></p>



<p>My humble request is to try it out and let me know the feedback. It would be even more awesome, if we can collaborate on this.</p>



<h2>Conclusion</h2>



<p>That&#8217;s it for this post. I sincerely hope that you will use this. In the next post, I will talk about some things I learned while building this, so stay tuned for that.</p>



<p>So long!</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/11/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-introduction/">Azure Sidekick &#8211; An AI Assistant to Answer Questions About Your Azure Resources (Introduction)</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/02/11/azure-sidekick-an-ai-assistant-to-answer-questions-about-your-azure-resources-introduction/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Microsoft Semantic Kernel &#8211; Some Tips &#038; Tricks To Get Prompt &#038; Completion Tokens</title>
		<link>https://gauravmantri.com/2024/02/07/microsoft-semantic-kernel-some-tips-tricks-to-get-prompt-completion-tokens/</link>
					<comments>https://gauravmantri.com/2024/02/07/microsoft-semantic-kernel-some-tips-tricks-to-get-prompt-completion-tokens/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Wed, 07 Feb 2024 01:38:52 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Prompt]]></category>
		<category><![CDATA[Prompt Engineering]]></category>
		<category><![CDATA[Semantic Kernel]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1940</guid>

					<description><![CDATA[<p>In my previous post, I talked about how you can get rendered prompts. In this post, I am going to talk about ways to get prompt and completion tokens when using Microsoft Semantic Kernel. What are Tokens? Let&#8217;s first begin with what tokens really are. In very simple terms, a token is the smallest unit [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/07/microsoft-semantic-kernel-some-tips-tricks-to-get-prompt-completion-tokens/">Microsoft Semantic Kernel &#8211; Some Tips &#038; Tricks To Get Prompt &#038; Completion Tokens</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In my previous <a rel="noreferrer noopener" href="https://gauravmantri.com/2024/02/05/microsoft-semantic-kernel-some-tips-tricks-to-get-rendered-prompts/" target="_blank">post</a>, I talked about how you can get rendered prompts. In this post, I am going to talk about ways to get prompt and completion tokens when using Microsoft Semantic Kernel.</p>



<h2>What are Tokens?</h2>



<p>Let&#8217;s first begin with what tokens really are. In very simple terms, <strong>a token is the smallest unit of data that a Large Language Model (LLM) can understand and process</strong>. The data could be text, an image, a video, a sound clip or any other data.</p>



<p>When it comes to text, a token could be a single character (like &#8220;a&#8221;), a partial word (like &#8220;happiness&#8221; in &#8220;unhappiness&#8221;), a full word (like &#8220;apple&#8221;) or even a combination of words. The key thing to remember is that a token is the smallest unit of data that an LLM can understand.</p>



<p>When dealing with tokens, you will come across two terms: Prompt tokens and Completion tokens.</p>



<ul><li><strong>Prompt tokens</strong> are the tokens representing input prompt (i.e. the data being fed to an LLM)</li><li><strong>Completion tokens</strong> are the tokens consumed by an LLM to process the prompt. It represents the data outputted by an LLM.</li></ul>



<h2>Why you should care about Tokens?</h2>



<p>So, the next question is why you should care about them. Simple answer is, you pay for these tokens :). This is how the service providers make money. You are charged for both prompt and completion tokens. The pricing varies based on the service provider and the model you are using.</p>



<p>Because you are paying for the tokens, you have to be extra careful with them. </p>



<p>You should ensure that your prompts are complete (otherwise you will not get a proper result) but concise and to the point so that you don&#8217;t end up breaking the bank. </p>



<p>Through your prompts, you should also put some constraints on the desired output. You have to be very explicit in your prompts about the size and kind  of data you expect an LLM to produce. For example, if you expect that your output should not be more than 500 characters, explicitly mention that.</p>



<h2>How to get prompt and completion tokens?</h2>



<p>So, how do you get prompt and completion tokens in your LLM application built using Microsoft Semantic Kernel? </p>



<p>I will show you fours ways to accomplish that. So, let&#8217;s start!</p>



<h3>1. Hooking into Kernel Events (Obsolete, Not Recommended)</h3>



<p>First way is by hooking into (Semantic) Kernel events. <code><a rel="noreferrer noopener" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.semantickernel.kernel?view=semantic-kernel-dotnet&amp;viewFallbackFrom=netstandard-2.0" target="_blank">Kernel</a></code> in Semantic Kernel exposes a <code><a rel="noreferrer noopener" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.semantickernel.kernel.functioninvoked?view=semantic-kernel-dotnet&amp;viewFallbackFrom=netstandard-2.0" target="_blank">FunctionInvoked</a></code>  event which gets fired when a function is invoked. You can consume this event to get the consumed tokens.</p>



<p>My code would be something like the following:</p>


<pre class="brush: plain; title: ; notranslate">
private Kernel GetKernel()
{
    var kernelBuilder = Kernel.CreateBuilder();
    var deploymentId = &quot;your-azure-openai-deployment-id&quot;;
    AzureOpenAIClient client = GetAzureOpenAIClientSomehow();        
    kernelBuilder.AddAzureOpenAIChatCompletion(deploymentId, client);
    var kernel = kernelBuilder.Build();
    kernel.FunctionInvoked += (sender, args) =&gt;
    {
        var metadata = args.Metadata;
        if (metadata == null || !metadata.ContainsKey(&quot;Usage&quot;)) return;
        var usage = (CompletionsUsage)metadata[&quot;Usage&quot;];
        if (usage == null) return;
        var promptTokens = usage.PromptTokens;
        var completionTokens = usage.CompletionTokens;
        // do something with the tokens
    };
    return kernel;
}

</pre>


<p><strong>However, you should not be using this approach as it has being marked as obsolete in the latest version</strong>. In fact, if you use this approach with version 1.3.0 of Semantic Kernel (which is the most current version at the time of writing this post), you will get a warning about not to use it.</p>



<h3>2. Using Filters (Experimental)</h3>



<p>This is another approach that you can take. I believe this feature was introduced recently and is recommended to use this approach over using kernel events.</p>



<p>Using filters is really easy. You basically create a custom filter class that implements <code>IFunctionFilter</code> interface and then implement <code>OnFunctionInvoking</code> and <code>OnFunctionInvoked</code> methods to suit your requirements. For example, I could simply write the prompt and completion tokens to console.</p>



<p>So my code would be something like:</p>


<pre class="brush: plain; title: ; notranslate">
private class FunctionFilter : IFunctionFilter
{
    public void OnFunctionInvoking(FunctionInvokingContext context)
    {
    }

    public void OnFunctionInvoked(FunctionInvokedContext context)
    {
        var metadata = context.Result.Metadata;
        if (metadata == null || !metadata.ContainsKey(&quot;Usage&quot;)) return;
        var usage = (CompletionsUsage)metadata[&quot;Usage&quot;];
        if (usage == null) return;
        var promptTokens = usage.PromptTokens;
        var completionTokens = usage.CompletionTokens;
        // do something with the tokens
    }
}
</pre>


<p>And this is how I would wire up the filter in the Kernel:</p>


<pre class="brush: plain; title: ; notranslate">
kernel.FunctionFilters.Add(new FunctionFilter());
</pre>


<p>My complete code for kernel would be:</p>


<pre class="brush: plain; title: ; notranslate">
private Kernel GetKernel()
{
    var kernelBuilder = Kernel.CreateBuilder();
    var deploymentId = &quot;your-azure-openai-deployment-id&quot;;
    AzureOpenAIClient client = GetAzureOpenAIClientSomehow();        
    kernelBuilder.AddAzureOpenAIChatCompletion(deploymentId, client);
    var kernel = kernelBuilder.Build();
    kernel.FunctionFilters.Add(new FunctionFilter());
    return kernel;
}
</pre>


<p>Please note that this is still experimental and may change (or even removed) in the future versions.</p>



<h3>3. Using Function Result</h3>



<p>If you are invoking functions in a non-streaming way i.e. you are waiting for the complete result to come back, you can make use of <code><a rel="noreferrer noopener" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.semantickernel.functionresult?view=semantic-kernel-dotnet&amp;viewFallbackFrom=netstandard-2.0" target="_blank">FunctionResult</a></code> to get the tokens.</p>



<p>So my code would be something like:</p>


<pre class="brush: plain; title: ; notranslate">
var function = GetKernelFunctionSomehow();
var kernelArguments = GetKernelArgumentsSomehow();
var result = await kernel.InvokeAsync(function, kernelArguments);
var metadata = result.Metadata;
if (metadata == null || !metadata.ContainsKey(&quot;Usage&quot;)) return;
var usage = (CompletionsUsage)metadata[&quot;Usage&quot;];
if (usage == null) return;
promptTokens = usage.PromptTokens;
completionTokens = usage.CompletionTokens;
// do something with tokens
</pre>


<h3>4. Using 3rd Party Library</h3>



<p>Approach 3 above works great if you are waiting for the entire response to come to your application however what I have noticed is that the user experience (UX) is not great in this case.</p>



<p>LLMs sends the response in a streaming way that is they spit out (partial) response as it becomes available and if possible, you should try to stream that response back to the user.</p>



<p>However, approach 3 above would not work in that case. From what I am told, Azure OpenAI does not even return the token usage as part of their response in this case and hence Semantic Kernel also does not provide this information.</p>



<p>In this case, what you can do is make use of 3rd party libraries. One such library that I have used is <code><a rel="noreferrer noopener" href="https://github.com/tryAGI/Tiktoken" target="_blank">Tiktoken</a></code>. You can use it to calculate the token consumption. There are many other libraries available like it.</p>



<p>So my code would be something like:</p>


<pre class="brush: plain; title: ; notranslate">
var prompt = GetPromptSomehow();
var function = GetKernelFunctionSomehow();
var kernelArguments = GetKernelArgumentsSomehow();
var result = kernel.InvokeStreamingAsync(function, kernelArguments);
StringBuilder responseStringBuilder = new StringBuilder();
await foreach (var item in result)
{
    var response = item.ToString();
    // store the partial response. We will use it in the end to calculate prompt and completion tokens.
    responseStringBuilder.Append(response);
}
var answer = responseStringBuilder.ToString();
var encodingForModel = Tiktoken.Encoding.TryForModel(&quot;model type e.g. gpt-4, gpt-3.5-turbo or gpt-35-turbo&quot;);
promptTokens = encodingForModel.CountTokens(prompt);
completionTokens = encodingForModel.CountTokens(answer);
// do something with tokens
</pre>


<h2>Summary</h2>



<p>That&#8217;s it for this post. I hope you have found it useful. My recommendation would be to use #3 or #4 approach based on your scenario (non-streaming v/s streaming). You can use #2 but definitely stay away from #1.</p>



<p>I would like to end this post with the same bit of warning I gave in my previous post: Semantic Kernel (and in general AI tools) are changing very rapidly (quite evident from the fact that kernel events are being deprecated within a few minor releases). I would highly recommend referencing official documentation for the most current functionality.</p>



<p>In the next post I will talk about the AI assistant I have been building. A lot of my learning came from building that AI assistant. I am pretty excited about it and can&#8217;t wait to share more with you.</p>



<p>Until then, Happy Coding!</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/07/microsoft-semantic-kernel-some-tips-tricks-to-get-prompt-completion-tokens/">Microsoft Semantic Kernel &#8211; Some Tips &#038; Tricks To Get Prompt &#038; Completion Tokens</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/02/07/microsoft-semantic-kernel-some-tips-tricks-to-get-prompt-completion-tokens/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Microsoft Semantic Kernel &#8211; Some Tips &#038; Tricks To Get Rendered Prompts</title>
		<link>https://gauravmantri.com/2024/02/05/microsoft-semantic-kernel-some-tips-tricks-to-get-rendered-prompts/</link>
					<comments>https://gauravmantri.com/2024/02/05/microsoft-semantic-kernel-some-tips-tricks-to-get-rendered-prompts/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Mon, 05 Feb 2024 04:01:49 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Prompt Engineering]]></category>
		<category><![CDATA[Semantic Kernel]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1934</guid>

					<description><![CDATA[<p>When you start building a new AI application, most likely you start with a very simple prompt where you write everything you need to do in that prompt only. However, as the application grows, you write more prompts and that&#8217;s when you start templatizing your prompts by extracting things that are common across all your [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/05/microsoft-semantic-kernel-some-tips-tricks-to-get-rendered-prompts/">Microsoft Semantic Kernel &#8211; Some Tips &#038; Tricks To Get Rendered Prompts</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>When you start building a new AI application, most likely you start with a very simple prompt where you write everything you need to do in that prompt only.</p>



<p>However, as the application grows, you write more prompts and that&#8217;s when you start templatizing your prompts by extracting things that are common across all your prompts and pass them as template variables to the prompts. </p>



<p>This leads to better manageability of the prompts but you lose the clarity as to what gets sent to the LLM. </p>



<p>Because now the prompt is a template with a number of template parameters, simply by looking at the prompt text will not tell you, what is being actually sent to the LLM. Furthermore, you may want to log the prompts (and response from LLM) somewhere in your system so that you can analyze the performance of these prompts. For these reasons, you would want to have access to the prompts that are being sent to the LLM,</p>



<p>In this post, I will show you three ways in Microsoft Semantic Kernel using which you find out the exact prompt that is being sent to the LLM for processing.</p>



<h2>Sample Prompt</h2>



<p>Let&#8217;s consider the following prompt that I wrote for an application I am building. BTW, I wrote this prompt with the help of an LLM <img src="https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> (you can read all about it <a href="https://gauravmantri.com/2024/01/29/writing-prompts-is-hard-luckily-theres-an-easy-way-out/" target="_blank" rel="noreferrer noopener">here</a>). I am writing my prompts in YAML format.</p>


<pre class="brush: plain; title: ; notranslate">
name: Rephrase
description: Use this function to reword an unclear question, considering previous context, for better comprehension.
template_format: handlebars
template: |
  &lt;message role=&quot;system&quot;&gt;
  Grounding Rules:
  ================
  {{#each grounding_rules}}
  - {{this}}
  {{/each}}
  
  The user has asked a question that may not be clear in its current form and may rely on the context of multiple 
  previous questions and answers. Your task is to rephrase or reword the question, taking into account the conversation history if available, 
  to improve its clarity for a Language Model (LLM) to answer it.
  
  Conversation History:
  ====================
  {{#each chat_history}}
  Question: {{Question}}
  Answer: {{Answer}}
  {{/each}}
  &lt;/message&gt;
  
  Current Question:
  =================
  &lt;message role=&quot;user&quot;&gt;{{question}}&lt;/message&gt;
  
  &lt;message role=&quot;system&quot;&gt;
  Considering the information provided to you, please rephrase or reword the current question to increase its clarity and 
  specificity for a language model. Consider identifying the key elements or concepts within the question, ensuring the 
  language is precise, and avoiding any ambiguity or overly complex language. Remember to incorporate the context provided 
  by the previous questions and answers. Your goal is to create a revised question that maintains the original intent, 
  but is more easily understood by an LLM when considering the conversation history.
  &lt;/message&gt;
input_variables:
  - name: question
    description: user question
    is_required: true
  - name: grounding_rules
    description: grounding rules for AI model to behave
    is_required: true
  - name: chat_history
    description: chat history
    is_required: true
execution_settings:
  default:
    temperature: 0
</pre>


<p>As you can see, my prompt template contains some template variables like <code>question</code>, <code>grounding_rules</code>, and <code>chat_history</code> that I am passing to the prompt. Semantic Kernel in turn parses the YAML, replaces these template variables with values that I pass in and then sends that prompt to the LLM.</p>



<h2>Solution</h2>



<p>So, how do we get the prompts. As I mentioned above, I will show you three ways by which you can get this information.</p>



<h3>1. Hooking into Kernel Events (Obsolete, Not Recommended)</h3>



<p>First way is by hooking into (Semantic) Kernel events. <code><a rel="noreferrer noopener" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.semantickernel.kernel?view=semantic-kernel-dotnet&amp;viewFallbackFrom=netstandard-2.0" target="_blank">Kernel</a></code> in Semantic Kernel exposes a <code><a rel="noreferrer noopener" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.semantickernel.kernel.promptrendered?view=semantic-kernel-dotnet" target="_blank">PromptRendered</a></code> event which gets fired when a prompt is rendered. You can consume this event to get the rendered prompt.</p>



<p>Your code would be something like the following:</p>


<pre class="brush: plain; title: ; notranslate">
private Kernel GetKernel()
{
    var kernelBuilder = Kernel.CreateBuilder();
    var deploymentId = &quot;your-azure-openai-deployment-id&quot;;
    AzureOpenAIClient client = GetAzureOpenAIClientSomehow();        
    kernelBuilder.AddAzureOpenAIChatCompletion(deploymentId, client);
    var kernel = kernelBuilder.Build();
    kernel.PromptRendered += (sender, args) =&gt;
    {
        Console.WriteLine($&quot;Rendered prompt: {args.RenderedPrompt}&quot;);
    };
    return kernel;
}
</pre>


<p><strong>However, you should not be using this approach as it has being marked as obsolete in the latest version</strong>. In fact, if you use this approach with version 1.3.0 of Semantic Kernel (which is the most current version at the time of writing this post), you will get a warning about not to use it.</p>



<h3>2. Use Filters (Experimental)</h3>



<p>This is another approach that you can take. I believe this feature was introduced recently and is recommended to use this approach over using kernel events.</p>



<p>Using filters is really easy. You basically create a custom filter class that implements <code>IPromptFilter</code> interface and then implement <code>PromptRendering</code> and <code>PromptRendered</code> methods to suit your requirements. For example, I could simply write the rendering and rendered prompts to console.</p>



<p>So my code would be something like:</p>


<pre class="brush: plain; title: ; notranslate">
private class PromptFilter : IPromptFilter
{
    public void OnPromptRendering(PromptRenderingContext context)
    {
    }

    public void OnPromptRendered(PromptRenderedContext context)
    {
        var prompt = context.RenderedPrompt;
        Console.WriteLine($&quot;Rendered prompt: ${prompt}&quot;);
    }
}
</pre>


<p>And this is how I would wire up the filter in the kernel:</p>


<pre class="brush: plain; title: ; notranslate">
kernel.PromptFilters.Add(new PromptFilter());
</pre>


<p>My complete code for kernel would be:</p>


<pre class="brush: plain; title: ; notranslate">
private Kernel GetKernel()
{
    var kernelBuilder = Kernel.CreateBuilder();
    var deploymentId = &quot;your-azure-openai-deployment-id&quot;;
    AzureOpenAIClient client = GetAzureOpenAIClientSomehow();        
    kernelBuilder.AddAzureOpenAIChatCompletion(deploymentId, client);
    var kernel = kernelBuilder.Build();
    kernel.PromptFilters.Add(new PromptFilter());
    return kernel;
}
</pre>


<p>Please note that this is still experimental and may change (or even removed) in the future versions.</p>



<h3>3. Manual Way</h3>



<p>Above 2 approaches would work great (though use only the 2nd approach and not the 1st one) however at times you would want to get the prompt inline in your application flow and not outside of it. </p>



<p>For example, the application I am building required me to calculate the prompt and completion tokens and send those back to the user as part of the response.</p>



<p>If your application has this kind of requirement, you can manually create the prompt from the prompt template by passing the arguments.</p>



<p>Here&#8217;s the code to do so:</p>


<pre class="brush: plain; title: ; notranslate">
var promptFileContents = await File.ReadAllTextAsync(promptFilePath);
var promptTemplateConfig = KernelFunctionYaml.ToPromptTemplateConfig(promptFileContents);
var factory = new HandlebarsPromptTemplateFactory();
if (!factory.TryCreate(promptTemplateConfig, out var promptTemplate)) throw new InvalidOperationException(&quot;Unable to create prompt template.&quot;);
var prompt = await promptTemplate.RenderAsync(kernel, kernelArguments);
</pre>


<p>Here, what I am doing is first reading the entire prompt template YAML file and then creating a <code><a rel="noreferrer noopener" href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.semantickernel.prompttemplateconfig?view=semantic-kernel-dotnet&amp;viewFallbackFrom=netstandard-2.0" target="_blank">PromptTemplateConfig</a></code> from it. Because my prompt template uses handlebars templating, I am creating a <code>HandlebarsPromptTemplateFactory</code> and extracting the prompt template out of my prompt template configuration. I would then render the prompt by passing the kernel and the arguments.</p>



<p>The advantage of this approach is that I can get my prompt inline with my code flow and then use it any way I see fit.</p>



<p>However, because this approach parses the raw YAML file, it will not work if your prompt template calls other functions (say for example, calling a native function) inside it. So, please use this approach cautiously.</p>



<h2>Summary</h2>



<p>That&#8217;s it for this post. I hope you have found the information useful. Semantic Kernel (and in general AI tools) are changing very rapidly (quite evident from the fact that kernel events are being deprecated within a few minor releases), I would highly recommend referencing official documentation for the most current functionality.</p>



<p>Happy Coding!</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/02/05/microsoft-semantic-kernel-some-tips-tricks-to-get-rendered-prompts/">Microsoft Semantic Kernel &#8211; Some Tips &#038; Tricks To Get Rendered Prompts</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/02/05/microsoft-semantic-kernel-some-tips-tricks-to-get-rendered-prompts/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Writing prompts is hard. Luckily, there&#8217;s an easy way out!</title>
		<link>https://gauravmantri.com/2024/01/29/writing-prompts-is-hard-luckily-theres-an-easy-way-out/</link>
					<comments>https://gauravmantri.com/2024/01/29/writing-prompts-is-hard-luckily-theres-an-easy-way-out/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Mon, 29 Jan 2024 00:06:18 +0000</pubDate>
				<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Large Language Models]]></category>
		<category><![CDATA[Prompt Engineering]]></category>
		<category><![CDATA[LLM]]></category>
		<category><![CDATA[Prompt]]></category>
		<category><![CDATA[Prompt Patterns]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1927</guid>

					<description><![CDATA[<p>In any Generative AI application, prompts are the heart and soul of the application. To get the most out of an LLM, every Gen AI developer must write effective prompts. Problem But writing prompts is hard! Believe me, it is hard :). In fact, if you want to torture a developer, ask them to write [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/01/29/writing-prompts-is-hard-luckily-theres-an-easy-way-out/">Writing prompts is hard. Luckily, there&#8217;s an easy way out!</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In any Generative AI application, prompts are the heart and soul of the application. To get the most out of an LLM, every Gen AI developer must write effective prompts.</p>



<h2>Problem</h2>



<p>But writing prompts is hard! Believe me, it is hard :). In fact, if you want to torture a developer, ask them to write prompts :D.</p>



<p>In order to write effective prompts, as a prompt writer:</p>



<ul><li>You must have subject matter expertise.</li><li>You must think very clearly and</li><li>You must be able to communicate your thoughts very clearly as well in your prompts.</li></ul>



<p>And, there in lies the problem.</p>



<p>I read a book on UX some time back and something that mentioned there stayed with me:</p>



<blockquote class="wp-block-quote"><p>The problem with developers designing the UX of an application is that if they think they can use it, everybody else can.</p></blockquote>



<p>This applies very well to us developers as well when we are building Gen AI applications. When we write prompts, we think as if we are going to answer the user questions and the same gets reflected in our prompts as well.</p>



<p>Recently, I was building an application where I want an LLM to rephrase the question asked by the user. As a developer, this is what I came up with:</p>


<pre class="brush: plain; title: ; notranslate">
You are an AI assistant expert in Azure and language comprehension.

Please rephrase the following question so that it is clear for you to answer.

While rephrasing, please remember the following things:
- It is ok to return the original question if it is already clear.
- Please use chat history given below to better rephrase the question.
 Chat history contains previous questions asked by the user.
- Do not answer the question while rephrasing.

Begin Chat History
==================
Question: User question
Response: LLM response
End Chat History
==================
User question: &quot;How many storage accounts?&quot;
</pre>


<p>As a developer, I thought this should be good enough for an LLM to answer. Unfortunately, that was not the case. At times, this would work but more than often I saw some random text outputted by the LLM.</p>



<p>So, how do we solve this?</p>



<h2>Solution: Use LLM to create the prompt</h2>



<p>Then it dawned on me, why not ask LLM to create the prompt. It makes sense to me for multiple reasons:</p>



<ul><li>LLMs are the subject matter expert as they are trained on all kinds of data that we (as developers) may not be aware of.</li><li>When properly instructed and controlled, they can not only think clearly but also communicate properly as well.</li><li>Most importantly, ultimately it is the LLM that is going to answer the prompt so it would be completely logical for it to write the prompt in a language and include all the necessary details so that it can understand when it comes to executing the prompt.</li></ul>



<h2>Steps</h2>



<p>Here are the steps I took for asking an LLM to come up with a prompt.</p>



<h3>Persona Setup</h3>



<p>I started by giving my LLM a persona (and stroked their ego a bit :)). This is what I did:</p>


<pre class="brush: plain; title: ; notranslate">
You are the world's best prompt engineer. 
In fact, no one can write prompts like you do.
</pre>


<h3>Describe the Ask</h3>



<p>Then, I clearly described the ask. At first, I provided very basic instruction like &#8220;<em>Write a prompt to rephrase a question</em>&#8221; and it came up with something like &#8220;<em>Craft a prompt that ingeniously transforms a direct query into an artful, indirect expression</em>&#8220;.</p>



<p>So, I put some more thoughts into it and came up with the following:</p>


<pre class="brush: plain; title: ; notranslate">
You are helping an AI developer with writing prompts. 
AI developer wants to write a prompt that would rephrase a 
question asked by the user of the application. 
User would ask a question that may or may not be clear. 
What user wants to do is use an LLM to rephrase/reword 
the question so that it becomes clear for the LLM to answer the rephrased/reworded question. 
Please be as comprehensive as possible when writing a prompt for this.
</pre>


<p>and this is what LLM came up with:</p>


<pre class="brush: plain; title: ; notranslate">
Title: Reframing and Clarifying User Questions for Improved 
LLM Understanding

Prompt:

An application user has asked a question that may not be clear in 
its current form. Your task is to rephrase or reword the question 
in a way that improves its clarity for a Language Model (LLM) 
to answer it.

Given Question: &quot;{user_question}&quot;

Please rephrase or reword this question to increase its clarity 
and specificity for a language model. Consider identifying the 
key elements or concepts within the question, ensuring the 
language is precise, and avoiding any ambiguity or overly 
complex language. Your goal is to create a revised question 
that maintains the original intent, but is more easily 
understood by an LLM.

Revised Question: &quot;&quot;
</pre>


<p>Much better, right?</p>



<p>However, I forgot to include about chat history so modified the prompt again like this:</p>


<pre class="brush: plain; title: ; notranslate">
Now let's say the question is based on some previous questions. 
For example, the user may have previously asked 
&quot;What is the capital of India&quot; to which LLM has 
responded with &quot;New Delhi&quot;. 
Now the user has asked &quot;How many states are in there&quot;. 
This question in itself is not complete and the previous 
question will be included in the chat history. 
Based on this information, can you rewrite the prompt?
</pre>


<p>LLM, replied back with the following:</p>


<pre class="brush: plain; title: ; notranslate">
Title: Contextual Rewording of User Questions for LLM Understanding

Prompt:

An application user has asked a question that may not be clear in 
its current form and also relies on the context of previous 
questions and answers. Your task is to rephrase or reword the question, taking into account the previous conversation history, 
to improve its clarity for a Language Model (LLM) to answer it.

Previous Question and Answer:
Question: &quot;{previous_question}&quot;
Answer: &quot;{previous_answer}&quot;

Current Question: &quot;{user_question}&quot;

Given this context, please rephrase or reword the current question 
to increase its clarity and specificity for a language model. 
Consider identifying the key elements or concepts within the 
question, ensuring the language is precise, and avoiding any 
ambiguity or overly complex language. Remember to incorporate 
the context provided by the previous question and answer. 
Your goal is to create a revised question that maintains the 
original intent, but is more easily understood by an LLM 
when considering the previous conversation.

Revised Question: &quot;&quot;
</pre>


<p>I realized that there can be multiple questions in the chat history that I did not mention in the previous prompt, so I submitted another prompt:</p>


<pre class="brush: plain; title: ; notranslate">
Now assume there are many previous questions and answers. Please revise the prompt based on this information.
</pre>


<p>LLM, graciously obliged me with the following:</p>


<pre class="brush: plain; title: ; notranslate">
Title: Contextual Rewording of User Questions Based on Conversation History for LLM Understanding

Prompt:

An application user has asked a question that may not be clear in its current form and relies on the context of multiple previous questions and answers. Your task is to rephrase or reword the question, taking into account the conversation history, to improve its clarity for a Language Model (LLM) to answer it.

Conversation History:
&quot;{previous_questions_and_answers}&quot;

Current Question: &quot;{user_question}&quot;

Given this context, please rephrase or reword the current question to increase its clarity and specificity for a language model. Consider identifying the key elements or concepts within the question, ensuring the language is precise, and avoiding any ambiguity or overly complex language. Remember to incorporate the context provided by the previous questions and answers. Your goal is to create a revised question that maintains the original intent, but is more easily understood by an LLM when considering the conversation history.

Revised Question: &quot;&quot;
</pre>


<p>So, so much better than my original prompt :).</p>



<h3>Test</h3>



<p>I was quite satisfied with the prompt. However before I could use it in the application, I wanted to see if the prompt is working fine. </p>



<p>So I used LLM again and fed it some questions and asked it to reply to those questions using the prompt it wrote.</p>



<p>Here&#8217;s what I asked:</p>


<pre class="brush: plain; title: ; notranslate">
Now let's test the prompt above with the following questions. 
Your task is to take each question and run the prompt and 
output both the prompt and the result. 
With each question, include the previous questions and 
responses in the chat history. Here are the questions:  
1. Does xyz storage account has tags?  
2. what is its type?  
3. Where it is located?  
4. What are the services supported by it?  
5. Please give me the storage endpoints. 
6. Does abc storage account has tags?  
7. what is its type?  
8. Where it is located?  
9. What are the services supported by it?
</pre>


<p>And this is how I responded:</p>


<pre class="brush: plain; title: ; notranslate">
Sure, I'll provide you with the prompts and the expected results for all 9 questions. I'll make assumptions for LLM responses to complete the exercise.

1.
Conversation History:
&quot;&quot;
Current Question: &quot;Does xyz storage account has tags?&quot;

Prompt:
Does the storage account named 'xyz' have any assigned tags?

2.
Conversation History:
&quot;Question: Does xyz storage account has tags?
Answer: Yes, the 'xyz' storage account has tags.&quot;
Current Question: &quot;what is its type?&quot;

Prompt:
What is the type of the 'xyz' storage account that has tags?

3.
Conversation History:
&quot;Question: Does xyz storage account has tags?
Answer: Yes, the 'xyz' storage account has tags.
Question: what is its type?
Answer: The 'xyz' storage account is a 'BlockBlobStorage' type.&quot;
Current Question: &quot;Where it is located?&quot;

Prompt:
Where is the 'BlockBlobStorage' type 'xyz' storage account, which has tags, located?

4.
Conversation History:
&quot;Question: Does xyz storage account has tags?
Answer: Yes, the 'xyz' storage account has tags.
Question: what is its type?
Answer: The 'xyz' storage account is a 'BlockBlobStorage' type.
Question: Where it is located?
Answer: The 'xyz' storage account is located in East US.&quot;
Current Question: &quot;What are the services supported by it?&quot;

Prompt:
What are the services supported by the 'xyz' storage account, which is a 'BlockBlobStorage' type and located in East US?

5.
Conversation History:
&quot;Question: Does xyz storage account has tags?
Answer: Yes, the 'xyz' storage account has tags.
Question: what is its type?
Answer: The 'xyz' storage account is a 'BlockBlobStorage' type.
Question: Where it is located?
Answer: The 'xyz' storage account is located in East US.
Question: What are the services supported by it?
Answer: The 'xyz' storage account supports Blob, File, Queue, Table, and Data Lake Gen2 services.&quot;
Current Question: &quot;Please give me the storage endpoints.&quot;

Prompt:
Could you provide the storage endpoints for the 'xyz' storage account which is a 'BlockBlobStorage' type, located in East US, and supports Blob, File, Queue, Table, and Data Lake Gen2 services?

6.
Conversation History:
&quot;&quot;
Current Question: &quot;Does abc storage account has tags?&quot;

Prompt:
Does the storage account named 'abc' have any assigned tags?

7.
Conversation History:
&quot;Question: Does abc storage account has tags?
Answer: Yes, the 'abc' storage account has tags.&quot;
Current Question: &quot;what is its type?&quot;

Prompt:
What is the type of the 'abc' storage account that has tags?

8.
Conversation History:
&quot;Question: Does abc storage account has tags?
Answer: Yes, the 'abc' storage account has tags.
Question: what is its type?
Answer: The 'abc' storage account is a 'BlockBlobStorage' type.&quot;
Current Question: &quot;Where it is located?&quot;

Prompt:
Where is the 'BlockBlobStorage' type 'abc' storage account, which has tags, located?

9.
Conversation History:
&quot;Question: Does abc storage account has tags?
Answer: Yes, the 'abc' storage account has tags.
Question: what is its type?
Answer: The 'abc' storage account is a 'BlockBlobStorage' type.
Question: Where it is located?
Answer: The 'abc' storage account is located in West US.&quot;
Current Question: &quot;What are the services supported by it?&quot;

Prompt:
What are the services supported by the 'abc' storage account, which is a 'BlockBlobStorage' type and located in West US?

</pre>


<p>Not bad, not bad at all!</p>



<h2>Summary</h2>



<p>That&#8217;s it for this post. I hope you have found it useful. Please share your thoughts by providing comments.</p>



<p>Happy Prompting!</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2024/01/29/writing-prompts-is-hard-luckily-theres-an-easy-way-out/">Writing prompts is hard. Luckily, there&#8217;s an easy way out!</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2024/01/29/writing-prompts-is-hard-luckily-theres-an-easy-way-out/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Using OpenAI Function Calling with Microsoft Semantic Kernel</title>
		<link>https://gauravmantri.com/2023/12/31/using-openai-function-calling-with-microsoft-semantic-kernel/</link>
					<comments>https://gauravmantri.com/2023/12/31/using-openai-function-calling-with-microsoft-semantic-kernel/#respond</comments>
		
		<dc:creator><![CDATA[Gaurav]]></dc:creator>
		<pubDate>Sun, 31 Dec 2023 12:22:55 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Azure Open AI]]></category>
		<category><![CDATA[OpenAI]]></category>
		<category><![CDATA[Semantic Kernel]]></category>
		<guid isPermaLink="false">https://gauravmantri.com/?p=1922</guid>

					<description><![CDATA[<p>In this post we are going to see how we can use OpenAI&#8217;s Function Calling feature with Microsoft Semantic Kernel. Context To explain the concepts in this post, let&#8217;s set the context. Let&#8217;s say that you are building an AI application that helps users answer basic questions about Maths (e.g. what is 20% of 100). [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2023/12/31/using-openai-function-calling-with-microsoft-semantic-kernel/">Using OpenAI Function Calling with Microsoft Semantic Kernel</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this post we are going to see how we can use OpenAI&#8217;s Function Calling feature with Microsoft Semantic Kernel.</p>



<h2>Context</h2>



<p>To explain the concepts in this post, let&#8217;s set the context. Let&#8217;s say that you are building an AI application that helps users answer basic questions about Maths (e.g. what is 20% of 100). For the sake of argument, let&#8217;s assume that our AI model is not capable of answering such questions.</p>



<p>So what we are doing to do is that we are going to write some functions which does mathematical operations like Add, Subtract, Multiply, and Division etc. In Semantic Kernel lingo, we are creating some native functions. In OpenAI lingo, we are creating some tools.</p>



<p>Now the challenge is how do we invoke these tools or functions based on the user prompts (which are in natural language, BTW). This is where OpenAI Function Calling comes in handy and we will see how we can use Semantic Kernel for that purpose.</p>



<h2>What is OpenAI Function Calling?</h2>



<p>Let&#8217;s first briefly talk about OpenAI Function Calling. This is a feature which was recently released by OpenAI.</p>



<p>The way I understand it is that you give a prompt in natural language and a list of functions (tools) to OpenAI. OpenAI then tries to find the best functions (tools) suited to fulfill user&#8217;s request and it then returns those function(s) back to the calling program. Not only it returns the function(s), it also tries to extract the arguments from the prompt that are needed to execute that function.</p>



<p>Considering our context, let&#8217;s say the user asks &#8220;<strong>what is 2 + 12</strong>&#8221; in our AI application. We pass this prompt and the list of functions (Add, Subtract, Multiply, and Division etc.) to OpenAI and it returns <strong>Add</strong> back to the calling application. If our Add function has 2 arguments say number1 and number2, OpenAI will also return back these arguments with value for number1 argument as 2 and the value for number2 argument as 12. Your application can then execute that function and return the result (14) to the user.</p>



<p>The way it works (I think) is that OpenAI extracts the intent of the prompt and then semantically compares it with the description of the functions.</p>



<p>Here&#8217;s the code for our Add function:</p>


<pre class="brush: csharp; title: ; notranslate">
    [KernelFunction, Description(&quot;Add two numbers&quot;)]
    public static double Add(
        [Description(&quot;The first number to add&quot;)] double number1,
        [Description(&quot;The second number to add&quot;)] double number2
    )
    {
        return number1 + number2;
    }
</pre>


<p>Here, the description of the function is very important. You must be concise yet very clear about what a function does for this whole thing to work properly.</p>



<p>Now when OpenAI sees the prompt (what is 2 + 12), it somehow infers that the user wants to add 2 numbers and then compares that with the description of the functions and determines that Add function is the most suited function to answer user&#8217;s prompt. It also then maps &#8220;2&#8221; with &#8220;number1&#8221; argument and &#8220;12&#8221; with &#8220;number2&#8221; argument and returns this information.</p>



<p>You can read more about OpenAI Function Calling here: <a href="https://platform.openai.com/docs/guides/function-calling" target="_blank" rel="noreferrer noopener">https://platform.openai.com/docs/guides/function-calling</a>.</p>



<h2>What is Semantic Kernel?</h2>



<p>Simply put, Semantic Kernel is an open-source SDK that helps you build AI applications. You can use C#, Python or Java to write AI applications using Semantic Kernel. </p>



<p>I wrote a post on overview Microsoft Semantic Kernel a few months back when it was first released that you can read here: <a rel="noreferrer noopener" href="https://gauravmantri.com/2023/09/03/microsoft-semantic-kernel-an-overview/" target="_blank">https://gauravmantri.com/2023/09/03/microsoft-semantic-kernel-an-overview/</a>. </p>



<p>You can read more about Semantic Kernel here: <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/" target="_blank" rel="noreferrer noopener">https://learn.microsoft.com/en-us/semantic-kernel/overview/</a>.</p>



<p>Semantic Kernel makes it super easy to make use of OpenAI Function Calling and that&#8217;s what we will see in this post.</p>



<h2>Code</h2>



<p>Let&#8217;s look at the code! I have built a simple console application. </p>



<p>For explanation, I will break the code in small chunks and then we will put the entire code together in the end.</p>



<h3>Plugin</h3>



<p>Let&#8217;s first write our plugin that will contain our functions (tools). Since our AI app deals with Maths, let&#8217;s call it <code>MathPlugin</code>. Here&#8217;s the code for that which I took from <a rel="noreferrer noopener" href="https://github.com/MicrosoftDocs/semantic-kernel-docs/blob/main/samples/dotnet/08-Creating-Functions-For-AI/plugins/MathPlugin.cs" target="_blank">here</a>:</p>


<pre class="brush: csharp; title: ; notranslate">
using System.ComponentModel;
using Microsoft.SemanticKernel;

namespace FunctionCallingWithSemanticKernel.Plugins;

public class MathPlugin
{
    [KernelFunction, Description(&quot;Take the square root of a number&quot;)]
    public static double Sqrt(
        [Description(&quot;The number to take a square root of&quot;)] double number1
    )
    {
        return Math.Sqrt(number1);
    }

    [KernelFunction, Description(&quot;Add two numbers&quot;)]
    public static double Add(
        [Description(&quot;The first number to add&quot;)] double number1,
        [Description(&quot;The second number to add&quot;)] double number2
    )
    {
        return number1 + number2;
    }

    [KernelFunction, Description(&quot;Subtract two numbers&quot;)]
    public static double Subtract(
        [Description(&quot;The first number to subtract from&quot;)] double number1,
        [Description(&quot;The second number to subtract away&quot;)] double number2
    )
    {
        return number1 - number2;
    }

    [KernelFunction, Description(&quot;Multiply two numbers. When increasing by a percentage, don't forget to add 1 to the percentage.&quot;)]
    public static double Multiply(
        [Description(&quot;The first number to multiply&quot;)] double number1,
        [Description(&quot;The second number to multiply&quot;)] double number2
    )
    {
        return number1 * number2;
    }

    [KernelFunction, Description(&quot;Divide two numbers&quot;)]
    public static double Divide(
        [Description(&quot;The first number to divide from&quot;)] double number1,
        [Description(&quot;The second number to divide by&quot;)] double number2
    )
    {
        return number1 / number2;
    }

    [KernelFunction, Description(&quot;Raise a number to a power&quot;)]
    public static double Power(
        [Description(&quot;The number to raise&quot;)] double number1,
        [Description(&quot;The power to raise the number to&quot;)] double number2
    )
    {
        return Math.Pow(number1, number2);
    }

    [KernelFunction, Description(&quot;Take the log of a number&quot;)]
    public static double Log(
        [Description(&quot;The number to take the log of&quot;)] double number1,
        [Description(&quot;The base of the log&quot;)] double number2
    )
    {
        return Math.Log(number1, number2);
    }

    [KernelFunction, Description(&quot;Round a number to the target number of decimal places&quot;)]
    public static double Round(
        [Description(&quot;The number to round&quot;)] double number1,
        [Description(&quot;The number of decimal places to round to&quot;)] double number2
    )
    {
        return Math.Round(number1, (int)number2);
    }

    [KernelFunction, Description(&quot;Take the absolute value of a number&quot;)]
    public static double Abs(
        [Description(&quot;The number to take the absolute value of&quot;)] double number1
    )
    {
        return Math.Abs(number1);
    }

    [KernelFunction, Description(&quot;Take the floor of a number&quot;)]
    public static double Floor(
        [Description(&quot;The number to take the floor of&quot;)] double number1
    )
    {
        return Math.Floor(number1);
    }

    [KernelFunction, Description(&quot;Take the ceiling of a number&quot;)]
    public static double Ceiling(
        [Description(&quot;The number to take the ceiling of&quot;)] double number1
    )
    {
        return Math.Ceiling(number1);
    }

    [KernelFunction, Description(&quot;Take the sine of a number&quot;)]
    public static double Sin(
        [Description(&quot;The number to take the sine of&quot;)] double number1
    )
    {
        return Math.Sin(number1);
    }

    [KernelFunction, Description(&quot;Take the cosine of a number&quot;)]
    public static double Cos(
        [Description(&quot;The number to take the cosine of&quot;)] double number1
    )
    {
        return Math.Cos(number1);
    }

    [KernelFunction, Description(&quot;Take the tangent of a number&quot;)]
    public static double Tan(
        [Description(&quot;The number to take the tangent of&quot;)] double number1
    )
    {
        return Math.Tan(number1);
    }

    [KernelFunction, Description(&quot;Take the arcsine of a number&quot;)]
    public static double Asin(
        [Description(&quot;The number to take the arcsine of&quot;)] double number1
    )
    {
        return Math.Asin(number1);
    }

    [KernelFunction, Description(&quot;Take the arccosine of a number&quot;)]
    public static double Acos(
        [Description(&quot;The number to take the arccosine of&quot;)] double number1
    )
    {
        return Math.Acos(number1);
    }

    [KernelFunction, Description(&quot;Take the arctangent of a number&quot;)]
    public static double Atan(
        [Description(&quot;The number to take the arctangent of&quot;)] double number1
    )
    {
        return Math.Atan(number1);
    }
}
</pre>


<h3>OpenAI Client</h3>



<p>Next, we will create an instance of OpenAI client. You will need the endpoint of your Azure OpenAI service and the key which you can get from Azure Portal (you can also use Azure AD credentials instead of key if you want).</p>


<pre class="brush: plain; title: ; notranslate">
const string AZURE_OPEN_AI_ENDPOINT = &quot;https://xyz.openai.azure.com/&quot;;
const string AZURE_OPEN_AI_KEY = &quot;00000000000000000000000&quot;;
const string AZURE_OPEN_AI_MODEL_ID = &quot;gpt-4-32k&quot;;

// create an instance of OpenAIClient.
var openAIClient = new OpenAIClient(new Uri(AZURE_OPEN_AI_ENDPOINT), new Azure.AzureKeyCredential(AZURE_OPEN_AI_KEY));
</pre>


<h3>Get Kernel</h3>


<pre class="brush: csharp; title: ; notranslate">
Kernel GetKernel()
{
    var kernelBuilder = Kernel.CreateBuilder()
        .AddAzureOpenAIChatCompletion(AZURE_OPEN_AI_MODEL_ID, openAIClient);
	
    var kernel = kernelBuilder.Build();
    kernel.Plugins.AddFromType&lt;MathPlugin&gt;();
    return kernel;
}
</pre>


<p>What we are doing above is creating an instance of Semantic Kernel and then adding our plugin (with all the functions/tools) to that kernel. </p>



<h3>OpenAI Prompt Execution Settings</h3>



<p>Next, we will set the OpenAI prompt execution settings.</p>


<pre class="brush: csharp; title: ; notranslate">
var promptExecutionSettings = new OpenAIPromptExecutionSettings()
{
    ToolCallBehavior = ToolCallBehavior.EnableKernelFunctions,
    Temperature = 0
};
</pre>


<p>There are two things I want to highlight in the code above:</p>



<ol><li><strong>Temperature</strong>: Temperature parameter sets the creativity of the model. Since we want our model to be constrained and give predictive results, we would want this setting to be 0. Try removing this parameter and you will get some really funny results back!</li><li><strong>ToolCallBehavior</strong>: This parameter controls how tools will be called. By setting the value to <code>ToolCallBehavior.EnableKernelFunctions</code>, we want Semantic Kernel to just pass the registered tools to OpenAI and return the matching tool. If we want Semantic Kernel to automatically invoke the matching tool returned by OpenAI, we would want to change this setting to <code>ToolCallBehavior.AutoInvokeKernelFunctions</code>. There are other settings as well for this parameter that you can read <a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.semantickernel.connectors.openai.toolcallbehavior?view=semantic-kernel-dotnet" target="_blank" rel="noreferrer noopener">here</a>.</li></ol>



<h3>Get Tool</h3>



<p>We are going to focus on invoking the function manually, so the code below gets the appropriate tool for the input prompt by using OpenAI Function Calling:</p>


<pre class="brush: csharp; title: ; notranslate">
// select the tool best suited to execute our prompt.
async Task&lt;OpenAIFunctionToolCall?&gt; SelectTool(string prompt)
{
    try
    {
        var chatCompletionService = new AzureOpenAIChatCompletionService(AZURE_OPEN_AI_MODEL_ID, openAIClient!);
        var result = await chatCompletionService.GetChatMessageContentAsync(new ChatHistory(prompt),
            promptExecutionSettings, kernel);
        var functionCall = ((OpenAIChatMessageContent)result).GetOpenAIFunctionToolCalls().FirstOrDefault();

        return functionCall;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        Console.WriteLine(ex.StackTrace);
        return null;
    }
}

</pre>


<p>Here, Semantic Kernel takes our prompt and the functions loaded in the kernel and send them to Open AI. It then parses the response from Open AI and returns the first tool. Simple, isn&#8217;t it?</p>



<p>However, please keep in mind that Open AI may not find a matching tool. This could happen when a user asks an irrelevant question like &#8220;What is the capital of France?&#8221;.</p>



<h3>Execute Function</h3>



<p>Last step would be to execute the function and get the result!</p>


<pre class="brush: csharp; title: ; notranslate">
var function = await SelectTool(prompt);
if (function != null)
{
    // now we try to get the plugin function and the arguments.
    kernel.Plugins.TryGetFunctionAndArguments(function, out KernelFunction? pluginFunction,
        out KernelArguments? arguments);
    Console.WriteLine($&quot;Plugin function: {pluginFunction!.Name}&quot;);
    if (arguments!.Any())
    {
        Console.WriteLine(&quot;Function arguments:&quot;);
        foreach (var argument in arguments!)
        {
            Console.WriteLine($&quot;Argument name: {argument.Key}; Argument value: {argument.Value}&quot;);
        }
    }
    // execute the plugin function.
    var result = await kernel.InvokeAsync(pluginFunction!, arguments);
    Console.WriteLine($&quot;{prompt}: {result.ToString()}&quot;);
}
else
{
    Console.WriteLine(&quot;I'm sorry but I am not able to answer your question. I can only answer simple mathematical questions.&quot;);
}

</pre>


<p>Here, first we are getting the kernel function to invoke along with the arguments (<code>kernel.Plugins.TryGetFunctionAndArguments(function, out KernelFunction? pluginFunction,     out KernelArguments? arguments)</code>) and then executing the function to get the result (<code>var result = await kernel.InvokeAsync(pluginFunction!, arguments)</code>).</p>



<p>That&#8217;s it!</p>



<h3>Complete Code</h3>



<p>Here&#8217;s the complete code:</p>


<pre class="brush: plain; title: ; notranslate">
using Azure.AI.OpenAI;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using FunctionCallingWithSemanticKernel.Plugins;

// Azure OpenAI settings. You can get these settings from portal.
const string AZURE_OPEN_AI_ENDPOINT = &quot;&lt;your-azure-openai-endpoint like https://xyz.openai.azure.com/&gt;&quot;;
const string AZURE_OPEN_AI_KEY = &quot;&lt;your-azure-openai-key like 44444444444444444444444444444444&gt;&quot;;
const string AZURE_OPEN_AI_DEPLOYMENT_ID = &quot;&lt;your-azure-openai-deployment-id like gpt-4-32k&gt;&quot;;

// create an instance of OpenAIClient.
var openAIClient = new OpenAIClient(new Uri(AZURE_OPEN_AI_ENDPOINT), new Azure.AzureKeyCredential(AZURE_OPEN_AI_KEY));

// get the kernel.
var kernel = GetKernel();

// set OpenAI prompt execution settings.
var promptExecutionSettings = new OpenAIPromptExecutionSettings()
{
    ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
    Temperature = 0
};
Console.WriteLine(&quot;Hello, I am an AI assistant that can answer simple math questions.&quot;);
Console.WriteLine(&quot;Please ask me questions like \&quot;What is 2 x 2\&quot; or \&quot;What is sqaure root of 3\&quot; etc.&quot;);
Console.WriteLine(&quot;To quit, simply type quit.&quot;);
Console.WriteLine(&quot;&quot;);
Console.WriteLine(&quot;Now ask me a math question, I am waiting!&quot;);
do
{
    var prompt = Console.ReadLine();
    if (!string.IsNullOrWhiteSpace(prompt))
    {
        if (prompt.ToLowerInvariant() == &quot;quit&quot;)
        {
            Console.WriteLine(&quot;Thank you! See you next time.&quot;);
            break;
        }
        else
        {
            // get the tool/function best suited to execute the function.
            var function = await SelectTool(prompt);
            if (function != null)
            {
                // now we try to get the plugin function and the arguments.
                kernel.Plugins.TryGetFunctionAndArguments(function, out KernelFunction? pluginFunction,
                    out KernelArguments? arguments);
                // execute the plugin function.
                var result = await kernel.InvokeAsync(pluginFunction!, arguments);
                Console.WriteLine($&quot;{prompt}: {result.ToString()}&quot;);
            }
            else
            {
                Console.WriteLine(&quot;I'm sorry but I am not able to answer your question. I can only answer simple math questions.&quot;);
            }
        }
    }
} while (true);


// select the tool best suited to execute our prompt.
async Task&lt;OpenAIFunctionToolCall?&gt; SelectTool(string prompt)
{
    try
    {
        var chatCompletionService = new AzureOpenAIChatCompletionService(AZURE_OPEN_AI_DEPLOYMENT_ID, openAIClient!);
        var result = await chatCompletionService.GetChatMessageContentAsync(new ChatHistory(prompt),
            new OpenAIPromptExecutionSettings()
            {
                ToolCallBehavior = ToolCallBehavior.EnableKernelFunctions,
                Temperature = 0
            }, kernel);
        var functionCall = ((OpenAIChatMessageContent)result).GetOpenAIFunctionToolCalls().FirstOrDefault();

        return functionCall;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        Console.WriteLine(ex.StackTrace);
        return null;
    }
}

// create an instance of Kernel and load all plugins and functions in the Kernel.
Kernel GetKernel()
{
    var kernelBuilder = Kernel.CreateBuilder()
        .AddAzureOpenAIChatCompletion(AZURE_OPEN_AI_DEPLOYMENT_ID, openAIClient);
	
    var kernel = kernelBuilder.Build();
    kernel.Plugins.AddFromType&lt;MathPlugin&gt;();
    return kernel;
}
</pre>


<p>You can download the complete solution from GitHub: <a rel="noreferrer noopener" href="https://github.com/gmantri/function-calling-with-semantic-kernel" target="_blank">https://github.com/gmantri/function-calling-with-semantic-kernel</a>.</p>



<h2>Conclusion</h2>



<p>That&#8217;s it for this post. I hope you will find it useful. If you have any feedback or questions, please feel to provide them.</p>



<p>Happy coding and Happy New Year 2024! </p>
<p>The post <a rel="nofollow" href="https://gauravmantri.com/2023/12/31/using-openai-function-calling-with-microsoft-semantic-kernel/">Using OpenAI Function Calling with Microsoft Semantic Kernel</a> appeared first on <a rel="nofollow" href="https://gauravmantri.com">GauravMantri.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gauravmantri.com/2023/12/31/using-openai-function-calling-with-microsoft-semantic-kernel/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
