> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mka1.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Ejecutar un trabajo de ajuste fino

> Ajusta finamente un modelo en GPUs alquiladas con un trabajo de Compute, monitorea el entrenamiento desde los registros y por SSH, y publica los pesos fusionados en mka1-repos.

Utiliza un trabajo de Compute cuando necesites GPUs para una carga de trabajo finita, como el ajuste fino.
Tú aportas una imagen de contenedor ordinaria y un comando; Compute asigna capacidad de GPU, ejecuta la carga de trabajo hasta completarla, transmite registros duraderos y libera el hardware automáticamente.

Compute no tiene un esquema de ajuste fino.
Un trabajo es una ejecución genérica de contenedor, así que esta guía ajusta finamente con [ms-swift](https://github.com/modelscope/ms-swift) puramente como carga útil de la carga de trabajo — cualquier stack de entrenamiento funciona de la misma manera.

<Note>
  Esta guía alquila GPUs y ejecuta tu propio contenedor de entrenamiento.
  Para la API de ajuste fino gestionada que entrena modelos de la plataforma a partir de archivos JSONL subidos, consulta [Ajustar finamente un modelo](/es/docs/fine-tuning).
</Note>

## Antes de comenzar

Necesitas:

| Requisito                                              | Notas                                                                                                                                                                                                                                                      |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Clave de API                                           | Envíala como `Authorization: Bearer <mka1-api-key>` en cada solicitud.                                                                                                                                                                                     |
| Compute habilitado para tu organización                | Compute es una lista de permitidos que falla en cerrado (fail-closed), porque hay dinero real en juego. Si las solicitudes devuelven `403 tenant_disabled`, pide a un administrador del clúster que habilite tu organización.                              |
| Una imagen de contenedor con tu stack de entrenamiento | Cualquier imagen Linux con GPU que el proveedor pueda descargar sin autenticación interactiva. Debe ejecutarse como root con `bash`, `curl`, `base64` y GNU coreutils disponibles, permitir HTTPS saliente y mantener la carga de trabajo en primer plano. |
| Clave pública SSH                                      | Opcional. Proporciona una al crear el trabajo si quieres depurar la ejecución de forma interactiva. Compute nunca genera ni guarda claves privadas.                                                                                                        |

Los trabajos pasan por `requested` → `allocating` → `provisioning` → `running` → `finalizing`, y luego aterrizan en un estado terminal:

* Una carga de trabajo que sale con `0` termina como `succeeded`; un fallo en cualquier fase termina como `failed`.
* Una terminación explícita, un límite alcanzado o un presupuesto de organización agotado mueven el trabajo a través de `terminating` hasta `terminated`.

<Warning>
  **La facturación comienza en la asignación, no en la disponibilidad.**

  El gasto se acumula desde el momento en que se asigna el hardware, así que el tiempo de aprovisionamiento y los arranques fallidos cuestan dinero.
  Establece `limits.max_runtime_hours` y `limits.max_cost_usd` en cada trabajo; un trabajo que alcanza un límite se termina y se libera de inmediato.
</Warning>

## Paso 1 - Elige un acelerador

Lista el catálogo curado de aceleradores y elige uno por su `name` estable.

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  import { SDK } from '@meetkai/mka1';

  const sdk = new SDK({
    bearerAuth: 'Bearer <mka1-api-key>',
  });

  const accelerators = await sdk.computeCatalog.listAccelerators({});
  console.log(accelerators.data);
  ```

  ```bash curl theme={null}
  curl "https://apigw.mka1.com/api/v1/compute/catalog/accelerators" \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

Cada entrada describe el hardware, las cantidades de GPU que puedes solicitar y las interconexiones que admite:

```json theme={null}
{
  "name": "nvidia-rtx-4090-24gb",
  "display_name": "NVIDIA GeForce RTX 4090 24GB",
  "vendor": "nvidia",
  "memory_gb": 24,
  "gpu_counts": [1, 2, 3, 4, 5, 6, 7, 8],
  "interconnects": ["pcie"]
}
```

## Paso 2 - Consulta precio y disponibilidad

Opcionalmente, solicita una cotización antes de crear nada.
Una cotización es una observación en vivo del mercado: devuelve si la configuración se puede obtener actualmente y el rango de precio por hora, y no reserva nada.

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  const quote = await sdk.computeCatalog.createComputeQuote({
    computeQuoteRequest: {
      resourceType: 'job',
      compute: {
        accelerator: 'nvidia-rtx-4090-24gb',
        gpuCount: 1,
        ephemeralDiskGb: 100,
      },
      container: {
        image: 'modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3',
      },
      access: { ssh: true },
    },
  });
  console.log(quote.available, quote.priceUsdHr);
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/compute/catalog/quotes \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --data '{
      "resource_type": "job",
      "compute": {
        "accelerator": "nvidia-rtx-4090-24gb",
        "gpu_count": 1,
        "ephemeral_disk_gb": 100
      },
      "container": {
        "image": "modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3"
      },
      "access": {"ssh": true}
    }'
  ```
</CodeGroup>

```json theme={null}
{
  "available": true,
  "price_usd_hr": {"min": 0.38, "max": 0.69},
  "observed_at": "2026-07-29T12:00:00Z",
  "expires_at": null
}
```

Una configuración no disponible sigue siendo una respuesta `200`, con `available: false` y límites de precio nulos.

## Paso 3 - Crea el trabajo

Crea el trabajo con el encabezado obligatorio `Idempotency-Key`.
Reintentar con la misma clave y el mismo cuerpo devuelve el mismo trabajo con un `200` en lugar de crear un duplicado; la misma clave con un cuerpo diferente devuelve `409 idempotency_conflict`.

Este ejemplo ejecuta un pequeño ajuste fino LoRA de Qwen2.5-0.5B-Instruct y fusiona el adaptador, replicando la prueba de humo de la plataforma:

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  const job = await sdk.computeJobs.createJob({
    idempotencyKey: '<unique-request-id>',
    computeJobCreate: {
      name: 'qwen-fine-tune',
      compute: {
        accelerator: 'nvidia-rtx-4090-24gb',
        gpuCount: 1,
        ephemeralDiskGb: 100,
      },
      container: {
        image: 'modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3',
        command: [
          'bash',
          '-lc',
          'swift sft --model Qwen/Qwen2.5-0.5B-Instruct --dataset tatsu-lab/alpaca#64 --max_steps 4 --per_device_train_batch_size 1 --max_length 256 --lora_rank 4 --save_steps 4 --logging_steps 1 --output_dir /tmp/output && swift export --adapters $(ls -d /tmp/output/*/checkpoint-* | tail -1) --merge_lora true --output_dir /tmp/merged',
        ],
        env: { USE_HF: '1' },
      },
      access: { sshPublicKey: 'ssh-ed25519 AAAA... you@example.com' },
      limits: { maxRuntimeHours: 2, maxCostUsd: 5 },
    },
  });
  console.log(job.id, job.state);
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/compute/jobs \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'Idempotency-Key: <unique-request-id>' \
    --data '{
      "name": "qwen-fine-tune",
      "compute": {
        "accelerator": "nvidia-rtx-4090-24gb",
        "gpu_count": 1,
        "ephemeral_disk_gb": 100
      },
      "container": {
        "image": "modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3",
        "command": ["bash", "-lc", "swift sft --model Qwen/Qwen2.5-0.5B-Instruct --dataset tatsu-lab/alpaca#64 --max_steps 4 --per_device_train_batch_size 1 --max_length 256 --lora_rank 4 --save_steps 4 --logging_steps 1 --output_dir /tmp/output && swift export --adapters $(ls -d /tmp/output/*/checkpoint-* | tail -1) --merge_lora true --output_dir /tmp/merged"],
        "env": {"USE_HF": "1"}
      },
      "access": {"ssh_public_key": "ssh-ed25519 AAAA... you@example.com"},
      "limits": {"max_runtime_hours": 2, "max_cost_usd": 5}
    }'
  ```
