Compreender as funções nativas

Concluído

Funções nativas são funções escritas em código nativo que podem ser chamadas pelo SDK do Kernel Semântico em seu aplicativo. Eles são úteis para executar tarefas que grandes modelos de linguagem (LLM) não podem fazer por conta própria. Você pode pensar em uma função nativa como uma habilidade que seu aplicativo pode executar.

Em módulos posteriores, você aprenderá a usar o Kernel Semântico para invocar automaticamente as funções nativas criadas e combinar sua lógica com prompts para o LLM. A combinação de serviços de conclusão de bate-papo com habilidades funcionais permite que você crie um agente de IA que possa executar uma ampla gama de tarefas. Mas, por enquanto, vamos nos concentrar em projetar funções nativas.

As funções nativas têm um determinado formato e uma estrutura de arquivo recomendada para ser usada pelo kernel. As funções nativas devem usar o KernelFunction decorador em suas definições. Eles também usam um Description campo para parâmetros. Por exemplo:

[KernelFunction, Description("Convert an amount of currency to USD")]
public static string ConvertCurrency(
  [Description("The currency")] string currency, 
  [Description("The amount")] double amount)
{
  // Code to convert currency
}

Você pode importar suas funções nativas para o kernel como um plugin. As classes que contêm funções nativas devem ser colocadas em um diretório "Plugins". As funções relacionadas devem ser colocadas no mesmo arquivo para manter o código organizado. Você também pode utilizar subdiretórios dentro do diretório "Plugins" para organizar ainda mais seu código.

Por exemplo, suponha que você tenha um aplicativo de lista todo. Um usuário deseja concluir um item em sua lista todo. O modelo de linguagem grande (LLM) não pode acessar a lista todo do usuário diretamente, mas você pode escrever uma função nativa para acessar a lista e marcar um item como completo. Por exemplo, o arquivo de lista todo pode conter o seguinte:

{
  "todoList": [
    {
      "task": "Complete coding exercise",
      "completed": false
    },
    {
      "task": "Practice Mandarin",
      "completed": false
    },
    {
      "task": "Buy groceries",
      "completed": false
    }
  ]
}

Você pode criar um arquivo TodoListPlugin.cs no diretório 'Plugins' com algum código para marcar a tarefa concluída:

using System.ComponentModel;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.SemanticKernel;

public class TodoListPlugin
{
    [KernelFunction, Description("Mark a todo list item as complete")]
    public static string CompleteTask([Description("The task to complete")] string task)
    {
        // Read the JSON file
        string jsonFilePath = $"{Directory.GetCurrentDirectory()}/todo.txt";
        string jsonContent = File.ReadAllText(jsonFilePath);

        // Parse the JSON content
        JsonNode todoData = JsonNode.Parse(jsonContent);

        // Find the task and mark it as complete
        JsonArray todoList = (JsonArray) todoData["todoList"];
        foreach (JsonNode taskNode in todoList)
        {
            if (taskNode["task"].ToString() == task)
            {
                taskNode["completed"] = true;
                break;
            }
        }

        // Save the modified JSON back to the file
        File.WriteAllText(jsonFilePath, JsonSerializer.Serialize(todoData));
        return $"Task '{task}' marked as complete.";
    }
}

Observe o KernelFunction decorador na CompleteTask função. Este decorador diz ao kernel que esta função pode ser acessada. O Description decorador diz ao kernel o que a função faz. A função aceita o task como uma cadeia de caracteres. As variáveis das funções do kernel devem incluir uma descrição que explique o que é a variável. Essa função também retorna uma cadeia de caracteres informando ao usuário que a tarefa foi marcada como concluída.

Program.cs No arquivo, você pode importar e chamar essa função nativa semelhante a como você chamaria um dos plug-ins internos. Por exemplo:

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Plugins.Core;

var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
  "your-deployment-name",
  "your-endpoint",
  "your-api-key",
  "deployment-model");
var kernel = builder.Build();
kernel.ImportPluginFromType<TodoListPlugin>();

var result = await kernel.InvokeAsync<string>(
  "TodoListPlugin", 
  "CompleteTask", 
  new() {{ "task", "Buy groceries" }}
);
Console.WriteLine(result);

Neste exemplo, kernel.InvokeAsync é chamado com o nome do plugin, nome da função e argumentos. O task argumento é "Comprar mantimentos". A função marcará a tarefa como concluída no arquivo de lista todo e retornará uma mensagem para o usuário.

Agora, seu agente de IA pode ajudar o usuário a concluir tarefas em sua lista de tarefas. Opcionalmente, você pode optar por indicar o tipo de retorno da função na InvokeAsync chamada. Caso contrário, um FunctionResult objeto será retornado.

No próximo exercício, você praticará a criação de seus próprios plugins com funções nativas.