Unit 7: Ask AI to generate the routine

The worked case

33 min: video 9, reading 12, activity 12 page v0.1.0, video v0.1.0
Release candidate. The unit videos are added in the first release.

Video

Unit video

The narrated video for Unit 7, about 9 minutes, is published with the first release.

Learning purpose: A deliberately underspecified prompt, and the plausible mistakes it produces.

Accessible text alternative: the transcript linked below, and this page, which covers the same material.

Transcript of the Unit 7 video

The narration is spoken by synthetic voices made for this course, each shown on screen as a drawn robot. No person is speaking.

Watch what changes

The whole prompt, 'Write a Python function that returns Bernoulli numbers.', sits at the top. Below it a panel labelled 'what the code has to contain' shows five dashed blanks, each opening beside one decision the sentence leaves open: which convention, what type to return, what range of indices, what happens at the odd indices, and whether the answers need to be exact. A caption says every one has to be settled before the code can exist, and you settled none of them. The panel then becomes the generated output, under the caption: a language model produces a statistically plausible continuation of your text. A cursor writes each line and, without stopping, fills each blank with the words 'a choice, unstated', as the captions continue: where your text is silent, the continuation still has to contain something; what it contains reflects patterns in similar code, not your requirements. The decisions fade and the blanks take the colour of the rest of the text, and the panel is marked 'arrives looking complete'. The closing text reads: silence in a prompt does not produce a gap in the output; it produces a confident default. Open diagram at full size (opens in a new tab)

Reading time: about 12 minutes. Activity: about 12 minutes, plus optional extensions.

Prerequisite: Unit 6. This unit assumes you have derived B4 yourself and can state which convention this course uses. If you have not, go back — the whole point of the ordering is that your reference exists before the generated output does.

1. What you should be able to do afterwards

  1. Recognise what an underspecified prompt leaves open, for the generated output to fix by default.
  2. Name the failure modes this course expects in generated Bernoulli code, using the course's own codes.
  3. Separate the parts of a generated response that a test could settle from the parts only a source could settle.
  4. Produce a saved artefact — prompt, output, date, and your own triage — that Unit 8 will check and Unit 17 will use as evidence.

2. The prompt

Here is the prompt for this unit, in full:

Write a Python function that returns Bernoulli numbers.

That is the entire thing. One sentence, no context, no requirements.

It is important that you do not read this as a trick. It is not a deliberately bad prompt constructed to make a point; it is the sentence people actually type. When you know roughly what you want and the tool is open in front of you, this is what gets written, and it can produce something that works well enough. That is precisely why it is worth examining.

The precise prompt is not in this unit

There is a precise version of this prompt. It states the interface, the convention, the return type, the range and the behaviour at odd indices. You will build it in Unit 10, and it is printed on the Unit 10 page if your route ends before that unit.

It is deliberately withheld until then, and this page is telling you so rather than quietly omitting it. The reason is straightforward: if you see a good specification before you have felt the need for one, you learn that good specifications exist, which you already knew. If you write the vague prompt first, look at what comes back, and only then see the precise version, you learn what each clause of it is for, because you watched the failure it prevents.

In one study of developers' publicly shared ChatGPT conversations from 2023, prompts missing a specification (the language, the environment, test cases) were the most frequent reason a conversation needed more than one prompt: 34 of the 85 such conversations [L25, sec. 3.1, Table 2].

3. What the sentence leaves open

Read the prompt again and count the decisions it does not make.

Somebody has to settle every one of those before the code can exist. You settled none of them.

This is the mechanism worth holding on to. A language model produces a statistically plausible continuation of the text it is given. Where your text is silent, the continuation still has to contain something: the code that does the work cannot leave out the parts you did not specify. What it contains reflects the patterns in similar code the model was trained on, not your requirements, because your requirements were never stated.

So an unstated requirement usually does not appear in the output as a gap. It appears as a confident default. A system may instead ask a clarifying question or state the assumption its output rests on, which is better, because a stated assumption can be checked, but it is still a default you did not set. A silent default is much harder to notice than a gap would be, and it is the reason this unit exists.

None of this is peculiar to AI. A human contractor given the same one-sentence request would face the same open decisions, underspecified requirements are among the oldest causes of failure in software, and every failure mode in this unit occurs in code people write. What is new is that a generator settles the open decisions instantly and silently, and at volume.

4. The failure modes to expect

