2025/01/24 | inaki
with openai
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:
.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.
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
Prueba