<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Xiaoyun Zhang</title>
    <description>The latest articles on DEV Community by Xiaoyun Zhang (@littlelittlecloud).</description>
    <link>https://dev.to/littlelittlecloud</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1778669%2Fee43997f-899f-423d-9d51-0f219d115f81.jpeg</url>
      <title>DEV Community: Xiaoyun Zhang</title>
      <link>https://dev.to/littlelittlecloud</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/littlelittlecloud"/>
    <language>en</language>
    <item>
      <title>LLM Canvas: The Story Behind</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Sun, 17 Aug 2025 05:33:20 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/llm-canvas-the-story-behind-11i6</link>
      <guid>https://dev.to/littlelittlecloud/llm-canvas-the-story-behind-11i6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Project Repository&lt;/strong&gt;: &lt;a href="https://github.com/LittleLittleCloud/llm-canvas" rel="noopener noreferrer"&gt;https://github.com/LittleLittleCloud/llm-canvas&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Project Website&lt;/strong&gt;: &lt;a href="https://littlelittlecloud.github.io/llm-canvas/" rel="noopener noreferrer"&gt;https://littlelittlecloud.github.io/llm-canvas/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Beginning: The Struggle with Linear Logs
&lt;/h2&gt;

&lt;p&gt;The initial inspiration for LLM Canvas came from a very practical pain point I encountered while developing Large Language Model (LLM) applications: how to effectively and clearly record and review LLM conversation histories.&lt;/p&gt;

&lt;p&gt;We know that many LLM conversation flows, especially in complex Agent applications, are not inherently linear. The entire workflow is more like a continuously growing tree, where each LLM call or Agent decision can be a new branch, and the final result is often an integration of wisdom from multiple branches.&lt;/p&gt;

&lt;p&gt;In such scenarios, traditional &lt;code&gt;print&lt;/code&gt; logging methods fall short. Linear text output forces developers to painfully "reconstruct" what should be an intuitive call tree in their minds. When dealing with multi-turn conversations and complex Agent interactions, this experience is undoubtedly painful. You need to guess and restore the model's real thinking path from a long series of flat, sequential records.&lt;/p&gt;

&lt;p&gt;It was then that an idea emerged: if there could be a tool that not only records but also &lt;strong&gt;visualizes&lt;/strong&gt; these messages and clearly &lt;strong&gt;organizes the parent-child and branching relationships between messages&lt;/strong&gt;, then the entire debugging and understanding process would become incredibly intuitive.&lt;/p&gt;

&lt;p&gt;This was the initial idea of LLM Canvas.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Idea to Reality: The Seven-Day Challenge
&lt;/h2&gt;

&lt;p&gt;After having the initial idea, I began thinking about how to make it a reality. I set several core principles for this project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rich UI is Essential&lt;/strong&gt;: To intuitively display multimodal content (such as images) and complex tree structures, a powerful frontend interface was indispensable. I chose React because of its mature ecosystem and excellent handling of complex states and views. Ultimately, I decided to package the frontend as static resources hosted by the Python backend, so users wouldn't need to install additional Node.js environments, greatly lowering the barrier to entry.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python First&lt;/strong&gt;: The core of the LLM ecosystem is in Python, so providing a simple and easy-to-use Python SDK was paramount. All functionality should be accessible through just a few lines of Python code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimal Dependencies&lt;/strong&gt;: I wanted users to be able to complete the entire installation with a single &lt;code&gt;pip install&lt;/code&gt; command. This meant I had to minimize external dependencies as much as possible, creating a truly "plug-and-play" tool.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rapid Validation&lt;/strong&gt;: This was an unvalidated market idea, and I didn't want to invest too much energy in the early stages. I set myself a goal: &lt;strong&gt;with the help of AI programming (Vibe Coding), complete a usable prototype (POC) within 7 days&lt;/strong&gt;. This was both a challenge and a rapid iteration strategy.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fortunately, AI programming tools greatly improved development efficiency, allowing me to focus on implementing core logic. In the end, I completed the first version within the planned 7 days, validating the feasibility of the core concept of "visualized branching conversations."&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Value: Managing Conversations Like Git
&lt;/h2&gt;

&lt;p&gt;If LLM Canvas has one core value, it's definitely &lt;strong&gt;conversation branch management&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When designing the API, I faced a core challenge: how to design an API that could elegantly handle simple linear conversations while also easily managing complex multi-thread conversation integration?&lt;/p&gt;

&lt;p&gt;This required creativity and was work that AI couldn't replace. After much deliberation, I suddenly realized that this need was remarkably similar to Git, the version control tool in software development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Git Repository&lt;/strong&gt; is like a complete conversation &lt;strong&gt;Canvas&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Git Branch&lt;/strong&gt; is like an independent conversation &lt;strong&gt;Thread&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Different branches can develop independently and then be &lt;strong&gt;merged&lt;/strong&gt;, which perfectly mirrors the pattern in LLM applications where multiple Agent branches work in parallel and then consolidate results.&lt;/li&gt;
&lt;li&gt;Most importantly, &lt;strong&gt;Git's distributed nature allows branches to operate in parallel&lt;/strong&gt;, just like how LLM or Agent calls can run concurrently across different conversation paths without blocking each other.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This analogy was a good fit. Git is one of the most familiar tools for programmers, and borrowing its mental model could greatly reduce the learning curve for developers.&lt;/p&gt;

&lt;p&gt;So I designed LLM Canvas's API based on Git's concepts. Developers no longer need to manually manage complex message IDs and parent-child relationships, but can instead focus on the higher-level "conversation threads" just like operating Git branches. Through familiar concepts like &lt;code&gt;checkout&lt;/code&gt; and &lt;code&gt;commit&lt;/code&gt;, they can easily create, switch, and manage complex conversation flows. This is the core idea behind the API design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Forward: From Tool to Interaction Paradigm
&lt;/h2&gt;

&lt;p&gt;For the future, I have two main directions of thinking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, horizontal expansion to create a more universal visualization tool.&lt;/strong&gt;&lt;br&gt;
LLM Canvas was designed with "universality" in mind from the beginning. Currently, we only provide a Python SDK, but the LLM ecosystem is multilingual. Therefore, I will consider adding support for more languages in the future, such as providing official client SDKs for C# and TypeScript developers, making LLM Canvas a standard tool in every LLM developer's debugging toolkit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, vertical deepening to explore new interaction paradigms.&lt;/strong&gt;&lt;br&gt;
The "branched conversations" provided by LLM Canvas is not only a powerful debugging tool, but also represents a novel, non-linear way of interacting with LLMs. Traditional chatbots follow a linear "question-answer" model, while with Canvas, users can start from any conversation node and explore in different directions, like freely navigating through a mind map.&lt;/p&gt;