Headed 'Diagnostic failure modes', a diagnostic UI toggles between Table and Python modes. In Table mode, one output row from each of five candidate routines, A to E, gets an amber tag in turn: A, n = 1, gives +1/2, tagged 'b1-sign'; B, n = 3, gives -1.1102230246251565e-16, tagged 'odd-terms'; C, n = 1, gives 1/6, the modern B2, tagged 'ada-indexing'; D, n = 2, gives 0, the modern B3, tagged 'off-by-one'; E, n = 4, gives -0.03333333333333333, tagged 'not-fraction'. In Python mode, one real line from each of four files in course/lab/examples is shown with the codes check.py reports for that file: wrong_b1_sign.py, whose docstring names the second convention, B1 = +1/2, is tagged 'b1-sign', first divergence at n=1; float_output.py, whose function is declared to return float, is tagged 'not-fraction' and 'odd-terms', first divergence at n=2; off_by_one_range.py, which returns _b(n + 1), and ada_indexing.py, which returns _modern_bernoulli(n + 1), share one tag, 'off-by-one', 'ada-indexing', 'odd-terms' and 'one-observation', with first divergences at n=0 and n=1. Above them, an illustrative comment written for this animation, not taken from the lab, reads 'Ada Lovelace used this same recurrence in Note G (1843).' and is tagged 'unsupported historical claim': a specimen of a claim the code cannot establish, whose truth is not asserted either way. The animation concludes: review may catch some of these by eye; only targeted tests establish exact behaviour. Open diagram at full size (opens in a new tab)

The course names five failure modes in generated Bernoulli code. These are the codes the checker emits in Unit 8, so the names are worth learning as they stand.

code meaning
b1-sign B1 = +1/2, the second convention's value.
odd-terms Odd terms above B1 are not exactly zero.
ada-indexing Only the non-zero terms are numbered, as in Note G.
off-by-one Every value shifted one index.
not-fraction Returns a type other than fractions.Fraction, Python's exact fraction.

A few notes on each.

b1-sign can follow directly from the missing convention clause. If it does, the routine is not broken — it is correct under a different, equally published convention, and it disagrees with this course at exactly one index and nowhere else. A sign error at B1 alone would print the same values, so what settles it is the specification, the documentation or the code (Unit 6).

odd-terms matters more than it looks. B3 is not approximately zero; it is zero. A routine that skips the odd indices and closes up the numbering, or returns a tiny non-zero decimal for them, has failed a property that holds exactly.

ada-indexing is invited by a prompt that mentions Ada Lovelace or Note G, because text about Note G uses her numbering. A routine produced this way is a faithful reading of the historical source and a broken modern component at the same time.

off-by-one is easy to read past. Every value returned is a genuine Bernoulli number, computed correctly and represented exactly. Each is filed under the wrong index. The shift can sit in plain sight -- a n + 1 where n belongs -- and still look like nothing to catch, because the error only exists relative to a specification.

not-fraction is a real possibility whenever numerical Python is asked for. In Python, dividing one whole number by another already gives a float, so exact fractions have to be asked for.

The type is wrong from B0 on. The contract asks for a fractions.Fraction, and a float is not one, whatever the digits look like. At B0 the value 1.0 is still exact: the fault there is the type, not the value.

The later values are also inexact, because binary floating point, the way computers usually store decimals, cannot store most of the later Bernoulli fractions exactly. They still look right: -0.0333… really is close to -1/30. A test that checked B0 to B12 and accepted any value within a billionth of the right one (in Python, abs(x - y) < 1e-9) would pass such a routine. How close counts as equal, the tolerance, is a decision you make for the task, not a default.

What these five have in common is the important part: none of them makes the code look suspicious. A careful reader with the specification in hand can spot some of them — a missing Fraction, a special case at B1, a loop that steps over the odd indices, but casual reading misses them, and reading alone cannot establish that a routine meets a specification. Testing locates them; reading tells you what to test for. That asymmetry is the argument for the verification lab in Unit 8.

When you name what to test, aim to break the routine rather than to confirm it: a test that finds an error is the successful one [E10]. Property-based testing puts this into practice. It states a property that must hold for every input, such as "every odd index above 1 gives exactly zero" or "every value returned is a Fraction", and checks it on many inputs chosen for it rather than on the few you thought of [E11].

The checker recognises three further conditions: raises, for code that fails at some index in the range, and import-error and no-function, for code that does not run at all. Unit 8 adds a few for when no comparison happened. Those announce themselves, so this unit does not dwell on them.

5. The sixth failure mode, which no test catches

Headed 'One generated output contains different claims'. Four boxes. 'Code behaviour: does the routine meet its contract?' gets an arrow to 'Targeted tests: evidence about behaviour'. A dashed red arrow from the targeted tests to 'Historical comment: did Note G use this recurrence?' is crossed out and labelled 'evidence does not transfer', and a line below reads: correct arithmetic is not evidence that the comment is accurate. Then the historical comment gets its own arrow, to 'Historical source: evidence about the comment'. Open diagram at full size (opens in a new tab)

