Training for agents
Fine-tune models on agent trajectories with the chat fine-tuning objective, submit trajectory datasets, and verify any round from its seed.
Agents live or die by how well their model handles multi-turn work: reading tool results, choosing the next call, knowing when to stop. The best data for that is the agent's own trajectories, corrected where it went wrong. Tenzro Network 1 trains on exactly that data. The chat fine-tuning objective turns agent trajectories into training examples, and decentralised training runs it across trainers on the network. Every round of every run is replayable from its seed, so the sponsor, the trainers and anyone else can check the result.
This page covers what is specific to agent training. For how runs, trainers and committees work in general, see Training.
The chat fine-tuning objective
ChatSft is supervised fine-tuning on chat and tool-call conversations. It is built for trajectories: a record of an agent's conversation, including the tools it had, the calls it made and the results it got back.
- Each assistant turn is one training example, including any tool calls it makes.
- The conversation up to that turn is rendered with the model's own chat template and used as context. It is masked, so it carries no loss.
- Only the tokens of the assistant turn carry loss. The model learns what to say and which tools to call, given everything before.
A common use is distillation: run a smaller model as your agent, have a stronger model or a person correct its trajectories, then fine-tune the smaller model on the corrected runs so it behaves well with a minimal harness. The same objective works for any curated set of good agent runs.
ChatSft runs on language models, with full fine-tuning or LoRA and QLoRA adapters, and with the same aggregation, commitments and receipts as any other run.
Trajectory format
A trajectory dataset is JSONL: one conversation per line, in the OpenAI-compatible chat format. Each line has a messages array and an optional tools array. Assistant turns can carry tool_calls, and tool results are messages with role set to tool.
{"tools":[{"type":"function","function":{"name":"get_price","description":"Current GPU price per epoch","parameters":{"type":"object","properties":{"accelerator":{"type":"string"}},"required":["accelerator"]}}}],"messages":[{"role":"system","content":"You buy compute for the user within budget."},{"role":"user","content":"Rent a workstation GPU if it is under budget."},{"role":"assistant","content":null,"tool_calls":[{"id":"call_1","type":"function","function":{"name":"get_price","arguments":"{\"accelerator\":\"workstation\"}"}}]},{"role":"tool","tool_call_id":"call_1","content":"{\"price\":\"within budget\"}"},{"role":"assistant","content":"The price is within budget. Booking one epoch now."}]}This line yields two examples: the first assistant turn, which is the tool call, and the final answer.
Before you publish, remove anything that should not leave your hands, such as API keys, personal data or private tool results. If the trajectories must stay private, use a Confidential run, where shards are encrypted for the trainers' attested enclaves.
Submit trajectories
-
Collect and clean. Export your agent's conversations to JSONL in the format above. Split large datasets into several shard files.
-
Publish the shards to the network's content-addressed store. Each file gets a
tenzro://blob/URI derived from its hash:bashtenzro iroh publish --file trajectories-000.jsonl # tenzro://blob/<hash> -
Write the task spec. Set the modality to
Languageand the objective toChatSft, pointdataset_refat your data and put the dataset's hash indataset_hash. An excerpt:json{ "architecture": { "family": "transformer-decoder", "modality": "Language", "metadata": { "hf_repo": "<catalog-model>" } }, "tier": "Open", "aggregation": "Mean", "objective": "ChatSft", "dataset_ref": "tenzro://blob/<hash>", "dataset_hash": "0x<dataset-manifest-hash>", "trainer_count": 8, "quorum": 5, "inner_steps": 100, "max_rounds": 20 }The model must be one from the network's catalog, and the full spec also sets the reward pool, the grace window and communication settings. See Training.
-
Post the run. Posting is an owner call, signed by your account:
bashtenzro train post-task --spec agent-sft.json -
Follow it with
tenzro train get-run --task-id <task-id>and collect the receipt withtenzro train get-receipt --task-id <task-id>when it completes. The result is a set of weights you can serve on the network. See Model serving.
Every round is replayable from its seed
Each round has a seed, derived from the run and the round number together with a block finalised before the round opened. Nobody can choose it after the fact. The seed fixes everything in the round that would otherwise be random:
- which shard each trainer trains on;
- the order of examples and batches;
- sampling, where the objective samples, and any other randomness in the inner loop;
- the committee that verifies and signs the round.
With the seed, the round's starting weights and the published shards, anyone can run the round again. The ledger holds what they need to compare against: the seed, the starting state root, each accepted submission with its payload hash and activation commitment, the committee's signatures and the new state root.
Verify a round
- Read the round record.
tenzro train get-run --task-id <task-id>shows the run's rounds and their state roots. The methodtenzro_training_getRunis open. - Fetch the inputs. Get the starting weights and each trainer's shard by hash from the content-addressed store. Hashes are checked on transfer, so you know you have the same bytes the trainers had.
- Re-run the inner loop for any trainer with the reference trainer package,
tenzro-trainer, using the round's seed, the starting weights and that trainer's shard. Compare your activation commitment, the per-step losses and the largest coordinates of the update, with the one the trainer submitted. Results agree within tolerance bands sized for floating-point differences between GPUs. - Re-run the aggregation over the accepted payloads with the run's aggregation rule and outer optimiser, and check that you arrive at the committed state root.
If a submission does not match, challenge it while the round is still open to dispute:
tenzro train challenge-commitment \
--task-id <task-id> --round 7 --fragment 0 \
--trainer-did did:tenzro:machine:<trainer> \
--recomputed recomputed-commitment.jsonA trainer whose submission fails the check is evicted and its bond is slashed. Because the challenge is re-execution from public inputs, it does not depend on trusting the challenger.