Tool Calling con Ollama y Gemma

Integrando skills para el control de dispositivos IoT de forma local.

El concepto de Tool Calling en Modelos Locales

El uso de skills (también conocido como tool calling o function calling) permite a los modelos de lenguaje interactuar con el mundo exterior. En lugar de simplemente generar texto, el modelo puede decidir "llamar" a una función predefinida, proporcionando los parámetros necesarios para ejecutar una acción, como consultar una base de datos o encender un dispositivo IoT.

Al ejecutar modelos como Gemma a través de Ollama, ganamos privacidad y baja latencia, lo cual es ideal para el control local de domótica e IoT sin depender de la nube.

Buenas Prácticas Habituales

  • Descripciones Precisas: El modelo se basa exclusivamente en las descripciones (docstrings) de la herramienta para saber cuándo y cómo usarla. Sé conciso pero exhaustivo.
  • Validación de Tipos (Type Hinting): Define claramente si un parámetro es un string, un entero o un booleano. Limita las opciones con enums siempre que sea posible.
  • Gestión de Errores Robustas: Si el modelo proporciona parámetros incorrectos, la función debe devolver un mensaje de error explícito (ej. "Error: dispositivo no encontrado, opciones válidas: ['luz_salon']") para que el modelo pueda autocorregirse.
  • System Prompt Direccional: Refuerza en el prompt del sistema que el modelo es un agente de control IoT y debe usar las herramientas disponibles para consultar el estado antes de asumir o adivinar nada.

Tutorial: Control de un Dispositivo IoT con Gemma

A continuación, implementaremos un script en Python que utiliza el SDK oficial de Ollama para permitir que un modelo Gemma (ej. Gemma 2 / 4B) encienda, apague o consulte el estado de una bombilla inteligente.

Paso 1: Definir la Skill (Función Ejecutable)

Primero, definimos la función que interactuará con el IoT en Python.

import ollama

# Estado simulado del dispositivo IoT
iot_devices = {
    "luz_salon": {"status": "off", "brightness": 0}
}

def control_iot(device_id: str, action: str, brightness: int = None) -> str:
    """Controla un dispositivo IoT. Retorna el resultado de la operación."""
    if device_id not in iot_devices:
        return f"Error: Dispositivo '{device_id}' no encontrado. Dispositivos disponibles: {list(iot_devices.keys())}"
    
    if action == "status":
        return f"El dispositivo {device_id} está {iot_devices[device_id]['status']} con brillo al {iot_devices[device_id]['brightness']}%."
    
    elif action in ["on", "off"]:
        iot_devices[device_id]["status"] = action
        if brightness is not None and action == "on":
            iot_devices[device_id]["brightness"] = brightness
        return f"Éxito: Dispositivo {device_id} cambiado a {action}."
    
    return "Error: Acción no válida."
                    

Paso 2: Configurar el esquema en Ollama

Definimos cómo la herramienta se presenta al LLM a través de JSON Schema.

# Definición del esquema de la herramienta
tool_schema = {
  "type": "function",
  "function": {
    "name": "control_iot",
    "description": "Controla o consulta el estado de dispositivos IoT como luces o enchufes en el hogar inteligente.",
    "parameters": {
      "type": "object",
      "properties": {
        "device_id": {
          "type": "string",
          "description": "Identificador del dispositivo IoT (ej. 'luz_salon')."
        },
        "action": {
          "type": "string",
          "enum": ["on", "off", "status"],
          "description": "La acción a ejecutar sobre el dispositivo."
        },
        "brightness": {
          "type": "integer",
          "description": "Nivel de brillo (1-100). Solo aplicar si la acción es 'on'."
        }
      },
      "required": ["device_id", "action"]
    }
  }
}
                    

Paso 3: Bucle de Interacción del Agente

Enviamos el mensaje al modelo Gemma, interceptamos su llamada a la herramienta, ejecutamos la función real y le devolvemos el resultado.

# 1. Definir la intención del usuario
messages = [{'role': 'user', 'content': 'Por favor, enciende la luz del salón y ponla al 80%.'}]

# 2. Llamar a Ollama (asegúrate de tener instalado el modelo, ej. gemma2)
response = ollama.chat(
    model='gemma2',
    messages=messages,
    tools=[tool_schema]
)

msg = response.get("message", {})

# 3. Detectar si el modelo decidió usar la herramienta
if msg.get("tool_calls"):
    print("El agente decidió usar herramientas:")
    
    for tool_call in msg["tool_calls"]:
        if tool_call["function"]["name"] == "control_iot":
            args = tool_call["function"]["arguments"]
            print(f"- Ejecutando control_iot con: {args}")
            
            # Ejecutar la skill en el mundo real
            result = control_iot(
                device_id=args.get("device_id"),
                action=args.get("action"),
                brightness=args.get("brightness")
            )
            print(f"- Resultado del dispositivo: {result}")
            
            # 4. Devolver la respuesta al agente
            messages.append(msg) # Guardar el mensaje del asistente con la llamada a la tool
            messages.append({'role': 'tool', 'content': result, 'name': 'control_iot'})
            
            # Generar la respuesta final humana
            final_response = ollama.chat(model='gemma2', messages=messages)
            print(f"\nRespuesta Final del Agente: {final_response['message']['content']}")
else:
    # El modelo respondió directamente sin usar herramientas
    print(f"Respuesta Directa: {msg.get('content')}")
                    

Puntos Clave a Recordar

Al implementar este tipo de arquitectura IoT:

  1. Latencia: Al ejecutar modelos de 2B a 9B parámetros como los de la familia Gemma (Gemma 2) localmente con Ollama, las respuestas para control de hardware son prácticamente en tiempo real.
  2. Seguridad: Nunca expongas tu control IoT a la nube si puedes manejarlo internamente. Ollama mantiene los datos dentro de tu red local.
  3. Independencia del Dispositivo: El modelo solo procesa intenciones. Tu código Python es el que realmente implementa los protocolos MQTT, Zigbee o HTTP hacia el hardware IoT.

Referencias y Lecturas Recomendadas

  • 📄 Villanueva Molina, F. J. et al. (IEEE Internet of Things Journal, 2026): From Voice to Shell: SLM-Based Assistant for IoT. Investigación académica del grupo ARCO sobre el despliegue de modelos de lenguaje pequeños (SLMs) para asistencia y control autónomo de hardware IoT en el borde.
  • 📄 Google Gemma Team (2024): Gemma 2: Improving Open Language Models at a Practical Size . Especificación técnica de la familia de modelos abiertos Gemma, optimizados para inferencia eficiente y ejecución de herramientas en hardware de consumo.
  • 🌐 Ollama Technical Documentation: Tool Calling and Structured Outputs with Local Models . Guía de integración de la API de Ollama para invocación de funciones deterministas en Python y JavaScript sin conexión a servicios en la nube.