Módulo 3: LM Studio - Introducción

Mini-Proyecto: Chatbot 100% Local

Descripción del proyecto

Proyecto final del Módulo 3: Chatbot completamente local (costo $0, privacidad 100%) usando LM Studio.

Tomarás el chatbot del Módulo 2 y lo adaptarás para funcionar 100% offline con Mistral 7B local.

Tiempo: 30-45 minutos
Dificultad: Media


🎯 Objetivo

Crear chatbot CLI local con:

  • ✅ Conversaciones con contexto
  • ✅ Cost tracking (confirmar $0)
  • ✅ Performance metrics (latency)
  • ✅ Persistence (JSON)
  • ✅ 100% offline (sin API calls cloud)

💻 Código Completo

#!/usr/bin/env python3
"""
Chatbot 100% Local con LM Studio
Módulo 3 - Proyecto Final
"""

import json
import time
from datetime import datetime
from pathlib import Path
from openai import OpenAI

# ============================================================================
# CONFIGURACIÓN
# ============================================================================

# Cliente LM Studio (local)
client = OpenAI(
    base_url="http://localhost:1234/v1",
    api_key="not-needed"
)

MODEL = "mistral-7b-instruct"  # Ajusta según tu modelo

Path("conversations_local").mkdir(exist_ok=True)

# ============================================================================
# CLASE CHATBOT LOCAL
# ============================================================================

