Σ 0:00▸ 0:00

Module 4 · Planning, Context & Multi-agent Systems · scripted

Executable Plans: When the Plan Leaves the Conversation

30 minoutcomes: agent-architectures, context-engineering

Overview

A plan written into the conversation steers the trajectory — but it still lives at conversation prices: re-read on every turn, metered by the word. This lesson takes the final step: when a task is well known and the plan can be fully specified, the plan can leave the conversation entirely — as code. The agent reads just enough of the world to learn its shape, writes an executable script, and an ordinary computer runs it: the intermediate results stay in the script's variables, the conversation stops growing with the size of the data it processes, and work becomes possible that could never fit through the window. Then we price what full planning costs — a frozen plan is deaf — and build the hybrid: plan, execute, and stand by, with the conversation resuming on surprise. The lesson ends on the continuum this whole arc has been climbing: from fully ad hoc to a library of proven scripts.

A fully specified plan can leave the conversation

Suppose the task is not a one-off mystery but a known, recurring job: every Monday, the lab compiles last week's instrument logs into an error report and mails it out. The steps are the same every week. If the plan can be specified completely — every step, every input, every output — then something remarkable becomes possible: the plan no longer needs the LLM to execute it. But for that to work, the plan cannot be prose. Prose needs a reader. The plan must be written in a language a computer can interpret — a script, real code — and then an ordinary computer runs it: no loop, no deliberation, no conversation at all. This is one of the most common ways working agents operate today: they write bash or Python scripts, hand them to the computer, and let the code do the work.

Picture the two shapes side by side. First, the loop executing the plan step by step — every intermediate result comes back into the conversation, and the LLM must be prompted again, on a longer conversation, for every single step:

The LLM executes every step — every result returns

That triangle spins once per step — fourteen files means fourteen trips around it, each one slower than the last because the conversation it re-reads has grown, and each one billed in full. Now the planned shape — the LLM speaks once, and the intermediate results never leave the computer:

The plan is executable — the computer hides the middle

The bill and the clock

Here is that first, single invocation — the one conversation the whole job needs:

Example 1 · The planner writes the script, once
user

You have the following tools available for use:


  1. list_files( dir )
  2. read_file( path )
  3. write_file( path, content )
  4. schedule( script, when )
  5. think( thought )

Task: Every Monday we compile last week's instrument logs into an error summary and email it to the lab list. Here is the logs directory — set this up to run without you.

