> ## 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.

# Executar um job de ajuste fino

> Faça o ajuste fino de um modelo em GPUs alugadas com um job do Compute, monitore o treinamento por logs e SSH e publique os pesos mesclados no mka1-repos.

Use um job do Compute quando precisar de GPUs para uma carga de trabalho finita, como ajuste fino.
Você traz uma imagem de contêiner comum e um comando; o Compute aloca capacidade de GPU, executa a carga de trabalho até a conclusão, transmite logs duráveis e desaloca o hardware automaticamente.

O Compute não tem esquema de ajuste fino.
Um job é uma execução genérica de contêiner, então este guia faz o ajuste fino com [ms-swift](https://github.com/modelscope/ms-swift) puramente como payload da carga de trabalho — qualquer stack de treinamento funciona da mesma forma.

<Note>
  Este guia aluga GPUs e executa seu próprio contêiner de treinamento.
  Para a API gerenciada de ajuste fino, que treina modelos da plataforma a partir de arquivos JSONL enviados, veja [Ajuste fino de um modelo](/pt/docs/fine-tuning).
</Note>

## Antes de começar

Você precisa de:

| Requisito                                            | Observações                                                                                                                                                                                                                                            |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Chave de API                                         | Envie-a como `Authorization: Bearer <mka1-api-key>` em toda requisição.                                                                                                                                                                                |
| Compute habilitado para sua organização              | O Compute usa uma lista de permissões que nega por padrão (fail-closed), porque há dinheiro real em jogo. Se as requisições retornarem `403 tenant_disabled`, peça a um administrador do cluster para habilitar sua organização.                       |
| Uma imagem de contêiner com sua stack de treinamento | Qualquer imagem Linux com GPU que o provedor consiga baixar sem autenticação interativa. Ela deve executar como root com `bash`, `curl`, `base64` e GNU coreutils disponíveis, permitir HTTPS de saída e manter a carga de trabalho em primeiro plano. |
| Chave pública SSH                                    | Opcional. Forneça uma na criação se quiser depurar a execução interativamente. O Compute nunca gera nem guarda chaves privadas.                                                                                                                        |

Os jobs passam por `requested` → `allocating` → `provisioning` → `running` → `finalizing` e então chegam a um estado terminal:

* Uma carga de trabalho que sai com `0` termina como `succeeded`; uma falha em qualquer fase termina como `failed`.
* Encerramento explícito, um limite atingido ou um orçamento da organização esgotado move o job por `terminating` até `terminated`.

<Warning>
  **A cobrança começa na alocação, não na prontidão.**

  Os gastos acumulam a partir do momento em que o hardware é alocado, então o tempo de provisionamento e as inicializações que falham custam dinheiro.
  Defina `limits.max_runtime_hours` e `limits.max_cost_usd` em todo job; um job que atinge um limite é encerrado e desalocado imediatamente.
</Warning>

## Passo 1 - Escolha um acelerador

Liste o catálogo curado de aceleradores e escolha um pelo seu `name` estável.

<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 descreve o hardware, as quantidades de GPU que você pode solicitar e as interconexões que ele suporta:

```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"]
}
```

## Passo 2 - Verifique preço e disponibilidade

Opcionalmente, solicite uma cotação antes de criar qualquer coisa.
Uma cotação é uma observação do mercado em tempo real: ela retorna se a configuração pode ser obtida no momento e a faixa de preço por hora, e não 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
}
```

Uma configuração indisponível ainda é uma resposta `200`, com `available: false` e limites de preço nulos.

## Passo 3 - Crie o job

Crie o job com um cabeçalho `Idempotency-Key` obrigatório.
Repetir com a mesma chave e o mesmo corpo retorna o mesmo job com `200` em vez de criar uma duplicata; a mesma chave com um corpo diferente retorna `409 idempotency_conflict`.

Este exemplo executa um pequeno ajuste fino LoRA do Qwen2.5-0.5B-Instruct e mescla o adapter, reproduzindo o smoke test da 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 os campos:

| Campo                                         | Observações                                                                                                                                                                                                                                |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `compute.accelerator`, `compute.gpu_count`    | Obrigatórios. Um nome de acelerador do catálogo e de 1 a 8 GPUs em um único nó.                                                                                                                                                            |
| `compute.ephemeral_disk_gb`                   | Obrigatório, sem valor padrão. Dimensione-o para a imagem mais todos os downloads; a imagem do ms-swift acima sozinha ocupa quase 60 GB.                                                                                                   |
| `container.command` ou `container.script_b64` | Exatamente um é obrigatório. `command` é um argv; `script_b64` é um script bash codificado em base64, mais fácil para cargas de trabalho com várias etapas.                                                                                |
| `container.env`                               | Variáveis de ambiente comuns. Nomes `MKA1_*` são reservados e `HF_ENDPOINT` pertence à plataforma, então ambos são rejeitados.                                                                                                             |
| `container.secret_env`                        | Variáveis de ambiente resolvidas a partir de segredos armazenados no lançamento. Use-o para tokens, para que nunca apareçam nas especificações de pod do provedor — veja a [seção do mka1-repos](#use-o-mka1-repos-para-datasets-e-pesos). |
| `limits`                                      | Opcional, mas recomendado. Os tetos de tempo de execução e de custo são independentes, e qualquer um deles encerra o job quando atingido.                                                                                                  |

A resposta é `201` com o job persistido no 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
}
```

