FAQ RAG Chatbot — PeopleForce HR SaaS
Sistema de chatbot inteligente de soporte para FAQs basado en Retrieval-Augmented Generation (RAG). Procesa un documento de preguntas frecuentes de una empresa HR SaaS, lo indexa en una base de datos vectorial y responde preguntas de usuarios recuperando los fragmentos más relevantes del documento para generar respuestas precisas con un LLM. Esto elimina la necesidad de búsquedas manuales y reduce la carga del equipo de soporte al cliente.
Arquitectura RAG
┌──────────────────────────────────────────────────────────────────┐
│ PIPELINE DE INDEXACIÓN │
│ │
│ faq_document.txt ──► Chunking ──► Embeddings ──► ChromaDB │
│ (300 chars, (text-embedding (persistente │
│ 50 overlap) -3-small) local) │
└──────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ PIPELINE DE CONSULTA │
│ │
│ Pregunta ──► Embedding ──► k-NN Search ──► Contexto ──► LLM │
│ del usuario de query (coseno, (top-k (gpt-4o │
│ ChromaDB) chunks) -mini) │
│ │ │
│ ▼ │
│ JSON Response │
│ { user_question, │
│ system_answer, │
│ chunks_related } │
└──────────────────────────────────────────────────────────────────┘
Instalación
1. Clonar el repositorio
git clone <repo-url>
cd kunz-mcp-project
2. Crear entorno virtual e instalar dependencias
python -m venv .venv
source .venv/bin/activate # macOS/Linux
# .venv\Scripts\activate # Windows
pip install -r requirements.txt
3. Configurar API Key
cp .env.example .env
# Edita .env y agrega tu clave de OpenAI:
# OPENAI_API_KEY=sk-...
Uso
Ejecutar el pipeline de indexación
python src/build_index.py
Esto carga data/faq_document.txt, lo divide en chunks, genera embeddings y los almacena en ChromaDB (data/chroma_db/).
Ejecutar una consulta
python src/query.py "¿Cuántos días de vacaciones me corresponden?"
Salida JSON de ejemplo:
{
"user_question": "¿Cuántos días de vacaciones me corresponden?",
"system_answer": "Todos los colaboradores de tiempo completo tienen derecho a 15 días hábiles de vacaciones al año a partir de su primer aniversario. Con más de 3 años de antigüedad, se reciben 20 días hábiles, y con más de 7 años, 25 días hábiles.",
"chunks_related": [
{
"text": "¿Cuántos días de vacaciones me corresponden?...",
"metadata": {
"chunk_index": 1,
"total_chunks": 30,
"source": "faq_document.txt"
}
}
]
}
Ejecutar el agente evaluador (Bonus)
python src/evaluator.py
Evalúa las respuestas en outputs/sample_queries.json y devuelve un puntaje 0-10 con justificación.
Servidor MCP (Model Context Protocol)
El proyecto incluye src/mcp_server.py, que expone el pipeline RAG como un servidor MCP para que agentes de IA (Claude Desktop, Cursor, VS Code con Copilot, etc.) puedan invocar las herramientas directamente.
Tools disponibles
| Tool | Descripción |
|---|---|
ask_hr_faq(question) | Pipeline RAG completo: busca en ChromaDB y genera respuesta con GPT-4o-mini |
evaluate_rag_response(user_question, system_answer, chunks_related) | Evalúa la calidad de una respuesta RAG (puntaje 0-10 con justificación) |
rebuild_index() | Reindexar el documento FAQ en ChromaDB (útil tras actualizar el FAQ) |
Prerequisito
Asegúrate de haber construido el índice antes de levantar el servidor:
python src/build_index.py
Opción A — Ejecutar directamente (modo stdio)
python src/mcp_server.py
Esto lanza el servidor en modo stdio, compatible con cualquier cliente MCP.
Opción B — Ejecutar con MCP CLI
mcp run src/mcp_server.py
Opción C — Integrar con Claude Desktop
Edita el archivo de configuración de Claude Desktop:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"hr-faq-rag": {
"command": "python",
"args": ["/ruta/absoluta/a/kunz-mcp-project/src/mcp_server.py"],
"env": {
"OPENAI_API_KEY": "sk-..."
}
}
}
}
Reinicia Claude Desktop. El servidor aparecerá como herramienta disponible en el chat.
Opción D — Integrar con Cursor o VS Code
Agrega en la configuración MCP del editor (.cursor/mcp.json o settings.json):
{
"mcpServers": {
"hr-faq-rag": {
"command": "python",
"args": ["src/mcp_server.py"]
}
}
}
Nota: el servidor lee
OPENAI_API_KEYdesde el archivo.envdel proyecto; si el editor no hereda el entorno, pásala explícitamente en el bloque"env"como se muestra en la Opción C.
Estructura del proyecto
kunz-mcp-project/
├── README.md # Documentación del proyecto
├── requirements.txt # Dependencias con versiones
├── .env.example # Plantilla de variables de entorno
├── config.yaml # Configuración del modelo, embeddings y RAG
├── data/
│ └── faq_document.txt # Documento FAQ fuente (≥1000 palabras)
├── src/
│ ├── __init__.py
│ ├── build_index.py # Pipeline de indexación (load → chunk → embed → store)
│ ├── query.py # Pipeline de consulta (search → generate → JSON)
│ ├── evaluator.py # Agente evaluador de calidad (bonus)
│ ├── mcp_server.py # Servidor MCP (expone los tools vía FastMCP)
│ └── shared/
│ ├── __init__.py
│ ├── config_loader.py # Carga config.yaml + .env
│ └── logger.py # Logger con Rich (colores y formato)
└── outputs/
└── sample_queries.json # ≥3 ejemplos de consulta-respuesta
Decisiones técnicas
Estrategia de chunking
Se usa RecursiveCharacterTextSplitter con chunk_size=300 y chunk_overlap=50.
- ¿Por qué recursivo? Los separadores jerárquicos (
\n\n→\n→.→) preservan los límites semánticos naturales del texto (secciones, párrafos, oraciones), produciendo chunks más coherentes que un corte por tamaño fijo. - ¿Por qué 300 caracteres? Genera chunks de ~75-125 tokens, dentro del rango requerido de 50-500 tokens. Chunks más pequeños mejoran la precisión de la búsqueda vectorial al reducir el ruido semántico.
- ¿Por qué 50 de overlap? El solapamiento asegura continuidad de contexto entre chunks adyacentes, evitando que información relevante quede cortada en un límite.
Método de búsqueda vectorial
Se usa k-NN (k-Nearest Neighbors) con similitud coseno sobre el índice HNSW de ChromaDB.
- ¿Por qué k-NN? Es el método más directo y predecible para búsqueda de vectores similares. ChromaDB optimiza internamente con HNSW (Hierarchical Navigable Small World) para búsquedas sub-lineales.
- ¿Por qué coseno? La similitud coseno mide la dirección semántica de los vectores, no su magnitud. Es ideal para embeddings de texto normalizados como los de OpenAI (
text-embedding-3-small), donde vectores con significado similar apuntan en la misma dirección. - Top-k = 3 retorna entre 2-5 chunks por consulta, suficientes para dar contexto sin introducir ruido.
Beneficios de RAG
- Actualización sin re-training: Basta con actualizar el documento fuente y re-indexar, sin necesidad de fine-tuning costoso del LLM.
- Transparencia: Cada respuesta incluye los chunks utilizados (
chunks_related), permitiendo verificar la fuente de información. - Atribución: Los metadatos de cada chunk (
source,chunk_index) habilitan la trazabilidad completa de la respuesta.
Configuración
El archivo config.yaml centraliza todos los parámetros:
| Parámetro | Valor | Descripción |
|---|---|---|
model.name | gpt-4o-mini | Modelo LLM para generación |
model.temperature | 0.3 | Baja temperatura para respuestas consistentes |
embedding.model | text-embedding-3-small | Modelo de embeddings (1536 dims) |
rag.chunk_size | 300 | Tamaño máximo de chunk en caracteres |
rag.chunk_overlap | 50 | Solapamiento entre chunks |
rag.top_k | 3 | Chunks a recuperar por consulta |
rag.collection | faq_hr_saas | Nombre de la colección en ChromaDB |