1. Separate the program, its parameters, and commercial tracking
The code describes the program's behavior. The configuration specifies the workload: model, data, batch, precision, and destination. Secrets grant access to the required resources. Keep these elements separate so you can change a run without rewriting the code or copying a token into a shared file.
The Kernodeck command reference lets you find the rental context in your account. The run identifier distinguishes executions of your program during that period. Link them in your notes if that helps, but don't ask your script to infer the computation state from the payment status.
Let's take a document classification project that must be launched several times on the same sample. The program contract describes the input file, the accepted parameters, the output folder, and how errors are reported. The data guide covers content validation; here, we organize the interface that connects these steps.
2. Write an explicit, versioned input contract
Document the required fields and accepted values. Avoid silent defaults for a decision that changes the result, such as the model or the device. A schema number distinguishes the configuration's shape from the code version; it does not replace the latter.
In this tutorial example, the JSON file contains a schema, an input path, a batch, and the requested device. Relative paths are read from the configuration folder. This rule chosen for the example avoids depending on the directory from which a colleague launches the command.
Reading valid JSON only validates its syntax. Your program must then check the types, fields, and project constraints. On error, it should stop before loading an expensive resource, with a message that names the parameter to fix.
{
"schema_version": 1,
"input": "../data/pilote.jsonl",
"batch_size": 4,
"device": "cuda"
}3. Prepare an entry point that rejects simple errors
argparse lets you declare options and generate help for your command. The following example is only a precheck: it reads the configuration, verifies its fields, and flags an already-used output folder. It loads neither model nor data into memory and does not test GPU availability.
Save this tutorial code in prepare_run.py if you want to adapt it. It is provided without a verified run. Then add your business checks in the application, rather than treating the final message as a computation result. The cuda device remains a request; PyTorch also uses this name with ROCm.
Rejecting an existing output directory is here a safeguard convention against mixing runs. A real recovery command must receive a separate option and checks. Don't turn a fresh launch into an implicit recovery just because files are present.
import argparse
import json
from pathlib import Path
parser = argparse.ArgumentParser(description="Validate a project launch")
parser.add_argument("--config", type=Path, required=True)
parser.add_argument("--run-dir", type=Path, required=True)
args = parser.parse_args()
try:
config_path = args.config.resolve()
config = json.loads(config_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
parser.error(f"Unreadable configuration: {exc}")
expected = {"schema_version", "input", "batch_size", "device"}
if not isinstance(config, dict) or set(config) != expected:
parser.error("Expected fields: schema_version, input, batch_size, device")
if type(config["schema_version"]) is not int or config["schema_version"] != 1:
parser.error("schema_version must be 1")
if type(config["batch_size"]) is not int or config["batch_size"] < 1:
parser.error("batch_size must be a positive integer")
if config["device"] not in ("cpu", "cuda"):
parser.error("device must be cpu or cuda")
if not isinstance(config["input"], str) or not config["input"]:
parser.error("input must be a non-empty path")
input_path = (config_path.parent / config["input"]).resolve()
run_dir = args.run_dir.resolve()
if not input_path.is_file():
parser.error("Input file missing")
if run_dir.exists():
parser.error("Choose a new output folder")
print(json.dumps({
"status": "configuration_validated",
"input": str(input_path),
"run_dir": str(run_dir),
"device_requested": config["device"],
"batch_size": config["batch_size"]
}, ensure_ascii=False))python prepare_run.py --config config/pilote.json --run-dir runs/pilote-0014. Give runs and their results an identity
Associate each launch with a short identifier that is unique within your campaign. Record the code revision, the schema, and the parameters actually used, then the data and model reference. Keep these values with the outputs so that a result does not depend on a configuration file modified later.
To compare two batch sizes, create two trials and two directories. Keep the same sample and identify the intentional difference. The names pilote-001 and pilote-002 do not by themselves explain what changed: the manifest links the name to the parameters.
Reserve a report format that your tools can read. It can distinguish items received, succeeded, rejected, and still to be processed. Choose a complete success rule and do not mark a trial as finished as soon as the first result is written. The program's exit code must remain consistent with that conclusion.
Scroll the table to read all columns.| File or state | Role | Expected check |
|---|---|---|
| manifest.json | Identity of the code, data, and parameters | Values actually used, with no secrets. |
| results.jsonl | One output per accepted item | Known identifiers and compliant format. |
| errors.jsonl | Rejected items and a useful reason | No silent disappearance and no unnecessary sensitive content. |
| summary.json | Trial conclusion and counters | Consistent total, files re-read before final status. |
5. Expose events that are useful to your tools
Make a few transitions visible: configuration accepted, data accessible, model loaded, first output written, and processing finished. A trace should let you answer "where is this launch at?" without copying the documents or prompts. Associate the step and the trial identifier with the message.
Python's logging module lets you organize messages by level and destination. Then choose your own event convention and document it. An application that writes an error and then exits successfully makes automation misleading; conversely, not every warning means the result is unusable.
Do not confuse an emitted event with a durable result: a "backup started" message does not prove that a file was re-read. For a long run, the dedicated guide explains the link between process and session. Above all, your interface must keep a conclusion accessible after the interactive connection ends.
6. Define failure, recovery, and final verification
Classify useful failures: invalid configuration, missing resource, computation error, and non-conforming result. For each one, provide a next action. Do not set up automatic retries without deciding which effects can be repeated: overwriting an output that was already accepted and resuming from a checkpoint require different rules.
Your final procedure explains how to launch, observe, stop, resume, and export. For document processing, keep the list of completed IDs and those to reprocess. For training, use a protocol that verifies saved states in a new process. A non-empty folder is not proof of a correct resume.
Finally, check the project from a new invocation, with a known configuration and a separate destination. Verify the expected rejections, then a short end-to-end run. The precheck on this page covers neither concurrent access, nor public exposure of a service, nor storage permissions: these topics require their own design.