DEV Community

Cover image for Usar OpenAI como Asistente para tu equipo de trabajo: Implementación y ventajas
Daniel J. Saldaña
Daniel J. Saldaña

Posted on • Originally published at danieljsaldana.dev on

Usar OpenAI como Asistente para tu equipo de trabajo: Implementación y ventajas

En este post, te mostraré cómo aprovechar OpenAI como un asistente eficiente para tu equipo de trabajo y la atención al cliente. Además, te compartiré un ejemplo práctico que podrás implementar en tus proyectos, y discutiremos las ventajas de utilizar Storage y vectores de OpenAI, específicamente en la funcionalidad de File Search Beta. Este artículo también incluye instrucciones sobre cómo configurar tu entorno de desarrollo para que puedas empezar a trabajar con OpenAI de inmediato.

¿Por qué usar OpenAI como Asistente?

OpenAI ofrece una serie de herramientas avanzadas que pueden transformar la manera en que gestionas las tareas diarias y la interacción con tus clientes. Estas son algunas de las principales ventajas de utilizar OpenAI:

  • Automatización de tareas repetitivas : OpenAI puede manejar automáticamente tareas como responder preguntas frecuentes, generar contenido basado en tus necesidades, y asistir en procesos creativos, liberando tiempo para que tu equipo se enfoque en tareas más estratégicas.
  • Análisis y procesamiento de grandes volúmenes de datos : Puedes usar OpenAI para generar reportes y realizar análisis detallados basados en datos, lo que facilita la toma de decisiones informadas.
  • Disponibilidad 24/7 : OpenAI puede operar sin descanso, lo que significa que tus clientes pueden recibir asistencia en cualquier momento del día, mejorando su experiencia general.

File Search Beta y Vector Stores

La funcionalidad File Search Beta de OpenAI es especialmente útil cuando se necesita acceder y buscar en documentos específicos. Esta herramienta permite que los asistentes de OpenAI accedan a documentos almacenados en un vector store, una base de datos de vectores optimizada para búsquedas semánticas y por palabra clave. Esto es ideal para responder preguntas complejas basadas en información externa, como documentos técnicos, manuales de productos o reportes financieros.

Ventajas de File Search Beta:
  • Capacidad de búsqueda mejorada : La combinación de búsquedas por palabra clave y semánticas asegura que siempre se encuentre la información más relevante.
  • Automatización en el procesamiento de documentos : OpenAI se encarga de parsear, fragmentar y generar embeddings (representaciones vectoriales) de los documentos, facilitando la búsqueda y recuperación de la información.
  • Escalabilidad : Un vector store puede contener hasta 10,000 archivos, lo que permite gestionar grandes cantidades de información que el asistente puede consultar rápidamente.

Implementación: Código completo

A continuación, te presento el código completo que necesitas para implementar un asistente con OpenAI que procese archivos Markdown y extraiga información clave. Además, se incluye la configuración del archivo .env necesario para que todo funcione correctamente.

1. Configuración del entorno

Primero, asegúrate de crear un archivo .env en el directorio raíz de tu proyecto con las siguientes variables de entorno:

OPENAI_API_KEY=tu_clave_de_api_de_openai
OPENAI_ASSISTANT_ID=tu_id_de_asistente_de_openai
VECTOR_STORE_ID=tu_id_de_vector_store

Enter fullscreen mode Exit fullscreen mode

Estas claves son esenciales para autenticar tu aplicación con los servicios de OpenAI y gestionar el almacenamiento y procesamiento de archivos.

2. Función personalizada dentro del Agente de OpenAI

El siguiente código JSON define una función personalizada que puedes incluir en tu asistente de OpenAI. Esta función procesa archivos Markdown subidos a OpenAI, extrae información clave y construye una URL completa basada en un URL base proporcionado.

{
  "name": "process_uploaded_markdown",
  "description": "Parse a Markdown file uploaded to OpenAI, extract key information, and structure the URL with a base URL.",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "file_id": {
        "type": "string",
        "description": "The ID of the Markdown file uploaded to OpenAI."
      },
      "base_url": {
        "type": "string",
        "description": "The base URL to construct the full URL for the post."
      }
    },
    "required": [
      "file_id",
      "base_url"
    ],
    "additionalProperties": false
  }
}

Enter fullscreen mode Exit fullscreen mode

Esta función será utilizada por el asistente para procesar los archivos y generar respuestas más informadas y contextuales.

3. Endpoint en Next.js: assistant.js

Este es el código de un endpoint en Next.js que interactúa con OpenAI para procesar archivos Markdown y generar respuestas basadas en el contenido de esos archivos.

import { enableCors } from "@/src/middleware/enableCors";
import { methodValidator } from "@/src/utils/methodValidator";
import OpenAI from "openai";

const API_KEY = process.env.OPENAI_API_KEY;
const ASSISTANT_ID = process.env.OPENAI_ASSISTANT_ID;