&lt;p&gt;Therefore, I might build a chatbot directly into the product, allowing users to interact with LLMs directly on the canvas, initiating calls from different branches and different contexts. This would evolve LLM Canvas from a "developer tool" into a more creative "interaction platform," similar to the direction explored by products like Flowith.&lt;/p&gt;

&lt;p&gt;In summary, LLM Canvas's future has both the breadth of being a foundational tool and the depth of becoming an innovative interaction platform. I'm very excited to see it flourish in both directions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Looking back on this development journey, two things stood out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On AI programming experience.&lt;/strong&gt;&lt;br&gt;
I found that as long as I clearly knew what I wanted the AI to do, it could be an incredibly reliable partner. The LLM Canvas project, with its 30,000 lines of code across the frontend and backend, was prototyped in just 7 days with AI's help. And 5 of those were weekdays when I was also juggling a full-time job, so I wasn't even coding at full capacity. This made me realize that AI programming is becoming a new form of core productivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the cost of idea validation.&lt;/strong&gt;&lt;br&gt;
This also made me realize that in the age of AI, the cost of bringing an idea to life has become very low. A project that might have taken months to build in the past can now have a prototype in a week or less. So, if you have a new idea now, consider giving it a try.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;I want to say to all the friends who are interested in LLM Canvas:&lt;/p&gt;

&lt;p&gt;If you've ever felt lost in linear logs and wished for a more intuitive way to understand and debug your LLM applications, welcome to &lt;strong&gt;Try it out&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;LLM Canvas is an open-source project born from real problems. It's still developing and needs the community's strength to help it grow. Every piece of &lt;strong&gt;Feedback&lt;/strong&gt; you provide is valuable, and of course, &lt;strong&gt;Pull Requests are welcome&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;I look forward to exploring a clearer, more intuitive future for LLM development with everyone.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>vibecoding</category>
      <category>ai</category>
    </item>
    <item>
      <title>Build a Weather Chatbot with DeepSeek v3 and OpenAI SDK: A Step-by-Step Guide</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Thu, 09 Jan 2025 11:35:18 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/build-a-weather-chatbot-with-deepseek-v3-and-openai-sdk-a-step-by-step-guide-1d1p</link>
      <guid>https://dev.to/littlelittlecloud/build-a-weather-chatbot-with-deepseek-v3-and-openai-sdk-a-step-by-step-guide-1d1p</guid>
      <description>&lt;p&gt;In this tutorial, we'll walk through creating a weather chatbot using &lt;a href="https://api-docs.deepseek.com/quick_start/pricing/" rel="noopener noreferrer"&gt;DeepSeek v3&lt;/a&gt; and &lt;a href="https://github.com/openai/openai-dotnet" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; SDK for .NET. The complete source code can be found in this &lt;a href="https://github.com/LittleLittleCloud/WeatherChat-DeepSeek" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;.NET SDK (8.0 or later)&lt;/li&gt;
&lt;li&gt;DeepSeek API key (You can sign up for an account with ￥10 credit at &lt;a href="https://deepseek" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a New Console Project
&lt;/h2&gt;

&lt;p&gt;First, let's set up our project by creating a new console application and installing required OpenAI SDK packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; WeatherChatbot
&lt;span class="nb"&gt;cd &lt;/span&gt;WeatherChatbot
dotnet add package OpenAI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up the DeepSeek Client
&lt;/h2&gt;

&lt;p&gt;Instead of using the default OpenAI endpoints, we'll configure our client to use DeepSeek's API. The DeepSeek API is compatible with the OpenAI API, so we can use the OpenAI SDK to connect to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenAIClientOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Set the DeepSeek API endpoint&lt;/span&gt;
    &lt;span class="n"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.deepseek.com"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpenAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ApiKeyCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEEP_SEEK_API_KEY&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// use deepseek-chat model which points to the latest v3 model&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetChatClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deepseek-chat"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defining Function Tools
&lt;/h2&gt;

&lt;p&gt;One of the key features we'll be using is the ability to define "tools" - functions that the language model can call when it needs specific information. We'll create one tool to retreive dummy weather information for a given location:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;getCurrentWeatherTool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateFunctionTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetCurrentWeather&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;functionDescription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Get the current weather in a given location"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;functionParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BinaryData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"The city and state, e.g. Boston, MA"&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="s"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"celsius"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"fahrenheit"&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"The temperature unit to use. Infer this from the specified location."&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"location"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="s"&gt;"""u8.ToArray())
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing the Chat Loop
&lt;/h2&gt;

&lt;p&gt;The heart of our chatbot is the conversation loop. Here's how we handle the interaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initialize the conversation with a user message&lt;/li&gt;
&lt;li&gt;Process the message using the DeepSeek model&lt;/li&gt;
&lt;li&gt;Handle any tool calls requested by the model&lt;/li&gt;
&lt;li&gt;Continue the conversation based on the model's response
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UserChatMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"What's the weather like in San Francisco?"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;requiresAction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;requiresAction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;useToolCall&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Last&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ToolChatMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;useToolCall&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteChatAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Tools&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;getCurrentWeatherTool&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteChatAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Handle different completion scenarios&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FinishReason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ChatFinishReason&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AssistantChatMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ChatFinishReason&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToolCalls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Handle tool calls&lt;/span&gt;
                &lt;span class="c1"&gt;// ...&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle other cases&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requiresAction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing the Weather Functions
&lt;/h2&gt;

&lt;p&gt;For this example, we've implemented simple placeholder functions for getting weather information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetCurrentWeather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"celsius"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Call the weather API here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"The weather in &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; is 72 degrees &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a production environment, you'd want to replace these with actual API calls to weather and location services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the Chatbot
&lt;/h2&gt;

&lt;p&gt;Now that we've set up our chatbot, we can run it and see the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyze9dxfh5jn1tja4uweu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyze9dxfh5jn1tja4uweu.gif" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llm</category>
      <category>deepseek</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Building a Multi-LLM Profanity Detector in C# using StepWise</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Sat, 04 Jan 2025 13:42:22 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/building-a-multi-llm-profanity-detector-in-c-using-stepwise-1p8m</link>
      <guid>https://dev.to/littlelittlecloud/building-a-multi-llm-profanity-detector-in-c-using-stepwise-1p8m</guid>
      <description>&lt;p&gt;This post demonstrates how to create a robust profanity detection workflow using multiple Large Language Models (LLMs) in C# with the StepWise framework. The complete code is available in &lt;a href="https://github.com/LittleLittleCloud/StepWise/blob/main/example/StepWise.Gallery.Agentic/ProfanityDetector.cs" rel="noopener noreferrer"&gt;ProfanityDetector.cs&lt;/a&gt;. You can visit &lt;a href="https://dev.to/littlelittlecloud/stepwise-a-powerful-c-workflow-engine-for-parallel-task-execution-2nc4"&gt;this blog post&lt;/a&gt; to learn more about StepWise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profanity Detector Overview
