Avanzado 13 minAPI

Llamadas a funciones y salidas JSON estructuradas con Ollama

La llamada a funciones permite que un LLM decida por sí mismo llamar a una función de tu código —consultar el tiempo, consultar una base de datos, enviar un correo electrónico— devolviendo los argumentos en el formato adecuado. La llamada a funciones en Ollama se basa en dos componentes: el parámetro format para garantizar un JSON válido y el campo tools de la API para declarar las funciones disponibles. Esta guía muestra ambos en Python, qué modelos locales son realmente fiables y cómo reforzar el conjunto con validación de esquemas y reintentos.

Por Mohamed Meguedmi·Actualización 2026-08-27·Probado en Windows, macOS y Linux

#Por qué usar llamadas a funciones en local

Un LLM genera texto, no acciones. El function calling salva esta brecha: en lugar de responder en prosa, el modelo devuelve un objeto estructurado que dice «llama a la función get_meteo con la ciudad Paris». Tu código ejecuta la función, recupera el resultado real y luego se lo devuelve al modelo, que redacta la respuesta final. Este es el mecanismo básico de los agentes y los asistentes que interactúan con el mundo exterior.

En local, el reto es doble. Primero, garantizar que la salida sea un JSON que se pueda analizar al 100 %: un modelo demasiado locuaz que añada «Aquí está el JSON:» rompe todo tu pipeline. Después, asegurarse de que el modelo elija la función correcta con los argumentos adecuados, lo cual se vuelve delicado con los modelos pequeños. Ollama gestiona ambas cosas a través de su API, pero con mecanismos de protección que conviene conocer.

Salidas JSON garantizadas
El parámetro format restringe la decodificación: el modelo solo puede producir un JSON sintácticamente válido, o incluso conforme a un esquema específico.
Llamada a funciones
El campo tools declara funciones en formato OpenAI; el modelo devuelve tool_calls con los argumentos a pasar.
100 % local
Todo funciona en tu máquina mediante el daemon de Ollama en http://localhost:11434, sin clave API ni filtración de datos.

#Requisitos previos y modelos compatibles

El kit Copiloto Local

Esta guía te lleva al modelo. El kit te lleva al copiloto que programa en tu editor.

  • Espacio en línea de por vida
  • PDF + archivos
  • Reembolsado 30 j

El modo JSON (parámetro format) funciona con cualquier modelo. La llamada a funciones mediante tools, en cambio, requiere un modelo entrenado para usar herramientas; de lo contrario, el campo tool_calls permanece vacío. No todos los modelos ofrecen los mismos resultados: un 3B «compatible» sobre el papel suele equivocarse en los argumentos, mientras que un 14B+ funciona bien con esquemas simples.

Ollama instalado
Daemon iniciado y accesible en http://localhost:11434. Verifica con ollama list.
SDK Python
pip install ollama pydantic — le client officiel plus Pydantic pour la validation.
Modelo confiable para el uso de herramientas
qwen3.5:9b, mistral-small (24B) y gpt-oss:20b son excelentes puntos de partida en 2026. En Q4: Qwen 3.5 9B ≈ 6,6 GB, un 24B ≈ 14 GB de VRAM.
GPU recomendado
Una RTX 3060 de 12 GB ejecuta Qwen 3.5 9B cómodamente; busca un modelo de 20-24B (RTX 4070/4080 de 16 GB) para un uso de herramientas realmente confiable.
i
Modo JSON ≠ llamada a función
El parámetro format garantiza un JSON válido, pero no provoca ninguna llamada a una función: el modelo rellena un objeto que TÚ interpretas. El campo tools, en cambio, activa una selección real de función por parte del modelo. A menudo se combinan ambos.

#Forzar un JSON válido con el parámetro format

El caso más sencillo: quieres que el modelo responda siempre con un JSON, nunca con texto libre. Pasa format: 'json' en la llamada a chat. Ollama restringe entonces la decodificación token por token para producir un objeto sintácticamente válido. Importante: mantén una instrucción explícita en el prompt que describa los campos esperados; de lo contrario, el modelo inventa una estructura.

json_mode.py
import ollama
import json

resp = ollama.chat(
    model='qwen3.5:9b',
    messages=[{
        'role': 'user',
        'content': (
            "Extrais le nom, la ville et l'age de ce texte et reponds "
            "UNIQUEMENT en JSON avec les cles nom, ville, age. "
            "Texte : Marie, 34 ans, habite a Lyon."
        ),
    }],
    format='json',  # contraint la sortie a un JSON valide
    options={'temperature': 0},
)

