IA abierta

Aprenda a incorporar las capacidades de inteligencia artificial que ofrecen OpenAI y Microsoft Azure OpenAI Service en microservicios mediante la plataforma Devprime a través de la función de extensibilidad del adaptador de extensiones.

Devprime usando OpenAI

Introducción

La plataforma Devprime acelera la productividad de los desarrolladores de software al ofrecer un diseño completo de arquitectura de software, componentes con comportamientos inteligentes, aceleradores para la implementación de código y actualizaciones con nuevas funciones.

En este artículo, analizaremos el uso del adaptador de extensiones presente en la arquitectura Devprime, que permite agregar comportamientos adicionales a través de componentes externos basados en la tecnología NuGet. Esto brinda la posibilidad de expandir las funcionalidades de la plataforma Devprime y adherirse a los estándares de arquitectura de software, mejorando la capacidad de mantenimiento, la reutilización y la capacidad de prueba.

Durante este artículo, usaremos el componente nuget Azure.AI.OpenAI que incorporará acceso a Microsoft Azure OpenAI y OpenAI API.

Lista de verificación y preparación del entorno inicial:

Ejemplo listo para usar con todos los pasos del artículo

Este artículo incluye un proyecto completo que muestra la característica descrita. Puede optar por descargarlo siguiendo los pasos a continuación o simplemente continuar con los siguientes elementos.

  1. Clonar el repositorio
    git clone https://github.com/devprime/examples.git
  2. Entra en la carpeta
    cd examples/extensions/openai/basic
  3. Actualice su licencia de Devprime
    dp stack
  4. Actualizar la configuración de MongoDB/RabbitMQ
  5. Actualice las credenciales de Azure OpenAI/OpenAI en appsettings.json
  • Localiza los elementos en DevPrime_Custom:
    ai.url /ai.credential /ai.deploymentmodel
  1. Ejecutar el microservicio

Creación de un microservicio para usar en el ejemplo

El primer paso es crear un nuevo microservicio para que podamos realizar personalizaciones en el Adaptador de extensiones, agregando el componente NuGet externo y preparándolo para interactuar con la API de OpenAI. El nombre de este microservicio se establecerá en “ms-ai”, como se muestra en el siguiente comando.
dp new ms-ai --state mongodb --stream rabbitmq

Después de la creación del nuevo microservicio, ingrese a la carpeta del proyecto “ms-ai” y podrá ver todas las implementaciones por Visual Studio Code, como se muestra en el artículo relacionado con creación del primer microservicio.

Adición de una regla de negocio

Las reglas de negocio en la arquitectura de la plataforma Devprime se basan en Domain-Driven Design (DDD), y para avanzar, necesitamos agregar una clase Aggregate Root dentro del proyecto Domain. Para facilitar este procedimiento, utilizaremos el siguiente comando disponible en la CLI de Devprime.
dp add aggregate AI

Obtenga una vista previa de la nueva clase creada por Visual Studio Code.
code src/Core/Domain/Aggregates/AI/AI.cs

Modifique la clase AI agregando una propiedad llamada “Prompt” como se muestra en el ejemplo siguiente.

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

Uso de los aceleradores de código de la plataforma Devprime

Ahora que ha implementado una regla de negocio, vamos a usar el acelerador de la CLI de Devprime para generar el código necesario e iniciar el microservicio en función de la regla de negocio inicial que proporcionó.

Ejecute el siguiente comando y escriba A para continuar con las implementaciones:

1
dp init

Después de completar este comando, ya tendrás las implementaciones básicas de tu microservicio y lo usaremos como referencia para pasar al siguiente paso, que consiste en incrustar el componente OpenAI en el Adaptador de Extensión.

Adición de la extensión Intelligence

En este punto, comenzaremos los procedimientos para habilitar una extensión de terceros proporcionada como componente NuGet en la plataforma Devprime, siguiendo patrones de desarrollo que garanticen la capacidad de mantenimiento y desacoplamiento.

Para acelerar este proceso, usaremos el siguiente comando, que implementa la nueva extensión en el servicio de aplicaciones a través de una interfaz e inyección de dependencias, y compilará la implementación inicial necesaria en el adaptador.

Ejecute el siguiente comando:
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

La clase IntelligenceService y su interfaz IIntelligenceService implementan las integraciones con la biblioteca OpenAI. Por otro lado, la clase Extensions y su interfaz IExtensions actúan como proxies, lo que permite que el contexto de ejecución de Devprime Pipeline acceda a todas las extensiones disponibles en la aplicación a través de la inyección de dependencias.

Añadiendo la referencia al componente OpenAI