&lt;/h2&gt;

&lt;p&gt;The profanity detection workflow consists of the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get input text from the user&lt;/li&gt;
&lt;li&gt;Send the text to three different AI models for analysis&lt;/li&gt;
&lt;li&gt;Collect votes from all models&lt;/li&gt;
&lt;li&gt;Make a final determination based on majority voting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What makes this workflow interesting is that it runs multiple AI models in parallel and uses a voting system to increase reliability. Some models might be more sensitive to certain types of content than others, so using multiple models helps provide a more balanced assessment.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/DJXSR2EPCfs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can try-out this workfow at the online demo by clicking the below button&lt;br&gt;
&lt;a href="https://stepwisegallery20241128154731.azurewebsites.net/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FLittleLittleCloud%2FStepWise%2Frefs%2Fheads%2Fmain%2Fasset%2Ftry-live-demo.svg" width="200" height="48"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0a3ued1ikphoapjeqkr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0a3ued1ikphoapjeqkr.png" alt="Image description" width="753" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new StepWise project
&lt;/h2&gt;

&lt;p&gt;To create a new StepWise project, you can use the StepWise template provided by StepWise. You can create a new StepWise project using the following command from dotnet-cli:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// &lt;span class="nb"&gt;install &lt;/span&gt;the StepWise template
dotnet new &lt;span class="nt"&gt;-i&lt;/span&gt; LittleLittleCloud.StepWise.Template

// create a new StepWise project
dotnet new stepwise-console &lt;span class="nt"&gt;-n&lt;/span&gt; PrepareDinnerProject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Define the Workflow
&lt;/h2&gt;

&lt;p&gt;In StepWise, we'll define our workflow as a C# class where each step is represented by a public async method with the &lt;code&gt;Step&lt;/code&gt; attribute. Let's start by creating the &lt;code&gt;ProfanityDetector&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfanityDetector&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ChatClient&lt;/span&gt; &lt;span class="n"&gt;_chatClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ProfanityDetector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Here we use deepseek v3 api for LLM inference because it's cost-effective.&lt;/span&gt;
        &lt;span class="c1"&gt;// You can also replace it with other LLM providers like OpenAI, Claude, etc&lt;/span&gt;
        &lt;span class="n"&gt;_chatClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatClientProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDeepSeekV3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"""
&lt;/span&gt;        &lt;span class="err"&gt;##&lt;/span&gt; &lt;span class="n"&gt;ProfanityDetector&lt;/span&gt;
        &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt; &lt;span class="n"&gt;helps&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;determine&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;given&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;contains&lt;/span&gt; &lt;span class="n"&gt;rude&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;profane&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="s"&gt;""")]
&lt;/span&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;README&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"README is Completed"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding User Input
&lt;/h2&gt;

&lt;p&gt;The first step is to get input from the user. We'll use StepWise's UI input capability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;StepWiseUITextInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Please provide the text you want to check"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing AI Analysis
&lt;/h2&gt;

