2025/01/24 | inaki
with openai

Buscar

Django + asistente OpenAI + identidad

Descripción de la imagen

En el mundo digital actual, los asistentes virtuales se han convertido en herramientas fundamentales para mejorar la interacción con los usuarios y brindar soporte automatizado en tiempo real. En este artículo, exploraremos paso a paso cómo instalar y configurar un asistente virtual utilizando la API de OpenAI en un proyecto desarrollado con Django. Este asistente será capaz de responder preguntas, procesar consultas, y mejorar la experiencia de los usuarios en tu plataforma.

A lo largo de esta guía, aprenderás a integrar OpenAI en tu aplicación Django, gestionar las solicitudes al asistente, y crear una interfaz web interactiva para que los usuarios puedan comunicarse fácilmente con el bot. Pero no nos detendremos ahí. Al final del artículo, abordaremos dos mejoras clave que llevarán tu asistente al siguiente nivel:

  1. Añadir identidad al asistente mediante archivos .txt: Configuraremos un archivo de texto donde podrás definir las directrices y personalidad del agente, logrando respuestas más consistentes y alineadas con los objetivos de tu proyecto.
  2. Implementar memoria contextual en el asistente: Aprenderemos a mantener el historial de las conversaciones para que el asistente pueda recordar detalles a lo largo de la interacción, mejorando significativamente la experiencia del usuario.

 

Pasos a seguir para la instalación y configuración:

Hemos creado un proyecto django con una app llamada assistant dentro de un directorio llamado applications:

applications/assistant

mkdir applications
cd applications
django-admin startapp assistant

 

En settings.py añadimos la app creada (en el caso que queramos añadirlo en una app aparte de home para que esté más ordenado)

INSTALLED_APPS = [
    ...
    'applications.assistant'
    ...
]

 

Editamos apps.py de applications/assistant

from django.apps import AppConfig

class AssistantConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'applications.assistant'

 

Instalar openai:

pip install openai

 

Creamos la API en openai, para eso necesitamos crear una cuenta de pago:

 

En settings.py añadir la api:

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "sk-proj-ktzgVEa1K...tu api de openai")

 

Crear archivo consumers.py en applications/assistant/consumers.py

import json
from channels.generic.websocket import AsyncWebsocketConsumer

class AssistantConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        # Aceptar la conexión WebSocket
        await self.accept()

    async def disconnect(self, close_code):
        # Opcional: Manejar la desconexión
        pass

    async def receive(self, text_data):
        # Recibir datos del cliente
        data = json.loads(text_data)
        user_message = data.get('message', '')

        # Responder al cliente
        await self.send(json.dumps({
            'message': f"Recibí tu mensaje: {user_message}"
        }))

 

En applications/assistant creamos un archivo urls.py

from django.urls import path
from . import views


urlpatterns = [
    path('chat/', views.chat_view, name='chat_view'),
    path('api/get_response/', views.get_bot_response, name='get_bot_response'),
    path('end_conversation/', views.end_conversation, name='end_conversation'),
]

 

En urls principal (el que tenemos junto a settings.py) añadimos la url para que apunte a nuestra url creada en assistant (también habrá que importar "include":

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('assistant/', include('applications.assistant.urls')),
]

 

A la altura de settings.py creamos un asgi.py

import os
from django.core.asgi import get_asgi_application

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from applications.assistant.urls import websocket_urlpatterns



os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'retegi.settings')

application = ProtocolTypeRouter({
    "http": get_asgi_application(),  # Manejo de solicitudes HTTP
    "websocket": AuthMiddlewareStack(  # Manejo de solicitudes WebSocket
        URLRouter(websocket_urlpatterns)  # Incluye las rutas WebSocket
    ),
})

 

En applications/assistant editamos el archivo views.py

from django.shortcuts import render
from django.http import JsonResponse
import openai
from django.conf import settings
import os

MAX_HISTORY = 20  # Máximo número de mensajes en el historial

def chat_view(request):
    return render(request, 'home/chat.html')

def load_instructions():
    instructions_path = os.path.join(settings.BASE_DIR, 'applications', 'assistant', 'indications', 'instructions.txt')
    try:
        with open(instructions_path, 'r', encoding='utf-8') as file:
            return file.read()
    except FileNotFoundError:
        return "Eres un asistente virtual amigable y útil."