data = json.loads(resp['message']['content'])
print(data)  # {'nom': 'Marie', 'ville': 'Lyon', 'age': 34}
→
Siempre temperature en 0
Para la extracción estructurada, establece temperature en 0. Buscas determinismo y conformidad, no creatividad. Esto reduce considerablemente las alucinaciones en los campos.

#JSON estructurado por esquema (salidas estructuradas)

Desde finales de 2024, Ollama también acepta un esquema JSON completo en format (no solo la cadena 'json'). La decodificación queda entonces obligada a respetar el esquema: tipos, campos obligatorios y enumeraciones. Es mucho más robusto que usar solo 'json', porque la estructura impide que el modelo produzca un objeto que no se ajuste al esquema. Con Pydantic, el esquema se genera automáticamente.

structured_output.py
import ollama
from pydantic import BaseModel

class Personne(BaseModel):
    nom: str
    ville: str
    age: int

resp = ollama.chat(
    model='mistral-small',
    messages=[{'role': 'user',
               'content': 'Marie, 34 ans, habite a Lyon.'}],
    format=Personne.model_json_schema(),  # schema JSON complet
    options={'temperature': 0},
)

# validation stricte : leve une erreur si non conforme
personne = Personne.model_validate_json(resp['message']['content'])
print(personne)  # nom='Marie' ville='Lyon' age=34

Aquí la decodificación se restringe a la estructura de Personne, y model_validate_json realiza una nueva validación en Python. Doble red de seguridad: se garantiza que la salida se puede analizar sintácticamente Y que cumple los tipos declarados. Este es el patrón recomendado para cualquier extracción de datos en producción local.

#API de herramientas paso a paso en Python

Pasemos al verdadero function calling. Se declaran las funciones en el campo tools en formato OpenAI (name, description, parameters en JSON Schema). El modelo lee estas definiciones y, si considera útil llamar a una función, devuelve uno o varios tool_calls en lugar de un mensaje de texto. A ti te toca ejecutar la función y devolver el resultado.

  1. 01
    Describir las funciones
    Para cada función, proporciona un name claro, una descripción precisa (el modelo la utiliza para elegir) y un campo parameters en JSON Schema que enumere los argumentos e indique cuáles son required.
  2. 02
    Enviar la llamada con tools
    Pasa la lista tools a ollama.chat. El modelo decide por sí mismo si llama a una función o responde directamente.
  3. 03
    Leer las tool_calls
    Revisa resp['message'].get('tool_calls'). Si está presente, el modelo quiere llamar a una función con los argumentos proporcionados.
  4. 04
    Ejecutar y devolver
    Llama a la función Python real, luego envía su resultado al modelo dentro de un mensaje de rol 'tool' para que redacte la respuesta final.
tools_definition.py
def get_meteo(ville: str) -> str:
    # ici un vrai appel API ; on simule
    return f"Il fait 22 C et ensoleille a {ville}."

tools = [{
    'type': 'function',
    'function': {
        'name': 'get_meteo',
        'description': "Renvoie la meteo actuelle d'une ville donnee.",
        'parameters': {
            'type': 'object',
            'properties': {
                'ville': {
                    'type': 'string',
                    'description': 'Nom de la ville, ex: Paris',
                },
            },
            'required': ['ville'],
        },
    },
}]

#El bucle llamada → ejecución → respuesta

El function calling es un proceso de ida y vuelta. Primera llamada: el modelo devuelve un tool_call. Ejecutas la función. Segunda llamada: envías el resultado y el modelo redacta la respuesta en lenguaje natural. Aquí tienes el ciclo completo, reutilizable para varias funciones.

boucle_tools.py
import ollama

dispatch = {'get_meteo': get_meteo}

messages = [{'role': 'user',
             'content': 'Quel temps fait-il a Marseille ?'}]

resp = ollama.chat(model='mistral-small',
                   messages=messages, tools=tools)
msg = resp['message']
messages.append(msg)

for call in msg.get('tool_calls') or []:
    fn = call['function']['name']
    args = call['function']['arguments']
    resultat = dispatch[fn](**args)  # execution reelle
    messages.append({
        'role': 'tool',
        'name': fn,
        'content': resultat,
    })

# second appel : le modele redige la reponse finale
final = ollama.chat(model='mistral-small', messages=messages)
print(final['message']['content'])
!
Nunca ejecutes los argumentos sin revisarlos
El modelo controla el nombre de la función y sus argumentos. Usa un diccionario de despacho (lista blanca) en lugar de eval o de un getattr dinámico, y valida cada argumento antes de la ejecución. Un modelo comprometido o que genere alucinaciones no debe poder llamar a cualquier función.