Agregue la referencia del componente NuGet Azure.AI.OpenAI al archivo de proyecto del adaptador de extensiones. Asegúrese de consultar el portal de NuGet para obtener la versión más reciente y ajustar la versión si es necesario:

Ejecute el siguiente comando en una sola línea:

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

Ahora que acaba de agregar un nuevo componente, aproveche la oportunidad de crear el microservicio para asegurarse de que
que todo está funcionando correctamente con esta nueva dependencia.

Ejecute el comando:

1
dotnet build

Implementación de la integración con el componente OpenAI

En este punto, vamos a implementar la integración con el componente OpenAI dentro del adaptador de extensiones. El primer paso es modificar la interfaz para incluir un método llamado “Conversation” y luego implementar el código que realiza la llamada a la API de OpenAI y devuelve el resultado en un formato que pueda ser transportado al contexto de nuestra aplicación.

Abra la interfaz IIntelligenceService en Visual Studio Code.
code src/Core/Application/Interfaces/Adapters/Extensions/IIntelligenceService.cs

Reemplácelo con el siguiente código:

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

Abra la clase IntelligenceService en Visual Studio Code.
code src/Adapters/Extensions/IntelligenceService/IntelligenceService.cs

Reemplácelo con el siguiente código:

  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; }
}

}

Adición de variables de entorno para su uso en OpenAI

En la implementación anterior usábamos el comando Dp.Settings.Default("ai.credential") para obtener el parámetro de una variable de entorno definida en el proyecto local en el archivo “src/App/appsettings.json”. Abra el bloque “DevPrime_Custom” siguiendo el siguiente ejemplo agregando la URL del servicio OpenAI, la credencial y la plantilla de implementación.

Abrir y editar desde 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"
  }

Se implementó la llamada al adaptador de extensión

El primer punto de contacto con la nueva funcionalidad puesta a disposición por la nueva extensión de OpenAI es el Handler, que en este contexto utilizaremos CreateAIEventHandler que tiene la función de interceptar un evento de dominio y mediar la integración con los recursos tecnológicos de la plataforma. Para admitir extensiones, hemos agregado herencia para la clase EventHandlerWithExtensions, así como para la interfaz IExtensions.

Es importante tener en cuenta que después de las implementaciones anteriores ahora tenemos un nuevo método Dp.Extensions.IntelligenceService.Conversation() que ejecuta el código externo incrustado dentro del adaptador de extensiones.

Esta nueva implementación ejecutará la llamada a OpenAI y devolverá el resultado. Para avanzar, abra el archivo y reemplace todo el código.

Abra el controlador de 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;
    }
}

Debido a esta modificación en el controlador, quite el archivo de prueba que no se usa en este ejemplo
usando el siguiente comando:

Eliminación de archivos
rm src/Tests/Core/Application/AI/CreateAIEventHandlerTest.cs

Modificando el método ADD en la raíz agregada

Ahora volveremos a la regla de negocio en la raíz agregada para realizar una personalización en el método “ADD”, reemplazándolo con el código siguiente para permitir que el evento de dominio “CreateAI” se ejecute y obtenga ese retorno. Este evento se procesará en el controlador “CreateAIEventHandler” responsable de interactuar con el adaptador de extensiones".

Abra la clase Aggregate Root en 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;
    }

Modificación de la interfaz y los servicios de la aplicación

Debido a la implementación en el adaptador de extensión, es necesario obtener la devolución de la raíz agregada en los servicios de aplicaciones. El primer paso es modificar la interfaz IAIService como se muestra en el siguiente ejemplo.

Abra la interfaz en el código de Vsiual Studio
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);
}

Después de la modificación de la interfaz en el paso anterior, es el momento de reflejar ese cambio en Servicios de aplicaciones. Busque el método Add a continuación y reemplácelo para que el servicio de aplicación devuelva el resultado de nuestra raíz agregada.

Abra Servicios de aplicaciones en 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 el microservicio integrado con OpenAI

Al final de esta implementación, ya podemos realizar las pruebas ejecutando el microservicio, personalizándolo con la integración de OpenAI.

Para iniciar el microservicio, ejecute uno de los siguientes scripts:

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

Ir al portal de swagger de microservicios
Devprime usando OpenAI

Publique en la API a través de Swagger completando el mensaje

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

Y echa un vistazo al resultado en el JSON a continuación:

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
}

Siga el ejemplo de registro generado automáticamente por el adaptador de observabilidad, que detalla todo el flujo interno, desde la recepción del POST hasta la integración con OpenAI en el adaptador de extensiones.

 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]

Más artículos sobre extesiones en la plataforma Devprime

Uso de SendGrid en microservicios
Uso de Humanizer en microservicios

Última modificación April 16, 2024 (2b35fcc8)