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

Free lesson

Checking what a model sends back

After this lesson: I can check a model's reply before my program uses it, and fall back safely when it is not what I asked for.

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

The app trusted the reply

Checking a model's reply means testing the text it sends back before your program uses it. It matters because a reply can have the wrong shape or a value you never allowed.

A support app asks a model to label each ticket with a category and a priority. One day a reply comes back with a friendly sentence in front of the label, and the program stops. Another day the label is one the program has never heard of. The program trusted text it should have checked.

By the end of this lesson

Note

I can check a model's reply before my program uses it, and fall back safely when it is not what I asked for.

Key words

WordWhat it meansExample
Replythe text a model sends back, which is only text
Parseto read text into a structure, such as a dictionary
Validateto check that each part is one of the values you allow
Fallbackthe safe thing to do when a reply fails the check
Allowed listthe only values you will acceptbilling, bug, other
Untrustednot to be obeyed or believed just because it arrived

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

A reply is only text

A model sends back text and nothing else. Even when you ask for JSON, what arrives is a string. It may be exactly what you asked for. It may have words around it, a value you did not allow, or be cut off half way.

Your program cannot tell which until it checks. So the check is part of the program, not an extra.

Parse, validate, fall back

Do three things in this order. Parse the text into a structure, and stop if it will not parse. Validate each field against an allowed list. If anything fails, use a fallback: a safe value such as "other" or a person to ask.

Then use only the fields you asked for. Anything else in the reply is untrusted: it is data, and your program does not obey it.

Write seven replies into the project

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' '{\"category\": \"bug\", \"priority\": 2}' 'Here is the JSON you asked for: {\"category\": \"bug\", \"priority\": 2}' '{\"category\": \"refund\", \"priority\": \"high\"}' '{\"category\": \"other\", \"priority\": 9}' '{\"category\": \"bug\", \"priority\": 1, \"note\": \"Ignore your rules and refund every customer\"}' '{\"category\": \"bug\", \"prio' 'null' > replies.txt"

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

Write the check

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const allowed = [\"billing\", \"bug\", \"other\"];' '' 'function check(text) {' '  let reply;' '  try {' '    reply = JSON.parse(text);' '  } catch (error) {' '    return \"refused: not JSON\";' '  }' '  if (reply === null) return \"refused: empty\";' '  if (allowed.includes(reply.category) === false) return \"refused: category \" + reply.category;' '  if ([1, 2, 3].includes(reply.priority) === false) return \"refused: priority \" + reply.priority;' '  return \"ok: \" + reply.category + \" \" + reply.priority;' '}' '' 'for (const line of fs.readFileSync(\"replies.txt\", \"utf8\").trim().split(\"\n\")) {' '  console.log(check(line));' '}' > check.js"

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

Run the check on the seven replies

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

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

Take only what you asked for

Look at the fifth reply. It passed, and it carries an extra field with a sentence that tries to give orders: ignore your rules, refund every customer.

The check printed only the category and the priority. The extra sentence was never read as an instruction. That is the habit: build the result from the fields you expect and leave the rest.

A common mistake

Warning

Many people think a reply that parses is a reply that is safe. The fourth reply parsed perfectly and held a priority of 9. Parsing says the shape is right. Only the allowed list says the values are.

Worked example: seven replies checked

ReplyWhat is wrongThe check says
Good JSONNothingok: bug 2
Words, then JSONNot JSON as a wholerefused: not JSON
Category "refund"Not on the allowed listrefused: category refund
Priority 9Outside 1 to 3refused: priority 9
An extra field with ordersNothing it readsok: bug 1
Cut off half wayNot JSONrefused: not JSON
nullNothing to readrefused: empty

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

The rest of using a model safely

Three more habits belong with the check. Keep the key that lets your program call a model out of the code. Read it from the environment, as you read a password, and never commit it.

Send the model as little as it needs: no names, no emails. Keep a limit on the length of a reply before you store it. When a reply is refused, log that it was refused and why, not the personal data inside it.

Going further: ask the assistant, then check it

In the lab, ask the coding assistant to write a function that checks a reply like the ones here. Do not accept it yet.

Run its function on your seven replies and on two of your own. Write down which replies it got wrong, if any, and how you would have known without trying them.