Skip to main content

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 a create_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)

  1. 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 guarda source=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)
  2. 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)
  3. 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)
  4. API
    • POST /ask/ recibe { "question": "..." } y devuelve { "response": "<texto>" }.
    • GET / devuelve un healthcheck simple.
  5. 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)

3) Cómo correrlo

Desarrollo

uvicorn app:app --reload --host 0.0.0.0 --port 8000

  • Crea un archivo .env con tu OPENAI_API_KEY. Para manejar variables, python-dotenv está 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 N directamente). 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

  • pypdf mantiene 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

  • tiktoken sirve 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, .env opcional). (fastapi.tiangolo.com)
  • Directorio de contenido
    • ./text con .pdf/.txt. Al crecer, persiste FAISS.
  • Dependencias clave
    • fastapi, uvicorn/gunicorn, langchain, langchain-openai, langchain-community, faiss-cpu, pypdf o PyPDF2, 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 OPTIONS preflight. (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_history por chatId (BD/Redis) y rehidrátalo en cada request.
  • “Deprecations de LangChain” → migra a langchain_openai y a create_retrieval_chain. (python.langchain.com)

10) Roadmap sugerido

  1. Persistencia FAISS + recarga incremental. (python.langchain.com)
  2. Memoria por usuario (usar chatId).
  3. CORS endurecido + rate limiting. (developer.mozilla.org)
  4. Plantillas de prompt versionadas + tests.
  5. Observabilidad (logs de latencia, hit rate del retriever, tokens).
  6. 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)