</CodeGroup>

Notas sobre los campos:

| Campo                                        | Notas                                                                                                                                                                                                                                                           |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `compute.accelerator`, `compute.gpu_count`   | Obligatorios. Un nombre de acelerador del catálogo y de 1 a 8 GPUs en un solo nodo.                                                                                                                                                                             |
| `compute.ephemeral_disk_gb`                  | Obligatorio, sin valor por defecto. Dimensiónalo para la imagen más todas las descargas; la imagen de ms-swift de arriba necesita por sí sola casi 60 GB.                                                                                                       |
| `container.command` o `container.script_b64` | Se requiere exactamente uno. `command` es un argv; `script_b64` es un script bash codificado en base64, más fácil para cargas de trabajo de varios pasos.                                                                                                       |
| `container.env`                              | Variables de entorno simples. Los nombres `MKA1_*` están reservados y `HF_ENDPOINT` es propiedad de la plataforma, por lo que ambos se rechazan.                                                                                                                |
| `container.secret_env`                       | Variables de entorno resueltas desde secretos almacenados en el arranque. Úsalas para tokens, de modo que nunca aparezcan en las especificaciones de pod del proveedor — consulta la [sección de mka1-repos](#usar-mka1-repos-para-conjuntos-de-datos-y-pesos). |
| `limits`                                     | Opcional pero recomendado. Los techos de tiempo de ejecución y de costo son independientes, y cualquiera de los dos termina el trabajo al alcanzarse.                                                                                                           |

La respuesta es `201` con el trabajo duradero en estado `requested`:

```json theme={null}
{
  "id": "job_9f2kQxWv3bT8mLpZ",
  "name": "qwen-fine-tune",
  "state": "requested",
  "reason": null,
  "compute": {"accelerator": "nvidia-rtx-4090-24gb", "gpu_count": 1, "ephemeral_disk_gb": 100},
  "accrued_usd": 0,
  "limits": {"max_runtime_hours": 2, "max_cost_usd": 5},
  "endpoints": [],
  "created_at": "2026-07-29T12:01:00Z",
  "started_at": null,
  "terminal_at": null
}
```

Que la creación tenga éxito significa que la solicitud fue aceptada, no que exista capacidad.
El agotamiento de capacidad, los errores del proveedor y los fallos de arranque aparecen después a través de `state`, `reason`, los registros y los eventos — nunca como un error HTTP en una creación que ya devolvió `201`.

## Paso 4 - Consulta el estado hasta que finalice

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  const job = await sdk.computeJobs.getJob({ id: 'job_9f2kQxWv3bT8mLpZ' });
  console.log(job.state, job.accruedUsd, job.ssh?.command);
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/compute/jobs/job_9f2kQxWv3bT8mLpZ \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

Mientras se ejecuta, el trabajo reporta el hardware que realmente obtuvo, el precio por hora capturado en la asignación, el gasto acumulado hasta el momento y un comando SSH listo para usar si proporcionaste una clave:

```json theme={null}
{
  "id": "job_9f2kQxWv3bT8mLpZ",
  "name": "qwen-fine-tune",
  "state": "running",
  "reason": null,
  "compute": {"accelerator": "nvidia-rtx-4090-24gb", "gpu_count": 1, "ephemeral_disk_gb": 100},
  "hardware": {"accelerator": "nvidia-rtx-4090-24gb", "gpu_count": 1, "gpu_memory_gb": 24},
  "price_usd_hr": 0.44,
  "accrued_usd": 0.07,
  "allocated_at": "2026-07-29T12:03:12Z",
  "ssh": {"host": "203.0.113.7", "port": 22022, "user": "root", "command": "ssh -p 22022 root@203.0.113.7"},
  "limits": {"max_runtime_hours": 2, "max_cost_usd": 5},
  "endpoints": [],
  "created_at": "2026-07-29T12:01:00Z",
  "started_at": "2026-07-29T12:04:40Z",
  "terminal_at": null
}
```

Cuando la carga de trabajo sale con `0`, el trabajo pasa por `finalizing` hasta `succeeded`, se registra `exit_code`, el recurso del proveedor se libera automáticamente y el gasto deja de acumularse.
Una salida distinta de cero termina en `failed` con un `reason` que explica el porqué.
Los trabajos terminales siguen siendo legibles para auditoría y uso.

## Paso 5 - Lee los registros y eventos

Los registros son duraderos y están paginados por cursor, y distinguen la salida de tu carga de trabajo de la salida del sistema.

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  const logs = await sdk.computeJobs.listJobLogs({
    id: 'job_9f2kQxWv3bT8mLpZ',
    source: 'user',
    order: 'desc',
    limit: 50,
  });
  for (const line of logs.data) {
    console.log(line.timestamp, line.stream, line.message);
  }
  ```

  ```bash curl theme={null}
  curl "https://apigw.mka1.com/api/v1/compute/jobs/job_9f2kQxWv3bT8mLpZ/logs?source=user&order=desc&limit=50" \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

```json theme={null}
{
  "data": [
    {
      "id": "log_01J1",
      "timestamp": "2026-07-29T12:06:02Z",
      "source": "user",
      "stream": "stdout",
      "message": "{'loss': 2.31, 'epoch': 0.25, 'step': 1}"
    }
  ],
  "next_cursor": null,
  "total": 412
}
```

Usa `source=system` para la salida de asignación y arranque, y `order=asc` (el valor por defecto) para paginar de más antiguo a más reciente.
Las transiciones del ciclo de vida como `provider_allocated`, `workload_started` y `workload_exited` también están disponibles como eventos estructurados:

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  const events = await sdk.computeJobs.listJobEvents({ id: 'job_9f2kQxWv3bT8mLpZ' });
  console.log(events.data.map((event) => event.type));
  ```

  ```bash curl theme={null}
  curl "https://apigw.mka1.com/api/v1/compute/jobs/job_9f2kQxWv3bT8mLpZ/events" \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

## Paso 6 - Detén un trabajo antes de tiempo

La terminación es explícita, idempotente y devuelve el recurso actual.

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  const job = await sdk.computeJobs.terminateJob({ id: 'job_9f2kQxWv3bT8mLpZ' });
  console.log(job.state);
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/compute/jobs/job_9f2kQxWv3bT8mLpZ/terminate \
    --request POST \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

No hay `DELETE`: el registro se conserva para auditoría, uso y conciliación.
Un trabajo que alcanza `limits.max_runtime_hours`, `limits.max_cost_usd` o un presupuesto de la organización se termina de la misma manera, sin periodo de gracia y sin garantía de resultados parciales, así que publica los artefactos desde dentro de la carga de trabajo antes de que salga.

## Usar mka1-repos para conjuntos de datos y pesos

Compute no almacena artefactos de las cargas de trabajo.
La ruta recomendada es [mka1-repos](/es/docs/repositories), el servicio de repositorios compatible con Hugging Face de la plataforma para modelos y conjuntos de datos: tu trabajo descarga el conjunto de datos desde ahí y publica los pesos fusionados de vuelta, todo desde dentro de la carga de trabajo.

Dos convenciones de la plataforma hacen que esto funcione:

1. Compute siempre inyecta `HF_ENDPOINT` en cada carga de trabajo, apuntando al endpoint de artefactos configurado para tu clúster.
   Las herramientas compatibles con Hugging Face — `huggingface_hub`, ms-swift con `USE_HF=1`, vLLM — resuelven los identificadores `<org>/<name>` contra él, de modo que las cargas de trabajo llegan a mka1-repos sin ningún cambio de código.
   No puedes sobrescribir `HF_ENDPOINT` por tu cuenta.
2. mka1-repos autentica con tu clave de API de MKA1.
   Almacena la clave como un secreto de Compute e inyéctala como `HF_TOKEN` mediante `secret_env`, para que nunca aparezca en línea en la especificación del trabajo.

Crea el secreto una sola vez:

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  const secret = await sdk.computeSecrets.createComputeSecret({
    computeSecretCreate: {
      name: 'artifact-repository',
      data: { HF_TOKEN: '<mka1-api-key>' },
    },
  });
  console.log(secret.id, secret.keys);
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/compute/secrets \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --data '{
      "name": "artifact-repository",
      "data": {"HF_TOKEN": "<mka1-api-key>"}
    }'
  ```
</CodeGroup>

Los valores de los secretos son de solo escritura; la respuesta devuelve solo el id y los nombres de las claves:

```json theme={null}
{
  "id": "sec_Vb3nRk8sQw1xYz2M",
  "name": "artifact-repository",
  "keys": ["HF_TOKEN"],
  "created_at": "2026-07-29T11:58:00Z"
}
```

Después escribe la carga de trabajo como un script que entrena desde tu repositorio de conjuntos de datos y publica los pesos fusionados:

```bash train-and-publish.sh theme={null}
set -euo pipefail

