Four parts make a clear prompt
A prompt is the text you send to a language model. The model reads only that text, so it cannot guess what you meant to add.
"Tell me about the match" gets a different answer every time. A prompt with a role, a task, limits and an output form gets one you can use.
By the end of this lesson
Note
I can turn a vague request into a prompt with a role, a task, limits and an output form.
Key words
| Word | What it means |
|---|---|
| Prompt | the text you send to a model |
| Role | who the model should act as |
| Task | the one job to do now |
| Limits | what the answer must not go beyond |
| Output form | the shape the answer must take |
| Checklist | a list of things to tick off |
The cells students fill in are left blank in this preview.
What are the four parts of a prompt?
The role says who the model acts as. The task says the one job. The limits say what it must not go beyond, such as length or sources. The output form says the shape of the answer, such as one sentence or a list.
Put each on its own line with a label. A label makes the prompt easy to read and easy to check.
The same request, badly and well
| Part | Vague | Clear |
|---|---|---|
| Role | (nothing) | You are a sports club reporter. |
| Task | Tell me about the match. | Write a news line about the result below. |
| Limits | (nothing) | One sentence. Use only the facts given. |
| Output form | (nothing) | Plain text, no title. |
The cells students fill in are left blank in this preview.
Write the model helper, part 1
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
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 prompt checker, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const vague = \"Tell me about the match.\";' 'const good = [' ' \"Role: You are a sports club reporter.\",' ' \"Task: Write a news line about the match result below.\",' ' \"Limits: One sentence. Use only the facts given.\",' ' \"Form: Plain text, no title.\",' ' \"Facts: Riverside 3, Hillside 1. Two goals by Sam.\",' '].join(\"\n\");' > say1.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the prompt checker, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const PARTS = [\"Role\", \"Task\", \"Limits\", \"Form\"];' 'function missing(prompt) {' ' const lines = prompt.split(\"\n\");' ' const gone = [];' ' for (const part of PARTS) {' ' const has = lines.some(function (l) {' ' return l.startsWith(part + \": \") && l.length > part.length + 2;' ' });' ' if (has === false) gone.push(part);' ' }' ' return gone;' '}' >> say1.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the prompt checker, part 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' for (const p of [vague, good]) {' ' const gone = missing(p);' ' console.log(\"score \" + (4 - gone.length) + \" of 4, missing: \" + (gone.join(\", \") || \"nothing\"));' ' }' ' const sample = \"Riverside beat Hillside 3 to 1, with two goals from Sam.\";' ' const reply = await ask([{ role: \"user\", content: good }], sample, 60);' ' console.log(\"model: \" + reply.text);' '}' 'main();' >> say1.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run and score two prompts
docker exec -w /home/node/project agent node say1.jsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
What the Run prints
The first two lines score the vague prompt and the written one. The vague one scores 0 of 4 and lists all four parts as missing. The written one scores 4 of 4.
The last line is one short reply from the lab's model. With no model it prints a Recorded sample first. The words in a reply can differ each run, and that is normal.
A common mistake
Warning
Many people think a longer prompt is a better prompt. It is not. A prompt is better when each part is there and says one thing. Add a line only when it limits the answer or shapes it.