Chat Help Bot – Guía de uso y fundamentos técnicos
Idea central: este servicio es un “bot” de ayuda para Explorax que lee tus documentos locales (./text), crea un índice semántico con embeddings y FAISS, y responde preguntas combinando la búsqueda en esos documentos con un modelo de lenguaje (patrón RAG, Retrieval-Augmented Generation). Así logramos respuestas útiles, actualizadas a tu contenido y con menos “alucinaciones”.
1) ¿Qué hace y por qué está construido así?
- FastAPI expone una API sencilla con dos endpoints: un healthcheck (
GET /) y el endpoint de preguntas (POST /ask/). FastAPI es rápido, tipado y genera documentación automática; además soporta middleware como CORS sin complicaciones. (fastapi.tiangolo.com) - RAG (Retrieval-Augmented Generation): antes de escribir una respuesta, el bot recupera fragmentos relevantes de tus PDFs/TXTs y los pasa al modelo para “anclar” lo que responde en tu propio material. Es el enfoque recomendado cuando el conocimiento vive fuera del modelo. (ibm.com)
- FAISS es la librería usada para búsquedas por similitud a gran velocidad en miles o millones de vectores (embeddings). Está escrita en C++ con bindings a Python y se usa ampliamente en producción para este tipo de índices. (faiss.ai)
- LangChain simplifica el armado de cadenas tipo “recupero → razono → respondo”. Tu código usa
ConversationalRetrievalChain, que funciona, pero hoy está deprecado; LangChain recomienda migrar acreate_retrieval_chain/create_history_aware_retriever(mismo concepto, API más clara). (api.python.langchain.com)
Flujo a alto nivel
[.txt/.pdf en ./text]
│ (carga + corte en chunks)
▼
[Embeddings OpenAI] → [Índice FAISS]
│ │
└──(retriever)◀──────┘
│
[Cadena de LangChain + LLM]
│
Respuesta JSON
2) Arquitectura actual (con palabras sencillas)
- Carga de contenido
- Se leen .pdf (con
PyPDF2.PdfReader) y .txt UTF-8, se dividen en fragmentos (~1000 caracteres, con solape 200) y se les guardasource=archivo. - Nota práctica: extracción de texto en PDF varía por archivo; si ves espacios raros u orden extraño, es una limitación común del parser y del propio PDF. Considera pypdf (la evolución de PyPDF2) o alternativas si tus PDFs son complejos/escaneados. (pypdf2.readthedocs.io)
- Se leen .pdf (con
- Vector store
- Se generan embeddings y se construye un índice FAISS cada vez que la app arranca (hoy no se persiste). FAISS está hecho para búsqueda rápida por similitud incluso con muchos documentos. (faiss.ai)
- Cadena de conversación
- Al llegar una pregunta, se usa un retriever sobre FAISS para traer los fragmentos más cercanos, y el LLM redacta la respuesta “aterrizada” a esos fragmentos.
- Tu implementación usa
ConversationalRetrievalChain; si vas a seguir mejorándola, conviene migrar a la versión recomendada en docs. (python.langchain.com)
- API
POST /ask/recibe{ "question": "..." }y devuelve{ "response": "<texto>" }.GET /devuelve un healthcheck simple.
- CORS y seguridad
- Por comodidad está abierto con
allow_origins=["*"]. Útil en pruebas, no ideal en producción: el comodín tiene implicaciones de seguridad y no permite credenciales. Lo recomendado es listar orígenes explícitos y ajustar métodos/headers. (fastapi.tiangolo.com)
- Por comodidad está abierto con
3) Cómo correrlo
Desarrollo
uvicorn app:app --reload --host 0.0.0.0 --port 8000
- Crea un archivo
.envcon tuOPENAI_API_KEY. Para manejar variables,python-dotenvestá bien, pero en producción es mejor inyectarlas desde el entorno/secret manager; Pydantic Settings te ayuda a tiparlas y validarlas. (PyPI)
Docker (básico)
docker build -t chat-help-bot .
docker run -p 80:80 --env-file .env -v %CD%/text:/app/text chat-help-bot # Windows CMD
# o
docker run -p 80:80 --env-file .env -v $(pwd)/text:/app/text chat-help-bot # Linux/macOS
- Para producción, es común correr Gunicorn con workers Uvicorn, que añade manejo de procesos y reinicios elegantes (o usar
uvicorn --workers Ndirectamente). Revisa las guías oficiales para contenedores y workers. (uvicorn.dev)
4) Endpoints
-
GET
/– Healthcheck.200 →
{ "message": "API está funcionando" } -
POST
/ask/– Pregunta al bot.Request:
{ "question": "..." }200 →
{ "response": "<texto>" }422 → error de validación
500 → error interno (por ejemplo, fallo del LLM o embeddings)
5) Decisiones técnicas (el “por qué” detrás)
- RAG vs. “puro LLM”: si tu conocimiento vive en PDFs/TXTs propios, RAG evita respuestas desactualizadas y reduce alucinaciones al fundamentar la salida en tu set de documentos. (ibm.com)
- FAISS: diseñado para búsqueda vectorial eficiente (CPU/GPU), escalable y maduro en la industria. (faiss.ai)
- Chunking: fragmentar los textos (con solape) mejora el recall del retriever sin perder coherencia en la respuesta.
- Memoria efímera: hoy la memoria de chat se crea por request; simple y aislado. Si querés “hilos” persistentes por usuario, guarda el historial por
chatId.
6) Buenas prácticas y mejoras recomendadas
6.1 Persistir el índice FAISS (arranque más rápido)
Hoy reconstruís el índice en cada arranque. Persistirlo a disco te ahorra tiempo/costo y permite cargas incrementales. LangChain expone save_local / load_local. (python.langchain.com)
# Al crear el índice
vectorstore.save_local("faiss_index")
# Al arrancar la app
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
emb = OpenAIEmbeddings()
vectorstore = FAISS.load_local("faiss_index", embeddings=emb, allow_dangerous_deserialization=True)
Nota: para muchos documentos, load_local también puede tardar; planifica tiempos y almacenamiento. (Reddit)
6.2 Migrar imports de LangChain a los paquetes modernos
LangChain separó integraciones en paquetes dedicados. Para OpenAI, usa langchain_openai (y considera migrar la cadena de recuperación): (python.langchain.com)
# Antes
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.chat_models import ChatOpenAI
# Después (recomendado)
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
6.5 Límites de tasa (rate limiting)
Para evitar abusos y controlar costos del LLM, agrega rate limiting. slowapi se integra fácil con FastAPI/Starlette (decoradores o middleware). (slowapi.readthedocs.io)
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address, default_limits=["30/minute"])
app.state.limiter = limiter
6.6 PDFs y encoding
pypdfmantiene documentación activa y ejemplos para extracción de texto. Si ves caracteres raros/espacios insertados, es una limitación conocida: cambia de extractor o aplica limpieza posterior. (pypdf.readthedocs.io)
6.7 Medición de tokens y costos
tiktokensirve para estimar tokens antes de llamar al LLM (útil para logs o guardrails). Revisa los ejemplos oficiales de conteo. (cookbook.openai.com)
6.8 Despliegue
- Con Docker, seguí la guía oficial de FastAPI para imágenes y workers; Uvicorn + Gunicorn es un patrón probado para producción. (fastapi.tiangolo.com)
7) Referencia rápida de configuración
- Variables
OPENAI_API_KEY: clave para embeddings y chat.- Sugerencia: centraliza la config con Pydantic Settings (tipado, defaults,
.envopcional). (fastapi.tiangolo.com)
- Directorio de contenido
./textcon.pdf/.txt. Al crecer, persiste FAISS.
- Dependencias clave
fastapi,uvicorn/gunicorn,langchain,langchain-openai,langchain-community,faiss-cpu,pypdfoPyPDF2,tiktoken.
8) API de ejemplo (curl)
curl -X POST http://localhost:8000/ask/ \
-H "Content-Type: application/json" \
-d '{"question":"¿Cómo instalo la app?"}'
Respuesta:
{ "response": "<texto con la respuesta contextual>" }
9) Resolución de problemas (rápido)
- “Me tarda mucho al iniciar” → persiste FAISS con
save_local/load_local. (python.langchain.com) - “No me cargan fuentes/JS por CORS” → no uses si envías credenciales; enumera orígenes y verifica
OPTIONSpreflight. (developer.mozilla.org) - “No extrae bien el texto del PDF” → prueba con pypdf o revisa otra librería si el PDF es escaneado/complicado. (pypdf.readthedocs.io)
- “Quiero hilos persistentes” → guarda
chat_historyporchatId(BD/Redis) y rehidrátalo en cada request. - “Deprecations de LangChain” → migra a
langchain_openaiy acreate_retrieval_chain. (python.langchain.com)
10) Roadmap sugerido
- Persistencia FAISS + recarga incremental. (python.langchain.com)
- Memoria por usuario (usar
chatId). - CORS endurecido + rate limiting. (developer.mozilla.org)
- Plantillas de prompt versionadas + tests.
- Observabilidad (logs de latencia, hit rate del retriever, tokens).
- Pruebas con
pytest + httpx(contrato y regresión).
Glosario mínimo
- Embedding: vector numérico que representa el significado de un texto.
- FAISS: motor de similarity search para buscar embeddings rápido. (faiss.ai)
- RAG: estrategia donde el LLM consulta tus documentos antes de responder. (ibm.com)