#Validación de esquema y patrones de reintentos

En local, los modelos pequeños a veces fallan: faltan argumentos, el tipo es incorrecto o la función no existe. Nunca confíes en la salida sin validar. Valida cada tool_call con Pydantic y, si la validación falla, vuelve a intentarlo incluyendo el mensaje de error en el contexto: a menudo el modelo se corrige en el segundo intento.

retry_validation.py
from pydantic import BaseModel, ValidationError

class MeteoArgs(BaseModel):
    ville: str

def valider_appel(call):
    fn = call['function']['name']
    if fn not in dispatch:
        raise ValueError(f"Fonction inconnue: {fn}")
    args = MeteoArgs.model_validate(call['function']['arguments'])
    return fn, args

def appel_avec_retry(messages, max_essais=3):
    for essai in range(max_essais):
        resp = ollama.chat(model='mistral-small',
                           messages=messages, tools=tools)
        try:
            calls = resp['message'].get('tool_calls') or []
            return [valider_appel(c) for c in calls], resp
        except (ValidationError, ValueError) as e:
            messages.append({
                'role': 'user',
                'content': f"Erreur: {e}. Corrige et reessaie.",
            })
    raise RuntimeError('Echec apres retries')
Validar antes de ejecutar
Un modelo de Pydantic por función captura los argumentos faltantes o mal tipados antes de que lleguen a tu código.
Reintentar con retroalimentación
Volver a introducir el mensaje de error en el contexto guía al modelo hacia la corrección. Casi siempre bastan 2-3 intentos.
Lista blanca de funciones
Rechaza cualquier nombre de función que no esté en dispatch. Es tanto una medida de seguridad como una protección contra las alucinaciones.
Fallback controlado
Después de N fallos, responde al usuario con un mensaje claro en lugar de dejar que el programa se bloquee, sobre todo con un modelo pequeño.

#Los riesgos de usar herramientas con modelos pequeños

El uso de herramientas es cognitivamente exigente: el modelo debe comprender la intención, elegir la función adecuada, mapear los argumentos y respetar el formato. Por debajo de 7B, los resultados son frágiles. A continuación, se detallan los puntos más comunes que fallan localmente y cómo solucionarlos.

tool_calls vacío
El modelo responde con texto en lugar de llamar a la función. A menudo se debe a un modelo no entrenado para usar herramientas o a una descripción de la función demasiado vaga. Cambia a Qwen 3.5 o Mistral Small y cuida las descripciones.
Argumentos incorrectos
El modelo inventa u omite campos. Márcalos como obligatorios mediante required en el esquema, reduce el número de funciones expuestas a la vez y valida sistemáticamente.
Función inventada por el modelo
El modelo llama a una función que no existe. Es obligatorio usar una lista blanca en el componente que despacha las llamadas.
JSON contaminado
Sin formato, un modelo pequeño añade texto alrededor del JSON. Usa siempre format='json' o un esquema para la extracción pura.
Demasiadas funciones
Con más de 5-6 herramientas, los modelos pequeños se confunden. Segmenta por subtarea o realiza el enrutamiento en dos etapas.
→
El equilibrio adecuado para el uso local
Para llamadas a funciones fiables sin una GPU de gama alta, mistral-small (24B) en Q4 (≈14 GB de VRAM) suele ofrecer la mejor relación calidad/recursos en una RTX 4080. Con menos recursos, qwen3.5:9b (≈6,6 GB) se defiende con unas pocas funciones bien descritas, y gpt-oss:20b es una alternativa muy rápida. Para un uso claramente orientado a agentes, glm-4.7-flash (MoE 30B-A3B, ≈19 GB) destaca si tienes 24 GB de VRAM.

#Para ir más allá

El function calling es la pieza básica de los agentes y las integraciones avanzadas. Estas guías del sitio amplían esta guía:

Integrar Ollama a través de la API REST en Python
El endpoint compatible con OpenAI en :11434, el streaming y el modo JSON en una verdadera aplicación FastAPI/Flask.
Crear un agente de IA local con LangChain y Ollama
Pasar de las llamadas a funciones sin más a un agente completo que encadena herramientas, memoria y razonamiento.
MCP y LLM local: conectar servidores MCP a Ollama
Estandarizar el acceso a herramientas (archivos, web, bases de datos) mediante Model Context Protocol en lugar de definir cada función manualmente.
¿Esta guía te ha ayudado?

¿Un comentario, un error, una precisión? Avísanos, eso mejora la guía para todos.