# Prompt de Implementación

## Contexto del sistema

Implementa un chatbot conversacional en Python usando exclusivamente herramientas gratuitas/open source. El chatbot recibe un texto de entrada (string) y devuelve una respuesta (string). No se implementa ningun canal (Twilio, Teams, web, etc.)

## Stack tecnologías

| Bloque              | Herramienta elegida                | Motivo                                             |
| ------------------- | ---------------------------------- | -------------------------------------------------- |
| Limpieza de entrada | `str` nativo de Python             | `strip()` y validación de longitud, sin librerías  |
| Guardrail entrada   | Lista de patrones + `re` estándar  | Detección de keywords y prompt injection           |
| Contexto / memoria  | `collections.deque` en memoria     | Ventana deslizante de historial, cero dependencias |
| LLM (generación)    | `Ollama` local con **Llama 3.1**   | Inferencia 100% local, sin costo, sin API key      |
| Llamada HTTP        | `requests`                         | POST al endpoint `/api/chat` de Ollama             |
| Guardrail salida    | Validación básica con `str` nativo | Longitud, vacío, fallback                          |
| Logging             | `logging` estándar de Python       | Trazabilidad sin dependencias externas             |

## Estructura de archivos a generar

```
chatbot/
├── chatbot.py: Módulo principal
├── config.py: Configuración centralizada
├── memory.py: Gestión de historial de conversación
├── guardrails.py: Filtros de seguridad básicos
├── requirements.txt: Dependencias mínimas
└── README.md: Instrucciones de instalación
```

## Flujo interno de respond()

```
texto_usuario (str)
      │
      ▼
 [1] Limpieza mínima
     strip() · rechazar si vacío o > MAX_INPUT_LENGTH caracteres
      │
      ▼
 [2] Guardrail de entrada: guardrails.check_input()
     Si bloqueado → return mensaje_rechazo
      │
      ▼
 [3] Recuperar historial: memory.get_history(session_id)
     Últimos MAX_HISTORY_TURNS turnos como lista de dicts
      │
      ▼
 [4] Construir lista de mensajes
     [{"role":"system","content":SYSTEM_PROMPT},
      {"role":"user","content":turno_1}, {"role":"assistant","content":resp_1},
      ...,
      {"role":"user","content":mensaje_actual}]
      │
      ▼
 [5] Llamada al LLM: _call_ollama(messages)
     POST /api/chat
      │
      ▼
 [6] Extraer texto: response_json["message"]["content"]
      │
      ▼
 [7] Guardrail de salida: guardrails.check_output()
     Si problemático: return mensaje_fallback
      │
      ▼
 [8] Guardar turno en memoria
      │
      ▼
 respuesta_final (str)
```

## Especificaciones por archivo

### `config.py`

Define las constantes.

```python
# Modelo Ollama a usar
MODEL_NAME = "llama3.1"

# URL base de Ollama
OLLAMA_BASE_URL = "http://localhost:11434"

# Máximo de turnos de historial a mantener en contexto
MAX_HISTORY_TURNS = 6

# Longitud máxima en caracteres aceptada para el mensaje de entrada
MAX_INPUT_LENGTH = 2000

# System prompt base
SYSTEM_PROMPT = "Eres un asistente conversacional"

# Mensajes de respuesta fija
MSG_BLOCKED  = ""
MSG_FALLBACK = ""
MSG_EMPTY    = ""
```

### `memory.py`

Gestión de memoria conversacional en memoria de proceso.

Estructura interna: \_sessions: dict[session_id: str, deque[dict]]

Cada elemento del deque es un mensaje con formato OpenAI-compatible: {"role": "user" | "assistant", "content": str}

Política de ventana deslizante:
El deque tiene maxlen = MAX_HISTORY_TURNS. Cuando se llena, el mensaje más antiguo se descarta automáticamente al agregar uno nuevo.

```python

from collections import deque
from config import MAX_HISTORY_TURNS

class ConversationMemory:
    def __init__(self):
        self._sessions: dict[str, deque] = {}

    def add_turn(self, session_id: str, role: str, content: str) -> None:

    def get_history(self, session_id: str) -> list[dict]:

    def clear_session(self, session_id: str) -> None:
```

### `guardrails.py`

Dos funciones independientes: una para la entrada, otra para la salida.
Ambas retornan tuple[bool, str] con el mismo contrato de salida:
(True, contenido_original)
(False, mensaje_de_rechazo)

```python

import re
from config import MSG_BLOCKED, MSG_FALLBACK

BLOCKED_PATTERNS: list[str] = [
    # Agregar patrones
]

def check_input(user_message: str) -> tuple[bool, str]:
    """
    Valida el mensaje del usuario antes de enviarlo al LLM.
    """

def check_output(bot_response: str) -> tuple[bool, str]:
    """
    Valida la respuesta generada por el LLM antes de entregarla al usuario.
    """
```

### `chatbot.py` — Módulo principal

Punto de entrada público:
respond(user_message, session_id) -> str

Diseño:

- No hay ramas, grafos ni agentes
- Cualquier error interno queda atrapado y retorna MSG_FALLBACK
- No importa LangChain ni LangGraph, la orquestación es puro Python

```python
logger = logging.getLogger(__name__)
memory = ConversationMemory()

def respond(user_message: str, session_id: str) -> str:
    """
    Procesa un mensaje de texto y retorna la respuesta del chatbot.

    Args:
        user_message: Texto plano del usuario.
        session_id:   Identificador único de la conversación.

    Returns:
        str: Respuesta en texto plano lista para entregar al usuario.
    """

def _call_ollama(messages: list[dict]) -> str:
    """
    Llama al endpoint /api/chat de Ollama y retorna el texto generado.

    Returns:
        str: Texto de la respuesta del modelo.
    """

def reset_session(session_id: str) -> None:
    """Limpia el historial de una sesión."""

if __name__ == "__main__":
    """Punto de entrada de la consola"""
```

### `requirements.txt`

- requests

## Notas

Sin LangChain, la orquestación es código Python puro: una función que construye una lista de dicts y llama a `requests.post`.
