AstrApp
Docs

Formatos de importación

Qué formatos acepta AstrApp al importar, cómo los convierte a ChatML y cómo representar llamadas a herramientas y razonamiento en tus ejemplos.

¿Qué formato debo usar?

El entrenamiento en AstrApp es SFT (fine-tuning supervisado) y su formato es ChatML. Todo lo que importas se convierte a ChatML: las conversaciones se guardan así y el entrenamiento las lee así. Si puedes elegir, escribe tus datos en ChatML; si ya los tienes en otro formato de la tabla de abajo, impórtalos tal cual.

Para los conceptos generales (tipos de dataset, plantillas de chat, tamaño) consulta la guía técnica Datasets para fine-tuning.

ChatML

Cada ejemplo es una lista de mensajes con su rol. En un archivo JSONL va una conversación por línea.

jsonl
{"messages": [{"role": "system", "content": "Eres un asistente de atención al cliente amable y profesional."}, {"role": "user", "content": "Hola, quiero saber el estado de mi pedido"}, {"role": "assistant", "content": "¡Hola! Con gusto te ayudo. ¿Me compartes tu número de pedido para revisarlo?"}]}

Roles

RolPara qué sirve
systemLa personalidad, el contexto y las reglas del asistente. Va una vez, al inicio.
userEl mensaje del usuario o cliente: una pregunta, una instrucción o una solicitud.
assistantLa respuesta ideal. Es lo que el modelo aprende a generar.
toolEl resultado de una herramienta que el asistente llamó.

En ChatML los roles no se traducen

Escribe exactamente system, user, assistant o tool. Un rol como human o gpt dentro de messages se guarda tal cual; si pasa en muchas conversaciones, la vista previa de la importación te avisa que no tienen mensajes de usuario o de asistente. Los alias (human, bot, cliente…) solo se traducen en ShareGPT, CSV, XML y TXT.

Varios turnos

Alterna mensajes de user y assistant dentro del mismo arreglo, con tantos turnos como necesites.

json
{
  "messages": [
    {
      "role": "system",
      "content": "Eres un asistente de ventas de una concesionaria de autos."
    },
    {
      "role": "user",
      "content": "Hola, busco un auto familiar"
    },
    {
      "role": "assistant",
      "content": "¡Bienvenido! Te ayudo a encontrarlo. ¿Cuántas personas viajan normalmente y para qué lo usarías?"
    },
    {
      "role": "user",
      "content": "Somos 5, dos adultos y tres niños. Lo usaríamos para ir a la escuela y viajar los fines de semana"
    },
    {
      "role": "assistant",
      "content": "Para cinco personas con ese uso te conviene una SUV mediana: espacio para todos, cajuela para el equipaje y buen rendimiento en ciudad. ¿Quieres que agendemos una prueba de manejo?"
    }
  ]
}

Cómo puede venir un archivo JSON

  • Un arreglo de conversaciones, o una sola conversación.
  • Un objeto que envuelve el arreglo en conversations o en data.
  • Un arreglo de mensajes sueltos ([{ "role": …, "content": … }]): se importa como una sola conversación.

En un arreglo JSON, el primer elemento decide el formato de todo el archivo: no mezcles formatos en el mismo arreglo. En JSONL cada línea se lee por separado.

Formatos que se convierten a ChatML

El importador detecta el formato por el contenido, no por la extensión del archivo.

FormatoEstructuraCómo queda en ChatML
ChatML{ messages: [{ role, content }] }Queda tal cual. Conserva tool_calls y name.
ShareGPT{ conversations: [{ from, value }] }human → user, gpt → assistant, observation → tool, function_call → llamada a herramienta.
Qwen (lista de turnos)[{ from, value }]Igual que ShareGPT, pero cada línea es directamente la lista de turnos.
Alpaca{ instruction, input?, output }instruction (más input, si hay) → user; output → assistant.
input / output{ input, output }input → user; output → assistant.
prompt / completion{ prompt, completion }prompt → user; completion → assistant.
CSV / TSVColumnas de rol y contenido, o de usuario y asistenteUna conversación por id o por fila. Ver Importar CSV.
TXT de WhatsAppExport del chat con fecha, hora y remitenteUna conversación por cada tramo del chat separado por 6 horas o más sin mensajes.
TXT con prefijos de rolLíneas que empiezan con Usuario: o Asistente:Todo el archivo queda como una conversación.
TXT con nombresUn chat entre dos personas, con el nombre de quien habla al inicio de cada línea (Ana: Hola)Todo el archivo queda como una conversación; una línea de guiones (---) separa varias. Eliges quién responde por tu negocio y los nombres no se guardan.
XML<conversation> con <message role="…">Cada <conversation> es una conversación.
ParquetColumnas de un formato JSON de esta tabla, o una columna messagesSe procesa en el servidor al importar.

