Guía Completa para Integrar la API de OpenAI en Java

1. Introducción

En este artículo, exploraremos el proceso de integración de la API del Cliente Java de OpenAI. Comenzaremos configurando el cliente Java en nuestro entorno de desarrollo, autenticando nuestras solicitudes a la API y demostrando cómo interactuar con los modelos de OpenAI para la generación de texto y tareas impulsadas por inteligencia artificial. A medida que el uso de la inteligencia artificial se convierte en una parte integral del desarrollo de software, comprender cómo interactuar con estas potentes herramientas nos permitirá crear aplicaciones más eficaces e innovadoras.

2. Dependencias

Primero, debemos importar las dependencias necesarias para nuestro proyecto. Podemos encontrar las librerías en el repositorio de Maven:

<dependency>
    <groupId>com.openai</groupId>
    <artifactId>openai-java</artifactId>
    <version>0.22.0</version>
</dependency>

Asegúrate de incluir esta dependencia en tu archivo pom.xml si usas Maven, lo que nos permitirá acceder a las funcionalidades de la API de OpenAI sin problemas.

3. Asistente de Aprendizaje

Vamos a construir una herramienta diseñada para ayudarnos a crear un currículo personalizado basado en los artículos y tutoriales disponibles en Baeldung. Aunque Internet proporciona vastos recursos, organizar esta información en un camino de aprendizaje coherente puede ser complicado. Aprender nuevos topics puede volverse rápidamente abrumador, ya que puede ser difícil identificar los recursos más efectivos y filtrar el contenido irrelevante.

Para abordar este problema, desarrollaremos un cliente sencillo que interactúe con ChatGPT. Este cliente nos permitirá navegar por la extensa colección de artículos de Baeldung y recibir recomendaciones guiadas adaptadas a nuestros objetivos de aprendizaje.

4. Token de API de OpenAI

Obtener un token de API es el primer paso para conectar nuestra aplicación a la API de OpenAI. Este token es una clave secreta que autentica nuestras solicitudes a los servidores de OpenAI. Para generar este token, debemos iniciar sesión en nuestra cuenta de OpenAI y crear una nueva clave API desde la página de configuración de API.

Después de obtener el token, podemos usarlo en nuestra aplicación Java para comunicarnos de forma segura con los modelos de OpenAI, asegurándonos de que nuestro código no exponga el token de API. Para este ejemplo, configuraremos la variable de entorno en nuestro IDE, como IntelliJ IDEA. Esto es adecuado para experimentos únicos; sin embargo, en producción, es más probable que usemos herramientas de gestión de secretos como AWS Secrets Manager, HashiCorp Vault o Azure Key Vault para almacenar y gestionar nuestras claves API de forma segura.

Podemos generar dos tipos de tokens: personales y para cuentas de servicio. El token personal es autodescriptivo. Los tokens para cuentas de servicio son utilizados para bots o aplicaciones que se conectan a nuestros proyectos de OpenAI. Mientras que ambos sirven, un token personal es suficiente para nuestro propósito.

5. OpenAIClient

Una vez que hemos configurado el token en nuestras variables de entorno, inicializamos una instancia de OpenAIClient, lo que nos permite interactuar con las APIs y recibir respuestas de ChatGPT:

OpenAIClient client = OpenAIOkHttpClient.fromEnv();

Ahora que hemos inicializado el cliente, profundicemos en las APIs de Completion y Assistants.

6. Completion API vs. Assistants API

Primero, consideremos las diferencias entre la Completion API y la Assistants API, para ayudarnos a determinar cuál es la más adecuada para diferentes tareas.

El enfoque de la Completion API es perfecto para tareas simples como generar respuestas cortas, escribir fragmentos de código o elaborar secciones de contenido. También es ideal para interacciones de un solo turno que involucran una pregunta o solicitud específica que produce una respuesta concisa.

Por otro lado, la Assistants API maneja problemas complejos que requieren interacción continua y es adecuada para múltiples tareas especializadas, como escribir un libro completo, desarrollar una aplicación de software o gestionar flujos de trabajo intrincados. Proporciona herramientas como búsqueda de archivos, intérprete de código y Función de Llamada.

Mientras que la Completion API se adapta bien a tareas rápidas, la Assistants API es idónea para proyectos a largo plazo con un enfoque estructurado.

7. Compleción Simple

Utilizando la Completion API, creamos una solicitud utilizando ChatCompletionCreateParams. La configuración mínima requiere un modelo de elección y una lista de mensajes, pero también puede aceptar temperatura, tokens máximos y otras opciones de ajuste.

Veamos ahora cómo podemos utilizar individualmente los métodos addDeveloperMessage() y addUserMessage():

Builder createParams = ChatCompletionCreateParams.builder()
  .model(ChatModel.GPT_4O_MINI)
  .addDeveloperMessage("You're helping me to create a curriculum to learn programming. Use only the articles from www.baeldung.com")
  .addUserMessage(userMessage);

El uso simple de Completion es directo: el modelo de IA recibe los mensajes y, tras procesarlos, responde con una estructura de texto que podemos transmitir e imprimir:

client.chat()
  .completions()
  .create(createParams.build())
  .choices()
  .stream()
  .flatMap(choice -> choice.message()
    .content()
    .stream())
  .forEach(System.out::println);

8. Compleción Conversacional

La característica conversacional permite un diálogo continuo con la IA hasta que salgamos del programa. Observemos cómo la respuesta y la interacción cambian ahora, mientras que el modelo y los mensajes permanecen igual:

do {
    List<ChatCompletionMessage> messages = client.chat()
      .completions()
      .create(createParamsBuilder.build())
      .choices()
      .stream()
      .map(ChatCompletion.Choice::message)
      .toList();

    messages.stream()
      .flatMap(message -> message.content().stream())
      .forEach(System.out::println);

    System.out.println("-----------------------------------");
    System.out.println("Anything else you would like to know? Otherwise type EXIT to stop the program.");

    String userMessageConversation = scanner.next();

    if ("exit".equalsIgnoreCase(userMessageConversation)) {
      scanner.close();
      return;
    }

    messages.forEach(createParamsBuilder::addMessage);
    createParamsBuilder
      .addDeveloperMessage("Continue providing help following the same rules as before.")
      .addUserMessage(userMessageConversation);
} while (true);

9. Asistentes

Ahora, veamos cómo podemos utilizar la clase Assistant con los parámetros mínimos requeridos. La clase Assistant admite herramientas como búsqueda de archivos, intérprete de código y función de llamada.

Primero, tenemos que inicializar un Assistant con un nombre y modelo, proporcionar instrucciones, asignar un rol, proporcionar contenido y asignarlo a un Thread y a una Run. Estos son obligatorios y específicos de OpenAI y facilitan la interacción de la IA:

Assistant assistant = client.beta()
  .assistants()
  .create(BetaAssistantCreateParams.builder()
    .name("Baeldung Tutor")
    .instructions("You're a personal programming tutor specialized in research online learning courses.")
    .model(ChatModel.GPT_4O_MINI)
    .build());

Thread thread = client.beta()
  .threads()
  .create(BetaThreadCreateParams.builder().build());

client.beta()
  .threads()
  .messages()
  .create(BetaThreadMessageCreateParams.builder()
    .threadId(thread.id())
    .role(BetaThreadMessageCreateParams.Role.USER)
    .content("I want to learn about Strings")
    .build());

Run run = client.beta()
  .threads()
  .runs()
  .create(BetaThreadRunCreateParams.builder()
    .threadId(thread.id())
    .assistantId(assistant.id())
    .instructions("You're helping me to create a curriculum to learn programming. Use only the articles from www.baeldung.com")
    .build());

En la API de OpenAI, un thread representa una secuencia de interacciones entre un usuario y un asistente. Cuando utilizamos la Assistants API, creamos un hilo para mantener el contexto a lo largo de múltiples intercambios.

Un run es una instancia de ejecución en la que un asistente procesa la entrada del usuario y genera una respuesta. Cada ejecución pertenece a un hilo y avanza a través de diferentes estados.

A continuación, ejecutamos el hilo recién creado y hacemos polling hasta que la IA complete la tarea asignada, continuando hasta que el estado cambie y difiera de QUEUED o IN_PROGRESS:

while (run.status().equals(RunStatus.QUEUED) ||
  run.status().equals(RunStatus.IN_PROGRESS)) {
    System.out.println("Polling run...");
    java.lang.Thread.sleep(500);
    run = client.beta()
      .threads()
      .runs()
      .retrieve(BetaThreadRunRetrieveParams.builder()
        .threadId(thread.id())
        .runId(run.id())
        .build());
}

Ahora que el estado ha cambiado a COMPLETED, continuamos procesando el flujo de respuestas e imprimimos los mensajes:

System.out.println("Run completed with status: " + run.status() + "\n");

if (!run.status().equals(RunStatus.COMPLETED)) {
  return;
}

BetaThreadMessageListPage page = client.beta()
  .threads()
  .messages()
  .list(BetaThreadMessageListParams.builder()
    .threadId(thread.id())
    .order(BetaThreadMessageListParams.Order.ASC)
    .build());

page.autoPager()
  .stream()
  .forEach(currentMessage -> {
    System.out.println(currentMessage.role());
    currentMessage.content()
      .stream()
      .flatMap(content -> content.text().stream())
      .map(textBlock -> textBlock.text().value())
      .forEach(System.out::println);
    System.out.println();
});

Al trabajar con grandes conjuntos de datos, la API de OpenAI devuelve resultados en páginas. autoPager() ayuda a manejar respuestas paginadas automáticamente, permitiéndonos recuperar y procesar múltiples resultados de forma eficiente sin iterar manualmente a través de páginas.

Finalmente, podemos eliminar el Assistant cuando ya no se necesite o reutilizar el mismo assistant.id() para tareas futuras:

AssistantDeleted assistantDeleted = client.beta()
  .assistants()
  .delete(BetaAssistantDeleteParams.builder()
    .assistantId(assistant.id())
    .build());

System.out.println("Assistant deleted: " + assistantDeleted.deleted());

Cabe destacar que las clases con el prefijo “Beta” indican características experimentales o en etapa inicial en la API de OpenAI. Estas características están sujetas a cambios, por lo que debemos usarlas con precaución en aplicaciones de producción.

10. Conclusión

Integrar la API de OpenAI en nuestra aplicación Java nos permite crear herramientas que aumentan la productividad y el aprendizaje. Podemos automatizar flujos de trabajo y desarrollar aplicaciones receptivas, abriendo el camino para más innovaciones impulsadas por IA.

Sería útil explorar LangChain4j con Quarkus y Spring AI Assistant para poderosas abstracciones que simplifican el trabajo con LLMs en Java. Estos marcos minimizan la dependencia de un solo proveedor y permiten integraciones flexibles de IA.

Con cada avance en el desarrollo de software, la comprensión y el manejo de herramientas como la API de OpenAI se vuelven esenciales para los programadores que buscan crear aplicaciones modernas y eficientes.