&lt;p&gt;Next, we'll add three parallel AI analysis steps. Each step will use the same LLM but with independent executions to provide different perspectives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Detect profane language using AI1"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AI1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"""
&lt;/span&gt;        &lt;span class="n"&gt;Please&lt;/span&gt; &lt;span class="n"&gt;determine&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;contains&lt;/span&gt; &lt;span class="n"&gt;rude&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;profane&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;inappropriate&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Answer&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
        &lt;span class="s"&gt;""";
&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;systemMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SystemChatMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_chatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteChatAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;systemMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI2 and AI3 methods follow the same pattern. Each AI step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Depends on the Input step&lt;/li&gt;
&lt;li&gt;Takes the input text as a parameter&lt;/li&gt;
&lt;li&gt;Sends a prompt to the LLM&lt;/li&gt;
&lt;li&gt;Returns a boolean indicating whether profanity was detected&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementing the Voting System
&lt;/h2&gt;

&lt;p&gt;Finally, we'll add the voting step that collects results from all three AI models and makes a final determination:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Collect votes and determine the result"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AI1&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AI2&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AI3&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Voting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AI1&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;ai1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AI2&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;ai2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AI3&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;ai3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ai1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ai2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ai3&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;yesVotes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;noVotes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;yesVotes&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;noVotes&lt;/span&gt; 
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"The text contains rude or profane language"&lt;/span&gt; 
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"The text does not contain rude or profane language"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Voting step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Depends on all three AI analysis steps&lt;/li&gt;
&lt;li&gt;Takes the results as parameters&lt;/li&gt;
&lt;li&gt;Uses simple majority voting to make the final determination&lt;/li&gt;
&lt;li&gt;Returns a human-readable result string&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add ProfanityDetector workflow to StepWise server
&lt;/h2&gt;

&lt;p&gt;After the &lt;code&gt;ProfanityDetector&lt;/code&gt; workflow is defined, we can add it to the StepWise server using the &lt;code&gt;StepWiseClient&lt;/code&gt; instance. This will allow the workflow to be executed in the StepWise UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Program.cs&lt;/span&gt;
&lt;span class="c1"&gt;// ...configure the StepWise server&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stepWiseClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StepWiseClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProfanityDetector&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;stepWiseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWorkflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateFromInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Wait for the host to shutdown&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForShutdownAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above creates a StepWise server and adds the &lt;code&gt;PrepareDinner&lt;/code&gt; workflow to the server. The server will be hosted on &lt;code&gt;http://localhost:5123&lt;/code&gt; by default. You can visit the URL to see the StepWise UI and execute the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This post demonstrated how to build a multi-LLM profanity detector in C# using the StepWise framework. By combining multiple AI models and using a voting system, we can create a more robust and reliable profanity detection workflow.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Create PrepareDinner workfow in StepWise</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Mon, 30 Dec 2024 02:09:58 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/create-preparedinner-workfow-in-stepwise-3od8</link>
      <guid>https://dev.to/littlelittlecloud/create-preparedinner-workfow-in-stepwise-3od8</guid>
      <description>&lt;p&gt;This post shows how to create a stepwise workflow in C# using &lt;code&gt;PrepareDinner&lt;/code&gt; as an example. The complete code is available at &lt;a href="https://github.com/LittleLittleCloud/StepWise/blob/main/example/HelloWorld/PrepareDinner.cs" rel="noopener noreferrer"&gt;PrepareDinner.cs&lt;/a&gt;. You can visit &lt;a href="https://dev.to/littlelittlecloud/stepwise-a-powerful-c-workflow-engine-for-parallel-task-execution-2nc4"&gt;this blog post&lt;/a&gt; to learn more about StepWise.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can try-out this workfow at the online demo by clicking the below button&lt;br&gt;
&lt;a href="https://stepwisegallery20241128154731.azurewebsites.net/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FLittleLittleCloud%2FStepWise%2Frefs%2Fheads%2Fmain%2Fasset%2Ftry-live-demo.svg" width="200" height="48"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4UmGGQbTdmU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare dinner overview
&lt;/h2&gt;

&lt;p&gt;The dinner preparation workflow consists of the following steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijshxtpexbbl613bq974.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijshxtpexbbl613bq974.png" alt="Image description" width="666" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where we can notice that some steps can be executed in parallel, such as &lt;code&gt;CutVegetables()&lt;/code&gt; and &lt;code&gt;BoilWater()&lt;/code&gt;, and some steps depend on the previous steps, such as &lt;code&gt;CookVegetables()&lt;/code&gt; and &lt;code&gt;CookMeat()&lt;/code&gt;. When all the steps are completed, the &lt;code&gt;ServeDinner()&lt;/code&gt; step can be executed. This dependency can be automatically resolved by the StepWise engine after the prepare dinner workflow is defined.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new StepWise project
&lt;/h2&gt;

&lt;p&gt;To create a new StepWise project, you can use the StepWise template provided by StepWise. You can create a new StepWise project using the following command from dotnet-cli:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// &lt;span class="nb"&gt;install &lt;/span&gt;the StepWise template
dotnet new &lt;span class="nt"&gt;-i&lt;/span&gt; LittleLittleCloud.StepWise.Template

// create a new StepWise project
dotnet new stepwise-console &lt;span class="nt"&gt;-n&lt;/span&gt; PrepareDinnerProject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Define workflow
&lt;/h2&gt;

&lt;p&gt;In StepWise, every workflow is represented as a basic C# class and every steps under that workflow is mapping to a public async method with &lt;code&gt;Step&lt;/code&gt; Attribution.&lt;/p&gt;

&lt;p&gt;Therefore, we can start by defining a &lt;code&gt;PrepareDinner&lt;/code&gt; class with the following steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file: PrepareDinner.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrepareDinner&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we can start adding the steps to the &lt;code&gt;PrepareDinner&lt;/code&gt; class. We will add &lt;code&gt;Start&lt;/code&gt; step first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file: PrepareDinner.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrepareDinner&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Start preparing dinner at "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Start&lt;/code&gt; step is a simple step that prints the current time to the console and returns the current time. This step is the first step in the workflow and does not depend on any other steps.&lt;/p&gt;

&lt;p&gt;One thing to note is the &lt;code&gt;Step&lt;/code&gt; attribute on the &lt;code&gt;Start&lt;/code&gt; method. This attribute tells the StepWise engine that this method is a step in the workflow.&lt;/p&gt;

&lt;p&gt;Next, we can add &lt;code&gt;CutVegetables&lt;/code&gt; and &lt;code&gt;BoilWaters&lt;/code&gt; steps to the &lt;code&gt;PrepareDinner&lt;/code&gt; class. These steps will depend on the &lt;code&gt;Start&lt;/code&gt; step, which is marked by the &lt;code&gt;DependOn&lt;/code&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file: PrepareDinner.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrepareDinner&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... previous steps&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CutVegetables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cutting vegetables..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Vegetables are cut in 2 seconds"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;BoilWater&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Boiling water..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Water is boiled in 3 seconds"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we can add &lt;code&gt;CookVegetables&lt;/code&gt; step. We want the &lt;code&gt;CookVegetables&lt;/code&gt; step to depend on the &lt;code&gt;CutVegetables&lt;/code&gt; step. This can be done by adding the &lt;code&gt;DependOn&lt;/code&gt; attribute to the &lt;code&gt;CookVegetables&lt;/code&gt; method. Other than the dependency, we also want to use the result of the &lt;code&gt;CutVegetables&lt;/code&gt; step in the &lt;code&gt;CookVegetables&lt;/code&gt; step. This can be done by adding the &lt;code&gt;FromStep&lt;/code&gt; attribute to the &lt;code&gt;CookVegetables&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file: PrepareDinner.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrepareDinner&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... previous steps&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CutVegetables&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BoilWater&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CookVegetables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CutVegetables&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cutVegetablesResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cooking vegetables..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Vegetables are cooked in 3 seconds. &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cutVegetablesResult&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can add the &lt;code&gt;ServeDinner&lt;/code&gt; step to the &lt;code&gt;PrepareDinner&lt;/code&gt; class. This step will depend on the &lt;code&gt;CookVegetables&lt;/code&gt; and &lt;code&gt;CookMeat&lt;/code&gt; steps. We can add the &lt;code&gt;DependOn&lt;/code&gt; attribute to the &lt;code&gt;ServeDinner&lt;/code&gt; method to specify the dependencies. We also want to use the results of the &lt;code&gt;CookVegetables&lt;/code&gt; and &lt;code&gt;CookMeat&lt;/code&gt; steps in the &lt;code&gt;ServeDinner&lt;/code&gt; step. This can be done by adding the &lt;code&gt;FromStep&lt;/code&gt; attribute to the &lt;code&gt;ServeDinner&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file: PrepareDinner.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrepareDinner&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... previous steps&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CookVegetables&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;DependOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CookMeat&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ServeDinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CookVegetables&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cookVegetablesResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CookMeat&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cookMeatResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Serving dinner..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Dinner is served in 1 second. &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cookVegetablesResult&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cookMeatResult&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we have defined the &lt;code&gt;PrepareDinner&lt;/code&gt; workflow with all the necessary steps. The workflow is ready to be executed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add PrepareDinner workflow to StepWise server
&lt;/h2&gt;

&lt;p&gt;After the &lt;code&gt;PrepareDinner&lt;/code&gt; workflow is defined, we can add it to the StepWise server using the &lt;code&gt;StepWiseClient&lt;/code&gt; instance. This will allow the workflow to be executed in the StepWise UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Program.cs&lt;/span&gt;
&lt;span class="c1"&gt;// ...configure the StepWise server&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stepWiseClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StepWiseClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;prepareDinner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PrepareDinner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;stepWiseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWorkflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateFromInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prepareDinner&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Wait for the host to shutdown&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForShutdownAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above creates a StepWise server and adds the &lt;code&gt;PrepareDinner&lt;/code&gt; workflow to the server. The server will be hosted on &lt;code&gt;http://localhost:5123&lt;/code&gt; by default. You can visit the URL to see the StepWise UI and execute the workflow.&lt;/p&gt;

</description>
      <category>stepwise</category>
      <category>dotnet</category>
      <category>ui</category>
    </item>
    <item>
      <title>Understanding Event-Based and Process-Based Workflows</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Mon, 16 Dec 2024 08:09:58 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/understanding-event-based-and-process-based-workflows-30le</link>
      <guid>https://dev.to/littlelittlecloud/understanding-event-based-and-process-based-workflows-30le</guid>
      <description>&lt;p&gt;When managing tasks and automating processes, whether in software development or business operations, two primary workflow types come into play: event-based and process-based workflows. Recognizing their differences helps in choosing the right approach for your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event-Based Workflows
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Event-based workflows are driven by external events or triggers, causing specific actions to occur often asynchronously. These workflows are ideal for systems requiring adaptability and quick response to real-time stimuli.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Consider a web page where a user clicks the "Submit" button. This action initiates several asynchronous tasks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validating the form data.&lt;/li&gt;
&lt;li&gt;Sending the data to a server.&lt;/li&gt;
&lt;li&gt;Displaying a confirmation message to the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Real-World Applications:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Social Media Reactions:&lt;/strong&gt; Liking or commenting on a post triggers notifications and updates for users in real-time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Online Notification Systems:&lt;/strong&gt; Such as whenever a new email is received or a milestone is achieved on a software platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Analogy with Reactive Programming:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Think of a restaurant where orders come in randomly. Chefs (reactive system) start preparing each order as it arrives, adapting to the inbound requests without a fixed order, akin to event-based workflows reacting to triggers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Process-Based Workflows
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
These workflows follow a strict, predefined sequence where each task is executed in order. This structured approach is beneficial for repeatable operations requiring consistency and control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In a document approval process, a document undergoes a clear sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Submission by the originator.&lt;/li&gt;
&lt;li&gt;Initial review by a manager.&lt;/li&gt;
&lt;li&gt;Subsequent approval by a department head.&lt;/li&gt;
&lt;li&gt;Final approval by an executive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Real-World Applications:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loan Processing Systems:&lt;/strong&gt; Loans pass through set stages of assessment and approval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manufacturing Assembly Lines:&lt;/strong&gt; Products are assembled through a fixed sequence to ensure quality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Analogy with Imperative Programming:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Similar to following a recipe for baking a cake, each step (mix ingredients, bake, cool) must be performed in order for successful completion.&lt;/p&gt;
&lt;h3&gt;
  
  
  Comparison Chart
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Event-Based Workflow&lt;/th&gt;
&lt;th&gt;Process-Based Workflow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nature&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Asynchronous, flexible&lt;/td&gt;
&lt;td&gt;Sequential, structured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trigger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Events&lt;/td&gt;
&lt;td&gt;Predefined process steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Non-linear&lt;/td&gt;
&lt;td&gt;Linear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Programming Style&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reactive Programming&lt;/td&gt;
&lt;td&gt;Imperative Programming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time notifications, dynamic interactions&lt;/td&gt;
&lt;td&gt;Structured approvals, manufacturing processes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Visual Aids
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Event-Based Workflow Diagram:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[User Clicks Button]
        |
    [Trigger Event]
    /      |       \
[Action 1][Action 2][Action 3]
(independent actions)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Process-Based Workflow Diagram:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Start] -&amp;gt; [Step 1] -&amp;gt; [Step 2] -&amp;gt; [Step 3] -&amp;gt; [End]
(Sequential steps)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Event-based workflows excel in environments that demand flexibility and quick adaptation to external triggers, such as real-time data operations, while process-based workflows are well-suited for tasks that require control and adherence to a linear progression, such as compliance and manufacturing processes. Understanding these distinctions helps in designing systems that optimize efficiency and response according to the specific context and requirements.&lt;/p&gt;

</description>
      <category>programming</category>
    </item>
    <item>
      <title>StepWise: A code-first, event-driven workflow framework for .NET</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Thu, 07 Nov 2024 23:41:53 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/stepwise-a-powerful-c-workflow-engine-for-parallel-task-execution-2nc4</link>
      <guid>https://dev.to/littlelittlecloud/stepwise-a-powerful-c-workflow-engine-for-parallel-task-execution-2nc4</guid>
      <description>&lt;p&gt;StepWise is an Open-Source .NET framework which assists you to code, visualize and execute event-base workflow. It is designed to help you build complex workflows in a simple and efficient way. StepWise comes with the following key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code-First&lt;/strong&gt;: Define workflows using C# code in your project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebUI&lt;/strong&gt; Visualize and execute workflows from your favorite browser using StepWise WebUI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-Driven&lt;/strong&gt;: Execute steps in parallel and resolve dependencies automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-Powered&lt;/strong&gt;: Work with &lt;code&gt;Geeno&lt;/code&gt;, a built-in AI assistant in StepWise WebUI to help you run and analyze workflows with ease.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/-bys_LlegX4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To learn more about StepWise, checkout the &lt;a href="https://github.com/LittleLittleCloud/StepWise/tree/main" rel="noopener noreferrer"&gt;project on Github&lt;/a&gt;. You can also try out the online samples by clicking the &lt;code&gt;try-live-demo&lt;/code&gt; button below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stepwisegallery20241128154731.azurewebsites.net/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FLittleLittleCloud%2FStepWise%2Frefs%2Fheads%2Fmain%2Fasset%2Ftry-live-demo.svg" width="200" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>tooling</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building an AI-Powered Equation Solver with GPT-4o, AutoGen.Net and StepWise</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Mon, 07 Oct 2024 05:33:05 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/building-an-ai-powered-equation-solver-with-gpt-4o-autogennet-and-stepwise-5g2a</link>
      <guid>https://dev.to/littlelittlecloud/building-an-ai-powered-equation-solver-with-gpt-4o-autogennet-and-stepwise-5g2a</guid>
      <description>&lt;h2&gt;
  
  
  Building an AI-Powered Equation Solver with AutoGen, GPT-4, and StepWise
&lt;/h2&gt;

&lt;p&gt;In this post, we'll walk through building an AI-powered equation solver that can extract equations from images and solve them using the power of GPT-4. We'll be using AutoGen, OpenAI's GPT-4o, and the StepWise framework to create a workflow to resolve equation from user input image.&lt;/p&gt;

&lt;p&gt;The complete source code for this project is available on &lt;a href="https://github.com/LittleLittleCloud/EquationSolver" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Mathematical equations are everywhere, from academic papers to whiteboards in meeting rooms. But what if you could simply take a picture of an equation and have an AI solve it for you? That's exactly what we're going to build!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;We'll create a .NET application that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Accepts an image input containing an equation&lt;/li&gt;
&lt;li&gt;Uses GPT-4's vision capabilities to extract the equation&lt;/li&gt;
&lt;li&gt;Converts the equation to LaTeX format&lt;/li&gt;
&lt;li&gt;Solves the equation using GPT-4&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's dive into the components and workflow of our solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Components
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/microsoft/autogen/tree/main/autogen" rel="noopener noreferrer"&gt;AutoGen&lt;/a&gt;&lt;/strong&gt;: A framework for building AI agents and workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI's GPT-4o&lt;/strong&gt;: A powerful language model with vision capabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/LittleLittleCloud/StepWise" rel="noopener noreferrer"&gt;StepWise&lt;/a&gt;&lt;/strong&gt;: A framework for creating, visualizing and executing workflows&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Workflow
&lt;/h2&gt;

&lt;p&gt;Our equation solver follows these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Image Input&lt;/strong&gt;: Accept an image containing an equation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Key Validation&lt;/strong&gt;: Ensure we have a valid OpenAI API key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image Validation&lt;/strong&gt;: Confirm the image contains exactly one equation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Equation Extraction&lt;/strong&gt;: Extract the equation from the image and convert it to LaTeX&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Equation Solving&lt;/strong&gt;: Solve the extracted equation&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxeqflqkxfuzo6juth9xq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxeqflqkxfuzo6juth9xq.png" alt="overview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's look at each step in more detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Image Input
&lt;/h3&gt;

&lt;p&gt;We use StepWise's &lt;code&gt;StepWiseUIImageInput&lt;/code&gt; attribute to create a user interface for image input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;StepWiseUIImageInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Please provide the image of the equation"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StepWiseImage&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;InputImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. API Key Validation
&lt;/h3&gt;

&lt;p&gt;We provide two options for the OpenAI API key: environment variable or manual input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[StepWiseUITextInput(description: "Please provide the OpenAI API key if env:OPENAI_API_KEY is not set, otherwise leave empty and submit")]
public async Task&amp;lt;string?&amp;gt; OpenAIApiKey()
{
    return null;
}

[Step(description: "Validate the OpenAI API key")]
public async Task&amp;lt;string&amp;gt; ValidateOpenAIApiKey(
    [FromStep(nameof(OpenAIApiKey))] string apiKey)
{
    // ... (key validation logic)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Image Validation
&lt;/h3&gt;

&lt;p&gt;We use GPT-4 to confirm that the image contains exactly one equation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Valid image input to confirm it contains exactly one equation"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ValidateImageInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InputImage&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="n"&gt;StepWiseImage&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... (image validation logic)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Equation Extraction
&lt;/h3&gt;

&lt;p&gt;GPT-4's vision capabilities are used to extract the equation and convert it to LaTeX:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Extract the equation from the image into LaTeX format"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ExtractEquationFromImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValidateImageInput&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InputImage&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="n"&gt;StepWiseImage&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... (equation extraction logic)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Equation Solving
&lt;/h3&gt;

&lt;p&gt;Finally, we use GPT-4 to solve the extracted equation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Solve the equation"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SolveEquation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InputImage&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="n"&gt;StepWiseImage&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FromStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExtractEquationFromImage&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;equation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... (equation solving logic)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;The magic happens in the &lt;code&gt;EquationSolver&lt;/code&gt; class, which combines all these steps into a cohesive workflow. We use AutoGen to create an AI agent with GPT-4, and StepWise to manage the workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EquationSolver&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IAgent&lt;/span&gt; &lt;span class="n"&gt;_agent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ... (steps implementation)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main application sets up a web server and initializes the workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureWebHostDefaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webBuilder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;webBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseUrls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:5123"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseStepWiseServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stepWiseClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StepWiseClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EquationSolver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateFromInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;stepWiseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWorkflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForShutdownAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By combining the power of AutoGen, GPT-4o, and StepWise, we've created an equation solver prototype that can extract and solve equations from images. This approach demonstrates the potential of AI in simplifying complex tasks and opens up possibilities for similar applications in various fields.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>autogen</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Freeloading gpt-4o, llama, and many more llms on Github Model in AutoGen.Net</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Wed, 11 Sep 2024 22:44:52 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/freeloading-gpt-4o-llama-and-many-more-llms-on-github-1ff3</link>
      <guid>https://dev.to/littlelittlecloud/freeloading-gpt-4o-llama-and-many-more-llms-on-github-1ff3</guid>
      <description>&lt;p&gt;Github recently introduced &lt;a href="https://docs.github.com/en/github-models/prototyping-with-ai-models" rel="noopener noreferrer"&gt;Github Models&lt;/a&gt;, which allows us to try and test from a wide range of model types, sizes, and specializations &lt;strong&gt;FOR FREE&lt;/strong&gt;. Considering the wide range of available models (gpt-4o, gpt-4o-mini, llama-3.1 8B, llama-3.1 405B), maybe now it's the second best time to have a github account. In this blog post, we'll explore how to freeload these models using &lt;a href="https://github.com/microsoft/autogen/tree/main" rel="noopener noreferrer"&gt;AutoGen.Net&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why GitHub Models?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Free Access: All you need is a GitHub account to get started.&lt;/li&gt;
&lt;li&gt;Diverse Model Selection: From GPT-4 to Llama-3.1, GitHub offers a variety of models to suit different needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a GitHub Account:&lt;/strong&gt; If you don't already have one, sign up for a free GitHub account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate an API Key:&lt;/strong&gt; Navigate to Settings &amp;gt; Developer settings &amp;gt; Token (classic) and generate a new token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step Up Your Environment:&lt;/strong&gt; Save your API key securely in your local environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Integrating with AutoGen.Net
&lt;/h2&gt;

&lt;p&gt;Here's a simple example of how to use the Llama-3.1 405B model with AutoGen.Net:&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a dotnet console app, and add package &lt;code&gt;AutoGen 0.2.0&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; GithubLLMFreeLoader
&lt;span class="nb"&gt;cd &lt;/span&gt;GithubLLMFreeLoader
dotnet add package AutoGen &lt;span class="nt"&gt;--version&lt;/span&gt; 0.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a simple chat sample using &lt;code&gt;llama-3.1-405B&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AutoGen.AzureAIInference&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AutoGen.AzureAIInference.Extension&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AutoGen.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Azure.AI.Inference&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;githubKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GH_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"YOUR_KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://models.inference.ai.azure.com"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChatCompletionsClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Azure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AzureKeyCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;githubKey&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChatCompletionsClientAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;chatCompletionsClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Meta-Llama-3.1-405B-Instruct"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterMessageConnector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterPrintMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tell me a long joke"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code snippet demostrates how to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect to Github Model&lt;/li&gt;
&lt;li&gt;Create an AutoGen.Net agent using &lt;code&gt;llama-3.1-405B&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start a conversation with agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitation
&lt;/h2&gt;

&lt;p&gt;Okay, here's the not-so-great part: there's a limit on how much you can use this service. Since it's free, they don't let you go crazy with it. It's fine for trying things out or playing around, but it's not going to cut it if you need it for serious, heavy-duty work. But hey, it's free, so we can't really complain, right?&lt;/p&gt;

</description>
      <category>github</category>
      <category>llm</category>
      <category>autogen</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Tool call with local model using Ollama and AutoGen.Net</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Mon, 29 Jul 2024 21:56:50 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/tool-call-with-local-model-using-ollama-and-autogennet-3o64</link>
      <guid>https://dev.to/littlelittlecloud/tool-call-with-local-model-using-ollama-and-autogennet-3o64</guid>
      <description>&lt;p&gt;Ollama, starting from version &lt;code&gt;0.3.0&lt;/code&gt;, supports tool calls with popular models like &lt;code&gt;llama 3.1&lt;/code&gt; and &lt;code&gt;mistral&lt;/code&gt;. This functionality allows LLM models to interact with external functions by incorporating tools within their prompts.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll demo the usage of tool call with local model by building a weather report agent using &lt;a href="https://microsoft.github.io/autogen-for-net/index.html" rel="noopener noreferrer"&gt;AutoGen.Net&lt;/a&gt; and &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. The weather report agent will query weather information from a dummy function and generate final weather information based on tool call result&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;To run this example, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ollama &amp;gt;= 0.3.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dotnet sdk 8.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create a .NET Console App
&lt;/h2&gt;

&lt;p&gt;First, create a new .NET console application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; weather-report-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Add AutoGen.Net Packages
&lt;/h2&gt;

&lt;p&gt;Next, add the necessary AutoGen.Net packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package AutoGen
dotnet add package AutoGen.SourceGenerator &lt;span class="c"&gt;# for type-safe function contract generation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Add the &lt;code&gt;WeatherReportTool&lt;/code&gt; Class
&lt;/h2&gt;

&lt;p&gt;Create a dummy tool class to fetch the weather report for a given city. The API in this class can be accessed by the LLM through tool calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherReportTool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Get the weather report for the given city&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="city"&amp;gt;city&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;weather report&amp;lt;/returns&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetWeatherReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s"&gt;$"""
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"{{city}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"temperature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"25°C"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"weather"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Sunny"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="s"&gt;""";
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;[Function]&lt;/code&gt; attribute allows &lt;code&gt;AutoGen.SourceGenerator&lt;/code&gt; to create a function contract from the structured comment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlxdm7semft3wsm52tb1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlxdm7semft3wsm52tb1.png" alt="Image description" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Create &lt;code&gt;WeatherReportTool&lt;/code&gt; and &lt;code&gt;FunctionCallMiddleware&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;FunctionCallMiddleware&lt;/code&gt; with function definitions and function maps, then register it to an agent for consumption.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;weatherReportTool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;WeatherReportTool&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;weatherReportToolMiddleware&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FunctionCallMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;weatherReportTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetWeatherReportFunctionContract&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;functionMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weatherReportTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetWeatherReport&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weatherReportTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetWeatherReport&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Create &lt;code&gt;OpenAIChatAgent&lt;/code&gt; and Connect to Ollama
&lt;/h2&gt;

