Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Training data for fine-tuning

After this lesson: I can write training examples a model can learn from.

60 min lesson6 commandsAI coding agent lab: use opencode to explain and fix code, and check its work

Training data is a list of examples

Fine-tuning teaches a model from examples you supply. Each example is a pair: what the model is given, and what you want back. Whatever your pairs do, the model copies.

So one careless spelling, repeated forty times, is forty lessons in the wrong thing.

By the end of this lesson

Note

I can write training examples a model can learn from.

Key words

WordWhat it means
Fine-tuningtraining a model further on your examples
Promptthe input half of a training pair
Completionthe answer half of a training pair
JSONLa file with one JSON object on each line
Labela fixed word that names a class

The cells students fill in are left blank in this preview.

One pair on every line

A JSONL file holds one example a line. Each line is a small JSON object with a prompt and a completion.

A line is easy to check alone. If line 90 is broken, lines 1 to 89 still work. That is why this form is common for training files.

Worked example: three lines

LineWhat it holdsVerdict
1prompt "I was charged twice", completion "billing"Good
2prompt "Wrong invoice" and no completionMissing completion
3prompt "Love your shop", completion "Other"Unknown label: the case is wrong

The cells students fill in are left blank in this preview.

Keep the form the same every time

A model learns the pattern in your pairs, including the faults. If "billing" is also spelt "Billing" and "bills", it learns three answers.

Choose a fixed list of labels, one spelling for each and one layout for every prompt. Then check the file with a program, not by eye.

A common mistake

Warning

Many people think more examples always help. A hundred clean pairs teach more than a thousand messy ones, because a model copies the faults too. Fix the file first, then add more.

Write the model helper, part 1

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// The token counts of a reply, or null when the reply carries none.' 'function count(data, name) {' '  return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the model helper, part 2

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"status \" + res.status);' '    const data = await res.json();' '    const text = data.choices[0].message.content;' '    return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"completion_tokens\"), recorded: false };' '  } catch (error) {' '    console.log(\"Recorded sample: no model answered (\" + error.message + \").\");' '    return { text: sample, inTokens: null, outTokens: null, recorded: true };' '  }' '}' 'module.exports = { ask };' >> model.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the line checker, part 1

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const LABELS = [\"billing\", \"delivery\", \"other\"];' 'const lines = [' '  JSON.stringify({ prompt: \"I was charged twice\", completion: \"billing\" }),' '  JSON.stringify({ prompt: \"Where is my parcel\", completion: \"delivery\" }),' '  JSON.stringify({ prompt: \"Love your shop\", completion: \"Other\" }),' '  JSON.stringify({ prompt: \"Wrong invoice\" }),' '  JSON.stringify({ prompt: \"\", completion: \"other\" }),' '  \"not json at all\",' '  JSON.stringify({ prompt: \"Box arrived crushed\", completion: \"delivery\" }),' '];' > adapt2.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the line checker, part 2

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function check(line) {' '  let row;' '  try { row = JSON.parse(line); } catch (e) { return \"not json\"; }' '  if (row === null || typeof row === \"string\" || typeof row === \"number\") return \"not an object\";' '  if (typeof row.prompt === \"undefined\" || typeof row.prompt === \"number\") return \"missing prompt\";' '  if (typeof row.completion === \"undefined\" || typeof row.completion === \"number\") return \"missing completion\";' '  if (row.prompt.trim() === \"\") return \"empty prompt\";' '  if (LABELS.includes(row.completion) === false) return \"unknown label\";' '  return \"\";' '}' >> adapt2.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the line checker, part 3

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  let ok = 0;' '  lines.forEach(function (line, i) {' '    const why = check(line);' '    if (why === \"\") ok = ok + 1;' '    else console.log(\"line \" + (i + 1) + \": \" + why);' '  });' '  console.log(ok + \" of \" + lines.length + \" lines ok\");' '  const q = \"In one sentence, why must every training example use the same label spelling?\";' '  const sample = \"A model learns each spelling as a different answer, so mixed spellings split what it learns.\";' '  const reply = await ask([{ role: \"user\", content: q }], sample, 40);' '  console.log(\"model: \" + reply.text);' '}' 'main();' >> adapt2.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Run the line checker

Command
docker exec -w /home/node/project agent node adapt2.js

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

What the checker found

The seven lines are invented. The checker reads each one alone and says the first fault it finds, so you fix one fault at a time.

The last line is one short model call. With no model it prints a Recorded sample.

About this lab and its source: AI coding agent lab: use opencode to explain and fix code, and check its work

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.