Get inspired to create with MSFT's Generative AI Samples
-
+## Lesson 4: Generative AI Samples
+
+*Get inspired to create with MSFT's Generative AI Samples*
> 💡 **Quick Summary**: Understand how Generative AI works with Microsoft Samples, get inspirated, and understand the best scenarios to apply it into new applications.
@@ -27,11 +25,9 @@
## Intro Video
-[](https://microsoft-my.sharepoint.com/:v:/p/brunocapuano/ERHGiBsTtVJBtN9VevaxdnwB6dfV_GCdFXbZL9D-GnEkew?e=Y4oAjD&nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTdHJlYW1XZWJBcHAiLCJyZWZlcnJhbFZpZXciOiJTaGFyZURpYWxvZy1MaW5rIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXcifX0%3D)
-
-In our first video, Bruno Capuano introduces some of the practical scenarios for .NET Generative AI!
-On the first demo, we use Ollama to create an application to generate descriptions for images quickly using Llama 3.2 Vision model running locally, later, four demos that showcase the power of Semantic Kernel, Language Models, and the .NET AI Extension.
+**INSERT: CHAT EXPLANATION VIDEO HERE**
+Generative AI is a powerful technology that has multiple possibilities for applications, from e-commerce to content creation. To demonstrate how Generative AI can be used in real-world scenarios, we'll explore some of the Microsoft samples that showcase the capabilities of Generative AI and how it can be integrated.
## eShopLite Demos
@@ -39,11 +35,15 @@ On the first demo, we use Ollama to create an application to generate descriptio
For our first two demos, we'll explore the eShopLite project, a simple e-commerce application for outdoor gear and camping enthusiasts that is augmented with Generative AI capabilities, such as search features optimization, Customer Support, and Realtime Audio Analysis.
+These demos use [Azure OpenAi](https://azure.microsoft.com/en-us/products/ai-services/openai-service) and [Azure Ai Foundry Models](https://ai.azure.com/) to do their inferences for the applications. Please, check more details on their repositories.
+
The first demo, we show how to use the Semantic Kernel to enhance the search capabilities, which can understand the context of the user's queries and provide accurate results.
### eShopLite with Semantic Search
-In eShopLite with Semantic Search, we use the Semantic Kernel to enhance the search capabilities of the e-commerce application. Semantic Kernel auxiliate us to create a more robust search engine that can understand the context of the user's queries and provide more accurate results. For example, if a user searches for "do you have something for cooking", the search engine can understand that the user is looking for kitchenware and show the most relevant products, in context of our sample, it returns Camping Cookware.
+In eShopLite with Semantic Search, we use the Semantic Kernel to enhance the search capabilities of the e-commerce application. Semantic Kernel auxiliate us to create a more robust search engine that can understand the context of the user's queries and provide more accurate results.
+
+For example, if a user searches for "do you have something for cooking", the search engine can understand that the user is looking for kitchenware and show the most relevant products, in context of our sample, it returns Camping Cookware.

@@ -145,7 +145,18 @@ var recordId = await _productsCollection.UpsertAsync(productVector);
_logger.LogInformation("Product added to memory: {Product} with recordId: {RecordId}", product.Name, recordId);
```
-The code above demonstrates how to iterate over the products and add them to the memory, create a new product vector, generate the embedding for the product information, convert the embedding result to a float array, and assign it to the product vector. The product is then added to the memory, repeating the process for each product in the collection. After that, when the user searches for a product, we can compare the user's query with the product vectors and return the most relevant products.
+
+The code above demonstrates how to iterate over the products and add them to the memory.
+
+After we create a new product vector, we use it to generate embedding for the product information, convert the embedding result to a float array, and assign it to the product vector.
+
+Look at `_productsCollection`, it is a reference to the container where the products are stored, using a CosmosDB call to get the response for the recordId. For in this case, for logging.
+
+The product is then added to the memory, repeating the process for each product in the collection.
+
+After that, when the user searches for a product, we can compare the user's query with the product vectors and return the most relevant products.
+
+
```csharp
try
@@ -203,7 +214,11 @@ try
}
```
-With the code above, we generate the embedding for the search query, search the vector database for the most similar product, and get a response message using the found product information. Helping the user find the products they need more easily, leading to a better shopping experience and increased sales. Moreover, as generative AI evolves, we need some telemetry and monitoring to understand the user's behavior and improve the search engine, this is where Azure Application Insights and .NET Aspire come in.
+With the code above, we generate the embedding for the search query, search the vector database for the most similar product, and get a response message using the found product information.
+
+Helping the user find the products they need more easily, leading to a better shopping experience and increased sales.
+
+Moreover, as generative AI evolves, we need some telemetry and monitoring to understand the user's behavior and improve the search engine, this is where Azure Application Insights and .NET Aspire come in.

@@ -211,9 +226,13 @@ With the code above, we generate the embedding for the search query, search the

+As Telemetry is essential to understand the user's behavior and improve services, we introduce Azure Application Insights to monitor the application's performance and user interactions.
+
Application Insights provides a comprehensive set of telemetry data, helping us to understand how our services are performing, and how users are interacting with the application and cloud usage.
-> 💡 **Pro Tip**: For more information on eShopLite with Semantic Search, look at the repository to learn more: https://github.com/Azure-Samples/eShopLite-SemanticSearch/
+In the image, we can see the Application Insights dashboard, providing how the services are performing, for example, the calls for our Database, number of requests, and the response time.
+
+> 💡 **Pro Tip**: For more information on eShopLite with Semantic Search, look at the repository to learn more: https://aka.ms/netaieshoplitesemanticsearch
### eShopLite with Realtime Analysis
@@ -221,7 +240,7 @@ In eShopLite with Realtime Analysis, we use the Realtime audio capabilities of G

-To implement this feature, we need to implement new features to create the endpoints for the Realtime Analysis, it can be found on the `StoreRealtime\ConversationManager.cs` implementation.
+To implement this feature, we need to implement new features to create the endpoints for the Realtime Analysis, it can be found on the `StoreRealtime\ConversationManager.cs` implementation for Realtime Analysis.
```csharp
@@ -240,6 +259,11 @@ public async Task RunAsync(
The current date is {DateTime.Now.ToLongDateString()}
""";
+```
+First, we define the initial prompt for the assistant, providing the user with instructions on how to interact with the chatbot. Remember to use prompts that are clear and concise, prompt engineering is essential to get accurate results from the AI models.
+
+``` csharp
+
// Notify the user that the connection is being established
await addMessageAsync("Connecting...");
// Send an initial greeting message
@@ -252,6 +276,14 @@ public async Task RunAsync(
// Add the AI functions to a list of tools
List tools = new List { contosoSemanticSearchTool, contosoSearchByProductNameTool };
+```
+
+We then notify the user that the chat is ready to start and send an initial greeting message. Then, AI functions to search products, semantic search, and search by product name are created and added to a list of tools. Which could be used to provide the user with relevant information on their queries.
+
+
+
+``` csharp
+
// Configure the conversation session options
var sessionOptions = new ConversationSessionOptions()
{
@@ -275,22 +307,26 @@ public async Task RunAsync(
}
```
-> 💡 **Pro Tip**: For more information on eShopLite with Realtime Audio, look at the repository to learn more: https://github.com/Azure-Samples/eShopLite-RealtimeAudio
+The conversation session options are configured, including the instructions, voice, and input transcription options. Using the `Whisper-1` model for the input transcription.
-See a local demo of the feature as File Search in action:
+Each tool is added to the session options, and the conversation session is started with the configured options. Those can be changed to fit the user's needs.
-
+
+> 💡 **Pro Tip**: For more information on eShopLite with Realtime Audio, look at the repository to learn more: https://aka.ms/netaieshopliterealtimechat
+
+---
## Chat with your Data
-Do you want to chat with your data? In this demo, we'll use the Chat with your Data application to generate a conversation with the user's data, using a simple interface to upload a file and extract insights from it.
+Chatting with your Data is a classic example of how Generative AI is used in real-world applications. This application allows users to interact with their data in a conversational way, making it easier to understand complex datasets and extract valuable insights.
+
+It deploys a Chat Application, a Document Manager, and an Aspire Dashboard to provide a seamless experience in the application. Check the application in the GitHub repository.
+

-> 💡**Pro Tip**: For more information on the Chat with your Data app, look at the repository to learn more: [Chat with your Data](https://github.com/Azure-Samples/netai-chat-with-your-data)
+> 💡**Pro Tip**: For more information on the Chat with your Data app, look at the repository to learn more: [Chat with your Data](https://aka.ms/netaichatwithyourdata)
@@ -333,7 +369,7 @@ await foreach (var delta in session.ProcessStreamingRequest(createWriterRequest)
}
```
-The type `CreateWriterRequest` needs to have three properties: `Research`, `Products`, and `Writing`. After that, it calls the `CreateSessionAsync` method, which looks like this:
+The type `CreateWriterRequest` needs to have three properties: `Research`, `Products`, and `Writing`. After getting them setted by processing the request, it calls the `CreateSessionAsync` method, which looks like this:
```csharp
internal async Task CreateSessionAsync(HttpResponse response)
@@ -465,15 +501,7 @@ In .NET Aspire, we notice how the components are orchestrated to create a seamle

-> 💡**Pro Tip**: For more information on the Creative Writer Agent, look at the repository to learn more: [Contoso Creative Writer](https://github.com/Azure-Samples/aspire-semantic-kernel-creative-writer)
-
-See a local demo of the Creative Writer Agent in action:
-
-
-
-
+> 💡**Pro Tip**: For more information on the Creative Writer Agent, look at the repository to learn more: [Contoso Creative Writer](https://aka.ms/netaicreativewriter)
## Conclusions and resources
@@ -484,13 +512,11 @@ Those are just a few examples of how you can use Generative AI in your applicati
> ⚠️ **Note**: If you encounter any issues, please, open an issue in the repository.
-- [Azure AI Agents - Instructions](./src/AzureAIAgents.md)
+- [eShopLite with DeepSeek](https://aka.ms/netaieshoplitedeepseekr1)
+- [eShopLite with Azure AI Search](https://aka.ms/netaieshoplitesemanticsearchazureaisearch)
### Next Steps
Learn about responsible AI practices and how to ensure that your AI models are ethical and have a positive impact!
-
-
+[Go to the next lesson](../05-ResponsibleAI/readme.md)
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/AgentSKLabs-04-FileSearch.csproj b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/AgentSKLabs-04-FileSearch.csproj
deleted file mode 100644
index b94e9bcd..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/AgentSKLabs-04-FileSearch.csproj
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
- Exe
- net9.0
- AgentSKLabs_04_FileSearch
- enable
- enable
- 4d9d76d2-c77b-4f69-99b1-dc43838a12d4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
-
-
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/ObjectExtensions.cs b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/ObjectExtensions.cs
deleted file mode 100644
index 9e133894..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/ObjectExtensions.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Text.Json;
-
-public static class ObjectExtensions
-{
- private static readonly JsonSerializerOptions s_jsonOptionsCache = new() { WriteIndented = true };
-
- public static string AsJson(this object obj)
- {
- return JsonSerializer.Serialize(obj, s_jsonOptionsCache);
- }
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/Program.cs b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/Program.cs
deleted file mode 100644
index dd217090..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/Program.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-#pragma warning disable SKEXP0001, SKEXP0110, OPENAI001
-
-// Define the agent
-using Microsoft.SemanticKernel.ChatCompletion;
-using Microsoft.SemanticKernel;
-using OpenAI.Files;
-using OpenAI.VectorStores;
-using Microsoft.SemanticKernel.Agents.OpenAI;
-using System.ClientModel;
-using OpenAI.Assistants;
-using OpenAI.Chat;
-using Microsoft.Extensions.Configuration;
-using AgentLabs;
-
-var config = new ConfigurationBuilder().AddUserSecrets().Build();
-var apiKey = config["AZURE_OPENAI_APIKEY"];
-var endpoint = config["AZURE_OPENAI_ENDPOINT"];
-var modelId = config["AZURE_OPENAI_MODEL"];
-
-var openAiAssistantDefinition = new OpenAIAssistantDefinition(modelId)
-{
- EnableFileSearch = true,
- Metadata = new Dictionary{
- { "file_search", "true" }
- },
- Instructions = "This assistant can help you with your questions. You can ask about camping, healthcare benefits, and more. The assistant will include and share information to existing content in the related information database. For each shared information, the assistant will declare that this is based on existing content and provide the necessary link to it.",
-};
-
-var provider = OpenAIClientProvider.ForAzureOpenAI(new ApiKeyCredential(apiKey), new Uri(endpoint));
-
-OpenAIAssistantAgent agent =
- await OpenAIAssistantAgent.CreateAsync(
- clientProvider: provider,
- definition: openAiAssistantDefinition,
- kernel: new Kernel());
-
-// Upload files - Using a table of fictional employees.
-// iterate through the folder sampledocs
-OpenAIFileClient fileClient = provider.Client.GetOpenAIFileClient();
-List fileIds = [];
-foreach (var file in Directory.GetFiles("sampledocs"))
-{
- await using Stream stream = File.OpenRead(file);
- OpenAIFile fileInfo = await fileClient.UploadFileAsync(stream, Path.GetFileName(file), FileUploadPurpose.Assistants);
- fileIds.Add(fileInfo.Id);
-}
-
-// Create a vector-store
-var vectorStoreOptions = new VectorStoreCreationOptions()
-{
- Name = "employees_vector_store"
-};
-foreach (var fileId in fileIds)
-{
- vectorStoreOptions.FileIds.Add(fileId);
-}
-VectorStoreClient vectorStoreClient = provider.Client.GetVectorStoreClient();
-CreateVectorStoreOperation result = await vectorStoreClient.CreateVectorStoreAsync(waitUntilCompleted: true, vectorStoreOptions);
-
-// Create a thread associated with a vector-store for the agent conversation.
-string threadId =
- await agent.CreateThreadAsync(
- new OpenAIThreadCreationOptions
- {
- VectorStoreId = result.VectorStoreId
- });
-
-// Respond to user input
-try
-{
- await AgentServiceHandler.InvokeAgentAsync(agent, threadId, "I want to go camping, but it seems that it maybe raining, what products do you suggest to go camping on rainy days?");
- await AgentServiceHandler.InvokeAgentAsync(agent, threadId, "what healthcare benefits are part of the Northwind Health plans?");
-}
-finally
-{
- await agent.DeleteThreadAsync(threadId);
- await agent.DeleteAsync();
- await vectorStoreClient.DeleteVectorStoreAsync(result.VectorStoreId);
-
- // Delete the uploaded files
- foreach (var fileInfo in fileIds)
- await fileClient.DeleteFileAsync(fileInfo);
-}
-
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Benefit_Options.pdf b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Benefit_Options.pdf
deleted file mode 100644
index 6a4c07dc..00000000
Binary files a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Benefit_Options.pdf and /dev/null differ
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Camping Chair.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Camping Chair.md
deleted file mode 100644
index 42658287..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Camping Chair.md
+++ /dev/null
@@ -1,5 +0,0 @@
-**Name:** Contoso Camping Chair
-**Description:** A comfortable and foldable chair with a sturdy frame. It includes a cup holder and a carrying bag for easy transport.
-**Color:** Red
-**Categories:** Furniture, Outdoor Gear, Camping Essentials
-**Price:** $29.99
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso LED Camping Lantern.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso LED Camping Lantern.md
deleted file mode 100644
index 7c4af031..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso LED Camping Lantern.md
+++ /dev/null
@@ -1,5 +0,0 @@
-**Name:** Contoso LED Camping Lantern
-**Description:** A bright and energy-efficient lantern with multiple lighting modes. It is rechargeable and has a long battery life.
-**Color:** Yellow
-**Categories:** Lighting, Outdoor Gear, Camping Essentials
-**Price:** $19.99
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Portable Camping Stove.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Portable Camping Stove.md
deleted file mode 100644
index 18bc08d4..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Portable Camping Stove.md
+++ /dev/null
@@ -1,5 +0,0 @@
-**Name:** Contoso Portable Camping Stove
-**Description:** A compact and lightweight stove perfect for outdoor cooking. It features adjustable flame control and easy setup.
-**Color:** Silver
-**Categories:** Cooking, Outdoor Gear, Camping Essentials
-**Price:** $39.99
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Sleeping Bag.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Sleeping Bag.md
deleted file mode 100644
index c201bd55..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Sleeping Bag.md
+++ /dev/null
@@ -1,5 +0,0 @@
-**Name:** Contoso Sleeping Bag
-**Description:** A warm and cozy sleeping bag suitable for all seasons. It is made with high-quality insulation and a soft inner lining.
-**Color:** Blue
-**Categories:** Sleeping Gear, Outdoor Gear, Camping Essentials
-**Price:** $49.99
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Waterproof Tent.md b/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Waterproof Tent.md
deleted file mode 100644
index 5d59fc7b..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-04-FileSearch/sampledocs/Contoso Waterproof Tent.md
+++ /dev/null
@@ -1,5 +0,0 @@
-**Name:** Contoso Waterproof Tent
-**Description:** A durable and spacious tent designed to keep you dry and comfortable in any weather. It includes a rainfly and mesh windows for ventilation.
-**Color:** Green
-**Categories:** Shelter, Outdoor Gear, Camping Essentials
-**Price:** $99.99
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/AgentSKLabs-10-CreativeWriter.csproj b/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/AgentSKLabs-10-CreativeWriter.csproj
deleted file mode 100644
index a0550525..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/AgentSKLabs-10-CreativeWriter.csproj
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
- Exe
- net9.0
- AgentSKLabs_10_CreativeWriter
- enable
- enable
- 28df85ff-c4b7-4cd7-8012-ec02f3e8124b
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/Program.cs b/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/Program.cs
deleted file mode 100644
index d92bd8cf..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKLabs-10-CreativeWriter/Program.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-#pragma warning disable SKEXP0001, SKEXP0110, OPENAI001
-
-using Microsoft.SemanticKernel.Agents.History;
-using Microsoft.SemanticKernel.Agents;
-using Microsoft.SemanticKernel.ChatCompletion;
-using Microsoft.SemanticKernel;
-using Microsoft.Extensions.Configuration;
-using AgentLabs;
-using Microsoft.SemanticKernel.Agents.Chat;
-
-const string ReviewerName = "ArtDirector";
-const string ReviewerInstructions =
- """
- You are an art director who has opinions about copywriting born of a love for David Ogilvy.
- The goal is to determine if the given copy is acceptable to print.
- If so, state that it is approved.
- If not, provide insight on how to refine suggested copy without examples.
- """;
-
-const string CopyWriterName = "CopyWriter";
-const string CopyWriterInstructions =
- """
- You are a copywriter with ten years of experience and are known for brevity and a dry humor.
- The goal is to refine and decide on the single best copy as an expert in the field.
- Only provide a single proposal per response.
- Never delimit the response with quotation marks.
- You're laser focused on the goal at hand.
- Don't waste time with chit chat.
- Consider suggestions when refining an idea.
- """;
-
-// Define the agents
-ChatCompletionAgent agentReviewer =
- new()
- {
- Instructions = ReviewerInstructions,
- Name = ReviewerName,
- Kernel = KernelServiceHandler.CreateKernelWithChatCompletion(),
- };
-
-ChatCompletionAgent agentWriter =
- new()
- {
- Instructions = CopyWriterInstructions,
- Name = CopyWriterName,
- Kernel = KernelServiceHandler.CreateKernelWithChatCompletion(),
- };
-
-KernelFunction terminationFunction =
- AgentGroupChat.CreatePromptFunctionForStrategy(
- """
- Determine if the copy has been approved. If so, respond with a single word: yes
-
- History:
- {{$history}}
- """,
- safeParameterNames: "history");
-
-KernelFunction selectionFunction =
- AgentGroupChat.CreatePromptFunctionForStrategy(
- $$$"""
- Determine which participant takes the next turn in a conversation based on the the most recent participant.
- State only the name of the participant to take the next turn.
- No participant should take more than one turn in a row.
-
- Choose only from these participants:
- - {{{ReviewerName}}}
- - {{{CopyWriterName}}}
-
- Always follow these rules when selecting the next participant:
- - After {{{CopyWriterName}}}, it is {{{ReviewerName}}}'s turn.
- - After {{{ReviewerName}}}, it is {{{CopyWriterName}}}'s turn.
-
- History:
- {{$history}}
- """,
- safeParameterNames: "history");
-
-// Limit history used for selection and termination to the most recent message.
-ChatHistoryTruncationReducer strategyReducer = new(1);
-
-// Create a chat for agent interaction.
-AgentGroupChat chat =
- new(agentWriter, agentReviewer)
- {
- ExecutionSettings =
- new()
- {
- // Here KernelFunctionTerminationStrategy will terminate
- // when the art-director has given their approval.
- TerminationStrategy =
- new KernelFunctionTerminationStrategy(terminationFunction, KernelServiceHandler.CreateKernelWithChatCompletion())
- {
- // Only the art-director may approve.
- Agents = [agentReviewer],
- // Customer result parser to determine if the response is "yes"
- ResultParser = (result) => result.GetValue()?.Contains("yes", StringComparison.OrdinalIgnoreCase) ?? false,
- // The prompt variable name for the history argument.
- HistoryVariableName = "history",
- // Limit total number of turns
- MaximumIterations = 10,
- // Save tokens by not including the entire history in the prompt
- HistoryReducer = strategyReducer,
- },
- // Here a KernelFunctionSelectionStrategy selects agents based on a prompt function.
- SelectionStrategy =
- new KernelFunctionSelectionStrategy(selectionFunction, KernelServiceHandler.CreateKernelWithChatCompletion())
- {
- // Always start with the writer agent.
- InitialAgent = agentWriter,
- // Returns the entire result value as a string.
- ResultParser = (result) => result.GetValue() ?? CopyWriterName,
- // The prompt variable name for the history argument.
- HistoryVariableName = "history",
- // Save tokens by not including the entire history in the prompt
- HistoryReducer = strategyReducer,
- // Only include the agent names and not the message content
- EvaluateNameOnly = true,
- },
- }
- };
-
-// Invoke chat and display messages.
-ChatMessageContent message = new(AuthorRole.User, "concept: maps made out of egg cartons.");
-chat.AddChatMessage(message);
-AgentServiceHandler.WriteAgentChatMessage(message);
-
-await foreach (ChatMessageContent responseMessage in chat.InvokeAsync())
-{
- AgentServiceHandler.WriteAgentChatMessage(responseMessage);
-}
-
-Console.WriteLine($"\n[IS COMPLETED: {chat.IsComplete}]");
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgentSKWPF-20-CreativeWriter.csproj b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgentSKWPF-20-CreativeWriter.csproj
deleted file mode 100644
index cb2e540d..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgentSKWPF-20-CreativeWriter.csproj
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
- WinExe
- net9.0-windows7.0
- enable
- enable
- true
- ac3f7570-8539-4dd0-83dc-84de2a5e11ac
- AgentsSK_20_CreativeWriter
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/ChatWorkflows/CreativeWriterChatWorkflowFactory.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/ChatWorkflows/CreativeWriterChatWorkflowFactory.cs
deleted file mode 100644
index 62470c46..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/ChatWorkflows/CreativeWriterChatWorkflowFactory.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using Microsoft.SemanticKernel;
-using Microsoft.SemanticKernel.Agents;
-using Microsoft.SemanticKernel.Agents.Chat;
-using Microsoft.SemanticKernel.Plugins.Web;
-using Microsoft.SemanticKernel.Plugins.Web.Bing;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.SemanticKernel.Connectors.OpenAI;
-using System.ComponentModel.DataAnnotations;
-using AgentsSK_20_CreativeWriter.Helpers;
-
-
-#pragma warning disable SKEXP0110, SKEXP0001, SKEXP0050, CS8600, CS8604
-
-namespace AgentsSK_20_CreativeWriter;
-
-public static class CreativeWriterChatWorkflowFactory
-{
- private const string Researcher = "Researcher";
- private const string CreativeWriter = "CreativeWriter";
- private const string Critic = "Critic";
- private const string TerminationKeyword = "approved";
-
- // Lazy Kernel initialization
- private static Kernel? _kernel;
- public static Kernel Kernel => _kernel ??= CreateKernel();
-
- // Create the Kernel lazily using the environment variables
- private static Kernel CreateKernel()
- {
- var builder = Kernel.CreateBuilder();
- builder.Services.AddSingleton();
-
- Kernel kernel = builder.AddAzureOpenAIChatCompletion(
- deploymentName: EnvironmentWellKnown.DeploymentName,
- endpoint: EnvironmentWellKnown.Endpoint,
- apiKey: EnvironmentWellKnown.ApiKey)
- .Build();
-
- BingConnector bing = new BingConnector(EnvironmentWellKnown.BingApiKey);
- kernel.ImportPluginFromObject(new WebSearchEnginePlugin(bing), "bing");
-
- return kernel;
- }
-
- public static IAgentGroupChat CreateChat(int characterLimit = 2000, int maxIterations = 3)
- {
- // Create agents using separate methods
- ChatCompletionAgent researcher = CreateResearcherAgent();
- ChatCompletionAgent creativeWriter = CreateCreativeWriterAgent(maxIterations);
- ChatCompletionAgent critic = CreateCriticAgent();
-
- var twoAgentChat = new ResearcherWriterCriticChat(
- researcher,
- creativeWriter,
- critic,
- maxIterations,
- TerminationKeyword);
-
- twoAgentChat.IsComplete = false;
-
- return twoAgentChat;
- }
-
-
- // Method to create the CreativeWriter Agent
- private static ChatCompletionAgent CreateCreativeWriterAgent(int maxIterations = 3)
- {
- string copyWriterInstructions = $"""
- You are a writer with ten years of experience and are known for brevity and a dry humor.
- The goal is to refine and decide on the single best copy as an expert in the field.
- The researcher will share with you information from online sources, that you will use in your creative process.
- Only provide a single proposal per response.
- You're laser focused on the goal at hand.
-
- The whole creative process should not take more than {maxIterations} iterations.
- """;
-
- return new ChatCompletionAgent
- {
- Instructions = copyWriterInstructions,
- Name = CreativeWriter,
- Kernel = Kernel,
- };
- }
-
- private static ChatCompletionAgent CreateResearcherAgent()
- {
- string researcherInstructions = $"""
- You are an expert searching the web.
- Research using the Bing Search plugin search engine on the requested topic.
- Always return the search results and the source Urls from the found documentation.
- Only return the results of the search results including the following information:
- - Search Result: [the result from the search query]
- - Source Url: [source Url]
- - Result Content: [if available]
- - Context: [if available]
- Do not return any other text.
- """;
-
- var researcherAgent = new ChatCompletionAgent()
- {
- Instructions = researcherInstructions,
- Name = Researcher,
- Kernel = Kernel,
- Arguments = new KernelArguments(
- new OpenAIPromptExecutionSettings()
- {
- ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
- }),
- };
- return researcherAgent;
- }
-
- private static ChatCompletionAgent CreateCriticAgent()
- {
- const string metaReviewerInstructions = @$"You are a critic reviewer. You aggregate and review the work of other writers and give a summarized final review from writer on the content.
-Your goal is to determine if the given copy is acceptable to print.
-If so, state that it is approved. Say ""{TerminationKeyword}"" to approve the copy.";
-
- return new ChatCompletionAgent
- {
- Instructions = metaReviewerInstructions,
- Name = Critic,
- Kernel = Kernel,
- };
- }
-}
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/EnvironmentWellKnown.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/EnvironmentWellKnown.cs
deleted file mode 100644
index 231a8b3a..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/EnvironmentWellKnown.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace AgentsSK_20_CreativeWriter;
-
-public static class EnvironmentWellKnown
-{
- // create a contructor to initialize the static variables
- static EnvironmentWellKnown()
- {
- var config = new ConfigurationBuilder().AddUserSecrets().Build();
- _deploymentName = config["AZURE_OPENAI_DEPLOYMENT"];
- _endpoint = config["AZURE_OPENAI_ENDPOINT"];
- _apiKey = config["AZURE_OPENAI_APIKEY"];
- _bingApiKey = config["BING_SEARCH_KEY"];
- }
-
- private static string? _deploymentName;
- public static string DeploymentName => _deploymentName ??= Environment.GetEnvironmentVariable("AzureOpenAI_Model");
-
- private static string? _endpoint;
- public static string Endpoint => _endpoint ??= Environment.GetEnvironmentVariable("AzureOpenAI_Endpoint");
-
- private static string? _apiKey;
- public static string ApiKey => _apiKey ??= Environment.GetEnvironmentVariable("AzureOpenAI_ApiKey");
-
- private static string? _bingApiKey;
- public static string BingApiKey => _bingApiKey ??= Environment.GetEnvironmentVariable("Bing_ApiKey");
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/StringToFlowDocumentConverter.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/StringToFlowDocumentConverter.cs
deleted file mode 100644
index a6f3da66..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticChatWorkflows/StringToFlowDocumentConverter.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Data;
-using System.Windows.Documents;
-
-namespace AgentsSK_20_CreativeWriter;
-
-public class StringToFlowDocumentConverter : IValueConverter
-{
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- if (value is string stringValue)
- {
- FlowDocument doc = new FlowDocument();
- doc.Blocks.Add(new Paragraph(new Run(stringValue)));
- return doc;
- }
- return null;
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
- {
- if (value is FlowDocument document)
- {
- TextRange textRange = new TextRange(document.ContentStart, document.ContentEnd);
- return textRange.Text.Trim(); // return string representation of FlowDocument
- }
- return string.Empty;
- }
-}
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticWorkflowModel.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticWorkflowModel.cs
deleted file mode 100644
index 446856ac..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AgenticWorkflowModel.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.ComponentModel;
-using Microsoft.SemanticKernel;
-using Microsoft.SemanticKernel.Agents;
-using Microsoft.SemanticKernel.Agents.OpenAI;
-using Microsoft.SemanticKernel.ChatCompletion;
-using Microsoft.SemanticKernel.Connectors.OpenAI;
-using Microsoft.SemanticKernel.Agents.Chat;
-using Microsoft.SemanticKernel.Plugins.Web;
-using Microsoft.SemanticKernel.Plugins.Web.Bing;
-using System.Net;
-using System.Threading;
-using Microsoft.Extensions.DependencyInjection;
-using System.Windows.Controls;
-using System.Windows.Documents;
-using System.Reflection.Metadata;
-using System.Windows.Media;
-
-#pragma warning disable SKEXP0110, SKEXP0001, SKEXP0050, CS8600, CS8604
-
-namespace AgentsSK_20_CreativeWriter;
-
-class AgenticWorkflowModel : INotifyPropertyChanged
-{
- testWindow02? mainWindow;
-
- private int _CharacterLimit = 2000;
- public IAgentGroupChat? ChatWorkflow { get; private set; }
-
- public int CharacterLimit
- {
- get { return _CharacterLimit; }
- set
- {
- if (_CharacterLimit != value)
- {
- _CharacterLimit = value;
- OnPropertyChanged("CharacterLimit");
- }
- }
- }
-
- public event PropertyChangedEventHandler? PropertyChanged;
-
- protected virtual void OnPropertyChanged(string propertyName)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- private string _Question = "Get info about Canadian popular stories for kids, and create a kids' story for programmers to learn more about this. the story should be short, 2 paragraphs";
- public string Question
- {
- get { return _Question; }
- set
- {
- if (_Question != value)
- {
- _Question = value;
- OnPropertyChanged("Question");
- }
- }
- }
-
- public AgenticWorkflowModel(testWindow02 mainWindow)
- {
- this.mainWindow = mainWindow;
- ChatWorkflow = CreativeWriterChatWorkflowFactory.CreateChat(CharacterLimit);
- }
-
- public async Task askQuestion()
- {
- if (ChatWorkflow == null)
- {
- updateResponseBox("Error", "No chat workflow is initialized for the selected workflow.");
- return;
- }
-
- string input = Question;
- ChatWorkflow.AddChatMessage(new ChatMessageContent(AuthorRole.User, input));
-
- updateResponseBox("Question", input);
-
- string finalAnswer = "";
-
- await foreach (var content in ChatWorkflow.InvokeAsync())
- {
- Color color;
- switch (content.AuthorName)
- {
- case "Researcher":
- color = Colors.DarkOrange;
- break;
- case "CreativeWriter":
- color = Colors.DarkGreen;
- break;
- case "Critic":
- color = Colors.DarkRed;
- break;
- default:
- color = Colors.DarkSlateBlue;
- break;
- }
- updateResponseBox(content.AuthorName, content.Content, color);
- }
- }
-
- public void updateResponseBox(string sender, string response)
- {
- updateResponseBox(sender, response, Colors.Black);
- }
-
- public void updateResponseBox(string sender, string response, Color color)
- {
- //Update mainWindow.ResponseBox to add the sender in bold, a colon, a space, and the response in normal text
- Paragraph paragraph = new Paragraph();
- Bold bold = new Bold(new Run(sender + ": "));
-
- bold.Foreground = new SolidColorBrush(color);
-
- paragraph.Inlines.Add(bold);
- Run run = new Run(response);
- paragraph.Inlines.Add(run);
- mainWindow.ResponseBox.Document.Blocks.Add(paragraph);
-
- // Scroll to the end after adding new content
- ScrollToEnd(mainWindow.ResponseBox);
- }
-
- private void ScrollToEnd(RichTextBox richTextBox)
- {
- if (richTextBox != null)
- {
- // Use Dispatcher to ensure it's invoked on the UI thread
- richTextBox.Dispatcher.BeginInvoke(new Action(() =>
- {
- richTextBox.ScrollToEnd();
- }));
- }
- }
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml
deleted file mode 100644
index 5655993f..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml.cs
deleted file mode 100644
index a4051b28..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/App.xaml.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System.Configuration;
-using System.Data;
-using System.Windows;
-
-namespace AgentsSK_20_CreativeWriter;
-
- ///
- /// Interaction logic for App.xaml
- ///
- public partial class App : Application
- {
- }
-
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/ApprovalTerminationStrategy.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/ApprovalTerminationStrategy.cs
deleted file mode 100644
index 791700b8..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/ApprovalTerminationStrategy.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Microsoft.SemanticKernel.Agents.Chat;
-using Microsoft.SemanticKernel.Agents;
-using Microsoft.SemanticKernel;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-#pragma warning disable SKEXP0110, SKEXP0001
-
-namespace AgentsSK_20_CreativeWriter;
-
-class ApprovalTerminationStrategy : TerminationStrategy
-{
- // Terminate when the final message contains the term "approve"
- protected override Task ShouldAgentTerminateAsync(Agent agent, IReadOnlyList history, CancellationToken cancellationToken)
- => Task.FromResult(history[history.Count - 1].Content?.Contains("approve", StringComparison.OrdinalIgnoreCase) ?? false);
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AssemblyInfo.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AssemblyInfo.cs
deleted file mode 100644
index b0ec8275..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/AssemblyInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Windows;
-
-[assembly: ThemeInfo(
- ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
- //(used if a resource is not found in the page,
- // or application resource dictionaries)
- ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
- //(used if a resource is not found in the page,
- // app, or any theme specific resource dictionaries)
-)]
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/Helpers/EnvironmentWellKnown.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/Helpers/EnvironmentWellKnown.cs
deleted file mode 100644
index 029bbe52..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/Helpers/EnvironmentWellKnown.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace AgentsSK_20_CreativeWriter.Helpers;
-
-public static class EnvironmentWellKnown
-{
- static EnvironmentWellKnown()
- {
- var config = new ConfigurationBuilder().AddUserSecrets().Build();
- _deploymentName = config["AZURE_OPENAI_DEPLOYMENT"];
- _endpoint = config["AZURE_OPENAI_ENDPOINT"];
- _apiKey = config["AZURE_OPENAI_APIKEY"];
- _bingApiKey = config["BING_SEARCH_KEY"];
- }
-
-
- private static string? _deploymentName;
- public static string DeploymentName => _deploymentName ??= Environment.GetEnvironmentVariable("AzureOpenAI_Model");
-
- private static string? _endpoint;
- public static string Endpoint => _endpoint ??= Environment.GetEnvironmentVariable("AzureOpenAI_Endpoint");
-
- private static string? _apiKey;
- public static string ApiKey => _apiKey ??= Environment.GetEnvironmentVariable("AzureOpenAI_ApiKey");
-
- private static string? _bingApiKey;
- public static string BingApiKey => _bingApiKey ??= Environment.GetEnvironmentVariable("Bing_ApiKey");
-}
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SearchFunctionFilter.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SearchFunctionFilter.cs
deleted file mode 100644
index 5135f5b6..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SearchFunctionFilter.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Microsoft.SemanticKernel;
-
-#pragma warning disable SKEXP0001, SKEXP0050, SKEXP0110
-
-namespace AgentsSK_20_CreativeWriter;
-
-class SearchFunctionFilter : IFunctionInvocationFilter
-{
- public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func next)
- {
- // We'll restore the color after we're done
- var prevColor = Console.ForegroundColor;
-
- // Indicate that the assistant is calling a function, but only if it's not an internal function
- var isInternal = context.Function.Name.StartsWith("internal_");
- if (!isInternal)
- {
- var args = context.Arguments.Select(x => $"\"{x.Key}\": \"{x.Value}\"") ?? new List();
- var json = "{" + string.Join(",", args) + "}";
-
- Console.ForegroundColor = ConsoleColor.DarkGray;
- Console.Write($"\rassistant-function: {context.Function.Name}({json}) = ");
- }
-
- // Call the next middleware in the pipeline
- await next(context);
-
- // Indicate that the assistant has finished calling the function, but only if it's not an internal function
- if (!isInternal)
- {
- try
- {
- var result = context.Result.GetValue() ?? string.Empty;
- Console.WriteLine(result);
-
- Console.ForegroundColor = prevColor;
- Console.Write("\nAssistant: ");
- }
- catch (Exception)
- {
- }
- }
- }
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/AgentGroupChatExt.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/AgentGroupChatExt.cs
deleted file mode 100644
index 9d22f767..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/AgentGroupChatExt.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using Microsoft.SemanticKernel.Agents.Chat;
-using Microsoft.SemanticKernel.ChatCompletion;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.SemanticKernel.Agents;
-
-#pragma warning disable SKEXP0110
-
-public class AgentGroupChatExt : IAgentGroupChat
-{
- private readonly AgentGroupChat _agentGroupChat;
-
- public AgentGroupChatExt(params Agent[] agents)
- {
- _agentGroupChat = new AgentGroupChat(agents);
- }
-
- public AgentGroupChatExt(AgentGroupChat agentGroupChat)
- {
- _agentGroupChat = agentGroupChat;
- }
-
- // Expose the ChatHistory from the underlying _agentGroupChat
- //public ChatHistory History => _agentGroupChat.History; //doesn't work as History is protected
- public ChatHistory History => _agentGroupChat.GetType().GetProperty("History", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(_agentGroupChat) as ChatHistory;
-
- // Properties from IAgentChat
- public bool IsActive => _agentGroupChat.IsActive;
-
- public bool IsComplete
- {
- get => _agentGroupChat.IsComplete;
- set => _agentGroupChat.IsComplete = value;
- }
-
- public AgentGroupChatSettings ExecutionSettings
- {
- get => _agentGroupChat.ExecutionSettings;
- set => _agentGroupChat.ExecutionSettings = value;
- }
-
- public IReadOnlyList Agents => _agentGroupChat.Agents;
-
- // Methods from IAgentChat
- public Task ResetAsync(CancellationToken cancellationToken = default)
- {
- return _agentGroupChat.ResetAsync(cancellationToken);
- }
-
- public void AddChatMessage(ChatMessageContent message)
- {
- _agentGroupChat.AddChatMessage(message);
- }
-
- public void AddChatMessages(IReadOnlyList messages)
- {
- _agentGroupChat.AddChatMessages(messages);
- }
-
- public IAsyncEnumerable GetChatMessagesAsync(CancellationToken cancellationToken = default)
- {
- return _agentGroupChat.GetChatMessagesAsync(cancellationToken);
- }
-
- public IAsyncEnumerable GetChatMessagesAsync(Agent agent, CancellationToken cancellationToken = default)
- {
- return _agentGroupChat.GetChatMessagesAsync(agent, cancellationToken);
- }
-
- // Methods from IAgentGroupChat
- public void AddAgent(Agent agent)
- {
- _agentGroupChat.AddAgent(agent);
- }
-
- public async IAsyncEnumerable InvokeAsync([EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- await foreach (var item in _agentGroupChat.InvokeAsync(cancellationToken).WithCancellation(cancellationToken))
- {
- yield return item;
- }
- }
-
- public async IAsyncEnumerable InvokeStreamingAsync([EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- await foreach (var item in _agentGroupChat.InvokeStreamingAsync(cancellationToken).WithCancellation(cancellationToken))
- {
- yield return item;
- }
- }
-
- public async IAsyncEnumerable InvokeStreamingAsync(Agent agent, [EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- await foreach (var item in _agentGroupChat.InvokeStreamingAsync(agent, cancellationToken).WithCancellation(cancellationToken))
- {
- yield return item;
- }
- }
-
- public async IAsyncEnumerable InvokeAsync(Agent agent, [EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- await foreach (var item in _agentGroupChat.InvokeAsync(agent, cancellationToken).WithCancellation(cancellationToken))
- {
- yield return item;
- }
- }
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/BaseAgentGroupChat.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/BaseAgentGroupChat.cs
deleted file mode 100644
index a24bf5b2..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/BaseAgentGroupChat.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Microsoft.SemanticKernel.Agents.Chat;
-using Microsoft.SemanticKernel.ChatCompletion;
-
-#pragma warning disable SKEXP0110
-
-namespace Microsoft.SemanticKernel.Agents;
-
-public abstract class BaseAgentGroupChat : IAgentGroupChat
-{
- protected readonly IAgentGroupChat _agentGroupChat;
-
- protected BaseAgentGroupChat(params ChatCompletionAgent[] agents)
- {
- _agentGroupChat = new AgentGroupChatExt(agents);
- }
-
- // Properties from IAgentChat
- public bool IsActive => _agentGroupChat.IsActive;
-
- // Properties from IAgentGroupChat
- public ChatHistory History => _agentGroupChat.History;
-
- public bool IsComplete
- {
- get => _agentGroupChat.IsComplete;
- set => _agentGroupChat.IsComplete = value;
- }
-
- public AgentGroupChatSettings ExecutionSettings
- {
- get => _agentGroupChat.ExecutionSettings;
- set => _agentGroupChat.ExecutionSettings = value;
- }
-
- public IReadOnlyList Agents => _agentGroupChat.Agents;
-
- // Methods from IAgentChat
- public virtual Task ResetAsync(CancellationToken cancellationToken = default)
- {
- return _agentGroupChat.ResetAsync(cancellationToken);
- }
-
- public virtual void AddChatMessage(ChatMessageContent message)
- {
- _agentGroupChat.AddChatMessage(message);
- }
-
- public virtual void AddChatMessages(IReadOnlyList messages)
- {
- _agentGroupChat.AddChatMessages(messages);
- }
-
- public virtual IAsyncEnumerable GetChatMessagesAsync(CancellationToken cancellationToken = default)
- {
- return _agentGroupChat.GetChatMessagesAsync(cancellationToken);
- }
-
- public virtual IAsyncEnumerable GetChatMessagesAsync(Agent agent, CancellationToken cancellationToken = default)
- {
- return _agentGroupChat.GetChatMessagesAsync(agent, cancellationToken);
- }
-
- // Methods from IAgentGroupChat
- public virtual void AddAgent(Agent agent)
- {
- _agentGroupChat.AddAgent(agent);
- }
-
- // Abstract methods to be implemented by derived classes
- public abstract IAsyncEnumerable InvokeAsync(CancellationToken cancellationToken = default);
-
- public abstract IAsyncEnumerable InvokeStreamingAsync(CancellationToken cancellationToken = default);
-
- public virtual IAsyncEnumerable InvokeAsync(Agent agent, CancellationToken cancellationToken = default)
- {
- return _agentGroupChat.InvokeAsync(agent, cancellationToken);
- }
-
- public virtual IAsyncEnumerable InvokeStreamingAsync(Agent agent, CancellationToken cancellationToken = default)
- {
- return _agentGroupChat.InvokeStreamingAsync(agent, cancellationToken);
- }
-
-}
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentChat.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentChat.cs
deleted file mode 100644
index 506ed908..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentChat.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Microsoft.SemanticKernel.ChatCompletion;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.SemanticKernel.Agents;
-
-#pragma warning disable SKEXP0110
-public interface IAgentChat
-{
- bool IsActive { get; }
-
- Task ResetAsync(CancellationToken cancellationToken = default);
-
- IAsyncEnumerable GetChatMessagesAsync(CancellationToken cancellationToken = default);
- IAsyncEnumerable GetChatMessagesAsync(Agent agent, CancellationToken cancellationToken = default);
-
- void AddChatMessage(ChatMessageContent message);
- void AddChatMessages(IReadOnlyList messages);
-
- IAsyncEnumerable InvokeAsync(CancellationToken cancellationToken = default);
- IAsyncEnumerable InvokeStreamingAsync(CancellationToken cancellationToken = default);
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentGroupChat.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentGroupChat.cs
deleted file mode 100644
index 5a33364a..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IAgentGroupChat.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Microsoft.SemanticKernel.Agents.Chat;
-using Microsoft.SemanticKernel.ChatCompletion;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-//namespace NovelCrafter.SemanticKernel;
-namespace Microsoft.SemanticKernel.Agents;
-
-#pragma warning disable SKEXP0110, SKEXP0001, SKEXP0050, CS8600, CS8604
-
-public interface IAgentGroupChat : IAgentChat
-{
- bool IsComplete { get; set; }
- ChatHistory History { get; }
-
- AgentGroupChatSettings ExecutionSettings { get; set; }
- IReadOnlyList Agents { get; }
-
- void AddAgent(Agent agent);
-
- IAsyncEnumerable InvokeAsync(CancellationToken cancellationToken = default);
-
- IAsyncEnumerable InvokeAsync(Agent agent, CancellationToken cancellationToken = default);
-
- IAsyncEnumerable InvokeStreamingAsync(CancellationToken cancellationToken = default);
-
- IAsyncEnumerable InvokeStreamingAsync(Agent agent, CancellationToken cancellationToken = default);
-}
-
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IChatMiddleware.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IChatMiddleware.cs
deleted file mode 100644
index 828c1879..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/IChatMiddleware.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Microsoft.SemanticKernel.Agents;
-
-public interface IChatMiddleware
-{
- ///
- /// the name of the middleware
- ///
- public string? Name { get; }
-
- ///
- /// Processes a message and optionally invokes the next middleware or the target agent.
- ///
- /// The message to process.
- /// Delegate to invoke the next middleware or agent.
- /// Cancellation token.
- /// The processed message.
- Task InvokeAsync(
- ChatMessageContent message,
- Func> next,
- CancellationToken cancellationToken = default);
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResearcherWriterCriticChat.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResearcherWriterCriticChat.cs
deleted file mode 100644
index d0ff22d8..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResearcherWriterCriticChat.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Microsoft.SemanticKernel.Agents;
-
-#pragma warning disable SKEXP0110
-
-public class ResearcherWriterCriticChat : BaseAgentGroupChat, IAgentGroupChat
-{
- private readonly ChatCompletionAgent _researcher;
- private readonly ChatCompletionAgent _writer;
- private readonly ChatCompletionAgent _criticAgent;
- private readonly int _maxIterations;
- private readonly string _terminationKeyword;
-
- public ResearcherWriterCriticChat(
- ChatCompletionAgent researcher,
- ChatCompletionAgent writer,
- ChatCompletionAgent criticAgent,
- int maxIterations,
- string terminationKeyword)
- : base(writer, criticAgent)
- {
- _researcher = researcher;
- _writer = writer;
- _criticAgent = criticAgent;
- _maxIterations = maxIterations;
- _terminationKeyword = terminationKeyword.ToLower();
- }
-
- public override async IAsyncEnumerable InvokeAsync([EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- for (int iteration = 0; iteration < _maxIterations; iteration++)
- {
- // researcher searchs for content online
-
- await foreach (var researcherMessage in _agentGroupChat.InvokeAsync(_researcher, cancellationToken).WithCancellation(cancellationToken))
- {
- yield return researcherMessage;
- }
-
- // Writer uses that content to produce a message
- await foreach (var workerMessage in _agentGroupChat.InvokeAsync(_writer, cancellationToken).WithCancellation(cancellationToken))
- {
- yield return workerMessage;
- }
-
- // CriticAgent responds to the WorkerAgent's message
- await foreach (var criticMessage in _agentGroupChat.InvokeAsync(_criticAgent, cancellationToken).WithCancellation(cancellationToken))
- {
- yield return criticMessage;
-
- if (CheckTermination(criticMessage))
- {
- IsComplete = true;
- yield break;
- }
- }
-
- if (IsComplete)
- {
- break;
- }
- }
- }
-
- public override IAsyncEnumerable InvokeStreamingAsync(CancellationToken cancellationToken = default)
- {
- // Not implemented
- throw new NotImplementedException();
- }
-
- private bool CheckTermination(ChatMessageContent message)
- {
- if (message.Content.ToLower().Contains(_terminationKeyword))
- {
- IsComplete = true;
-
- return true;
- }
-
- return false;
- }
-
-}
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResultProcessing.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResultProcessing.cs
deleted file mode 100644
index f7091149..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/SemanticKernel/ResultProcessing.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Microsoft.SemanticKernel.Agents;
-
-public enum ResultProcessing
-{
- Append,
- Replace
-}
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml
deleted file mode 100644
index fb46d901..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml.cs b/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml.cs
deleted file mode 100644
index d93e8581..00000000
--- a/04-Practical.NETGenAISamples/src/AgentSKWPF-20-CreativeWriter/testWindow02.xaml.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using AgentsSK_20_CreativeWriter;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Shapes;
-
-namespace AgentsSK_20_CreativeWriter;
-
-///
-/// Interaction logic for testWindow02.xaml
-///
-public partial class testWindow02 : Window
-{
-
- private CancellationTokenSource _cts; // For debouncing
-
- // Store the widths for each expander
- private Dictionary expanderWidths = new Dictionary
- {
- { "Code", 400 },
- { "Configuration", 250 }
- };
- private readonly double _collapsedWidth = 56; // Width for the collapsed header
- private AgenticWorkflowModel? agenticWorkflowModel;
-
- public testWindow02()
- {
- InitializeComponent();
- _cts = new CancellationTokenSource();
- agenticWorkflowModel = new AgenticWorkflowModel(this);
-
- this.DataContext = agenticWorkflowModel;
- }
-
-
- private async void AskButton_Click(object sender, RoutedEventArgs e)
- {
- // Disable the Ask button
- AskButton.IsEnabled = false;
-
- // Call the askQuestion method
- await agenticWorkflowModel?.askQuestion();
-
- // Re-enable the Ask button after the process is finished
- AskButton.IsEnabled = true;
-
- }
-
- private void QuestionBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
- {
- if (e.Key == System.Windows.Input.Key.Enter)
- {
- AskButton_Click(sender, e);
- }
- }
-}
diff --git a/04-Practical.NETGenAISamples/src/AzureAIAgents-04-FileSearch/AzureAIAgents-04-FileSearch.csproj b/04-Practical.NETGenAISamples/src/AzureAIAgents-04-FileSearch/AzureAIAgents-04-FileSearch.csproj
deleted file mode 100644
index ff6745dd..00000000
--- a/04-Practical.NETGenAISamples/src/AzureAIAgents-04-FileSearch/AzureAIAgents-04-FileSearch.csproj
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
- Exe
- net9.0
- AgentLabs_04_RAG
- enable
- enable
- 18358710-10ea-480a-83c6-8164c311a263
-
-
-
-
-
-
-
-
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
-
-
diff --git a/04-Practical.NETGenAISamples/src/AzureAIAgents-04-FileSearch/Program.cs b/04-Practical.NETGenAISamples/src/AzureAIAgents-04-FileSearch/Program.cs
deleted file mode 100644
index 8c060132..00000000
--- a/04-Practical.NETGenAISamples/src/AzureAIAgents-04-FileSearch/Program.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using Azure;
-using Azure.AI.Projects;
-using Azure.Identity;
-using Microsoft.Extensions.Configuration;
-using System.Collections.Concurrent;
-
-// Create Agent Client
-var config = new ConfigurationBuilder().AddUserSecrets().Build();
-var options = new DefaultAzureCredentialOptions
-{
- ExcludeEnvironmentCredential = true,
- ExcludeWorkloadIdentityCredential = true,
- TenantId = config["tenantid"]
-};
-var connectionString = config["connectionString"];
-var client = new AgentsClient(connectionString, new DefaultAzureCredential(options));
-
-// upload the files and get the file Ids
-var fileIds = new ConcurrentBag();
-var fileToId = new ConcurrentDictionary();
-var files = Directory.EnumerateFiles("products");
-
-await Task.WhenAll(files.Select(async file =>
-{
- var uploadFileResponse = await client.UploadFileAsync(filePath: file, purpose: AgentFilePurpose.Agents);
- fileIds.Add(uploadFileResponse.Value.Id);
- fileToId.TryAdd(file, uploadFileResponse.Value.Id);
-}));
-
-// Create a vector store with the files and wait for it to be processed.
-VectorStore vectorStore = await client.CreateVectorStoreAsync(
- fileIds: fileIds.ToList(),
- name: "products_vector_store");
-
-FileSearchToolResource fileSearchToolResource = new FileSearchToolResource();
-fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id);
-
-// Create an agent with toolResources and process assistant run
-Response agentResponse = await client.CreateAgentAsync(
- model: "gpt-4o-mini",
- name: "Outdoor Sales Assistants",
- instructions: "You are an Sales Assistant working in an Outdoor Sales Company.",
- tools: new List { new FileSearchToolDefinition() },
- toolResources: new ToolResources() { FileSearch = fileSearchToolResource });
-Agent agent = agentResponse.Value;
-
-// Create thread for communication
-Response threadResponse = await client.CreateThreadAsync();
-AgentThread thread = threadResponse.Value;
-
-// user question
-Response userMessageResponse = await client.CreateMessageAsync(
- thread.Id,
- MessageRole.User,
- "I want to go camping, but it seems that it maybe raining, what do you suggest to go camping on rainy days?");
-ThreadMessage userMessage = userMessageResponse.Value;
-
-// agent task to answer the question
-Response agentMessageResponse = await client.CreateMessageAsync(
- thread.Id,
- MessageRole.Agent,
- "Please address the user as their name. The user has a basic account. Suggest products from the catalog when it's possible. Include any specific details of the suggested products like name, description and price.");
-ThreadMessage agentMessage = agentMessageResponse.Value;
-
-// run the agent thread
-Response runResponse = await client.CreateRunAsync(
- thread.Id,
- assistantId: agent.Id
- );
-ThreadRun run = runResponse.Value;
-
-// wait for the response
-do
-{
- await Task.Delay(TimeSpan.FromMilliseconds(100));
- runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id);
- Console.Clear();
- Console.WriteLine($"{DateTime.Now} Run status: {runResponse.Value.Status}");
-}
-while (runResponse.Value.Status == RunStatus.Queued
- || runResponse.Value.Status == RunStatus.InProgress);
-Console.Clear();
-
-// show the response
-Response> afterRunMessagesResponse = await client.GetMessagesAsync(thread.Id);
-IReadOnlyList messages = afterRunMessagesResponse.Value.Data;
-
-// sort the messages by creation date
-messages = messages.OrderBy(m => m.CreatedAt).ToList();
-
-// Note: messages iterate from newest to oldest, with the messages[0] being the most recent
-foreach (ThreadMessage threadMessage in messages)
-{
- Console.WriteLine($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
- foreach (MessageContent contentItem in threadMessage.ContentItems)
- {
- if (contentItem is MessageTextContent textItem)
- {
- Console.WriteLine(textItem.Text);
-
- // if there is any annotations, show them in the console
- if (textItem.Annotations != null && textItem.Annotations.Count > 0)
- {
- Console.WriteLine("");
- Console.WriteLine(">> Annotations:");
- foreach (MessageTextFileCitationAnnotation annotation in textItem.Annotations)
- {
- string fileName = fileToId.FirstOrDefault(x => x.Value == annotation.FileId).Key;
- Console.WriteLine($"\tFile Name [Id]: {fileName} [{annotation.FileId}]");
- Console.WriteLine($"\tStart-End: {annotation.StartIndex}-{annotation.EndIndex}");
- Console.WriteLine($"\tText: {annotation.Text}");
- }
- }
- }
- else if (contentItem is MessageImageFileContent imageFileItem)
- {
- Console.WriteLine($"
-
Lesson 4: Generative AI Samples
-
Azure AI Agents - Guide
-
Use Azure in a Compact sample!
-
-
-## Program.cs Functionality
-
-The `Program.cs` file demonstrates how to use the Azure AI Projects SDK to create an AI agent that can assist with sales inquiries. Below is a detailed explanation of the functionality implemented in the file.
-
-### Prerequisites
-- .NET 9
-- Azure AI Foundry Project connection string
-- Azure tenant ID
-
-This project requires the following user secrets
-- Azure AI Foundry Project connection string.
- At the moment, it should be in the format:
-
- ```
- ;;;
- ```
-
-- Azure tenant id
-
-Set them using the following commands:
-
-```bash
-dotnet user-secrets init
-dotnet user-secrets set "connectionstring" ";;;"
-dotnet user-secrets set "tenantid" ""
-```
-
-### Steps
-
-1. **Configuration and Secrets Setup**
- - Read user secrets
-
-2. **Create Agent Client**
- - Build the configuration and set up the `DefaultAzureCredentialOptions` with the tenant ID.
- - Create an `AgentsClient` using the connection string and credentials.
-
-3. **Upload Files and Get File IDs**
- - Enumerate files in the "products" directory.
- - Upload each file asynchronously and store the file IDs in a `ConcurrentBag`.
-
-4. **Create Vector Store**
- - Create a vector store with the uploaded file IDs and wait for it to be processed.
-
-5. **Create Agent**
- - Define the agent model, name, instructions, tools, and tool resources.
- - Create the agent using the `AgentsClient`.
-
-6. **Create Communication Thread**
- - Create a thread for communication with the agent.
-
-7. **User Question and Agent Response**
- - Send a user question to the agent.
- - Define the agent's task to answer the question, including addressing the user by name and suggesting products from the catalog.
-
-8. **Run the Agent Thread**
- - Run the agent thread and wait for the response.
- - Display the response messages, including any annotations and images.
-
-### Main Flow Layout
-
-```mermaid
-flowchart TD
- A[Start] --> B[Create Agent Client]
- B --> C[Upload Files and Get File IDs]
- C --> D[Create Vector Store with Files]
- D --> E[Create FileSearchToolResource]
- E --> F[Create Agent with Tool Resources]
- F --> G[Create Thread for Communication]
- G --> H[User Question]
- H --> I[Agent Task to Answer Question]
- I --> J[Run the Agent Thread]
- J --> K[Wait for Response]
- K --> L[Show the Response]
- L --> M[Sort Messages by Creation Date]
- M --> N[Display Messages and Annotations]
- N --> O[End]
-```
-
-### References
-For more detailed information, refer to the [Azure AI Projects Documentation](https://learn.microsoft.com/en-us/dotnet/api/overview/azure/ai.projects-readme?context=%2Fazure%2Fai-services%2Fagents%2Fcontext%2Fcontext&view=azure-dotnet-preview).
-
-
diff --git a/04-Practical.NETGenAISamples/src/AzureAIAgents.sln b/04-Practical.NETGenAISamples/src/AzureAIAgents.sln
deleted file mode 100644
index a742cd82..00000000
--- a/04-Practical.NETGenAISamples/src/AzureAIAgents.sln
+++ /dev/null
@@ -1,52 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.12.35527.113
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureAIAgents-04-FileSearch", "AzureAIAgents-04-FileSearch\AzureAIAgents-04-FileSearch.csproj", "{DDAB528B-69F0-493F-A1F8-54C72D0E7C6D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentSKLabs-04-FileSearch", "AgentSKLabs-04-FileSearch\AgentSKLabs-04-FileSearch.csproj", "{C9261BB2-2EB3-4473-AF4B-A7B6D90A6228}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Semantic Kernel", "Semantic Kernel", "{83985DB5-EA76-4D9F-BC40-9A346907CF73}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure AI Agent", "Azure AI Agent", "{31A05A14-899C-4FE7-872F-D4927408AF4E}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentSKLabs-10-CreativeWriter", "AgentSKLabs-10-CreativeWriter\AgentSKLabs-10-CreativeWriter.csproj", "{AE56A27F-6DBE-4967-9686-0FEE43EDC712}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentSKWPF-20-CreativeWriter", "AgentSKWPF-20-CreativeWriter\AgentSKWPF-20-CreativeWriter.csproj", "{04FE5C29-9B6E-4991-90E3-021661CF981E}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SK Demo Apps", "SK Demo Apps", "{DCF5DA03-2042-4975-9AE8-A022CD87FE34}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {DDAB528B-69F0-493F-A1F8-54C72D0E7C6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DDAB528B-69F0-493F-A1F8-54C72D0E7C6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DDAB528B-69F0-493F-A1F8-54C72D0E7C6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DDAB528B-69F0-493F-A1F8-54C72D0E7C6D}.Release|Any CPU.Build.0 = Release|Any CPU
- {C9261BB2-2EB3-4473-AF4B-A7B6D90A6228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C9261BB2-2EB3-4473-AF4B-A7B6D90A6228}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C9261BB2-2EB3-4473-AF4B-A7B6D90A6228}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C9261BB2-2EB3-4473-AF4B-A7B6D90A6228}.Release|Any CPU.Build.0 = Release|Any CPU
- {AE56A27F-6DBE-4967-9686-0FEE43EDC712}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AE56A27F-6DBE-4967-9686-0FEE43EDC712}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AE56A27F-6DBE-4967-9686-0FEE43EDC712}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AE56A27F-6DBE-4967-9686-0FEE43EDC712}.Release|Any CPU.Build.0 = Release|Any CPU
- {04FE5C29-9B6E-4991-90E3-021661CF981E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {04FE5C29-9B6E-4991-90E3-021661CF981E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {04FE5C29-9B6E-4991-90E3-021661CF981E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {04FE5C29-9B6E-4991-90E3-021661CF981E}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {DDAB528B-69F0-493F-A1F8-54C72D0E7C6D} = {31A05A14-899C-4FE7-872F-D4927408AF4E}
- {C9261BB2-2EB3-4473-AF4B-A7B6D90A6228} = {83985DB5-EA76-4D9F-BC40-9A346907CF73}
- {AE56A27F-6DBE-4967-9686-0FEE43EDC712} = {83985DB5-EA76-4D9F-BC40-9A346907CF73}
- {04FE5C29-9B6E-4991-90E3-021661CF981E} = {DCF5DA03-2042-4975-9AE8-A022CD87FE34}
- EndGlobalSection
-EndGlobal
diff --git a/04-Practical.NETGenAISamples/src/SKShared/AgentServiceHandler.cs b/04-Practical.NETGenAISamples/src/SKShared/AgentServiceHandler.cs
deleted file mode 100644
index 3b89c4f6..00000000
--- a/04-Practical.NETGenAISamples/src/SKShared/AgentServiceHandler.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-#pragma warning disable SKEXP0001, SKEXP0110, OPENAI001
-
-// Local function to invoke agent and display the conversation messages.
-using Microsoft.SemanticKernel.Agents.OpenAI;
-using Microsoft.SemanticKernel.Agents;
-using Microsoft.SemanticKernel.ChatCompletion;
-using Microsoft.SemanticKernel;
-using OpenAI.Assistants;
-using OpenAI.Chat;
-
-namespace AgentLabs;
-
-public class AgentServiceHandler
-{
- public async static Task InvokeAgentAsync(OpenAIAssistantAgent agent, string threadId, string input)
- {
- Microsoft.SemanticKernel.ChatMessageContent message = new(AuthorRole.User, input);
- await agent.AddChatMessageAsync(threadId, message);
- WriteAgentChatMessage(message);
-
- await foreach (Microsoft.SemanticKernel.ChatMessageContent response in agent.InvokeAsync(threadId))
- {
- WriteAgentChatMessage(response);
- }
- }
- public static void WriteAgentChatMessage(Microsoft.SemanticKernel.ChatMessageContent message)
- {
- // Include ChatMessageContent.AuthorName in output, if present.
- string authorExpression = message.Role == AuthorRole.User ? string.Empty : $" - {message.AuthorName ?? "*"}";
-
- // Include TextContent (via ChatMessageContent.Content), if present.
- string contentExpression = string.IsNullOrWhiteSpace(message.Content) ? string.Empty : message.Content;
- bool isCode = message.Metadata?.ContainsKey(OpenAIAssistantAgent.CodeInterpreterMetadataKey) ?? false;
- string codeMarker = isCode ? "\n [CODE]\n" : " ";
- Console.WriteLine($"\n# {message.Role}{authorExpression}:{codeMarker}{contentExpression}");
-
- // Provide visibility for inner content (that isn't TextContent).
- foreach (KernelContent item in message.Items)
- {
- if (item is AnnotationContent annotation)
- {
- Console.WriteLine($" [{item.GetType().Name}] {annotation.Quote}: File #{annotation.FileId}");
- }
- else if (item is FileReferenceContent fileReference)
- {
- Console.WriteLine($" [{item.GetType().Name}] File #{fileReference.FileId}");
- }
- else if (item is ImageContent image)
- {
- Console.WriteLine($" [{item.GetType().Name}] {image.Uri?.ToString() ?? image.DataUri ?? $"{image.Data?.Length} bytes"}");
- }
- else if (item is FunctionCallContent functionCall)
- {
- Console.WriteLine($" [{item.GetType().Name}] {functionCall.Id}");
- }
- else if (item is FunctionResultContent functionResult)
- {
- Console.WriteLine($" [{item.GetType().Name}] {functionResult.CallId} - {functionResult.Result?.AsJson() ?? "*"}");
- }
- }
-
- if (message.Metadata?.TryGetValue("Usage", out object? usage) ?? false)
- {
- if (usage is RunStepTokenUsage assistantUsage)
- {
- WriteUsage(assistantUsage.TotalTokenCount, assistantUsage.InputTokenCount, assistantUsage.OutputTokenCount);
- }
- else if (usage is ChatTokenUsage chatUsage)
- {
- WriteUsage(chatUsage.TotalTokenCount, chatUsage.InputTokenCount, chatUsage.OutputTokenCount);
- }
- }
-
- void WriteUsage(int totalTokens, int inputTokens, int outputTokens)
- {
- Console.WriteLine($" [Usage] Tokens: {totalTokens}, Input: {inputTokens}, Output: {outputTokens}");
- }
- }
-}
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/SKShared/KernelServiceHandler.cs b/04-Practical.NETGenAISamples/src/SKShared/KernelServiceHandler.cs
deleted file mode 100644
index a5862947..00000000
--- a/04-Practical.NETGenAISamples/src/SKShared/KernelServiceHandler.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma warning disable SKEXP0001, SKEXP0110, OPENAI001
-
-// Local function to invoke agent and display the conversation messages.
-using Microsoft.SemanticKernel.Agents.OpenAI;
-using Microsoft.SemanticKernel.Agents;
-using Microsoft.SemanticKernel.ChatCompletion;
-using Microsoft.SemanticKernel;
-using OpenAI.Assistants;
-using OpenAI.Chat;
-using Azure.Identity;
-using Microsoft.Extensions.Configuration;
-
-namespace AgentLabs;
-
-public class KernelServiceHandler
-{
- public static Kernel CreateKernelWithChatCompletion()
- {
- var builder = Kernel.CreateBuilder();
- AddChatCompletionToKernel(builder);
- return builder.Build();
- }
-
- public static void AddChatCompletionToKernel(IKernelBuilder builder)
- {
- var config = new ConfigurationBuilder().AddUserSecrets().Build();
- var apiKey = config["AZURE_OPENAI_APIKEY"];
- var endpoint = config["AZURE_OPENAI_ENDPOINT"];
- var deployment = config["AZURE_OPENAI_DEPLOYMENT"];
-
- builder.AddAzureOpenAIChatCompletion(deployment, endpoint, apiKey);
-
- }
-
-}
\ No newline at end of file
diff --git a/04-Practical.NETGenAISamples/src/SKShared/ObjectExtensions.cs b/04-Practical.NETGenAISamples/src/SKShared/ObjectExtensions.cs
deleted file mode 100644
index 9e133894..00000000
--- a/04-Practical.NETGenAISamples/src/SKShared/ObjectExtensions.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Text.Json;
-
-public static class ObjectExtensions
-{
- private static readonly JsonSerializerOptions s_jsonOptionsCache = new() { WriteIndented = true };
-
- public static string AsJson(this object obj)
- {
- return JsonSerializer.Serialize(obj, s_jsonOptionsCache);
- }
-}
diff --git a/README.md b/README.md
index 213dde4e..1b962930 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ You'll learn how to implement Generative AI into .NET projects, from basic text
| # | **Lesson Link** | **Description** | **Video** | **Extra Learning** |
| --- | --- | --- | --- | --- |
| 01 | [**Intro to Generative AI Basics for .NET Developers**](./01-IntroToGenAI/readme.md) |
Overview of generative models and their applications in .NET
| [](https://microsoft-my.sharepoint.com/:v:/p/brunocapuano/EYehflkLCx9Di3QcSKOVNRMBGH_YS-6f-qtVrE-O5iB3iA?e=ypAQ4b&nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTdHJlYW1XZWJBcHAiLCJyZWZlcnJhbFZpZXciOiJTaGFyZURpYWxvZy1MaW5rIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXcifX0%3D) | [Learn More](https://aka.ms/genainet) |
-| 02 | [**Setting Up for .NET Development with Generative AI**](./02-SettingUp.NETDev/readme.md) |
Using libraries like `Microsoft.Extensions.AI` and `OpenAI .NET`.
Choosing between GitHub Models API and local models like Ollama.
| [](https://microsoft-my.sharepoint.com/:v:/p/brunocapuano/ERTkzBSAfKJEiLw2HLnzHnkBMEbpk17hniaVfr8lCm6how?e=gWOr33&nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTdHJlYW1XZWJBcHAiLCJyZWZlcnJhbFZpZXciOiJTaGFyZURpYWxvZy1MaW5rIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXcifX0%3D) | [Learn More](https://aka.ms/genainet) |
+| 02 | [**Setting Up for .NET Development with Generative AI**](./02-SetupDevEnvironment/readme.md) |
Using libraries like `Microsoft.Extensions.AI` and `OpenAI .NET`.
Choosing between GitHub Models API and local models like Ollama.
| [](https://microsoft-my.sharepoint.com/:v:/p/brunocapuano/ERTkzBSAfKJEiLw2HLnzHnkBMEbpk17hniaVfr8lCm6how?e=gWOr33&nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTdHJlYW1XZWJBcHAiLCJyZWZlcnJhbFZpZXciOiJTaGFyZURpYWxvZy1MaW5rIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXcifX0%3D) | [Learn More](https://aka.ms/genainet) |
| 03 | [**Core Generative AI Techniques with .NET**](./03-CoreGenerativeAITechniques/readme.md) |
Text generation, conversational flows, and multimodal capabilities (vision and audio).
| [](https://microsoft-my.sharepoint.com/:v:/p/brunocapuano/ERTkzBSAfKJEiLw2HLnzHnkBMEbpk17hniaVfr8lCm6how?e=gWOr33&nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTdHJlYW1XZWJBcHAiLCJyZWZlcnJhbFZpZXciOiJTaGFyZURpYWxvZy1MaW5rIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXcifX0%3D) | [Learn More](https://aka.ms/genainet) |
| 04 | [**Practical .NET Generative AI Samples**](./04-Practical.NETGenAISamples/readme.md) |
Chatbots using libraries like `Microsoft.Extensions.AI`, `Semantic Kernel` and `OpenAI .NET`.
Video analyzers
Document Automation.
| [](https://microsoft-my.sharepoint.com/:v:/p/brunocapuano/ERTkzBSAfKJEiLw2HLnzHnkBMEbpk17hniaVfr8lCm6how?e=gWOr33&nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTdHJlYW1XZWJBcHAiLCJyZWZlcnJhbFZpZXciOiJTaGFyZURpYWxvZy1MaW5rIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXcifX0%3D) | [Learn More](https://aka.ms/genainet) |
| 05 | [**Responsible Use of Generative AI in .NET Apps**](./05-ResponsibleGenAI/readme.md) |
Ethical considerations, bias mitigation, and secure implementations.
| [](https://microsoft-my.sharepoint.com/:v:/p/brunocapuano/ERTkzBSAfKJEiLw2HLnzHnkBMEbpk17hniaVfr8lCm6how?e=gWOr33&nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJTdHJlYW1XZWJBcHAiLCJyZWZlcnJhbFZpZXciOiJTaGFyZURpYWxvZy1MaW5rIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXcifX0%3D) | [Learn More](https://aka.ms/genainet) |