A criação bem-sucedida significa que a requisição foi aceita, não que exista capacidade.
Esgotamento de capacidade, erros do provedor e falhas de bootstrap aparecem depois através de `state`, `reason`, logs e eventos — nunca como um erro HTTP em uma criação que já retornou `201`.

## Passo 4 - Consulte até a conclusão

<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>

Durante a execução, o job reporta o hardware que realmente obteve, o preço por hora capturado na alocação, os gastos até o momento e um comando SSH pronto para uso, se você forneceu uma chave:

```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
}
```

Quando a carga de trabalho sai com `0`, o job passa por `finalizing` até `succeeded`, o `exit_code` é registrado, o recurso do provedor é desalocado automaticamente e os gastos param de acumular.
Uma saída diferente de zero termina em `failed` com um `reason` explicando o motivo.
Jobs em estado terminal permanecem legíveis para auditoria e uso.

## Passo 5 - Leia logs e eventos

Os logs são duráveis e paginados por cursor, e distinguem a saída da sua carga de trabalho da saída do 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
}
```

Use `source=system` para a saída de alocação e bootstrap, e `order=asc` (o padrão) para paginar do mais antigo para o mais recente.
Transições de ciclo de vida como `provider_allocated`, `workload_started` e `workload_exited` também estão disponíveis como eventos estruturados:

<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>

## Passo 6 - Interrompa um job antecipadamente

O encerramento é explícito, idempotente e retorna o recurso atual.

<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>

Não há `DELETE`: o registro é retido para auditoria, uso e reconciliação.
Um job que atinge `limits.max_runtime_hours`, `limits.max_cost_usd` ou um orçamento da organização é encerrado da mesma forma, sem período de carência e sem garantia de resultados parciais, então publique os artefatos de dentro da carga de trabalho antes que ela termine.

## Use o mka1-repos para datasets e pesos

O Compute não armazena artefatos de carga de trabalho.
O caminho recomendado é o [mka1-repos](/pt/docs/repositories), o serviço de repositórios da plataforma compatível com Hugging Face para modelos e datasets: seu job baixa o dataset dele e publica os pesos mesclados de volta nele, tudo de dentro da carga de trabalho.

Duas convenções da plataforma fazem isso funcionar:

1. O Compute sempre injeta `HF_ENDPOINT` em toda carga de trabalho, apontando para o endpoint de artefatos configurado para o seu cluster.
   Ferramentas compatíveis com Hugging Face — `huggingface_hub`, ms-swift com `USE_HF=1`, vLLM — resolvem identificadores `<org>/<name>` contra ele, então as cargas de trabalho alcançam o mka1-repos sem nenhuma mudança de código.
   Você não pode sobrescrever `HF_ENDPOINT` por conta própria.
2. O mka1-repos autentica com sua chave de API MKA1.
   Armazene a chave como um segredo do Compute e injete-a como `HF_TOKEN` através de `secret_env`, para que ela nunca apareça inline na especificação do job.

Crie o segredo uma única 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>

Os valores de segredos são somente escrita; a resposta retorna apenas o id e os nomes das chaves:

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

Depois escreva a carga de trabalho como um script que treina a partir do seu repositório de dataset e publica os pesos mesclados:

```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')
"
```

Codifique-o com `base64 < train-and-publish.sh` e referencie o segredo no corpo de criação:

```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"}
    }
  }
}
```

Quando o job é concluído com sucesso, os pesos mesclados ficam disponíveis para download em `acme/qwen2.5-0.5b-support` por qualquer pessoa da sua organização, e um serviço do Compute pode servi-los diretamente — veja [Implantar um servidor de modelos](/pt/docs/compute-deployment).

<Note>
  Um encerramento no meio da execução destrói qualquer trabalho ainda não publicado.
  Publicar de dentro da carga de trabalho, como acima, é o mecanismo de checkpoint suportado nesta versão.
</Note>

## Acompanhe os gastos

Toda resposta de job carrega `accrued_usd`, e o endpoint de uso agrega os gastos entre recursos para uma janela de tempo:

<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
}
```

A cobrança é medida em minutos inteiros por recurso, e os gastos do Compute saem dos mesmos orçamentos da organização que qualquer outro serviço MKA1.

## Referência da API

Para o esquema completo de requisição e resposta, abra os grupos do Compute na [Referência de API](/pt/api-reference/introduction).

## Veja também

* [Implantar um servidor de modelos](/pt/docs/compute-deployment) - sirva os pesos que este job produziu atrás de um endpoint autenticado.
* [Gerenciar repositórios](/pt/docs/repositories) - crie e gerencie os repositórios que este job lê e nos quais publica.
* [Usar repositórios com o Compute](/pt/docs/repositories) - envie datasets e pesos via git e conecte-os às cargas de trabalho.
* [Ajuste fino de um modelo](/pt/docs/fine-tuning) - a API gerenciada de ajuste fino para modelos da plataforma.