&lt;p&gt;Since Ollama supports the OpenAI-compatible completion API, we can use &lt;code&gt;OpenAIChatAgent&lt;/code&gt; to connect to the Ollama service as a third-party endpoint.&lt;/p&gt;

&lt;p&gt;First, create a &lt;code&gt;CustomHttpClientHandler&lt;/code&gt; to redirect all requests to the Ollama server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomHttpClientHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HttpClientHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_modelServiceUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CustomHttpClientHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;modelServiceUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_modelServiceUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modelServiceUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpRequestMessage&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestUri&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_modelServiceUrl&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestUri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathAndQuery&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create an &lt;code&gt;OpenAIChatAgent&lt;/code&gt; and connect it to the Ollama endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;openAIClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpenAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenAIClientOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;HttpClientHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CustomHttpClientHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://ollama.com/api/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"llama3.1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpenAIChatAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;openAIClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;openAIClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;systemMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"You are a weather assistant."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterMessageConnector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// convert AutoGen message to OpenAI message&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weatherReportToolMiddleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// register function call middleware&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateReplyAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ToolCallAggregateMessage&lt;/span&gt; &lt;span class="n"&gt;toolCallMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toolCallMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateReplyAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterPrintMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// print message to console&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Step: Ask for a Weather Report!
&lt;/h2&gt;