class LocalChatBot:
    """Chatbot 100% local con LM Studio."""
    
    def __init__(self):
        self.messages = [
            {"role": "system", "content": "Eres un asistente útil y amigable."}
        ]
        self.total_time = 0.0
        self.query_count = 0
        self.conversation_id = datetime.now().strftime("%Y%m%d_%H%M%S")
    
    def chat(self, user_message: str) -> str:
        """Envía mensaje y retorna respuesta."""
        
        self.messages.append({"role": "user", "content": user_message})
        
        # Medir latency
        start = time.time()
        
        try:
            response = client.chat.completions.create(
                model=MODEL,
                messages=self.messages,
                temperature=0.7,
                max_tokens=150
            )
            
            latency = time.time() - start
            self.total_time += latency
            self.query_count += 1
            
            assistant_message = response.choices[0].message.content
            self.messages.append({"role": "assistant", "content": assistant_message})
            
            print(f"[Latency: {latency:.2f}s]")
            
            return assistant_message
            
        except Exception as e:
            print(f"❌ Error: {e}")
            return None
    
    def show_stats(self):
        """Muestra estadísticas."""
        if self.query_count == 0:
            print("\n📊 Sin queries aún")
            return
        
        avg_latency = self.total_time / self.query_count
        
        print("\n" + "="*50)
        print("📊 ESTADÍSTICAS")
        print("="*50)
        print(f"Queries:        {self.query_count}")
        print(f"Tiempo total:   {self.total_time:.2f}s")
        print(f"Latency avg:    {avg_latency:.2f}s")
        print(f"💰 Costo:       $0.00 (100% local!)")
        print("="*50 + "\n")
    
    def show_history(self):
        """Muestra historial."""
        print("\n" + "="*50)
        print("HISTORIAL")
        print("="*50)
        
        for i, msg in enumerate(self.messages[1:], 1):
            role = "TÚ" if msg["role"] == "user" else "BOT"
            print(f"\n[{i}] {role}: {msg['content']}")
        
        print("\n" + "="*50 + "\n")
    
    def save_conversation(self):
        """Guarda conversación."""
        filename = f"conversations_local/conversation_{self.conversation_id}.json"
        
        data = {
            "conversation_id": self.conversation_id,
            "timestamp": datetime.now().isoformat(),
            "model": MODEL,
            "messages": self.messages[1:],
            "query_count": self.query_count,
            "total_time": self.total_time,
            "avg_latency": self.total_time / self.query_count if self.query_count > 0 else 0,
            "cost": 0.0  # $0 local!
        }
        
        with open(filename, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        
        print(f"💾 Conversación guardada: {filename}")

# ============================================================================
# MAIN
# ============================================================================

def main():
    """CLI principal."""
    
    print("\n" + "="*50)
    print("🤖 CHATBOT 100% LOCAL (LM Studio)")
    print("="*50)
    print(f"\nModelo: {MODEL}")
    print("Costo: $0.00 (offline)")
    print("\nComandos:")
    print("  - 'salir'       → Terminar")
    print("  - 'historial'   → Ver historial")
    print("  - 'stats'       → Ver estadísticas")
    print("\n" + "="*50 + "\n")
    
    # Verificar servidor LM Studio
    print("Verificando servidor LM Studio...")
    try:
        models = client.models.list()
        print(f"✅ Servidor activo ({len(models.data)} modelos disponibles)\n")
    except Exception as e:
        print(f"❌ Error: {e}")
        print("\n⚠️ Asegúrate que LM Studio está corriendo con servidor activo")
        print("   Settings → Local Server → Start Server\n")
        return
    
    bot = LocalChatBot()
    
    # Loop principal
    while True:
        try:
            user_input = input("Tú: ").strip()
            
            if user_input.lower() in ["salir", "exit", "quit"]:
                print("\n👋 ¡Hasta pronto!\n")
                bot.show_stats()
                bot.save_conversation()
                break
            
            if user_input.lower() == "historial":
                bot.show_history()
                continue
            
            if user_input.lower() == "stats":
                bot.show_stats()
                continue
            
            if not user_input:
                print("⚠️ Escribe un mensaje.\n")
                continue
            
            response = bot.chat(user_input)
            
            if response:
                print(f"\nBot: {response}\n")
        
        except KeyboardInterrupt:
            print("\n\n👋 Interrumpido por usuario\n")
            bot.show_stats()
            bot.save_conversation()
            break
        
        except Exception as e:
            print(f"\n❌ Error inesperado: {e}\n")
            break

if __name__ == "__main__":
    main()

🚀 Uso

1. Iniciar servidor LM Studio:

  • LM Studio → Settings → Start Server
  • Verifica 🟢 "Server running on port 1234"

2. Cargar modelo:

  • Home → My Models → Load (Mistral 7B)

3. Ejecutar chatbot:

python chatbot_local.py

📊 Ejemplo de Conversación

==================================================
🤖 CHATBOT 100% LOCAL (LM Studio)
==================================================

Modelo: mistral-7b-instruct
Costo: $0.00 (offline)

Comandos:
  - 'salir'       → Terminar
  - 'historial'   → Ver historial
  - 'stats'       → Ver estadísticas

==================================================

Verificando servidor LM Studio...
✅ Servidor activo (1 modelos disponibles)

Tú: Hola, ¿qué modelo eres?
[Latency: 4.2s]

Bot: Soy Mistral 7B, un modelo de lenguaje local ejecutándose en tu máquina.

Tú: ¿Cuál es la capital de Francia?
[Latency: 3.8s]

Bot: La capital de Francia es París.

Tú: stats

==================================================
📊 ESTADÍSTICAS
==================================================
Queries:        2
Tiempo total:   8.00s
Latency avg:    4.00s
💰 Costo:       $0.00 (100% local!)
==================================================

Tú: salir

👋 ¡Hasta pronto!

==================================================
📊 ESTADÍSTICAS
==================================================
Queries:        2
Tiempo total:   8.00s
Latency avg:    4.00s
💰 Costo:       $0.00 (100% local!)
==================================================

💾 Conversación guardada: conversations_local/conversation_20240215_143022.json

✅ Rúbrica de Auto-Evaluación

Funcionalidad (40 pts):

  • (10) Chatbot responde coherentemente
  • (10) Mantiene contexto
  • (10) Comandos funcionan (historial, stats, salir)
  • (10) Conversaciones guardadas

Performance (30 pts):

  • (10) Latency tracking funciona
  • (10) Stats precisas
  • (10) Costo confirmado $0

Local (30 pts):

  • (15) Funciona sin OpenAI API
  • (15) Funciona offline (desconecta WiFi y prueba)

Total: ___/100


🎯 Comparación con Módulo 2

MétricaMódulo 2 (OpenAI)Módulo 3 (LM Studio)
Latency1.5s4.0s (2.6x más lento)
Costo (100 queries)$0.10$0.00
PrivacidadCloud100% local ✅
Offline
Setup5 min30 min

🔗 Extensiones Opcionales

1. Comparador side-by-side:

# Envía query a ambos (OpenAI + LM Studio)
# Compara respuestas y latency

2. Auto-switch por latency:

# Si LM Studio >10s → fallback a OpenAI

3. Hybrid mode:

# Queries simples → LM Studio ($0)
# Queries complejas → GPT-4 ($$$)

✅ Resumen del Módulo 3

Lo que dominaste:

  • ✅ LM Studio setup completo
  • ✅ Model management (descarga, quantization)
  • ✅ API local compatible OpenAI
  • ✅ Migración trivial de código
  • ✅ Chatbot 100% local funcionando

Trade-offs entendidos:

  • Costo $0 vs Latency 3-5x más lenta
  • Privacidad máxima vs Setup más complejo
  • Offline vs Calidad 10% menor

➡️ Próximo paso

Siguiente módulo: Módulo 4 - Ollama (Local CLI)

Aprenderás Ollama, alternativa a LM Studio pero:

  • CLI en vez de GUI (más pro)
  • Docker support (producción)
  • Multi-node clustering (escalabilidad)

Tiempo: 2-3 horas


¡Felicitaciones! Completaste el Módulo 3. 🎉

Tiempo total módulo: 2-3 horas
Siguiente: Módulo 4 - Ollama