Alpaca, input/output y prompt/completion aceptan además un campo system opcional, que se convierte en el primer mensaje. En ShareGPT, un turno con un from desconocido se descarta.

Un ejemplo de cada uno. En JSONL cada línea se lee por separado, así que este archivo se importa tal cual y produce cinco conversaciones:

ejemplos.jsonl
{"conversations": [{"from": "human", "value": "¿Cuál es el horario?"}, {"from": "gpt", "value": "De lunes a viernes, de 9 a 18 h."}]}
[{"from": "human", "value": "¿Tienen estacionamiento?"}, {"from": "gpt", "value": "Sí, gratuito para clientes."}]
{"instruction": "Clasifica el mensaje del cliente", "input": "El pedido llegó tarde y abierto.", "output": "Queja: retraso y empaque dañado."}
{"input": "¿Aceptan tarjeta?", "output": "Sí, todas las tarjetas de crédito y débito."}
{"prompt": "¿Hacen factura?", "completion": "Sí, pídela con tu RFC al pagar."}

CSV y hojas de cálculo

Un CSV puede traer una fila por mensaje (columnas de rol y contenido) o una fila por conversación (columnas de usuario y asistente). Las columnas que se reconocen y los errores típicos están en Importar CSV.

Chats exportados de WhatsApp

Un export de WhatsApp se reconoce por su contenido (las líneas con fecha, hora y remitente), aunque el archivo tenga otra extensión. AstrApp descarta los avisos de multimedia omitida, de cifrado y de llamadas perdidas.

Quién queda como asistente

Quien escribe primero en el chat queda como user y el segundo participante como assistant; si hay más participantes, también quedan como user. Si tu negocio escribió primero, los roles quedan invertidos: revísalos en la vista previa antes de importar.

Llamadas a herramientas

Si tu modelo debe consultar APIs o bases de datos, incluye en los ejemplos cuándo llama a cada herramienta y cómo usa el resultado.

Con campos estructurados

El asistente lleva la llamada en tool_calls y el resultado va en un mensaje con rol tool y el name de la herramienta. Es lo que genera la Síntesis cuando le das herramientas, y el importador conserva esos campos en archivos JSON y JSONL.

json
{
  "messages": [
    {"role": "system", "content": "Eres un asistente de ventas de una concesionaria. Consulta el inventario antes de dar precios."},
    {"role": "user", "content": "¿Tienen algún sedán disponible?"},
    {"role": "assistant", "content": "", "tool_calls": [{"name": "buscar_vehiculos", "arguments": {"tipo_carroceria": "Sedan"}}]},
    {"role": "tool", "name": "buscar_vehiculos", "content": "2 resultados: Toyota Corolla 2024, $359,900 MXN; Honda Civic 2024, $399,900 MXN"},
    {"role": "assistant", "content": "Tengo dos sedanes disponibles: el Toyota Corolla 2024 en $359,900 MXN y el Honda Civic 2024 en $399,900 MXN. ¿Quieres agendar una prueba de manejo?"}
  ]
}

Con etiquetas en el texto

También puedes escribir la llamada dentro del texto, con etiquetas <tool_call> en el mensaje del asistente y <tool_response> en el de la herramienta, como las usan los modelos Qwen.