assistant
list_files(dir = "logs/last-week")
user
ctd-01.log · ctd-02.log · weather-mast.log · spectro-07.log …
(14 files, plain text, one entry per line)
assistant
read_file(path = "logs/last-week/ctd-01.log")
user
[2026-09-07 03:12:44 INFO depth=41.2m ok
2026-09-07 03:13:44 ERROR conductivity sensor timeout …]
assistant
think(thought = "Plain-text lines, one entry each, with
INFO/WARN/ERROR levels. I'll write the plan as a script the
computer can run on its own.")
user
OK.
assistant
write_file(path = "weekly_report.py", content = "[the script below]")
user
OK. weekly_report.py written.
assistant
schedule(script = "weekly_report.py", when = "Mondays 07:00")
user
Scheduled.
✎ noteThe judgment happened here — reading two samples and deciding what a correct report means. It happened once, in one short conversation, and its output is not a description of the work: it IS the work, in executable form. Now look at what it wrote.

weekly_report.py — the plan, as code the computer runs

# Written by the agent, executed by the computer. No LLM below
# this line.

files = list_dir("logs/last-week")

matches = []
for f in files:
    for line in read_lines(f):
        if "ERROR" in line or "WARN" in line:
            matches.append((instrument_of(f), line))

counts  = tally(matches, by="instrument")
summary = render(REPORT_TEMPLATE, counts, matches)

write("reports/week-" + week_number() + ".md", summary)
send_email("lab-list", "Weekly instrument report", summary)

From now on, Monday costs nothing. Compare the bills — and remember that in the loop, every turn re-sends the whole conversation:

The same Monday, two ways

AS AN AGENT LOOPAS A SCRIPT
14 files × (read, scan, decidepython weekly_report.py —
"what next?") — each turnrun by the computer, top
re-reading a conversationto bottom.
that now CONTAINS the log
excerpts it already processed.LLM invocations: 0 conversation re-read: never
Dozens of turns, each re-billingtokens billed: none
a growing conversation — to make
choices that were never choices.Same report, every week, at the price of electricity.

And the deepest win is one we can state straight out of the context window lesson. When the computer executes the code, the LLM never sees the intermediate results. They live in the script's variables, flowing from line to line inside the computer:

What the LLM never sees

inside the script:
read_lines(...) 38,412 log lines, file by file
matches 217 lines
counts 14 numbers
summary one table
inside the conversation:
— nothing. Nobody re-read anything.

Recall the search result that ate the window: thousands of words of results, permanently in the conversation, competing with the goal for space and making every turn a harder read. Here, the same bulk never enters a conversation at all. Planning is not only thinking ahead — it is routing the heavy intermediate work around the conversation entirely, letting the computer absorb what would have flooded it.

There is a predictability win too: the script does the same thing every run, and you can read it before it ever executes — code can be reviewed, tested, and versioned. Better still, if a library of proven scripts already exists, the LLM's job shrinks to picking one, the most predictable move of all.

The principle

A plan that is fully specified no longer needs its author.
Written as CODE, it leaves the conversation and executes:
token-free, predictable, reviewable before it runs — and the
intermediate results stay in the computer's variables,
never touching the context window.

The conversation must not grow with the data

This move is not reserved for scheduled jobs. It becomes decisive in the middle of ordinary, one-off tasks — the moment the data is bigger than the conversation should be. Make it concrete. The task: thirty CSV files, each holding a quarter's transaction table for one subsidiary; compute each subsidiary's net margin and combine the thirty results into one ranked summary. Watch both shapes attempt it.

Example 2 · Thirty files through the conversation
user

You have the following tools available for use:


  1. list_files( dir )
  2. read_file( path )
  3. think( thought )

Task: Each CSV in data/ is one subsidiary's quarterly transaction table. Compute each subsidiary's net margin, then combine them into one ranked summary.

assistant
list_files(dir = "data")
user
subsidiary-01.csv … subsidiary-30.csv (30 files)
assistant
read_file(path = "data/subsidiary-01.csv")
user
[2,400 rows of raw table flood in: date, account, revenue_nok,
cost_nok, … — roughly 40,000 tokens of CSV]
assistant
think(thought = "Summing revenue and cost across the 2,400 rows…
subsidiary-01's net margin is 7.1%. Now the next file.")
user
OK.
assistant
read_file(path = "data/subsidiary-02.csv")
user
[another 2,400 rows — another ~40,000 tokens of table]
✎ noteTwo files in, and the conversation already drags ~80,000 tokens of raw table behind it — re-sent and re-read on every later turn. Twenty-eight files to go. Each iteration is slower and pricier than the last; somewhere around the window's edge, the early tables start falling off the wall — and the agent is doing thousands of row-level additions in its head, which is exactly the kind of arithmetic the ferry example showed it slipping on.
Example 3 · One file to learn the shape, then code
user

The same task — with run_code( script ) added to the tools.

assistant
read_file(path = "data/subsidiary-01.csv")
user
[first 20 rows: date, account, revenue_nok, cost_nok, …]
assistant
think(thought = "The structure is clear, and the calculation is
identical for all thirty files — so I'll write it once, as code,
and let the computer do the reading.")
user
OK.
assistant
run_code(script = "
  results = []
  for f in list_dir('data'):
      rows = read_csv(f)
      margin = (sum(rows.revenue_nok) - sum(rows.cost_nok))
               / sum(rows.revenue_nok)
      results.append((subsidiary_of(f), margin))
  sort results by margin, descending
  return results
")
user
subsidiary-17  11.8%
subsidiary-04  10.2%
… (30 lines) …
subsidiary-22  −2.4%
assistant

Here is the ranked summary across all thirty subsidiaries — subsidiary-17 leads at 11.8%; subsidiary-22 is the only one operating at a loss.

✎ noteCount what the conversation holds: a 20-row sample, a ten-line script, and a 30-line answer. The 72,000 rows were read — by the computer, inside run_code, exactly once, with exact arithmetic. And this conversation would be the SAME SIZE if the files held two thousand rows each or two million.

That last sentence is the deep advantage, and it deserves to be stated as a law:

The scaling law

Processed IN the conversation, the conversation grows in
proportion to the DATA: every row becomes tokens, re-read and
re-billed on every later turn.

Processed BY emitted code, the conversation grows only with the
DESCRIPTION of the task: one sample, one script, one result —
no matter how large the data becomes.

Follow the law to its consequence. The in-conversation version doesn't merely cost more — past some data size it becomes impossible: the conversation has a limit, and thirty full tables will not fit inside it at any price. The emitted-code version never approaches the limit, because the data never enters the conversation at all. Emitting executable plans is how an agent processes a world that is larger than its window — and working agents do this constantly, writing small Python or bash scripts mid-task so the conversation only ever has to hold the answer.

What full planning costs

But remember what the agent loop was for: reports flow back into the conversation, and the next choice is conditioned on them. A script has no next choice. It is deaf — the plan was frozen into code before the world spoke. Then, one Monday:

The Monday the world changed

07:00 — weekly_report.py runs, as always. But the new spectrometer was installed on Thursday, and spectro-07.log is now JSON:

{"level": "error", "msg": "calibration drift", …}

the loop scans it for lines containing ERROR or WARN … 0 matches.

The report goes out on time:
"spectro-07 — 0 errors. All clear."← confidently wrong

No line of the script malfunctioned. Every statement executed exactly as written. The plan was frozen before the world spoke — and nothing in the code can hear it now.

This is the trade, stated plainly: everything the script saved — the deliberation, the re-reading, the tokens — was saved by removing the very thing that lets an agent notice a surprise.

The hybrid: plan, execute, stand by

We do not have to choose between the two. Give the script an ear: build checks into the code — assertions about what the world should look like — and when a check fails, don't improvise and don't silently continue. Raise, and let the surprise do what everything in this course does: arrive as a message, into a conversation.

weekly_report.py, with an ear

# added inside the scanning loop:
if size(f) > 0 and file_matches(f) == 0:
    raise OddFile(f)   # halt before the report;
                       # deliver the exception to the agent
Example 4 · The exception resumes the conversation
user

[exception from weekly_report.py at 07:00] OddFile: logs/last-week/spectro-07.log is 1.4 MB but matched 0 error lines. Run halted before the report.

assistant
read_file(path = "logs/last-week/spectro-07.log")
user
[JSON records, one per line: {"level": "error", "msg":
"calibration drift", …} — 41 entries at level error or warn]
assistant

The new spectrometer logs JSON, not plain text — the scan's ERROR pattern can't see its errors. For this week: I'll extract its 41 error entries myself, fold them into the summary, and send the report. And before next Monday, weekly_report.py needs a JSON rule for this instrument — the fix belongs in the code, not in my hands.

✎ noteJudgment at the ends, machinery in the middle. Fifty-one quiet Mondays ran at code prices; the surprising one ran at conversation prices — and only the surprising one. The exception entered the conversation as what everything enters a conversation as: a message, read by a mind that can adapt.

The hybrid shape

PLAN     a short conversation writes the script — judgment, once
EXECUTE  the computer runs the code — no conversation, no tokens
CHECK    the script tests its own outcome, and raises on surprise
RESUME   the exception enters a conversation — ad hoc judgment,
         exactly where it is needed, and nowhere else

Later in this module, the Planner–Executor archetype grows this shape into a full architecture — plans as reviewable artifacts, approval before execution, and the discipline of answering every exception with a new plan rather than a quiet intervention.

The continuum

The planning continuum

fully ad hocevery action chosen mid-conversation, after the world speaks — maximum adaptation, maximum tokens
plan firstthe plan written into the conversation, and the actions follow it; revision stays one message away — the trajectory is steered, still alive
plan as codea conversation writes the plan as an executable script; the computer runs it; the conversation stands by, resuming on surprise — judgment only at the ends
existing scriptno planning at run time: pick a proven script from the library — cheapest, most predictable, least adaptive

Each rung down: fewer tokens, more predictability. Each rung up: more room for the world to surprise you — and an agent still standing there to notice.

The question is never "should agents plan?" It is: how much of the conversation do we write before the world speaks — and where does the conversation stand while the computer works? The rest of this module lives on this spectrum: when a task needs no agent at all, how code can steer at the forks a plan can't foresee, and how the planner–executor turns the hybrid into a discipline.

Place the judgment
  • List everything weekly_report.py never paid for that the loop version would have. Where, physically, did the 38,412 log lines live — and where would they have lived in the loop?
  • In the thirty-file task, say exactly what the conversation contains when Example 3 finishes. Now put Example 2 at file 15: what does ITS conversation contain, and what has it cost so far? Which of the two answers changes if every file doubles in size?
  • The JSON surprise: at which rung of the continuum would it have been cheapest to absorb — and what would that rung have cost during the fifty-one quiet weeks?
Your turn

Take a recurring task from your research. (1) Place it on the continuum and defend the placement. (2) Write the script its plan would become — real, runnable pseudo-code in Python or bash — and mark the variables holding intermediate results that never enter the conversation. (3) Name the surprise most likely to break it, write the check that raises, and draft the first message of the conversation that resumes.