
Cómo Crear un Dataset de Entrenamiento de Tool-Calling para Fine-Tuning
La mayor brecha en las guías de fine-tuning: nadie cubre cómo construir realmente el dataset. Aquí tienes un proceso paso a paso para crear datos de entrenamiento de tool-calling — desde documentación de esquemas hasta expansión sintética y formateo JSONL — con ejemplos reales para un agente de servicio al cliente de 5 herramientas.
Cada guía sobre fine-tuning de modelos de tool-calling asume que ya tienes los datos. "Solo prepara tu dataset de entrenamiento en formato JSONL," dicen, y luego saltan directamente al comando de entrenamiento.
Eso se salta la parte más difícil.
Construir un dataset de tool-calling de alta calidad es el 80% del trabajo. La arquitectura del modelo, los hiperparámetros de entrenamiento, el rango de LoRA — nada de eso importa si tus datos de entrenamiento son escasos, desbalanceados o les faltan casos extremos.
Esta guía cubre el proceso real. Construiremos un dataset de entrenamiento completo para un agente de servicio al cliente de 5 herramientas, desde cero ejemplos hasta un archivo JSONL listo para producción.
El Objetivo: Un Agente de Servicio al Cliente de 5 Herramientas
Estamos construyendo datos de entrenamiento para un agente que maneja soporte al cliente con cinco herramientas:
lookup_order— Buscar un pedido por ID de pedido o emailcheck_status— Obtener el estado actual de un pedido existenteinitiate_refund— Iniciar un proceso de reembolso para un pedidoupdate_address— Cambiar la dirección de envío de un pedidoescalate_to_human— Transferir la conversación a un agente humano
Cinco herramientas. Lo suficientemente simple para seguir, lo suficientemente complejo para ser real. Construyamos el dataset.
Paso 1: Documenta Tu Esquema de Herramientas
Cada herramienta necesita un esquema JSON preciso. Este es el contrato que tu modelo aprende a seguir. Esquemas vagos producen salidas vagas.
{
"name": "lookup_order",
"description": "Find a customer order by order ID or email address. Use when the customer wants to find or reference a specific order.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID (format: ORD-XXXXX)"
},
"email": {
"type": "string",
"description": "Customer email address to search orders"
}
},
"required": []
}
}
Reglas clave para esquemas:
- Las descripciones importan más que los nombres. El modelo aprende cuándo llamar a una herramienta a partir de la descripción, no solo del nombre de la función. "Find a customer order by order ID or email address" enseña al modelo qué entradas mapean a esta herramienta.
- Sé explícito sobre los formatos de parámetros. "format: ORD-XXXXX" previene que el modelo genere números sin formato.
- Marca los campos requeridos correctamente. En el ejemplo anterior, ningún parámetro es requerido porque el cliente podría proporcionar un ID de pedido o un email.
- Incluye valores enum donde sea aplicable. Si un parámetro solo acepta valores específicos, enuméralos.
Aquí está el esquema para initiate_refund:
{
"name": "initiate_refund",
"description": "Start a refund for a specific order. Use when the customer explicitly requests a refund or return.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID to refund (format: ORD-XXXXX)"
},
"reason": {
"type": "string",
"enum": ["defective", "wrong_item", "not_received", "changed_mind", "other"],
"description": "Reason category for the refund"
},
"amount": {
"type": "number",
"description": "Refund amount in USD. Omit for full refund."
}
},
"required": ["order_id", "reason"]
}
}
Documenta las cinco herramientas de esta manera antes de escribir un solo ejemplo de entrenamiento. Los esquemas son tanto la entrada que tu modelo verá durante la inferencia como el plano para generar salidas correctas.
Paso 2: Genera Ejemplos Semilla
Comienza con 10-20 mensajes de usuario escritos a mano por herramienta. Estos son los ejemplos semilla que capturan los patrones centrales de cómo un usuario real activaría cada herramienta.
Para lookup_order:
"Can you find my order? It's ORD-48291"
"I placed an order last week but can't find the confirmation. My email is jane@example.com"
"Where's my order ORD-77432?"
"I need to check on an order I made. The order number is ORD-15003"
"Look up order ORD-62810 please"
"I can't find my order. I used sarah.jones@gmail.com to purchase"
"What happened to ORD-33102?"
"Can you pull up my order? Email is mike.chen@company.com"
"Find order ORD-90145"
"I have an order number: ORD-55678. Can you look it up?"
Para escalate_to_human:
"I want to talk to a real person"
"This isn't helping, let me speak with a manager"
"Can I talk to someone who can actually help?"
"Transfer me to a human agent please"
"I'd rather discuss this with a person"
"Get me a supervisor"
"I need human support, not a bot"
"This is too complicated for a chatbot, connect me to support"
"I want to file a formal complaint — put me through to a manager"
"None of these options work. I need a real person."
Nota la variedad. Algunos mensajes son corteses, algunos están frustrados. Algunos incluyen parámetros exactos (IDs de pedido), algunos son vagos. Esta variedad es lo que enseña al modelo a manejar usuarios reales.
Escríbelos a mano. No uses un LLM para ejemplos semilla. Necesitas que estos estén basados en cómo hablan tus usuarios reales. Si tienes registros de chat reales, explótalos para patrones.
Paso 3: Expansión Sintética
Diez ejemplos por herramienta no son suficientes para ajustar de manera confiable. Necesitas 50-200+ por herramienta. Aquí es donde entra la generación sintética.
Usa un modelo frontier (GPT-4, Claude) para expandir tu conjunto semilla. El prompt importa:
You are generating training data for a customer service AI that can call tools.
Given these seed examples of messages that should trigger the "initiate_refund" tool,
generate 50 new variations.
Rules:
- Vary the phrasing, formality, and specificity
- Include different order IDs (format: ORD-XXXXX with random 5-digit numbers)
- Include different refund reasons (defective, wrong_item, not_received, changed_mind, other)
- Some should mention partial refunds with specific amounts
- Some should be vague ("I want my money back") and some specific ("Please refund $34.99 for ORD-12345, the item was defective")
- Include typos, abbreviations, and casual language in ~20% of examples
- Do NOT include messages that are ambiguous between tools
Seed examples:
[paste your 10-15 seed examples]
Ejecuta esto para cada herramienta. Revisa la salida. Elimina cualquier ejemplo que se sienta irreal o sea ambiguo entre herramientas.
Crítico: genera el par de entrenamiento completo, no solo el mensaje del usuario. Para cada mensaje de usuario, genera la respuesta del asistente correspondiente con la llamada de herramienta correcta:
{
"messages": [
{
"role": "system",
"content": "You are a customer service assistant. You have access to the following tools: [tool schemas here]. Call the appropriate tool based on the customer's message. If no tool is appropriate, respond conversationally."
},
{
"role": "user",
"content": "I need a refund for ORD-48291. The product arrived broken."
},
{
"role": "assistant",
"tool_calls": [
{
"function": {
"name": "initiate_refund",
"arguments": "{\"order_id\": \"ORD-48291\", \"reason\": \"defective\"}"
}
}
]
}
]
}
Paso 4: Agrega Ejemplos Negativos
Este es el paso que la mayoría de las personas se saltan, y es la razón por la que la mayoría de los modelos de tool-calling ajustados se activan en exceso.
Los ejemplos negativos son mensajes que NO deberían activar ninguna llamada de herramienta. El modelo debería responder conversacionalmente en su lugar.
Apunta a que el 20% de tu dataset total sean ejemplos negativos. Para un agente de 5 herramientas con 100 ejemplos por herramienta (500 ejemplos positivos), necesitas aproximadamente 125 ejemplos negativos.
Tipos de ejemplos negativos:
Conversación general:
"Hi, how are you?"
"Thanks for the help!"
"What are your business hours?"
"Do you sell laptops?"
Mensajes casi acertados (cercanos a una herramienta, pero no exactamente):
"What's your refund policy?" (preguntando SOBRE reembolsos, no solicitando uno)
"How long does shipping usually take?" (no preguntando sobre un pedido específico)
"Can I change my order?" (ambiguo — ¿cambiar qué exactamente?)
"What happens if my package is lost?" (hipotético, no un caso específico)
Mensajes multi-intención donde la llamada de herramienta no es clara:
"I want to return my order and also ask about your new products"
"Is there a way to track packages in general?"
Formatea los ejemplos negativos con una respuesta conversacional del asistente en lugar de una llamada de herramienta:
{
"messages": [
{
"role": "system",
"content": "You are a customer service assistant. You have access to the following tools: [tool schemas here]. Call the appropriate tool based on the customer's message. If no tool is appropriate, respond conversationally."
},
{
"role": "user",
"content": "What's your return policy?"
},
{
"role": "assistant",
"content": "Our return policy allows returns within 30 days of delivery for a full refund. Would you like to initiate a return for a specific order?"
}
]
}
Los ejemplos casi acertados son los más importantes. Enseñan al modelo la frontera entre "preguntar sobre reembolsos" y "solicitar un reembolso". Sin ellos, el modelo llamará a initiate_refund cada vez que alguien mencione la palabra "refund".
Paso 5: Formatea como JSONL
Tu dataset final es un archivo .jsonl — un objeto JSON por línea. Aquí está el formato completo:
{"messages": [{"role": "system", "content": "You are a customer service assistant..."}, {"role": "user", "content": "Find my order ORD-48291"}, {"role": "assistant", "tool_calls": [{"function": {"name": "lookup_order", "arguments": "{\"order_id\": \"ORD-48291\"}"}}]}]}
{"messages": [{"role": "system", "content": "You are a customer service assistant..."}, {"role": "user", "content": "I need a refund for ORD-12345, it arrived broken"}, {"role": "assistant", "tool_calls": [{"function": {"name": "initiate_refund", "arguments": "{\"order_id\": \"ORD-12345\", \"reason\": \"defective\"}"}}]}]}
{"messages": [{"role": "system", "content": "You are a customer service assistant..."}, {"role": "user", "content": "What are your business hours?"}, {"role": "assistant", "content": "We're available Monday through Friday, 9am to 6pm EST. How can I help you today?"}]}
Reglas clave de formateo:
- Un objeto JSON por línea. Sin formato bonito, sin saltos de línea dentro de un objeto.
- Los argumentos son una cadena JSON, no un objeto anidado. Nota
"arguments": "{\"order_id\": \"ORD-48291\"}"— el valor es una cadena que contiene JSON, no un objeto JSON crudo. Esto coincide con el formato de function-calling de OpenAI que la mayoría de los frameworks de fine-tuning esperan. - El mensaje de sistema es idéntico en todos los ejemplos. Incluye el esquema completo de herramientas en cada mensaje de sistema. No abrevies.
- Orden consistente de claves. Siempre
roleantes decontentotool_calls.
Verificaciones de Calidad Antes del Entrenamiento
Ejecuta estas validaciones en tu dataset antes de iniciar un fine-tune:
Cumplimiento de Esquema
Cada entrada de tool_calls debe referenciar un nombre de función que exista en tu esquema de herramientas. Cada cadena de arguments debe parsearse como JSON válido y coincidir con los tipos de parámetros definidos en el esquema.
import json
def validate_example(example, valid_tools):
for msg in example["messages"]:
if "tool_calls" in msg:
for call in msg["tool_calls"]:
name = call["function"]["name"]
assert name in valid_tools, f"Unknown tool: {name}"
args = json.loads(call["function"]["arguments"])
# Validate args against schema...
Validación de Parámetros
Verifica que los valores de parámetros generados sean realistas:
- Los IDs de pedido siguen el formato
ORD-XXXXX - Las direcciones de email son sintácticamente válidas
- Los valores enum coinciden con el conjunto permitido
- Los parámetros requeridos siempre están presentes
- Las cantidades numéricas son razonables (no negativas, no $999,999)
Verificación de Balance
Cuenta ejemplos por herramienta. Si lookup_order tiene 150 ejemplos pero escalate_to_human tiene 30, el modelo estará sesgado hacia lookup. Apunta a una distribución aproximadamente igual, con una tolerancia de +/-30%.
| Herramienta | Ejemplos | % del Dataset |
|---|---|---|
| lookup_order | 100 | 16% |
| check_status | 100 | 16% |
| initiate_refund | 100 | 16% |
| update_address | 100 | 16% |
| escalate_to_human | 100 | 16% |
| Sin herramienta (negativo) | 125 | 20% |
| Total | 625 | 100% |
Cobertura de Casos Extremos
Audita para estos casos específicos:
- Parámetros opcionales faltantes: ¿El modelo lo maneja cuando un usuario proporciona un email pero no un ID de pedido?
- Múltiples herramientas posibles: "I want to check on my refund" — ¿es esto
check_statusu otra cosa? - Extracción de parámetros desde lenguaje natural: "I moved to 742 Evergreen Terrace, Springfield" — ¿puede el modelo extraer la dirección?
- Contexto conversacional: "Yes, go ahead" después de una confirmación de reembolso — ¿el modelo maneja turnos de seguimiento?
Guías de Volumen
¿Cuántos datos realmente necesitas? Depende de la complejidad:
Enrutamiento simple de herramientas (solo clasificación): 50-100 ejemplos por herramienta. El modelo solo necesita aprender qué herramienta mapea a qué intención. La extracción de parámetros es directa.
Extracción compleja de parámetros: 200+ ejemplos por herramienta. Cuando los parámetros requieren parsear lenguaje natural (direcciones, fechas, descripciones de productos), necesitas más ejemplos mostrando los patrones de extracción.
Conversaciones multi-turno: 300+ ejemplos por herramienta. Si el modelo necesita manejar mensajes de seguimiento, solicitudes de aclaración y contexto de turnos anteriores, el dataset crece significativamente.
Para nuestro agente de servicio al cliente de 5 herramientas, 100 ejemplos por herramienta más 125 negativos (625 total) es un punto de partida sólido. Puedes ajustar un modelo 7B con esto en menos de 30 minutos en una sola GPU.
Secuencias Multi-Turno
Las interacciones reales con clientes no son de un solo turno. Un cliente podría buscar un pedido, luego verificar su estado, luego solicitar un reembolso — todo en una conversación. Tu dataset debería incluir secuencias multi-turno:
{
"messages": [
{"role": "system", "content": "You are a customer service assistant..."},
{"role": "user", "content": "Can you find order ORD-48291?"},
{"role": "assistant", "tool_calls": [{"function": {"name": "lookup_order", "arguments": "{\"order_id\": \"ORD-48291\"}"}}]},
{"role": "tool", "content": "{\"order_id\": \"ORD-48291\", \"status\": \"delivered\", \"items\": [\"Blue Widget\"], \"total\": 34.99}"},
{"role": "assistant", "content": "I found your order ORD-48291. It shows as delivered. You ordered a Blue Widget for $34.99. How can I help with this order?"},
{"role": "user", "content": "The widget arrived damaged. I want my money back."},
{"role": "assistant", "tool_calls": [{"function": {"name": "initiate_refund", "arguments": "{\"order_id\": \"ORD-48291\", \"reason\": \"defective\", \"amount\": 34.99}"}}]}
]
}
Incluye 20-30 secuencias multi-turno en tu dataset. Enseñan al modelo a mantener el contexto y usar información de resultados de herramientas previas al hacer llamadas posteriores.
Errores Comunes a Evitar
Error 1: Copiar y pegar la misma estructura de oración. "Please refund ORD-11111," "Please refund ORD-22222," "Please refund ORD-33333" — esto enseña al modelo un patrón, no el concepto. Varía la formulación.
Error 2: Gramática perfecta en cada ejemplo. Los usuarios reales escriben "wheres my order," "plz refund," "i need to chnage my adress." Incluye estos en tus datos de entrenamiento.
Error 3: Sin ejemplos ambiguos. La frontera entre "verificar estado" y "buscar un pedido" es difusa. Incluye ejemplos que exploren esta frontera, con decisiones de etiquetado consistentes.
Error 4: Saltar el mensaje de sistema. Algunas personas entrenan sin mensaje de sistema, luego agregan uno en tiempo de inferencia. El modelo nunca ha visto ese mensaje de sistema antes. Entrena con el system prompt exacto que usarás en producción.
Error 5: No validar el JSONL. Una sola línea malformada hará fallar tu ejecución de entrenamiento 45 minutos después. Valida que cada línea se parsee como JSON antes de comenzar.
Ship AI that runs on your users' devices.
Ertas early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.
Poniendo Todo Junto
Aquí está el proceso completo, de principio a fin:
- Documenta todos los esquemas de herramientas con descripciones precisas y tipos de parámetros
- Escribe 10-20 ejemplos semilla por herramienta a mano, basados en patrones reales de usuarios
- Usa un modelo frontier para expandir a 50-100+ variaciones por herramienta
- Agrega 20% de ejemplos negativos, especialmente mensajes casi acertados
- Formatea como JSONL con estructura consistente
- Valida cumplimiento de esquema, corrección de parámetros y balance
- Incluye 20-30 secuencias de conversación multi-turno
- Ejecuta una pasada final de deduplicación
Tiempo total: 4-8 horas para un agente de 5 herramientas. Ejemplos totales: 500-750. Tiempo de entrenamiento en un modelo 7B con LoRA: 20-40 minutos.
El resultado es un modelo que enruta a la herramienta correcta el 90%+ del tiempo, genera parámetros válidos consistentemente, y sabe cuándo NO llamar a ninguna herramienta. Todo ejecutándose localmente, a cero costo por consulta.
El dataset es el modelo. Constrúyelo bien.
Lectura Adicional
- Datos Sintéticos para Fine-Tuning: La Guía Completa — Inmersión profunda en el uso de modelos frontier para generar datos de entrenamiento de alta calidad a escala.
- ¿Cuántos Datos Realmente Necesitas para Fine-Tuning? — Benchmarks de volumen a través de diferentes tipos de tareas, desde clasificación hasta generación.
- Fine-Tuning para Tool Calling: Cómo Construir Agentes de AI Confiables — La guía complementaria cubriendo entrenamiento de modelos, configuración de LoRA y despliegue para agentes de tool-calling.
Ship AI that runs on your users' devices.
Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.
Keep reading

Distilling Claude/GPT into a 7B Model for Production: Step-by-Step
A step-by-step tutorial for distilling the capabilities of Claude or GPT-4o into a 7B parameter model for local production deployment — from dataset generation through fine-tuning to GGUF export.

How to Distill Open-Source Models Legally: A Step-by-Step Guide
A practical guide to model distillation the right way: using open-source teacher models with permissive licenses, your own domain data, and a clear legal path to model ownership.

Shipping AI Search in Your SaaS Without Per-Query API Costs
A step-by-step tutorial for building natural language search using a fine-tuned 3B-7B model. Includes training data sourcing, model selection, GGUF deployment via Ollama, and latency benchmarks.