Construindo um AI agente de IA com semântica Kernel, C# C#, OpenAI e MongoDB Atlas
Avalie esse Tutorial
A geração de IA do continua decole e AI AI os agentes da IA do não são diferentes. Mais e mais desenvolvedores estão sendo solicitados a desenvolver soluções que integrem AI agentes de IA à experiência do usuário. Isso ajuda as empresas a reduzir custos ao tentar resolver muitas dúvidas ou solicitações de usuários sem a necessidade de interação humana.
Com o verão se aproximando, não há melhor momento para pensar em comida reconciliadora nessas dias frios. Por esse motivo, neste tutorial, criaremos um agente de IA usando o Semantic Kernel da Microsoft para C#, OpenAI e MongoDB Atlas para ajudá-lo a decidir se você já tem os componentes para preparar uma comida que deseja fazer ou se precisa deve apenas ir a um restaurante arejado que é recomendado e deixar outra pessoa fazer a comida!
Nosso agente será composto por algumas partes:
- Um plugin para recuperar uma lista de componentes disponíveis de um arquivo de texto
- Um prompt que usa IA AI para retornar qual é a cozinha da comida fornecida
- Outro plugin para pesquisar uma collection em nosso cluster MongoDB Atlas
Para explicar brevemente,plugin um plugin -in é um código personalizado marcado como disponível para AI a IA e que pode ser chamado para obter alguma funcionalidade a partir do código, como API chamar uma API ou interagir com outros serviços.
Um prompt também é personalizado, mas cria texto, geralmente com entradas dinâmicas, que é então usado como entrada para AI que a IA execute a tarefa com base na entrada.
O Microsoft Aprenda tem um curso fantástico sobre Semantic Kernel, se você quiser saber mais sobre ambos.
Se quiser saber mais sobre a integração da geração aumentada de recuperação (RAG) com o Semantic Kernel, você pode aprender a criar um bot de recomendações de filmes que usa o MongoDB Atlas para armazenar os dados e as incorporações vetoriais e usa o Atlas Vector Search sob o capô por meio de um Conector Semantic Kernel para pesquisar os dados.
Mas antes de nos distraírmos pela faminta, vamos começar, para que tenhamos nosso agente pronto a tempo do restaurante!
Você precisará de algumas coisas para acompanhar este tutorial:
- Agrupamento do Atlas M0 implantado com o conjunto de dados de amostra completo carregado
- Conta OpenAI e chave API
- .NET 9 SDK
- O código inicial que pode ser encontrado na ramificaçãostart“” no Github Github
Para economizar tempo com parte do código e arquivos, o repositório inicial vem com algumas coisas já disponíveis imediatamente.

