ChatML Format Guide

    Chat Markup Language para conversaciones estructuradas con LLM

    Conversation

    Specification

    ChatML (Chat Markup Language) es un formato de texto estructurado para representar conversaciones multi-turno con modelos de lenguaje grandes. Introducido originalmente por OpenAI y adoptado en toda la industria, ChatML usa tokens especiales para delimitar los límites de los mensajes e identificadores de rol, proporcionando una forma inequívoca de codificar conversaciones en una secuencia única de tokens para entrenamiento e inferencia del modelo. El formato asegura que el modelo pueda distinguir de forma confiable entre instrucciones del sistema, entradas del usuario y respuestas del asistente, incluso cuando el contenido del mensaje contiene texto que de otro modo podría confundirse con elementos estructurales.

    El formato ChatML envuelve cada mensaje en delimitadores de tokens especiales. Cada mensaje comienza con un token de inicio seguido del identificador de rol y un salto de línea, contiene el cuerpo del mensaje y termina con un token de fin. Los roles estándar son "system" (instrucciones de comportamiento), "user" (entrada humana) y "assistant" (respuesta del modelo). Algunas implementaciones extienden esto con roles adicionales como "tool" para resultados de llamadas a funciones o "ipython" para salida de ejecución de código. Los tokens especiales (típicamente representados como <|im_start|> y <|im_end|>) se agregan al vocabulario del modelo durante el entrenamiento y nunca se generan como parte de la salida de texto regular.

    ChatML se ha convertido en la plantilla de chat predeterminada para muchos modelos open-source incluyendo Qwen, Yi y modelos ajustados con el formato compatible con OpenAI. La biblioteca Hugging Face Transformers usa plantillas de chat basadas en Jinja2 para convertir entre listas de mensajes estructurados y texto formateado en ChatML, con el tokenizador de cada modelo especificando su propia plantilla. Esta estandarización permite un comportamiento consistente entre diferentes proveedores de modelos y frameworks de inferencia, ya que el formato estructurado elimina la ambigüedad sobre dónde termina un mensaje y comienza otro.

    When to Use ChatML

    Usa ChatML cuando tu modelo objetivo utilice la plantilla de chat ChatML — esto incluye modelos de la familia Qwen, modelos de la familia Yi y muchos fine-tunes comunitarios basados en el formato de conversación de OpenAI. Si tu framework de entrenamiento (Axolotl, LLaMA-Factory, TRL) espera que especifiques una plantilla de chat y tu modelo base fue pre-entrenado o ajustado con instrucciones usando ChatML, deberías preparar tus datos de entrenamiento en formato ChatML o en un formato estructurado que pueda convertirse a ChatML.

    Elige ChatML sobre texto sin formato o formatos de prompt ad-hoc cuando necesites detección inequívoca de límites de mensajes. Los delimitadores de tokens especiales aseguran que el modelo nunca confunda el contenido del mensaje con marcadores estructurales, lo cual es crítico para la seguridad (prevenir inyección de prompts) y la confiabilidad (asegurar la alternancia correcta de turnos en conversaciones multi-turno). ChatML también es el formato preferido cuando tu despliegue usa el formato de API compatible con OpenAI, ya que la lista de mensajes estructurados se mapea directamente a la codificación ChatML.

    ChatML es menos relevante cuando tu modelo objetivo usa una plantilla de chat diferente (LLaMA usa un formato diferente, Mistral usa sus propios tokens). En esos casos, usa la plantilla nativa del modelo. El formato también es innecesario para tareas de un solo turno donde formatos más simples como Alpaca son suficientes, o cuando tu framework de entrenamiento maneja la aplicación de plantillas automáticamente a partir de datos de conversación estructurados (formato ShareGPT).

    Schema / Structure

    text
    ChatML Token Structure:
    <|im_start|>system
    {system_message}<|im_end|>
    <|im_start|>user
    {user_message}<|im_end|>
    <|im_start|>assistant
    {assistant_message}<|im_end|>
    
    Special Tokens:
      <|im_start|>  - Message start delimiter
      <|im_end|>    - Message end delimiter
    
    Roles:
      system     - Behavioral instructions (optional, first message)
      user       - Human/user input
      assistant  - Model/AI response
      tool       - Tool/function call result (extended format)
    
    Jinja2 Chat Template (HuggingFace):
    {% for message in messages %}
    <|im_start|>{{ message.role }}
    {{ message.content }}<|im_end|>
    {% endfor %}
    <|im_start|>assistant
    Especificación del formato ChatML con tokens especiales, roles y plantilla Jinja2

    Example Data

    text
    <|im_start|>system
    You are a senior software engineer who provides concise, practical code reviews.<|im_end|>
    <|im_start|>user
    Please review this Python function:
    
    def get_user(id):
        conn = sqlite3.connect('app.db')
        cursor = conn.cursor()
        cursor.execute(f"SELECT * FROM users WHERE id = {id}")
        return cursor.fetchone()<|im_end|>
    <|im_start|>assistant
    This function has several critical issues:
    
    1. **SQL Injection vulnerability**: Using f-string interpolation in SQL queries is dangerous. Use parameterized queries instead:
    ```python
    cursor.execute("SELECT * FROM users WHERE id = ?", (id,))
    ```
    
    2. **Resource leak**: The database connection is never closed. Use a context manager:
    ```python
    with sqlite3.connect('app.db') as conn:
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM users WHERE id = ?", (id,))
        return cursor.fetchone()
    ```
    
    3. **Hardcoded database path**: Move 'app.db' to a configuration variable.
    
    4. **No error handling**: Add try/except for database errors and handle the case where no user is found.<|im_end|>
    <|im_start|>user
    Good points. Can you show the corrected version?<|im_end|>
    <|im_start|>assistant
    ```python
    import sqlite3
    from typing import Optional, Tuple
    
    def get_user(user_id: int, db_path: str = "app.db") -> Optional[Tuple]:
        try:
            with sqlite3.connect(db_path) as conn:
                cursor = conn.cursor()
                cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
                return cursor.fetchone()
        except sqlite3.Error as e:
            logging.error(f"Database error fetching user {user_id}: {e}")
            return None
    ```
    
    Key improvements: parameterized query, context manager, type hints, configurable DB path, error handling, and renamed parameter to avoid shadowing the built-in `id`.<|im_end|>
    Conversación multi-turno de revisión de código formateada en ChatML con system prompt

    Ertas Support

    Ertas Data Suite soporta ChatML como formato de importación y exportación para datos de entrenamiento conversacionales. Puedes importar datasets de texto formateados en ChatML, aplicar redacción de PII preservando la estructura de tokens especiales, y exportar conversaciones procesadas con límites de mensajes intactos. La plataforma valida la estructura ChatML incluyendo el emparejamiento correcto de tokens, identificadores de rol válidos y patrones correctos de alternancia de mensajes.

    Ertas Studio maneja la aplicación de la plantilla ChatML automáticamente al ajustar modelos que usan la plantilla de chat ChatML, convirtiendo datos de conversación estructurados a secuencias ChatML correctamente tokenizadas durante la preparación de datos de entrenamiento.

    Related Resources

    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.