&lt;p&gt;Once the weather report agent is created, you can ask it questions like "What's the weather in New York?"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
&lt;/span&gt;&lt;span class="n"&gt;What&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt; &lt;span class="n"&gt;York&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="s"&gt;""";
&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response will be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--------------------&lt;/span&gt;
TextMessage from assistant
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
The current temperature &lt;span class="k"&gt;in &lt;/span&gt;New York is 25°C &lt;span class="o"&gt;(&lt;/span&gt;77°F&lt;span class="o"&gt;)&lt;/span&gt;, and it&lt;span class="s1"&gt;'s a sunny day.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion and Further Reading
&lt;/h2&gt;

&lt;p&gt;In this blog, we demonstrated how to enable tool calls using local models with Ollama and AutoGen.Net.&lt;/p&gt;

&lt;p&gt;Thanks for reading! You can find the complete sample code in this &lt;a href="https://github.com/LittleLittleCloud/weather-report-agent" rel="noopener noreferrer"&gt;repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ollama</category>
      <category>autogen</category>
      <category>llm</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>How to write release note with LLM agents</title>
      <dc:creator>Xiaoyun Zhang</dc:creator>
      <pubDate>Sun, 14 Jul 2024 07:29:47 +0000</pubDate>
      <link>https://dev.to/littlelittlecloud/how-to-write-release-note-with-llm-agents-57no</link>
      <guid>https://dev.to/littlelittlecloud/how-to-write-release-note-with-llm-agents-57no</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;This blog shows how to simplify the process of writing release notes using &lt;code&gt;issue-helper&lt;/code&gt; and &lt;code&gt;gpt&lt;/code&gt; agent in &lt;a href="https://github.com/LittleLittleCloud/Agent-ChatRoom" rel="noopener noreferrer"&gt;Agent ChatRoom&lt;/a&gt;. The &lt;code&gt;issue-helper&lt;/code&gt; pulls issues in a milestone and &lt;code&gt;gpt&lt;/code&gt; generates a release note based on these issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Agent ChatRoom
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/LittleLittleCloud/Agent-ChatRoom" rel="noopener noreferrer"&gt;&lt;code&gt;Agent ChatRoom&lt;/code&gt;&lt;/a&gt; is a multi-agent platform built on top of &lt;a href="https://microsoft.github.io/autogen-for-net/" rel="noopener noreferrer"&gt;AutoGen.Net&lt;/a&gt; and &lt;a href="https://github.com/dotnet/orleans" rel="noopener noreferrer"&gt;Orleans&lt;/a&gt;. It comes with a web-based UI that provides built-in support for multi-agent group chat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvqz9a4qge4fr42p02n5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvqz9a4qge4fr42p02n5.png" alt="demo: switch theme with ps-gpt and ps-runner" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A release note on github usually includes the following sections&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improvements&lt;/li&gt;
&lt;li&gt;Bug Fix&lt;/li&gt;
&lt;li&gt;New Feature&lt;/li&gt;
&lt;li&gt;API Break Change&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2uh1r7u7pmkuxxaevlx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2uh1r7u7pmkuxxaevlx.png" alt="A release note example from AutoGen.Net" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To write a release note like the one above, the normal steps are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collect all issues in a milestone and classify these issues into corresponding sections&lt;/li&gt;
&lt;li&gt;complete each section by summarizing the issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manually completing these steps can be time-consuming. However, with the help of issue-helper and gpt agent, we no longer need to collect issues from github and summarize them one by one. Instead, we can ask issue-helper to find all issues in a milestone, and ask gpt to generate release note based on found issues.&lt;/p&gt;

&lt;p&gt;In the rest of the blog, we will show a step-by-step guide on how to write release note using Agent ChatRoom, &lt;code&gt;issue-helper&lt;/code&gt; and &lt;code&gt;gpt&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install Agent ChatRoom and configure agents
&lt;/h2&gt;

&lt;p&gt;Agent Chatroom is published as a dotnet tool package on nuget.org. To install Agent Chatroom, first make sure you install &lt;code&gt;dotnet 8.x&lt;/code&gt; SDK, then run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; ChatRoom.Client &lt;span class="nt"&gt;--version&lt;/span&gt; 0.4.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start a chatroom, you need to provide a configuration file which includes credentials like openai-api-key, along with settings for the issue-helperand gpt agents. To simplify the process, you can begin with an empty configuration template by using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create configuration from chatroom_empty template and save it to config.json&lt;/span&gt;
&lt;span class="c"&gt;# You can also list all available templates using list-templates command&lt;/span&gt;
chatroom create &lt;span class="nt"&gt;-t&lt;/span&gt; chatroom_empty &lt;span class="nt"&gt;-o&lt;/span&gt; config.json 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;create&lt;/code&gt; command will also generate a JSON schema file compatible with modern code editors like VS Code, providing code intellisense to assist you in completing the configuration.&lt;/p&gt;

&lt;p&gt;To add &lt;code&gt;issue-helper&lt;/code&gt; and &lt;code&gt;gpt&lt;/code&gt; agent to the chatroom, you need to add &lt;code&gt;chatroom_github_configuration&lt;/code&gt; and &lt;code&gt;chatroom_openai_configuration&lt;/code&gt; section to config.json&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd7oc3wqzhejyxtzg76c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd7oc3wqzhejyxtzg76c.png" alt="Image description" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you complete the configuration, save the file and start the chatroom using the following command. You will see output similar to the example below, which shows the url of web-based UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chatroom run &lt;span class="nt"&gt;-c&lt;/span&gt; config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9dl48u2cpsrfceyot3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9dl48u2cpsrfceyot3l.png" alt="Image description" width="730" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsivnk7j00cl0vwpcewjs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsivnk7j00cl0vwpcewjs.png" alt="chatroom web-based UI" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create a group chat with issue helper and gpt
&lt;/h2&gt;

&lt;p&gt;To create a group chat with agents, click on the (+) button on the top of the channel panel, and select the agents you want to add. In this case, both &lt;code&gt;issue-helper&lt;/code&gt; and &lt;code&gt;gpt&lt;/code&gt; are added for issue retrieval and writing release note. Other than that, we also select &lt;code&gt;DynamicGroupChat&lt;/code&gt; which selects the next speaker using LLM as group chat orchestrator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84ugbezna4dpjo0rnnga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84ugbezna4dpjo0rnnga.png" alt="Image description" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking on save button, we can see the group chat &lt;code&gt;ReleaseChannel&lt;/code&gt; is created and we can start asking agents to write release note.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sifqg1ri108npl2xm38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sifqg1ri108npl2xm38.png" alt="Image description" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Ask issue-helper to retrieve issues in milestone
&lt;/h2&gt;

&lt;p&gt;In this step, we will ask &lt;code&gt;issue-helper&lt;/code&gt; to find all completed issues in milestone: 0.4.2, which is the most recent completed milestone in Agent-Chatroom.&lt;/p&gt;

&lt;p&gt;As we can see, the &lt;code&gt;issue-helper&lt;/code&gt; returns a list of issues and their summary, which is exactly what we need to write release note. Except for this time, we no longer need to pull them manually from github.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyo6lhs32l361m0ak32b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyo6lhs32l361m0ak32b.png" alt="find all issues with milestone 0.4.2" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Step: Ask gpt to write release note
&lt;/h2&gt;

&lt;p&gt;In the final step, we can ask &lt;code&gt;gpt&lt;/code&gt; to create a release note. Because &lt;code&gt;gpt&lt;/code&gt; and &lt;code&gt;issue-helper&lt;/code&gt; are in the same group chat, they can share the context and reuse the issues returned from &lt;code&gt;issue-helper&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3f7i53ure7ww5end1e7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3f7i53ure7ww5end1e7.png" alt="Image description" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog, we show how to write release note using &lt;code&gt;gpt&lt;/code&gt; and &lt;code&gt;issue-helper&lt;/code&gt; agents in Agent ChatRoom.&lt;/p&gt;

&lt;p&gt;Feedback and comments are welcome. If you found this blog useful, please give &lt;a href="https://github.com/LittleLittleCloud/Agent-ChatRoom" rel="noopener noreferrer"&gt;Agent ChatRoom&lt;/a&gt; a star on github.&lt;br&gt;
Happy coding!&lt;/p&gt;

</description>
      <category>llm</category>
      <category>ai</category>
      <category>dotnet</category>
      <category>github</category>
    </item>
  </channel>
</rss>