json
{
  "messages": [
    {
      "role": "system",
      "content": "Eres un asistente de ventas de una concesionaria. Tienes acceso a herramientas para buscar vehículos en el inventario."
    },
    {
      "role": "user",
      "content": "¿Tienen algún sedán disponible?"
    },
    {
      "role": "assistant",
      "content": "<tool_call>\n{\"name\": \"buscar_vehiculos\", \"arguments\": {\"tipo_carroceria\": \"Sedan\"}}\n</tool_call>"
    },
    {
      "role": "tool",
      "content": "<tool_response>\n{\"total\": 2, \"vehiculos\": [{\"marca\": \"Toyota\", \"modelo\": \"Corolla\", \"anio\": 2024, \"precio\": 359900}, {\"marca\": \"Honda\", \"modelo\": \"Civic\", \"anio\": 2024, \"precio\": 399900}]}\n</tool_response>"
    },
    {
      "role": "assistant",
      "content": "Encontré 2 sedanes disponibles:\n\n1. **Toyota Corolla 2024**: $359,900 MXN\n2. **Honda Civic 2024**: $399,900 MXN\n\n¿Te doy más detalles de alguno o agendamos una prueba de manejo?"
    }
  ]
}

Reglas que el modelo va a imitar

  • Un mensaje tool por cada llamada, en el mismo orden que las llamadas.
  • El asistente solo afirma datos que devolvió una herramienta: precios, existencias o fechas que ningún resultado trae no deben aparecer en su respuesta.
  • Los resultados pueden ser simulados: no necesitas conectar herramientas reales para crear el dataset.

Razonamiento con <think>

Puedes enseñar al modelo a razonar antes de responder: el razonamiento va al inicio del mensaje del asistente, entre etiquetas <think>.

json
{
  "messages": [
    {
      "role": "system",
      "content": "Eres el asistente de una tienda en línea. Envío estándar (3 a 5 días): $99 MXN. Express (1 a 2 días): $199 MXN. El envío estándar es gratis en pedidos desde $999 MXN."
    },
    {
      "role": "user",
      "content": "Mi carrito suma $850, ¿cuánto pago de envío a Guadalajara?"
    },
    {
      "role": "assistant",
      "content": "<think>\nEl carrito suma $850, menos de $999, así que no aplica el envío gratis. Opciones: estándar $99 o express $199.\n</think>\n\nTu carrito suma $850, así que el envío todavía tiene costo:\n\n- **Estándar (3 a 5 días):** $99 MXN\n- **Express (1 a 2 días):** $199 MXN\n\nSi agregas $149 más, el envío estándar es gratis."
    }
  ]
}

Úsalo cuando

  • La respuesta requiere cálculos o lógica.
  • Hay decisiones condicionales (si X, entonces Y).
  • El modelo debe decidir qué herramienta usar.
  • Hay que sopesar varios factores antes de responder.

Evítalo cuando

  • Las respuestas son simples y directas (saludos, preguntas frecuentes).
  • La velocidad de respuesta es la prioridad: razonar alarga cada respuesta.
  • La conversación es puramente informativa.
AstrApp no trata distinto el bloque <think>: se entrena como parte de la respuesta, y tu modelo lo va a generar. Si no quieres mostrarle el razonamiento al usuario final, quítalo en la aplicación que presenta la respuesta.

Harmony y el formato de salida

En Conversaciones › Síntesis eliges un Formato de salida (ChatML, Alpaca, ShareGPT, Harmony o Qwen), y en Datasets › Importar datos un Formato de exportación (ChatML, Alpaca, ShareGPT o Qwen). Esa elección queda registrada en el dataset, pero no cambia cómo se guardan las conversaciones: AstrApp las guarda en ChatML, Datasets › Exportar las descarga en JSONL con messages y el entrenamiento las lee en ChatML.

El importador no lee archivos Harmony

Un archivo con el system aparte y los turnos en conversations con role y content no produce ninguna conversación. Pásalo a ChatML antes de importarlo: pon el system como primer mensaje de messages.

¿No estás seguro del formato?

Empieza con ChatML para tu primer fine-tuning. Agrega herramientas o razonamiento cuando tengas un modelo base que funcione bien. Las opciones para generarlos están en Configuración avanzada.