OpenAI

Aprenda como incorporar os recursos de inteligência artificial oferecidos pela OpenAI e pelo Microsoft Azure OpenAI Service nos microsserviços, utilizando a plataforma Devprime através do recurso de extensibilidade no Adapter de Extensions.

Devprime using OpenAI

Introdução

A plataforma Devprime acelera a produtividade do desenvolvedor de software, oferecendo um projeto completo de arquitetura de software, componentes com comportamentos inteligentes, aceleradores para implementação de código e atualizações com novos recursos.

Neste artigo, abordaremos a utilização do Adapter de Extensions presente na arquitetura Devprime, que permite adicionar comportamentos adicionais por meio de componentes externos baseados na tecnologia do NuGet. Isso proporciona a possibilidade de expandir as funcionalidades da plataforma Devprime e aderir aos padrões de arquitetura de software, melhorando a manutenibilidade, reuso e testabilidade.

Durante esse artigo utilizaremos o componente nuget Azure.AI.OpenAI que incopora o acesso a API do Microsoft Azure OpenAI e OpenAI.

Cheklist e preperação do ambiente inicial:

Exemplo pronto com todos os passos do artigo

Este artigo inclui um projeto completo que demonstra o recurso discutido. Você pode optar por fazer o download seguindo as etapas abaixo ou simplesmente prosseguir para os próximos itens.

  1. Efetue um clone no repo
    git clone https://github.com/devprime/examples.git
  2. Entre na pasta
    cd examples/extensions/openai/basic
  3. Atualize a sua licença Devprime
    dp stack
  4. Atualize as configurações do MongoDB / RabbitMQ
  5. Atualize as credenciais do Azure OpenAI / OpenAI no appsettings.json
  • Localize em DevPrime_Custom os itens:
    ai.url / ai.credential / ai.deploymentmodel
  1. Execute o microsserviço

Criando um microserviço para utilizar no exemplo

O primeiro passo é criar um novo microsserviço para que possamos realizar customizações no Adapter de Extensions, adicionando o componente externo NuGet e preparando-o para interagir com a API da OpenAI. O nome deste microsserviço será definido como “ms-ai”, conforme demonstrado no comando abaixo.
dp new ms-ai --state mongodb --stream rabbitmq

Após a criação do novo microsserviço entre na pasta do projeto “ms-ai” e já poderá visualizar todas as implementações pelo Visual Studio Code conforme demonstrado no artigo relacionado a criação do primeiro microsserviço.

Adicionando uma regra de negócio

As regras de negócio na arquitetura da plataforma Devprime são baseadas em Domain-Driven Design (DDD), e para avançarmos, é necessário adicionar uma classe Aggregate Root dentro do projeto Domain. Para facilitar esse procedimento, utilizaremos o comando abaixo disponível no Devprime CLI.
dp add aggregate AI

Visualize a nova classe criada pelo Visual Studio Code.
code src/Core/Domain/Aggregates/AI/AI.cs

Modifique a classe AI adicionando uma propriedade chamada “Prompt” conforme exemplo abaixo.

1
2
3
4
5
namespace Domain.Aggregates.AI;
public class AI : AggRoot
{
         public string Prompt { get; private set; }
}

Utilizando os aceleradores de código da plataforma Devprime

Agora que você já implementou uma regra de negócio, vamos usar o acelerador do Devprime CLI para gerar o código necessário e iniciar o microsserviço com base na regra de negócio inicial que você forneceu.

Execute o comando a seguir e digite A para avançar nas implementações:

1
dp init

Após a conclusão deste comando, você já terá as implementações básicas do seu microsserviço e utilizaremos como referência para avançar para o próximo passo, que envolve a incorporação do componente OpenAI no Adapter de Extension.

Adicionando a extension Intelligence

Neste ponto, iniciaremos os procedimentos para habilitar uma extensão de terceiros fornecida como um componente NuGet na plataforma Devprime, seguindo padrões de desenvolvimento que garantem a manutenibilidade e desacoplamento.

Para acelerar esse processo, usaremos o comando abaixo, que implementa a nova extensão no serviço de aplicação por meio de uma interface e injeção de dependência, e construirá a implementação inicial necessária no adaptador.

Execute o comando abaixo:
dp add extensions IntelligenceService