def get_bot_response(request):
    if request.method == 'POST':
        user_message = request.POST.get('message', '')
        openai.api_key = settings.OPENAI_API_KEY

        # Carga las instrucciones desde el archivo
        instructions = load_instructions()

        # Obtener el historial de la sesión
        conversation_history = request.session.get('conversation_history', [])

        # Agregar el mensaje del usuario al historial
        conversation_history.append({"role": "user", "content": user_message})

        # Limitar el historial al máximo definido
        if len(conversation_history) > MAX_HISTORY:
            conversation_history = conversation_history[-MAX_HISTORY:]

        try:
            # Incluir las instrucciones como el primer mensaje en el historial
            messages = [{"role": "system", "content": instructions}] + conversation_history

            # Enviar el historial completo al modelo
            response = openai.ChatCompletion.create(
                model="gpt-3.5-turbo",
                messages=messages
            )

            bot_reply = response['choices'][0]['message']['content']

            # Agregar la respuesta del bot al historial
            conversation_history.append({"role": "assistant", "content": bot_reply})

            # Guardar el historial actualizado en la sesión
            request.session['conversation_history'] = conversation_history

            return JsonResponse({'reply': bot_reply})
        except Exception as e:
            print(f"Error con OpenAI: {e}")  # Depuración
            return JsonResponse({'error': str(e)})

    return JsonResponse({'error': 'Invalid request'})

def end_conversation(request):
    if 'conversation_history' in request.session:
        del request.session['conversation_history']
    return JsonResponse({'status': 'Conversación finalizada'})

Creamos un directorio "templates" a la altura de manage.py y lo configuramos en settings.py y también habrá que import os 'import os':

import os

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.i18n',
            ],
        },
    },
]

Archivo index.html con el código javascript necesario:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

</head>
  <body>

      <div>
        <div id="chat-box">
          <!-- Los mensajes del chat se mostrarán aquí -->
        </div>
      </div>
      <!-- Zona de entrada fija -->
      <div>
        <form id="message-form" method="post">
          <input type="text" id="user-message">
          <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
          <button>Enviar</button>
        </form>
      </div>



<!--OpenAI-->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
    $(document).ready(function () {
        // Función para enviar mensaje
        function sendMessage() {
            let userMessage = $('#user-message').val();
            if (userMessage.trim() !== '') {
                // Limpiar el chat-box antes de agregar nuevos mensajes
                //$('#chat-box').empty();

                $('#chat-box').append(`<p class="user-message"><strong>Tú:</strong> ${userMessage}</p>`);
                $('#user-message').val('');

                // Desplazarse automáticamente al final del chat
                $('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);

                $.ajax({
                    type: 'POST',
                    url: '/assistant/api/get_response/',
                    data: {
                        'message': userMessage,
                        'csrfmiddlewaretoken': $('input[name="csrfmiddlewaretoken"]').val()
                    },
                    success: function (response) {
                        if (response.reply) {
                            $('#chat-box').append(`<p class="bot-message"><strong>Bot:</strong> ${response.reply}</p>`);
                            $('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
                        } else {
                            $('#chat-box').append('<p class="bot-message"><strong>Error:</strong> Algo salió mal.</p>');
                        }
                    },
                    error: function () {
                        $('#chat-box').append('<p class="bot-message"><strong>Error:</strong> No se pudo conectar con el servidor.</p>');
                    }
                });
            }
        }

        // Enviar mensaje al hacer clic en el botón
        $('#send-button').click(function () {
            sendMessage();
        });

        // Enviar mensaje al presionar Enter
        $('#user-message').keypress(function (e) {
            if (e.which === 13) {
                e.preventDefault();
                sendMessage();
            }
        });
    });
</script>
  </body>
</html>

 


Observación: Al realizar pruebas vemos que funciona correctamente pero no tiene memoria. Hemos realizado pruebas, y en el caso de añadir memoria (por ejemplo de las útimas 10 preguntas y respuesta) se perdía la posibilidad de utilizar el archivo instructions.txt que contiene instrucciones para el agente virtual. Estoy haciendo pruebas para poder utilizar tanto la memoria como el archivo instrucions.txt

Comentarios

inaki
Ene 27, 2025

Prueba