swift sft \
  --model Qwen/Qwen2.5-0.5B-Instruct \
  --dataset acme/support-conversations \
  --num_train_epochs 1 \
  --per_device_train_batch_size 1 \
  --lora_rank 8 \
  --output_dir /tmp/output

swift export \
  --adapters $(ls -d /tmp/output/*/checkpoint-* | tail -1) \
  --merge_lora true \
  --output_dir /tmp/merged

python -c "
from huggingface_hub import HfApi
api = HfApi()
api.create_repo('acme/qwen2.5-0.5b-support', exist_ok=True)
api.upload_folder(repo_id='acme/qwen2.5-0.5b-support', folder_path='/tmp/merged')
"
```

Codifícalo con `base64 < train-and-publish.sh` y referencia el secreto en el cuerpo de creación:

```json theme={null}
{
  "container": {
    "image": "modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3",
    "script_b64": "<base64 of train-and-publish.sh>",
    "env": {"USE_HF": "1"},
    "secret_env": {
      "HF_TOKEN": {"secret_id": "sec_Vb3nRk8sQw1xYz2M", "key": "HF_TOKEN"}
    }
  }
}
```

Cuando el trabajo tiene éxito, los pesos fusionados pueden descargarse desde `acme/qwen2.5-0.5b-support` por cualquier miembro de tu organización, y un servicio de Compute puede servirlos directamente — consulta [Desplegar un servidor de modelos](/es/docs/compute-deployment).

<Note>
  Una terminación a mitad de la ejecución destruye todo lo que no se haya publicado aún.
  Publicar desde dentro de la carga de trabajo, como arriba, es el mecanismo de checkpointing soportado en esta versión.
</Note>

## Supervisar el gasto

Cada respuesta de trabajo incluye `accrued_usd`, y el endpoint de uso agrega el gasto entre recursos para una ventana de tiempo:

<CodeGroup>
  ```ts MKA1 SDK theme={null}
  const usage = await sdk.computeUsage.getComputeUsage({ resourceType: 'job' });
  console.log(usage.summary);
  ```

  ```bash curl theme={null}
  curl "https://apigw.mka1.com/api/v1/compute/usage?resource_type=job" \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

```json theme={null}
{
  "window": {"from": "2026-06-29T12:10:00Z", "to": "2026-07-29T12:10:00Z"},
  "summary": {"total_usd": 12.41, "job_usd": 4.12, "service_usd": 8.29},
  "data": [
    {
      "resource_type": "job",
      "resource_id": "job_9f2kQxWv3bT8mLpZ",
      "name": "qwen-fine-tune",
      "billable_seconds": 1560,
      "accrued_usd": 0.19
    }
  ],
  "next_cursor": null,
  "total": 6
}
```

La facturación se mide en minutos completos por recurso, y el gasto de Compute se descuenta de los mismos presupuestos de organización que cualquier otro servicio de MKA1.

## Referencia de API

Para ver el esquema completo de solicitud y respuesta, abre los grupos de Compute en la [Referencia de API](/es/api-reference/introduction).

## Ver también

* [Desplegar un servidor de modelos](/es/docs/compute-deployment) - sirve los pesos que produjo este trabajo detrás de un endpoint autenticado.
* [Gestionar repositorios](/es/docs/repositories) - crea y gestiona los repositorios de los que este trabajo lee y en los que publica.
* [Usar repositorios con Compute](/es/docs/repositories) - sube conjuntos de datos y pesos por git y conéctalos a las cargas de trabajo.
* [Ajustar finamente un modelo](/es/docs/fine-tuning) - la API de ajuste fino gestionada para modelos de la plataforma.