1
2
3
4
5
6
7
8
9
# created
/src/Core/Application/Interfaces/Adapters/Extensions/IIntelligenceService.cs
/src/Adapters/Extensions/IntelligenceService/IntelligenceService.cs
# modified
/src/Core/Application/Interfaces/Adapters/Extensions/IExtensions.cs 
/src/Adapters/Extensions/Extensions.cs 
/src/Adapters/Extensions/GlobalUsings.cs 
/src/App/App.cs
/src/App/GlobalUsings.cs

A classe IntelligenceService e a sua interface IIntelligenceService implementam as integrações com a biblioteca OpenAI. Por outro lado, a classe Extensions e sua interface IExtensions atuam como proxies, permitindo que o contexto de execução do Devprime Pipeline acesse todas as extensões disponíveis na aplicação por meio da injeção de dependência.

Adicionando a referência ao componente da OpenAI

Adicione a referência do componente NuGet Azure.AI.OpenAI ao arquivo de projeto do Adapter de Extensions. Certifique-se de verificar o portal NuGet em busca da versão mais atual e ajuste a versão, se necessário:

Execute o comando abaixo em uma única linha:

1
2
dotnet add src/Adapters/Extensions/DevPrime.Extensions.csproj package 
Azure.AI.OpenAI --version 1.0.0-beta.12

Agora que você acabou de adicionar um novo componente aproveite e efetue um build do microsserviço para se certificar
que está tudo funcionando corretamente com essa nova dependência.

Execute o comando:

1
dotnet build

Implementando a integração com o componente da OpenAI

Neste momento, vamos implementar a integração com o componente OpenAI dentro do Adapter de Extensions. O primeiro passo é modificar a interface para incluir um método chamado “Conversation” e, em seguida, implementar o código que realiza a chamada à API do OpenAI e retorna o resultado em um formato que pode ser transportado para o contexto de nossa aplicação.

Abra Abra a interface IIntelligenceService no Visual Studio Code.
code src/Core/Application/Interfaces/Adapters/Extensions/IIntelligenceService.cs

Substitua pelo código abaixo:

1
2
3
4
5
namespace Application.Interfaces.Adapters.Extensions;
public interface IIntelligenceService
{
     string Conversation(string prompt);
}

Abra Abra a classe IntelligenceService no Visual Studio Code.
code src/Adapters/Extensions/IntelligenceService/IntelligenceService.cs

Substitua pelo código abaixo:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using System.Collections.Generic;
using Azure;
using Azure.AI.OpenAI;

namespace DevPrime.Extensions.IntelligenceService;
public class IntelligenceService : DevPrimeExtensions, IIntelligenceService
{
    private string Credential { get; set; }
    private string URL { get; set; }
    private string DeploymentModel { get; set; }


    public IntelligenceService(IDpExtensions dp) : base(dp)
    {
        Credential = Dp.Settings.Default("ai.credential");
        URL = Dp.Settings.Default("ai.url");
        DeploymentModel = Dp.Settings.Default("ai.deploymentmodel");
    }

    public string Conversation(string prompt)
    {


        return Dp.Pipeline(ExecuteResult: () =>
        {

            Dp.Observability.Log("Starting OpenAI");

            // Prepare AI Configurations
            var chatCompletionsOptions = new ChatCompletionsOptions()
            {
                Messages =      {
                                },
                Temperature = (float)0.7,
                MaxTokens = 800,
                NucleusSamplingFactor = (float)0.95,
                FrequencyPenalty = 0,
                PresencePenalty = 0,
                DeploymentName = DeploymentModel
            };


            // Creating OpenAI Client
            var openAiClient = new OpenAIClient(new System.Uri(URL),
            new AzureKeyCredential(Credential));

            // Add prompt to conversation 
            //ChatRequestSystemMessage / ChatRequestUserMessage / ChatRequestAssistantMessage     

            chatCompletionsOptions.Messages.Add
            (new ChatRequestUserMessage(prompt));
           

            // Prepare for interaction
            Response<ChatCompletions> response =
            openAiClient.GetChatCompletions (chatCompletionsOptions);

            //OpenAI Result
            var airesponse = response.GetRawResponse().Content.ToString();
            var root = System.Text.Json.JsonSerializer.Deserialize<Root>(airesponse);
            Message message = root.choices[0].message;
            var airesult = message.content;

            Dp.Observability.Log("Finalizing OpenAI");
            return airesult;
        });

    }