- Dentro de
Data/cupboardinventory.txt
há uma lista de componentes que você pode encontrar em um guarda-louça ou pia. Você sempre pode fazer alterações se quiser adicionar ou remover componentes, e elas podem ser insensíveis a maiúsculas e minúsculas. Usaremos isso para simular quais componentes estão disponíveis. Restaurants.cs
dentro de Models tem propriedades que nos importam para um restaurante. Como parte do conector MongoDB disponível para Semantic Kernel (que já está adicionado ao projeto), temos acesso ao MongoDB C# Driver sob o capô. Isso significa que podemos aproveitar o poder representar nossos documentos como classes simples.- Program.cs tem um método já implementado dentro dele chamado
GenerateEmbeddingsForCuisine()
. Isso ocorre porque queremos gerar incorporações para o campo de cozinha para documentos disponíveis no conjunto de dados de amostra para que eles estejam disponíveis para o aplicação posteriormente. No entanto, não precisamos criar incorporações para cada documento. Só precisamos de um bom tamanho de amostra para que ela possa buscar 1000 documentos.
Se você quiser entender mais sobre como esse método funciona, a seção sobre como adicionar documentos ao armazenamento de memória no artigo Semantic Kernel e RAG o detalha.
O código inicial que estamos usando é um .NET aplicação de console .NET tradicional usando .NET 9.NET. Embora possamos Go ir em frente e adicionarappsettings arquivos “” e configurá-los em nossa
Program.cs
classe , isso é excessivo para uma demonstração simples. Então, vamos aproveitar as variáveis de ambiente.Antes de começarmos a usar nossas variáveis de ambiente no código, vamos salvá-las. Execute cada um dos seguintes comandos (dependendo do seu sistema operacional (sistema operacional)), um a um, na linha de comando de sua escolha:
1 export OPENAI_API_KEY=”<REPLACE WITH YOUR OPEN AI API KEY>” # MacOS/Linux 2 set OPENAI_API_KEY=”<REPLACE WITH YOUR OPEN AI API KEY>” # Windows” 3 4 export OPENAI_MODEL_NAME=”<REPLACE WITH YOUR MODEL OF CHOICE>” 5 set OPENAI_MODEL_NAME_”<REPLACE WITH YOUR MODEL OF CHOICE>” 6 7 export MONGODB_ATLAS_CONNECTION_STRING=”<YOUR MONGODB ATLAS CONNECTION STRING>” 8 set MONGODB_ATLAS_CONNECTION_STRING=”<YOUR MONGODB ATLAS CONNECTION STRING>”
Agora, podemos adicionar o seguinte código dentro
Program.cs
, abaixo das declarações de uso mas antes da definição do método, para buscar essas variáveis de ambiente:1 string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new ArgumentNullException("Environment.GetEnvironmentVariable(\"OPENAI_API_KEY\")"); 2 string modelName = Environment.GetEnvironmentVariable("OPENAI_MODEL_NAME") ?? "gpt-4o-mini";
Isto verifica a presença desses valores e lança uma exceção ou define um padrão. Gpt-4o-mini é um modelo perfeitamente aceitável para o nosso caso de uso.
Também queremos chamar o método existente para gerar as incorporações para nossos dados de amostra. No entanto, antes de podermos fazer isso, precisamos atualizá-lo para usar a chave de API que agora temos disponível como variável.
Dentro do método, atualize a chamada para usar o Open AI para a geração de incorporação de texto.
1 memoryBuilder.WithOpenAITextEmbeddingGeneration( 2 "text-embedding-3-small", 3 "<YOUR OPENAI APIKEY>" 4 );
Substitua a string da chave API OpenAI pela variável
apiKey
.Agora, podemos adicionar uma chamada ao método anterior no arquivo, depois de configurar as variáveis a partir das variáveis de ambiente:
1 await GenerateEmbeddingsForCuisine();
Pode levar alguns minutos para gerar as incorporações, então agora é um bom momento para executar o aplicação pela primeira vez:
1 dotnet run
Você sempre pode continuar com o tutorial enquanto espera ou fazer um café. Depois que a nova
embedded_cuisines
coleção tiver entre 900 e 1000 documentos (ou em torno do número selecionado se tiver escolhido alterar o limite no método), interrompa o aplicação e exclua ou comente a chamada para o método.Agora que temos nossos dados de amostra com cozinhas incorporadas disponíveis, é hora de começar a configurar o Semantic Kernel para que possamos começar a disponibilizar as ferramentas para atingir nosso objetivo relacionado à alimentação ao AI agente de IA nas seções posteriores.
1 var builder = Kernel.CreateBuilder(); 2 3 builder.Services.AddOpenAIChatCompletion( 4 modelName, 5 apiKey); 6 7 var kernel = builder.Build();
Agora, a instância do kernel está configurada e pronta para o Go Go. Podemos começar a criar a primeira ferramenta para nossa IA AI optar por usar: o plugin -in de plugin componentes.
Conforme mencionado anteriormente neste tutorial, temos uma lista de componentes disponíveis em um
.txt
arquivo dentro da pasta de dados que podemos usar para simular a busca dos componentes de uma API (se você tiver uma pia digital, por exemplo).Portanto, o primeiro plugin que vamos escrever é um que obtém todos os componentes desse arquivo. O agente pode então usar isso para obter todos os componentes disponíveis antes de decidir se estão faltando algum componente necessário para preparar a comida escolhida.
- Na raiz do projeto, adicione uma nova pasta chamada
Plugins
. - Crie uma nova classe dentro dessa pasta chamada
IngredientsPlugin
. - Cole o seguinte código dentro da classe:
1 [ ]2 public static string GetIngredientsFromCupboard() 3 { 4 // Ensures that the file path functions across all operating systems. 5 string baseDir = AppContext.BaseDirectory; 6 string projectRoot = Path.Combine(baseDir, "..", "..", ".."); 7 string filePath = Path.Combine(projectRoot, "Data", "cupboardinventory.txt"); 8 return File.ReadAllText(filePath).ToLower(); 9 }
Observação: se o seu editor de texto não adicionar automaticamente as declarações de uso, adicione o seguinte no topo do arquivo:
1 using System.ComponentModel; 2 using Microsoft.SemanticKernel;
Aqui temos um método simples definido chamado GetIngredientsFromCupboard.
É anotado com esta definição KernelFunction com uma propriedade Descrição . Isso informa à IA AI que esse método está disponível e também para que serve. Isso é usado para ajudá-lo a decidir quando e se chamar esse método para realizar uma tarefa.
O código dentro do método é um código C# bastante comum para ler um arquivo. O resultado de
Directory.GetCurrentDirectory()
é diferente dependendo do sistema operacional e de onde o aplicação está sendo executado. Portanto, a maior parte desse código é apenas para obter o caminho do arquivo de forma independente do sistema operacional.O que considero inteligentes é que o método retorna o conteúdo do arquivo (em letras minúsculas para consistência) e é assim que a IA AI tem acesso a ele, combinando C# código C# básico com o conhecimento de que a função existe!
Agora precisamos disponibilizar o plugin -in para usarmos mais tarde, quando criarmos um prompt para o que queremos alcançar.
Portanto, após a última chamada para
var kernel = builder.Build();
, adicione o seguinte para importar o plugin-in:1 kernel.ImportPluginFromType<IngredientsPlugin>();
Agora é hora de fazer o prompt GetCuisine. Precisamos de uma maneira de fazer AI com que a IA nos informe qual é a preparação da comida, então é aqui que entra a criação de uma solicitação.
Existem duas maneiras de criar um prompt: por meio de dois arquivos (um JSON arquivo de configuração JSON e um arquivo txt de prompt) ou com um arquivo YAML. É fácil entender o YAML com sua abordagem de indentação. Então, vamos usar a abordagem anterior.
- Crie uma
Prompts
pasta chamada na raiz do projeto. - Crie uma pasta dentro dessa pasta chamada
GetCuisine
. - Crie um novo arquivo chamado
config.json
e insira o seguinte JSON:
1 { 2 "schema": 1, 3 "type": "completion", 4 "description": "Identify the cuisine of the user's requested meal", 5 "execution_settings": { 6 "default": { 7 "max_tokens": 800, 8 "temperature": 0 9 } 10 }, 11 "input_variables": [ 12 { 13 "name": "cuisine", 14 "description": "Text from the user that contains their requested meal", 15 "required": true 16 } 17 ] 18 }
Isso especifica a configuração do nosso prompt e o que ele faz; ou seja, para a conclusão do chat, ele deve ter 0 Criatividade (
temperature: 0
), também conhecido como ser específico, e haverá uma variável de entrada disponível chamada cozinha que conterá a comida solicitada. Como input_variables
é uma array, você também pode especificar múltiplas variáveis de entrada, se necessário, aqui.Crie outro arquivo na pasta chamado,
skprompt.txt
que é o que criará dinamicamente o texto para que a IA AI entenda o que está sendo solicitado dela. Em seguida, adicione o seguinte:1 Return a single word that represents the cuisine of the requested meal that is sent: {{$cuisine}}. 2 3 For example, if the meal is mac and cheese, return American. Or for pizza it would be Italian. 4 Do not return any extra words, just return the single name of the cuisine.
Este é um exemplo de geração de uma declaração rápida e uso de uma boa engenharia AI rápida para
{{$cuisine}}
moldar o double quão bem a config.json
IA entende e responde. O é como você preenche dinamicamente o prompt com valores. Isso deve começar com um sinal $, estar dentro das chaves duplas e corresponder ao nome de uma variável de entrada declarada na array dentro do arquivo.A maneira de disponibilizar prompts para a IA AI como um plugin plugin -in é ligeiramente diferente em comparação com os plug-ins.
Após a chamada para importar o ResultsPlugin, adicione o seguinte:
1 string baseDir = AppContext.BaseDirectory; 2 string projectRoot = Path.Combine(baseDir, "..", "..", ".."); 3 var plugins = kernel.CreatePluginFromPromptDirectory(projectRoot + "/Prompts");
O Semantic Kernel é inteligente e pode encontrar todos os prompts disponíveis no diretório de prompts. Em seguida, ele estará disponível posteriormente em uma array de nomes de plugin -ins (com o nome da pasta, no nosso caso, GetCuisine).
Por último, queremos criar outro plugin, desta vez para restaurantes e interagir com nosso cluster MongoDB Atlas .
- Crie uma classe dentro da pasta
Plugins
chamadaRestaurantsPlugin
. - Adicione as seguintes declarações de uso e a declaração de namespace :
1 using FoodAgentDotNet.Models; 2 using Microsoft.SemanticKernel; 3 using Microsoft.SemanticKernel.Connectors.MongoDB; 4 using Microsoft.SemanticKernel.Connectors.OpenAI; 5 using Microsoft.SemanticKernel.Memory; 6 using MongoDB.Driver; 7 using System; 8 using System.Collections.Generic; 9 using System.ComponentModel; 10 using System.Linq; 11 using System.Text; 12 using System.Threading.Tasks; 13 14 namespace FoodAgentDotNet.Plugins;
Em seguida, substitua o restante da classe pelo seguinte:
1 2 public class RestaurantsPlugin 3 { 4 static readonly string mongoDBConnectionString = Environment.GetEnvironmentVariable("MONGODB_ATLAS_CONNECTION_STRING"); 5 static readonly string openAIApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY"); 6 7 [ ]8 public static async Task<List<Restaurant>> GetRecommendedRestaurant( 9 [string cuisine) ] 10 { 11 var mongoDBMemoryStore = new MongoDBMemoryStore(mongoDBConnectionString, "sample_restaurants", "restaurants_index"); 12 var memoryBuilder = new MemoryBuilder(); 13 memoryBuilder.WithOpenAITextEmbeddingGeneration( 14 "text-embedding-3-small", 15 openAIApiKey ); 16 memoryBuilder.WithMemoryStore(mongoDBMemoryStore); 17 var memory = memoryBuilder.Build(); 18 19 var restaurants = memory.SearchAsync( 20 "embedded_cuisines", 21 cuisine, 22 limit: 5, 23 minRelevanceScore: 0.5 24 ); 25 26 List<Restaurant> recommendedRestaurants = new(); 27 28 await foreach(var restaurant in restaurants) 29 { 30 recommendedRestaurants.Add(new Restaurant 31 { 32 Name = restaurant.Metadata.Description, 33 // We include the cuisine so that the AI has this information available to it 34 Cuisine = restaurant.Metadata.AdditionalMetadata, 35 }); 36 } 37 return recommendedRestaurants; 38 } 39 }
Há muito aqui, então vamos dar uma olhada.
Alguns desses recursos ainda são considerados experimentais, portanto os avisos estão desativados.
Assim como no plugin -in de componentes anterior, adicionamos o atributo ao método para marcá-lo como KernelFunction. No entanto, desta vez, também passamos um argumento para a cozinha, então adicionamos um atributo de descrição adicional para descrever à IA AI para que serve o argumento.
Em seguida, construímos o armazenamento de memória e configuramos o MongoDB como nosso armazenamento de memória. Também configuramos a incorporação de texto OpenAI novamente, pois ela precisará gerar incorporações para a cozinha passada para uso na pesquisa vetorial.
Em seguida, reunimos essas peças para pesquisar explicitamente em nossa
embedded_cuisines
coleção até cinco restaurantes que possam se adequar à cozinha solicitada.Criamos uma lista de restaurantes recomendados, atribuindo os valores que nos importam antes de retornar essa lista para que a IA AI a tenha disponível.
Agora precisamos retornar a
Program.cs
brevemente para adicionar nosso novo plugin-in. Após a chamada anterior para adicionar o ClusterPlugin, adicione o seguinte para adicionar também o RestaurantsPlugin:1 kernel.ImportPluginFromType<RestaurantsPlugin>();
Ao criar o
MongoDBMemoryStore
objeto, passamos a ele um índice chamado restaurants_index
, mas isso ainda não existe. Então, vamos mudar isso!Ele estará disponível muito em breve (versão 3.1 do driver C#), mas, por enquanto, não há uma maneira legível e legível de criar programaticamente um índice de pesquisa vetorial em C#.
A maneira mais fácil de criar uma é na interface do usuário do Atlas em um navegador ou por meio do MongoDB Compass.
Não Go entrarei em detalhes aqui, pois já temos muito conteúdo sobre como fazer isso. Confira nossa documentação que mostra como fazer isso, se precisar de ajuda.
Você pode utilizar o seguinte JSON para definir o índice de pesquisa vetorial:
1 { 2 "fields": [ 3 { 4 "type": "vector", 5 "path": "embedding", 6 "numDimensions": 1536, 7 "similarity": "cosine" 8 } 9 ] 10 }
Recomendamos chamar o índice
restaurants_index
para corresponder ao código. Se você optar por usar outra coisa, certifique-se de atualizar o código colado anteriormente dentro de RestaurantsPlugin
.Agora que temos todas as peças definidas, é hora de unir tudo. Vamos solicitar a entrada do usuário e usar essa resposta para construir o que queremos que AI a IA faça.
Primeiro, vamos dizer à IA AI que queremos que ela atenda às chamadas automaticamente por conta própria. Adicione o seguinte após a chamada para criar a variável plugins:
1 OpenAIPromptExecutionSettings settings = new() 2 { 3 ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions 4 };
Em seguida, vamos adicionar a interação com o usuário:
1 Console.WriteLine("What would you like to make for dinner?"); 2 var input = Console.ReadLine();
Agora queremos criar um prompt que especifique o que queremos alcançar e comece a adicionar nosso primeiro plugin-in:
1 string ingredientsPrompt = @"This is a list of ingredients available to the user: 2 {{IngredientsPlugin.GetIngredientsFromCupboard}} 3 4 Based on their requested dish " + input + ", list what ingredients they are missing from their cupboard to make that meal and return just the list of missing ingredients. If they have similar items such as pasta instead of a specific type of pasta, don't consider it missing";
Você pode ver aqui como o prompt é construído. Informamos que temos componentes disponíveis que eles podem obter chamando o método que passamos entre double colchetes duplos. Ele está no formato PluginName.Method, então pode parecer familiar.
Em seguida, damos a ele acesso à resposta do usuário sobre o que ele quer Comer e usamos isso para solicitar que ele veja quais componentes estão faltando para fazer a refação. Mais uma vez, há um pouco de engenharia rápida ocorrendo no final, para evitar que seja muito complicado e ignore componentes perfeitamente válidos.
Podemos então invocar esse prompt:
1 var ingredientsResult = await kernel.InvokePromptAsync(ingredientsPrompt, new(settings)); 2 3 var missing = ingredientsResult.ToString().ToLower() 4 .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) 5 .Where(line => line.StartsWith("- ")) 6 .ToList();
A IA AI tem uma tendência de retornar muitos textos explicativos extras junto com a lista de componentes, de modo que a variável ausente apenas faz uma formatação básica para pegar apenas a lista de componentes ausentes, pois precisamos ser muito específicos.
Agora queremos nos entreter um pouco com o usuário e decidir se ele tem componentes suficientes para fazer a comida ou algo semelhante, ou se simplesmente não deve se dar ao trabalho de ir Go a um restaurante! Mas, além de sugerir que eles Go vão a um restaurante, usaremos nosso plugin -in personalizado plugin para recomendá-los também!
1 var cuisineResult = await kernel.InvokeAsync( 2 plugins["GetCuisine"], 3 new() { { "cuisine", input } } 4 ); 5 6 if (missing.Count >= 5) 7 { 8 string restaurantPrompt = @"This is the cuisine that the user requested: " + cuisineResult + @". Based on this cuisine, recommend a restaurant for the user to eat at. Include the name and address 9 {{RestaurantsPlugin.GetRecommendedRestaurant}}"; 10 11 var kernelArguments = new KernelArguments(settings) 12 { 13 { "cuisine", cuisineResult } 14 }; 15 16 var restaurantResult = await kernel.InvokePromptAsync(restaurantPrompt, kernelArguments); 17 18 Console.WriteLine($"You have so many missing ingredients ({missing.Count}!), why bother? {restaurantResult}"); 19 } 20 else if(missing.Count < 5 && missing.Count > 0) 21 { 22 Console.WriteLine($"You have most of the ingredients to make {input}. You are missing: "); 23 foreach (var ingredient in missing) 24 { 25 Console.WriteLine(ingredient); 26 } 27 string similarPrompt = @"The user requested to make " + input + @" but is missing some ingredients. Based on what they want to eat, suggest another meal that is similar from the " + cuisineResult + " cuisine they can make and tell them the name of it but do not return a full recipe"; 28 var similarResult = await kernel.InvokePromptAsync(similarPrompt, new(settings)); 29 30 Console.WriteLine(similarResult); 31 } 32 else { 33 Console.WriteLine("You have all the ingredients to make " + input + "!"); 34 string recipePrompt = @"Find a recipe for making " + input; 35 var recipeResult = await kernel.InvokePromptAsync(recipePrompt, new(settings)); 36 Console.WriteLine(recipeResult); 37 }
Como buscamos todos os prompts disponíveis no diretório de prompts anteriormente, agora os temos disponíveis na array de plugins que mencionei. Então, começando por descobrir qual é a cozinha.
A chamada usa um objeto KernelArguments que contém valores que queremos disponibilizar, portanto, criamos um novo in-line para a chamada, passando o nome da variável de entrada, correspondendo
cuisine
ao que definimos anteriormente e ao valor que queremos atribuir a isso.Em seguida, usamos algumas declarações básicas if/else para lidar com as várias condições, que vão desde a falta de muitos componentes, a falta de apenas alguns e a falta de nenhum!
Dentro de cada um, um prompt ligeiramente diferente para a AI IA é criado e usado, com a resposta então enviada ao usuário.
Agora que temos todo o código, vamos testá-lo!
Depurar a partir do seu IDE ou com o .NET CLI.
dotnet run
Em seguida, você verá o prompt perguntando o que quer para o restaurante. Tente inserir sua comida favorita e veja como ela funciona!
Dependendo do modelo, pode levar alguns segundos para ser executado, portanto, se você não obter uma resposta instantânea ao nome da sua comida, não se desespere!

Uau! Neste tutorial, reunimos o poder do MicrosoftSemantic Kernel da Microsoft, da OpenAI e do MongoDB Atlas para criar um poderoso AI agente de IA que ajuda os usuários a descobrir se devem ir ao supermercado ou Go sair parajantar!
Por que você não tenta experimentar diferentes comidas ou receitas no arquivo de texto incluído e veja quais recomendações recebe?
Se você tiver uma cozinha inteligentes que tenha uma API para lhe informar quais componentes você tem, pode até tentar combiná-los para que alguns dos dados sejam genuinamente seus!
Boa tarde!
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.