Integra IA local con GPT4All en un chatbot con Python y FastAPI. Fase 4: respuestas inteligentes, feedback educativo y personalización usando historial del usuario.

En esta fase llevaremos nuestro chatbot al siguiente nivel: IA local con GPT4All.
Hasta ahora, el bot podía:
Pero todavía no aprendía de nuestros errores ni podía dar feedback personalizado.
Ahora vamos a integrar GPT4All y preparar la base para que el bot pueda aprender de tu historial y actuar como un profesor de inglés real.
Todo esto seguirá siendo 100% local, sin depender de servicios externos de pago.
| Herramienta | Uso |
|---|---|
| GPT4All | Modelo de IA local para generar respuestas inteligentes |
| python-dotenv | Parametrizar variables críticas como el modelo |
Añadimos la librería de GPT4All a requirements.txt:
fastapi==0.127.1
uvicorn==0.39.0
language-tool-python==2.9.5
sqlalchemy==2.0.4
gpt4all==2.8.2
python-dotenv==1.2.1
Instalamos todo con:
pip install -r requirements.txt
Crear archivo .env en la raíz del proyecto:
# Nombre del modelo GPT4All que quieres usar
MODEL_NAME=Meta-Llama-3-8B-Instruct-Q4_0.gguf
Para que tu tutor de inglés sea de alta calidad y esté preparado para el uso de bases de datos vectoriales (RAG) en el futuro (fase 4.1), recomiendo usar Meta Llama 3 8B Instruct. Es el equilibrio perfecto: es lo suficientemente inteligente para dar explicaciones gramaticales precisas y tiene una ventana de contexto amplia para manejar los datos que le envíes desde una base vectorial más adelante.
Para que nuestro bot actúe como profesor educativo, necesitamos un modelo instructivo que entienda bien las preguntas y explique conceptos:
Notas importantes:
📦 Ejemplos de modelos disponibles
Estos son algunos de los modelos que puedes usar con GPT4All:
| Modelo | Tamaño | Enfoque |
|---|---|---|
| Meta-Llama-3-8B-Instruct-Q4_0.gguf | 4.7 GB | Inteligencia y razonamiento superior. Tutoría avanzada y RAG. Es el más “listo”. |
| mistral-7b-openorca.gguf2.1.1.gguf | 4.1 GB | Muy rápido y sigue instrucciones al pie de la letra. Conversación fluida y corrección directa. |
| Nous-Hermes-2-Mistral-7B-DPO.Q4_0.gguf | 4.1 GB | Creatividad y respuestas más humanas/naturales. Práctica de conversación informal (Slang). |
| orca-mini-3b-gguf2-q4_0.gguf | 2.0 GB | Ligero y muy rápido en computadoras viejas. Pruebas rápidas de desarrollo. |
| all-MiniLM-L6-v2.gguf | 45 MB | Especializado en vectores (no para chatear). Base de Datos Vectorial (Embeddings). |
¿Cuál elegir según tu situación?
Modelos más grandes suelen responder de forma más coherente y detallada, pero requieren más memoria.
Creamos un servicio modular para encapsular GPT4All en:
app/services/gpt_service.py
import os
import logging
from dotenv import load_dotenv
from gpt4all import GPT4All
from app.database.crud import messages_crud
from sqlalchemy.orm import Session
from fastapi.concurrency import run_in_threadpool
load_dotenv()
logger = logging.getLogger("uvicorn.error")
MODEL_NAME = os.getenv("MODEL_NAME", "Meta-Llama-3-8B-Instruct.Q4_0.gguf")
class GPTService:
def __init__(self):
self.model = None
def initialize(self):
"""
Verifica la existencia del modelo y lo carga/descarga.
"""
if self.model is not None:
return
logger.info(f"GPTService: Verificando/Descargando modelo {MODEL_NAME}...")
try:
# Esto iniciará la descarga si no existe.
# El progreso de gpt4all debería salir en consola ahora.
self.model = GPT4All(MODEL_NAME)
logger.info("GPTService: Modelo cargado y listo.")
except Exception as e:
logger.error(f"GPTService: Error al cargar el modelo: {e}")
raise e
async def generate_reply(self, user_id: str, db: Session, user_message: str) -> str:
"""
Genera la respuesta del chatbot basada en el historial del usuario y su mensaje actual.
"""
# Recuperar historial del usuario
history = messages_crud.get_history_by_user(db, user_id)
context = ""
# Últimos 5 mensajes (o menos si no hay tantos)
for h in history[-5:]:
context += f"Original: {h.original_text}\nCorrected: {h.corrected_text}\n"
# Preparar prompt
prompt = f"""
Eres un profesor de inglés. Tu alumno ha enviado el siguiente mensaje:
{user_message}
Historial reciente del usuario:
{context}
Corrige y explica de forma amable si es necesario. Responde en inglés.
"""
# Ejecutar la generación en un thread pool para no bloquear el event loop
response = await run_in_threadpool(self.model.generate, prompt)
return response
Qué hace:
Por ahora, GPT4All solo utiliza el historial disponible en la base de datos para generar respuestas; no modifica ni actualiza sus parámetros internos. Esto hace que el bot recuerde errores pasados y pueda dar feedback más personalizado, sentando la base para la Fase 4.1 (memoria vectorial).
app/routers/messages_router.py
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from typing import List
from app.schemas.message_schemas import Message, MessageResponse, MessageHistoryResponse
from app.database.crud import messages_crud
from app.database.session import SessionLocal
from app.services.grammar_service import LanguageToolService
from app.services.gpt_service import GPTService
router = APIRouter()
grammar_service = LanguageToolService()
gpt_service = GPTService() # Instancia única del GPTService
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.post("/sendMessage", response_model=MessageResponse)
async def send_message(msg: Message, db: Session = Depends(get_db)):
# Lógica de corrección de texto
corrected, feedback = await grammar_service.check_text(msg.text)
# Guardar historial
messages_crud.create_message(db, msg.user_id, msg.text, corrected)
# Generar respuesta GPT de forma asíncrona
try:
# Antes:
gpt_reply = await gpt_service.generate_reply(msg.user_id, db, corrected)
# Ahora:
gpt_reply = await gpt_service.generate_reply(msg.user_id, db, msg.text)
except Exception as e:
gpt_reply = "Lo siento, no puedo responder a tu mensaje ahora."
return MessageResponse(original=msg.text, corrected=corrected, message=gpt_reply)
@router.get("/history/{user_id}", response_model=List[MessageHistoryResponse])
def get_history(user_id: str, db: Session = Depends(get_db)):
messages = messages_crud.get_history_by_user(db, user_id)
return [
MessageHistoryResponse(original=m.original_text, corrected=m.corrected_text)
for m in messages
]
Qué cambia respecto a la Fase 3:
from contextlib import asynccontextmanager
from fastapi import FastAPI
from app.database.base import Base
from app.database.session import engine
from app.routers import messages_router
@asynccontextmanager
async def lifespan(app: FastAPI):
print("\nAplicación iniciando...", flush=True)
# Startup: Base de Datos
Base.metadata.create_all(bind=engine)
# Startup: GPT
# Importante: Esto descargará el modelo si no existe y PUEDE TARDAR MUCHO.
messages_router.gpt_service.initialize()
yield
print("[LIFESPAN] Aplicación cerrándose...", flush=True)
app = FastAPI(lifespan=lifespan)
app.include_router(messages_router.router)
{
"user_id": "amoelcodigo",
"text": "Hello, ow are you?"
}
Posible respuesta:
{
"original": "Hello, ow are you?",
"corrected": "Hello, ow, are you?",
"message": "¡Hola! Me alegra verte aquí.\n \n Lo siento mucho, pero parece que hay un pequeño problema con tu escritura en inglés. Quiero ayudarte a mejorarla. Vamos a revisar juntos el mensaje que enviaste: \"Hello, ow, are you?\"\n\nLa corrección es la siguiente:\n\"Hello, how are you?\"\n\n¿Por qué? Bueno, cuando hablamos o escribimos en inglés, hay algunas reglas importantes para seguir. En este caso, se trata de la puntuación y el orden de las palabras.\n\nEn español, \"ow\" no tiene un significado específico, pero en inglés es una interjección que se utiliza para expresar dolor o sorpresa (como cuando alguien te pincha con algo). Sin embargo, en este contexto, parece que estás tratando de preguntar por la salud del otro. En ese caso, debes usar \"how\" (¿cómo?)"
}
💡 Nota importante: La corrección automática (corrected) de LanguageTool detecta errores simples de gramática y ortografía, pero puede fallar en errores semánticos o contextuales. Por ejemplo, si el usuario escribe “I am your fater”, corrected podría devolver “I am your fatter”, lo cual cambia completamente el significado. Gracias a GPT4All, la IA ve el mensaje original, interpreta la intención del usuario y genera una corrección educativa correcta junto con una explicación clara. Esto convierte al bot en un profesor de inglés real, que no solo corrige sino que enseña.
En la Fase 4.1 implementaremos memoria avanzada con embeddings y almacenamiento vectorial, de manera que el bot aprenda de tus errores pasados, recuerde tu progreso y genere feedback cada vez más personalizado.
Si quieres ir a la fase 4.1 haz click aquí