    public class ContentFilterResults
{
    public bool filtered { get; set; }
    public string severity { get; set; }
}

public class PromptFilterResults
{
    public int prompt_index { get; set; }
    public ContentFilterResults content_filter_results { get; set; }
}

public class Message
{
    public string role { get; set; }
    public string content { get; set; }
    public ContentFilterResults content_filter_results { get; set; }
}

public class Choice
{
    public int index { get; set; }
    public string finish_reason { get; set; }
    public Message message { get; set; }
    public ContentFilterResults content_filter_results { get; set; }
}

public class Usage
{
    public int prompt_tokens { get; set; }
    public int completion_tokens { get; set; }
    public int total_tokens { get; set; }
}

public class Root
{
    public string id { get; set; }
    public string _object { get; set; }
    public int created { get; set; }
    public string model { get; set; }
    public List<PromptFilterResults> prompt_filter_results { get; set; }
    public List<Choice> choices { get; set; }
    public Usage usage { get; set; }
}

}

Adicionando váriaveis de ambiente para uso no OpenAI

Na implementação anterior nós utilizamos o comando Dp.Settings.Default("ai.credential") para obter o parametro de uma variavel de ambiente definida no projeto local no arquivo “src/App/appsettings.json”. Abra o bloco “DevPrime_Custom” seguindo exemplo abaixo adicionando a URL do servíco de OpenAI, a credencial e o modelo de deployment.

Abra e edite pelo Visual Studio Code
code src/App/appsettings.json

1
2
3
4
5
6
  "DevPrime_Custom": {
    "stream.processevents": "aievents",
    "ai.url": "Insert Azure OpenAI URL / OpenAI URL",
    "ai.credential": "Insert API KEY",
    "ai.deploymentmodel": "Insert deployment name"
  }

Implementado a chamada ao adapter de extension

O primeiro ponto de contato com a nova funcionalidade disponibilizada pela nova Extension do OpenAI é o Handler que nesse contexto utilizaremos o CreateAIEventHandler que tem o papel de interceptar um evento de dominio e mediar a integração com os recursos tecnologicos da plataforma. Para que tenha suporte ao Extensions nós adicionamos a herança para a classe EventHandlerWithExtensions além de adicionarmos a interface IExtensions.

É importante observar que após as implementações anteriores agora nós temos a dispoção dentro da arquitetura Devprime um novo metodo Dp.Extensions.IntelligenceService.Conversation() que executa o código externo incoporado dentro do Adapter de Extensions.

Essa nova implementação executará a chamada ao OpenAI e retornará o resultado. Para avançar abra o arquivo e substitua todo o código.

Abra o Handler pelo Visual Studio Code.
code src/Core/Application/EventHandlers/AI/CreateAIEventHandler.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
namespace Application.EventHandlers.AI;
public class CreateAIEventHandler : 
EventHandlerWithExtensions<CreateAI, IExtensions>
{
    public CreateAIEventHandler(IExtensions extensions, IDp dp) : 
    base(extensions, dp)
    {
    }
    public override dynamic Handle(CreateAI createAI)
    {
        var aI = createAI.Get<Domain.Aggregates.AI.AI>();
        var result = Dp.Extensions.IntelligenceService.Conversation(aI.Prompt);
        return result;
    }
}

Em função dessa modificação no Handler remova o arquivo de testes que não é utilizado nesse exemplo
utilizando o comando abaixo:

Removendo arquivo
rm src/Tests/Core/Application/AI/CreateAIEventHandlerTest.cs

Modificando o método ADD no Aggregate Root

Agora retornaremos à regra de negócio no Aggregate Root para realizar uma customização no método “ADD”, substituindo-o pelo código abaixo para permitir a execução do evento de domínio “CreateAI” e obter esse retorno. Esse evento será processado no Handler “CreateAIEventHandler” responsável pela interação com o Adapter de Extensions."

