Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

"never": ["publish anything", "email anyone", "change the reading list"]

That line from the v1 scope is the one I care about most, and the scout from Chapter 3 breaks it: it still has a publish_summary action in its toolbox, and it picks one paper per day instead of judging every paper.

This chapter builds the scout the scope describes, and nothing more. Most of it is plain. The part worth slowing down for is how v1 is kept inside its scope. I could write “never publish anything” in the prompt, and a good model would usually listen. For a rule whose whole job is to limit damage, usually isn’t enough.

Chapter 5 of 12 · Continuous Development

Where we left off: We have a written scope and 24 labeled papers, but no scout that matches the scope.

What we fix now: Build v1 exactly to scope, with its limits enforced by code, and record a trace of everything it does.

By the end, you can:

  • build a scout whose allowed actions come straight from the scope, so forbidden actions simply don’t exist

  • record a trace for every paper, showing each step the scout took and why

  • explain why looking at the scout’s suggestions can’t tell you how good it is

The fix: limits in code, not in the prompt

There are two ways to stop v1 from publishing:

  1. Ask nicely. Put “never publish anything” in the prompt.

  2. Remove the ability. Don’t give v1 a publish tool at all, and refuse any action that isn’t on the scope’s list.

The first depends on the model following an instruction every single time. The second doesn’t depend on the model at all: if v1 proposes publishing, there’s nothing to run, so the code refuses. I still tell the model which actions exist, because that helps it choose well. The guarantee comes from the code.

It’s the same split as Chapter 2, one level up: the model proposes, the code decides what actually happens.

Now v1 itself, in three parts:

  • Actions come from the scope. TOOLS only has functions for actions the scope allows. publish_summary isn’t defined anywhere, and execute refuses anything not in scope_v1["allowed_actions"].

  • One paper at a time. judge_paper runs the loop for a single paper until the model proposes suggest or skip, with a confidence between 0 and 1.

  • Everything is recorded. Each step goes into a trace, and each finished paper goes into log. I log more than I need right now: adding a field later doesn’t help with the runs that already happened.

v1 still needs a model, so the page uses a hand-written stand-in again. This one reads from a table with one entry per paper: its confidence, its reason, and whether it opens the full text. I wrote the table for this course and left realistic mistakes in it on purpose, so Chapter 6 has something real to measure. It is not recorded from a real model; the Colab bonus runs a real one.

That’s v1 on Monday: four suggestions with confidences and reasons, and a last line that sums up the “suggest” setting. The scout suggests; I decide.

If something in Monday’s list already looks off to you, hold on to it. First, the forbidden action getting refused.

On P19 the stand-in tried publish_summary. However sure it was, the action isn’t in the scope, so execute refused it, the refusal went into the trace, and the scout went on to a normal verdict.

Every decision leaves a trace like this. Here’s how to read one:

Traces look like bookkeeping now. In Chapter 8 they’re the main tool for working out why the scout gets things wrong. Try show_trace("P03"): what did the scout look at before deciding?

You can judge the three papers on the list, and maybe product search (P11) already looks wrong. But the question was about papers v1 missed, and those aren’t on the list. A skipped paper is invisible in the output. Counting misses means going through every Tuesday paper, including the five it skipped, and checking each against the right answer. That’s what Chapter 4’s labels are for.

Bonus (Colab only): a real model as v1

Open this notebook in Colab to run v1 with a real model:

Open In Colab

Get a free key at aistudio.google.com/apikey (no credit card needed), then paste it into API_KEY below. Treat it like a password: fine to paste into your own private Colab copy while experimenting, never commit it to a public repository or notebook.

A real model will behave differently from the stand-in: other confidences, maybe other mistakes, and it may open full texts more often. Keep its log. Chapter 6’s evaluation works the same way on a real model’s verdicts.

Quick check

Before moving on, answer for yourself (no peeking ahead):

  1. v1 has no publish_summary tool, and execute refuses anything outside the scope. Why is that stronger than writing “never publish” in the prompt?

Check your reasoning ▸

A prompt instruction depends on the model following it every time, and models usually do but not always. Removing the tool doesn’t depend on the model at all: there is nothing to run, so a publish request can only ever be refused. A strong answer adds that you’d still describe the allowed actions in the prompt, because that helps the model choose well, but the guarantee lives in code.

  1. When the stand-in tried to publish on P19, what happened next, and why is it useful that the refusal went into the trace?

Check your reasoning ▸

The code refused, the refusal was fed back as the result of that step, and the scout went on to give a normal verdict. Recording it means you can later count how often v1 tries things outside its scope, which is useful evidence when deciding whether it’s ready for more autonomy.

  1. Why does judge_paper store every verdict in log, including skips?

Check your reasoning ▸

Because skips are where missed papers hide. If you only kept suggestions, you could never count how many relevant papers v1 passed over. The log is what makes a full comparison against the labels possible in the next chapter.

  1. Run show_trace("P03") and show_trace("P09"). What did the scout look at before deciding, and what does that have in common with what you needed when labeling them in Chapter 4?

Check your reasoning ▸

In both cases it only read the abstract and never opened the full text. In Chapter 4, P09 was one of the papers you needed the full text to label correctly, and the full text of P03 makes clear it’s about human memory. A good answer spots the pattern: the scout skips the step that settles exactly these cases. Hold on to it; Chapter 8 is about finding patterns like this systematically.

  1. Tuesday’s output showed three suggestions. Why can’t you tell from it how many relevant papers v1 missed?

Check your reasoning ▸

Because skipped papers don’t appear in the output at all. A miss looks exactly like a correct skip until you compare it against the right answer. Only checking every verdict, suggestions and skips, against the labels tells you how many relevant papers were missed.


You can now: build a scout whose limits come from its scope and are enforced in code, record a trace for every decision, and explain why reading its suggestions can’t tell you how good it is.

But one problem remains: “the suggestions look fine” is an impression, not a measurement, and it can’t see missed papers at all. The next chapter compares every one of v1’s verdicts against the labels, turns the result into numbers, and uses them to decide whether v1 is ready to ship.

Next → Chapter 6: Design Evals and Deploy