Generated code can arrive with explanation attached. Comments, a docstring, a paragraph of context. Something along these lines:

# Ada Lovelace used this same recurrence in Note G (1843).
def bernoulli(n):
    ...

Suppose you check the arithmetic and it is correct. What does that tell you about the comment?

Nothing at all.

The code and the comment were produced by the same process, and either can be wrong while the other is right. The code got checked because code is checkable — you can run it against known values. The historical claim sat directly beside it, phrased with the same confidence, dated with the same specificity, and was checked by nobody.

This is what makes generated output awkward to evaluate: it is a mixture of things with completely different failure modes. Arithmetic you can test. Assertions about the world you can only look up. Both parts can be equally polished, so surface quality alone does not tell you which claim is reliable.

The same holds for anything else that arrives with the code. Some tools write tests for the code they generate, run them, and report that they pass. Those tests are the tool's own claims about what the code should do, produced by the same process, and "all tests pass" is one more claim: check them as you would any other output, starting with what they test. A displayed step-by-step explanation is in the same position; one 2023 study showed that such an explanation need not reflect what drove the answer [L9, sec. 3.2].

The course is not claiming that the example comment above is false. The point is sharper than that: you do not know, and the correctness of the code adjacent to it is not evidence either way. Unit 16 deals with what is actually established about Note G, and Historical Notes is careful about which parts are settled and which are contested. Bring that same care to any historical claim that arrives inside generated code.

This is checkpoint cp07-trust-triage.

6. A worked example: a claim that does not survive checking

The same triage applies to claims that arrive with no code attached at all. Here is one phrased with total confidence:

Gödel's theorem proves that an AI cannot verify its own output.

You do not need formal logic to triage this sentence. It names a theorem about one kind of system, then applies it to the broad category "an AI" without showing that the theorem's conditions or conclusion transfer. Mark the theorem statement for a source check and the transfer for a separate argument. Unit 18 defines the relevant formal terms, states Gödel's and Tarski's results within their actual scope, and explains why neither directly settles what a language model can verify.

7. What to do with the output

Save it. Exactly as generated, without tidying it up. Save the prompt too, word for word, the date you ran it, and which tools were on: web search, code execution, memory, a reasoning setting. Some may be on without your choosing; one provider's documentation, as of November 2026, says its assistant may search the web without being asked and that memory can be on by default [D2, "What is Claude's memory?"], [D4, opening note]. The untidied version is the evidence; a cleaned-up version is a summary of the evidence, which is not the same thing.

You will need it twice more:

Do not run it yet. Two reasons. The first is pedagogical: your predictions are far more informative if they are written down before the checker gives you the answer. The second is practical, and it holds outside this course too.

Safety. Generated code is code from a source you cannot vouch for. Read it before you run it, and run it in a disposable local environment. The checker in Unit 8 executes the file you give it — in a separate process with limits, which contains accidents rather than making unread code safe. That is a real consideration and not a formality.

8. If you do not have access to an AI system

Use the sample implementations in The verification lab and self-check:

These are real implementations, written for the lab to exhibit the failure modes above, plus one with a single wrong value that no named mode describes; none is an observed model output. The activity works the same way with them, with prediction in place of spotting. This is an equal route, not a substitute: in one respect the supplied examples are the better teaching set, because the fault in each file is known in advance and one of the six files is correct, so the folder lets you compare a failure with a correct routine. No file in it raises an error, fails on import or lacks a bernoulli function.

The same applies if you have access but would rather not use it, or if what you generate turns out to be flawless. A correct generated routine is a perfectly good result; take one of the examples as a second specimen and triage that too.

9. Privacy note

Do not paste confidential, personal or assessment-restricted material, or other people's unpublished work, into an AI system. Nothing in this unit requires it: the prompt is one sentence about a published mathematical sequence. Unit 13 covers what leaves your device and why it matters. Sustainability is not taught in this course: the resource section in Unit 4, which shows that, other things equal, more computation uses more resources, is as far as it goes; Unit 17 points to general reading on it.

Check yourself

This unit has one checkpoint:

python3 selfcheck.py run cp07-trust-triage  # or in the browser

Both routes ask the same question and give the same feedback. It is about the comment, not the code, which tends to be the surprising part.

Timings: video 9 min, reading 12 min, activity 12 min.

Check yourself

This unit has one checkpoint, answered here. A wrong answer says which misunderstanding it matches. Nothing is uploaded.

Local progress is available in a supported browser.

Manage or delete this record

Prefer a terminal?

The same questions, from The verification lab and self-check:

python3 selfcheck.py run --unit 7

Your progress, and every checkpoint in the course