Abra a classe Aggregate Root no Visual Studio Code
code src/Core/domain/aggregates/AI/AI.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public virtual string Add()
 {
    var result = Dp.Pipeline(ExecuteResult: () =>
    {
        ValidFields();
        ID = Guid.NewGuid();
        IsNew = true;
        var processresult = Dp.ProcessEvent<string>(new CreateAI());
        return processresult;
    });
        return result;
    }

Modificando a Interface e Application Services

Devido à implementação no Adapter de Extension, é necessário obter o retorno do Aggregate Root no Application Services. O primeiro passo é modificar a interface IAIService conforme o exemplo abaixo.

Abra a Interface no Vsiual Studio Code
code src\Core\Application\Interfaces\Services\IAIService.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
namespace Application.Interfaces.Services;
public interface IAIService
{
    string Add(Application.Services.AI.Model.AI command);
    void Update(Application.Services.AI.Model.AI command);
    void Delete(Application.Services.AI.Model.AI command);
    Application.Services.AI.Model.AI 
    Get(Application.Services.AI.Model.AI query);
    PagingResult<IList<Application.Services.AI.Model.AI>>
    GetAll(Application.Services.AI.Model.AI query);
}

Após a modificação da interface no passo anterior, é hora de refletir essa alteração no Application Services. Localize o método Add abaixo e faça a substituição para que o serviço de aplicação retorne o resultado do nosso Aggregate Root.

Abra o Application Services no Visual Studio Code.
code src\Core\Application\Services\AI\AIService.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    public string Add(Model.AI command)
    {
      return Dp.Pipeline(ExecuteResult: () =>
        {
            var process = command.ToDomain();
            Dp.Attach(process);
            var processresult = process.Add();
            return processresult;
        });
    }

Explorando o microsserviço integrado com o OpenAI

Ao final dessa implementação nós já podemos realizar os testes executando o microsserviço customizando com a integração do OpenAI.

Inicie o microsserviço executando um dos scripts abaixo:

1
.\run.ps1 (Windows) or ./run.sh (Linux, macOS)

Acesse o portal do swagger do microsserviço
Devprime using OpenAI

Efetue um post na API pelo Swagger preenchendo o prompt

1
2
3
4
{
  "prompt": "Explain how the Devprime platform enhances developer productivity
   in one sentence?"
}

e confira o resultado no json abaixo:

1
2
3
4
5
6
{
  "response": "The Devprime platform streamlines development workflows
   and automates repetitive tasks,   allowing developers to focus on
   writing high-quality code and delivering projects faster.",
  "success": true
}

Acompanhe o exemplo de registro de log gerado automaticamente pelo Adapter de Observability, que detalha todo o fluxo interno, desde o recebimento do POST até a integração com o OpenAI no Adapter de Extensions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[App]["Powered by DevPrime"][Version]["7.0.59"][License]["Developer"]
[App]["Start"]["ms-ai"][Configuration]["Appsettings"][AppVersion]["1.0.0.0"]
[App][Idempotency]["Disable"][RID 3b8cdd1a-48cc-44ea-9d4d-af36db35e4e4]
[State][Type "MongoDB"][Alias "State1"]["Database"]["Enable"]
[App][Tenancy]["Disable"]
[App][Observability]["Enable"][Log "Enable"][Export "Enable"][Type "seq"]
[Trace "Disable"][Metrics "Disable"]
[Security]["Disable"]
[Web]["https://localhost:5001"]["http://localhost:5000"][Host]["Production"]
[Parameters]["Appsettings"]
[Stream][Type "RabbitMQ"][Alias "Stream1"]["Enable"]
[Web]["HTTP"][AI][POST /v1/ai]
[Origin "https://localhost:5001/swagger/index.html"]
[Application][AIService][Add][RID aa13bca3-76f3-4b51-b35e-c05e24e0cad3]
[Domain][AI][Add]
[Domain][AI][ProcessEvent]["CreateAI"]
[Application][EventHandler]["CreateAIEventHandler"][Event]["CreateAI"]
[Extensions][IntelligenceService][Conversation]
[Custom][Starting OpenAI]
[Custom][Finalizing OpenAI]

Mais artigos sobre extesions na plataforma Devprime

Utilizando o SendGrid em microsserviços
Utilizando o Humanizer em microsserviços

Última modificação January 10, 2024 (967dcac3)