async function assistantHandler(req, res) {
  console.log(`Recibida solicitud ${req.method} en /api/assistant`);

  await methodValidator(req, res, 'POST');

  if (res.headersSent) {
    return;
  }

  const openai = new OpenAI({
    apiKey: API_KEY,
    headers: {
      'OpenAI-Beta': 'assistants=v2',
    }
  });

  try {
    const { prompt, threadId, fileId } = req.body;

    if (!prompt) {
      res.status(400).json({ error: "Falta el parámetro 'prompt' en la solicitud." });
      return;
    }

    let thread;

    if (!threadId) {
      thread = await openai.beta.threads.create();
      console.log("Nuevo thread creado:", thread.id);
    } else {
      thread = { id: threadId };
      console.log("Usando thread existente:", thread.id);
    }

    if (fileId) {
      const base_url = "https://danieljsaldana.dev";

      const functionCall = await openai.chat.completions.create({
        model: 'gpt-4',
        messages: [{ role: 'system', content: 'Extract relevant information from the markdown file.' }],
        functions: [
          {
            name: 'process_uploaded_markdown',
            description: 'Parse and extract key information from a Markdown file.',
            parameters: {
              type: 'object',
              properties: {
                file_id: { type: 'string', description: 'The ID of the Markdown file.' },
                base_url: { type: 'string', description: 'The base URL to construct the full URL.' }
              },
              required: ['file_id', 'base_url']
            }
          }
        ],
        function_call: {
          name: 'process_uploaded_markdown',
          arguments: JSON.stringify({ file_id: fileId, base_url })
        }
      });

      const processedData = JSON.parse(functionCall.choices[0].message.function_call.arguments);

      let content = `He procesado el archivo Markdown. Aquí está la información relevante:\n\nTítulo: ${processedData.title}\nDescripción: ${processedData.description}\nFecha: ${processedData.date}\nURL: ${processedData.url}\nTags: ${processedData.tags}\nCategorías: ${processedData.categories}`;

      content = content.replace(/【\d+:\d+†source】/g, '');

      await openai.beta.threads.messages.create(thread.id, {
        role: "assistant",
        content: content,
      });
    } else {
      await openai.beta.threads.messages.create(thread.id, {
        role: "user",
        content: prompt,
      });
    }

    let completeResponse = '';

    const run = await openai.beta.threads.runs.stream(thread.id, {
      assistant_id: ASSISTANT_ID,
    });

    run.on('textDelta', (textDelta) => {
      if (textDelta && typeof textDelta.value === 'string') {
        completeResponse += textDelta.value;
      }
    });

    run.on('end', () => {
      completeResponse = completeResponse.replace(/【\d+:\d+†source】/g, '');

      res.writeHead(200, { "Content-Type": "text/plain" });
      res.end(completeResponse);
    });

    run.on('error', (error) => {
      console.error('Error en el streaming:', error.message);
      res.status(500).json({ error: 'Error en el streaming de la respuesta' });
    });

  } catch (error) {
    console.error('Error al llamar a la API de OpenAI:', error.message);
    res.status(500).json({ error: 'Error al procesar la solicitud' });
  }
}

export default enableCors(assistantHandler);

Enter fullscreen mode Exit fullscreen mode

Este endpoint es esencial para manejar solicitudes del usuario y procesar archivos con la ayuda de OpenAI.

4. Script de Build en Astro: vector.cjs

Este script se ejecuta durante el proceso de build y se encarga de subir archivos Markdown a OpenAI y agregarlos a un vector store. Es una parte crucial para preparar tu entorno y asegurarte de que tus archivos estén disponibles para ser consultados por el asistente.

const fs = require('fs');
const path = require('path');
const OpenAI = require('openai');
require('dotenv').config();

const VECTOR_STORE_ID = process.env.VECTOR_STORE_ID;
const POSTS_DIRECTORY = path.join(__dirname, '..', 'src', 'content', 'posts');
const OUTPUT_DIRECTORY = path.join(__dirname, '..', 'src', 'output');

if (!fs.existsSync(OUTPUT_DIRECTORY)) {
  fs.mkdirSync(OUTPUT_DIRECTORY, { recursive: true

 });
}

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const fileExistsInVectorStore = async (fileName) => {
  try {
    const response = await openai.beta.vectorStores.files.list(VECTOR_STORE_ID);
    const files = response.data?.files || [];

    return files.some(file => file.file_name === fileName);
  } catch (error) {
    console.error('Error al listar archivos en el vector store:', error);
    return false;
  }
};

const uploadFilesToVectorStore = async () => {
  try {
    const posts = fs.readdirSync(POSTS_DIRECTORY);
    const allowedExtensions = ['.txt', '.md', '.pdf'];

    for (const post of posts) {
      const postPath = path.join(POSTS_DIRECTORY, post);
      const postExtension = path.extname(post);

      if (allowedExtensions.includes(postExtension)) {
        const exists = await fileExistsInVectorStore(post);

        if (exists) {
          console.log(`Archivo ${post} ya existe en el vector store, se omite la subida.`);
        } else {
          const uploadedFile = await openai.files.create({
            file: fs.createReadStream(postPath),
            purpose: 'assistants',
          });

          console.log(`Archivo subido: ${post} con ID: ${uploadedFile.id}`);

          await openai.beta.vectorStores.files.createAndPoll(VECTOR_STORE_ID, {
            file_id: uploadedFile.id
          });
          console.log(`Archivo ${post} agregado al vector store ${VECTOR_STORE_ID}`);
        }
      } else {
        console.log(`Archivo ${post} ignorado (extensión no permitida)`);
      }
    }
  } catch (error) {
    console.error('Error al subir archivos al vector store:', error);
  }
};

uploadFilesToVectorStore();

Enter fullscreen mode Exit fullscreen mode

Implementación de OpenAI

Este script es parte del proceso automatizado para asegurarte de que los archivos que deseas procesar estén disponibles en el vector store antes de que el asistente los utilice.

Con estos tres componentes - la función personalizada en el agente, el endpoint en Next.js, y el script de build en Astro estarás listo para implementar un asistente inteligente que pueda procesar y extraer información clave de tus documentos, mejorando la productividad y la calidad del servicio en tu equipo de trabajo.

Top comments (0)