Right, and still no use
A bank chat feature gives a correct answer in one long paragraph of hard words. One customer reads slowly. Another uses a screen reader. A third has a poor signal and waits ten seconds.
A right answer that a person cannot use is still a failure. Access is part of the quality of a feature.
By the end of this lesson
Note
I can make an AI feature usable by people with different needs.
Key words
| Word | What it means | Example |
|---|---|---|
| Screen reader | software that reads a page out loud | |
| Plain language | short sentences and everyday words | |
| Jargon | a hard word only experts know | subsequently |
| Reading time | how long a reply takes to read | |
| Alternative | another way to do the same job |
The cells students fill in are left blank in this preview.
Plain language can be checked by rule
Plain language is not a feeling. You can set rules. No sentence over 12 words. No word from a short list of jargon. No reply over 40 words.
A rule can be run over every recorded reply. The ones that break it are the ones to rewrite.
Time, waits and other ways in
Work out the reading time: words times 60, divided by words a minute. A 40 word reply at 120 words a minute takes 20 seconds. Slow readers need longer.
Show a visible wait for a slow reply, and let people stop it. Offer an alternative to chat: a button, a form or a phone number.
A common mistake
Warning
Many people think access is only about disability, so it can wait. It is about everyone: a tired person, a new reader, a bad signal. Test the rules on every reply and offer another way in, not only a chat box.
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 plain language checker, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const RULES = { maxSentence: 12, maxWords: 40, jargon: [\"utilise\", \"subsequently\", \"parameter\"], wpm: 120 };' 'const REPLIES = [' ' { id: \"r1\", text: \"Press the green button. Then pick a date.\" },' ' { id: \"r2\", text: \"You should subsequently utilise the parameter menu to change the date before confirming.\" },' ' { id: \"r3\", text: \"Your order is on its way. It should arrive on Friday. If it does not, tell us and we will look into it.\" },' '];' > access.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the plain language checker, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function words(text) {' ' return text.split(\" \").filter(function (w) { return w.length > 0; });' '}' 'function sentences(text) {' ' return text.split(\".\").map(function (s) { return s.trim(); }).filter(function (s) { return s.length > 0; });' '}' >> access.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the plain language checker, part 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function problems(text) {' ' const out = [];' ' const longest = Math.max.apply(null, sentences(text).map(function (s) { return words(s).length; }));' ' if (longest > RULES.maxSentence) out.push(\"long sentence\");' ' if (words(text).length > RULES.maxWords) out.push(\"too long\");' ' const hard = words(text.toLowerCase()).filter(function (w) { return RULES.jargon.includes(w); });' ' if (hard.length > 0) out.push(\"hard words\");' ' return out;' '}' >> access.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the plain language checker, part 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' for (const r of REPLIES) {' ' const secs = Math.ceil((words(r.text).length * 60) / RULES.wpm);' ' console.log(r.id + \" \" + words(r.text).length + \" words, \" + secs + \" s, \" + JSON.stringify(problems(r.text)));' ' }' ' const msg = \"Rewrite in plain words: You should subsequently utilise the parameter menu to change the date.\";' ' const reply = await ask([{ role: \"user\", content: msg }], \"Next, use the menu to change the date.\", 30);' ' console.log(\"plain: \" + reply.text);' '}' 'main();' >> access.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run the plain language checker
docker exec -w /home/node/project agent node access.jsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
One possible output
| Line | What it shows |
|---|---|
| r1 8 words, 4 s, [] | Short and plain, so no problems |
| r2 13 words, 7 s, ["long sentence","hard words"] | One long sentence with two hard words |
| r3 23 words, 12 s, [] | Under every limit, so it passes |
The cells students fill in are left blank in this preview.