The last post ended by saying the next one would put the heater model next to an LLM. This series has drawn that line twice already. The first post said that if you ask a chatbot what happens when you turn the heater on, "you'll get a fluent, plausible story about the room warming up", and the 101 post put it in a table: for "what if?", an LLM "narrates a plausible story" and a world model "rolls the trajectory forward".

I meant to repeat that comparison with the room from 104 as the example. Before writing it down a third time I thought I had better check it, so I gave Claude Opus 5.5 the same room, the same readings and the same questions I had asked the thirteen-weight model, and scored the answers against the simulator. The table in 101 did not survive the experiment. The reasons it failed say more about where each kind of model belongs than the table did.

Everything below ran on 27 October 2026 against claude-opus-5-5 at its default settings, three runs per question. Every answer, with its token count and cost, is in heater-llm-runs.csv.

The door, asked of Claude

The test room is the one from 104: 22 degrees inside, minus four outside, heater on throughout. At step 10 someone opens a door that no log records, and it stays open until step 20. In 104, at step 13, the latent model had watched the room fall three steps in a row, its private field read 0.81, and it forecast five steps ahead almost exactly.

Claude got the same fourteen readings as a CSV and nothing else.

step,room_c,outside_c,heater_until_next_step 0,22.00,-4,on 1,21.39,-4,on ... 11,13.14,-4,on 12,10.10,-4,on 13,7.76,-4, 

The question was to forecast steps 14 to 18 with the heater on, and to end with the five numbers as JSON. Here is what came back, next to the real room and the 104 model.

step real room 104 model Claude, run 2 Claude, run 3
14 6.03 6.52 6.00 6.02
15 4.76 5.41 4.67 4.69
16 3.67 4.41 3.66 3.69
17 3.10 3.51 2.90 2.92
18 2.65 2.71 2.32 2.34
RMSE 0.53 0.18 0.16

Two runs out of three beat my model. Claude did it by spotting that the drops after step 10 shrink by a constant ratio and fitting a decay curve to them:

The behaviour changed sharply, with drops of 4.05, then 3.04, then 2.34 °C. Each drop is about 0.75–0.77 times the previous one. That is classic exponential decay toward a new, much colder equilibrium.

It put the new equilibrium at about half a degree. The true figure, from the constants hidden in the simulator, is 0.8. Then it guessed at a cause the log never mentions: "something changed around step 10. Possibly the heater failed or weakened, or a large heat loss opened up, such as a window or door."

The 104 model has a field that tracks the door and no name for it. Claude has a name for it and no field. That difference comes up again further down.

Run 1 matched the others for four steps and then forecast 1.2 degrees for step 18. It had read my "the heater stays on from step 13 to step 17" as the heater going off for the last step, said so, and gave 2.25 as the answer if it stayed on. That one is my wording, and I have left it in the scores.

With the log pasted in

The 104 model learned from 4,800 logged steps, so the fair comparison gives Claude the same 4,800 steps. As CSV that is 77,142 input tokens per question. Two of the three answers came back with the room's constants written out. Run 1 put the closed-room leak at 0.067 of the gap per step and the heater at 1.2 degrees, then found the door:

During these events the loss rate rises to about 0.25 × (room − outside), while the heater still adds about 1.2 °C.

realRoom uses 0.07, 1.2 and 0.25. Run 3 got the same three numbers and cited two episodes from the log where the door had opened ("episode 3 from 17.80 °C and episode 22 from 17.58 °C"). I checked both against the simulator's private trace, and the door was open in each.

run 1 run 2 run 3
forecast for steps 14 to 18 6.0, 4.75, 3.85, 3.25, 2.9 6.0, 5.0, 4.6, 4.6, 4.9 6.0, 4.7, 3.8, 3.1, 2.7
RMSE against the real room 0.15 1.28 0.07

Run 3 is the best forecast anything has made of this room in the series. All three runs noticed that door events in the log end after somewhere between two and eighteen steps, and allowed for this one ending. Runs 1 and 3 nudged their last steps up a little. Run 2 leaned on it hard and blended the two cases into a forecast that levels off at 4.6 and turns back up. Its reasoning was sound, but the door stayed open. It was answering a question about a distribution with one number, which is a problem the heater model has too, and one this series comes back to.

This is what system identification looks like when the system is a textbook one. Newton's law of cooling is in every physics course Claude read, so the form of the model came for free, and the log only had to supply three numbers. My thirteen weights had to discover the form as well as the numbers, from the same rows, with no prior at all.

The same question as a decision

A forecast is the easy half. The reason to have a world model is to choose what to do, so the second question was a small planning problem, posed at step 5 of the same room while the door was still shut and the room was at 19.15:

I want the room at or above 12 °C at every reading from step 6 to step 25, with the heater on for as few steps as possible. Which of steps 5 to 24 should the heater be on for, and what is the lowest the room gets? And could the heater hold the room at 16 °C or above for those 20 steps if it ran the whole time?

Because I wrote the room, I know the answer. With the true constants the fewest is fifteen: coast for five steps while the room falls to 12.11, then heat for the rest. And no, the heater cannot hold 16, because at minus four outside a closed room with the heater on settles at 13.1.

This time there were three versions of Claude. One got the six readings and no tools. One got the readings and the 4,800-row log. The third got the readings and the 104 model, wrapped as a tool.

const simTool: Anthropic.Tool = { name: "simulate_room", description: "Run the room's fitted world model forward from the current reading (step 5) under a heater schedule, without touching the real heater. Returns the predicted reading at each following step.", input_schema: { type: "object", properties: { heater: { type: "array", items: { type: "string", enum: ["on", "off"] }, description: "Heater setting from step 5 onwards, one entry per step. Entry i is the setting from step 5+i to step 6+i.", }, }, required: ["heater"], additionalProperties: false, }, strict: true, }; 

Behind the tool is the 104 model. It observes the room up to step 5, so its private field is set, and then runs act blind under whatever schedule Claude asks for, which is the rollout from 101 with learned weights.

function simulate(from: number, heater: boolean[]): number[] { const m = new WorldModel(latent.w); m.reset(); for (let i = 0; i < from; i++) m.observe(truth[i], script[i], truth[i + 1]); let s = truth[from]; const out: number[] = []; for (const on of heater) { s = m.act(s, on ? "turnHeaterOn" : "turnHeaterOff"); out.push(+s.roomTemp.toFixed(2)); } return out; } 

The loop around it is the standard tool-use loop: send the question, run any simulate_room calls Claude makes, send the predictions back, and repeat until it answers. Each plan was then run in the real room 200 times, with fresh draughts each time and the door shut.

heater steps lowest, as Claude said real lowest, mean of 200 stays at 12 or above
true optimum 15 12.08 125 of 200
Claude, readings only (all three runs) 16 12.9 13.00 200 of 200
Claude with the log, run 1 15 12.11 12.08 125 of 200
Claude with the log, runs 2 and 3 16 12.9, 12.7 13.00 200 of 200
Claude with the 104 model, runs 2 and 3 13 12.04 10.61 0 of 200
Claude with the 104 model, run 1 13 12.27 10.95 0 of 200

Every run answered the 16 degree question correctly. On the schedule, Claude with six readings fitted a leak of 0.076 and a heater of 1.30, worked out that the heater settles the room at about 13.2, and chose to coast for four steps and heat for sixteen, leaving a margin of about 0.9 degrees, "which allows for some error in the fitted model". Claude with the log found the true optimum once, to the hundredth of a degree, and noted that its margin was 0.1 and that switching on a step earlier would be safer. The other two log runs took that safer option.

Claude with my world model as a tool did worst. It made seven to nine calls, reasoned carefully about each result, and settled on thirteen heater steps with a predicted low of 12.04. In the real room that plan drops to 10.6, every time.

Why the tool made it worse

Claude's search was fine. I tried all 1,048,576 schedules against the 104 model, which took about two seconds, and the fewest heater steps it allows is thirteen as well. Claude found a thirteen-step plan in seven to nine calls. The model's optimum is wrong.

One run's second call was the heater off for all twenty steps. The model said the room would end at 5.25 degrees. The real room ends at about 1.4. Every outside temperature in the training log was between 2 and 12 degrees. At minus four, with the heater off for long stretches, the model is well outside anything it has seen, and its weights do not extrapolate the way physics does. The 104 post noted that the latent model's room and outside weights were no longer equal and opposite, and that the gradient had found "a different split that fits the log just as well". That split fits the log, and it does not fit minus four. The five-step forecasts in 104 were close because five steps is not long enough for the error to build up, and twenty steps is.

Heater off for twenty steps at minus four outside, door shut

The heater left off for twenty steps from the step 5 reading, door shut. The 104 model agrees with the room for about five steps and then cools too slowly, ending almost four degrees warm. The green line is the model from the next section.

Download the data (CSV)

So the world model gave confident, wrong numbers, and Claude believed them, as it should have done with nothing to say otherwise. The tool returned a list of temperatures with no sign that the last fifteen of them were guesswork. The plan that is cheapest on a flawed model is the one that runs closest to the model's flaws, because that is where the model is most generous. Any search does this, whether the searcher is an LLM or a loop over every schedule there is.

Claude on its own did better because it brought a prior. A leak proportional to the temperature difference, plus a constant from the heater, is the right shape for this room, and Claude knew that shape before it saw a reading. My thirteen weights had no prior to bring, and away from their data that cost them.

Giving the model Claude's shape

The first thing I tried was more data. Refitted on logs that include days down to minus six, the thirteen weights swung the other way and predicted a heater-off room colder than the outside air, which no room can be. The problem was the shape of the model, and more rows of the same shape do not fix a shape.

Claude had already described the right one. The runs that saw the log, between them, spelled it out: a leak proportional to the gap between inside and outside, a constant from the heater, and a second, bigger leak that comes and goes. The 104 model can have that shape without giving up anything that made it a world model. The private field stays, observe stays, and the backwards walk through each episode stays. Only act changes.

act(state: State, action: Action): State { const w = this.w, heaterOn = heaterAfter(state, action); const gap = state.outsideTemp - state.roomTemp; const change = (w.leak + w.doorLeak * this.#field) * gap + (heaterOn ? w.heat : 0); return { roomTemp: state.roomTemp + change, outsideTemp: state.outsideTemp, heaterOn }; } 

That is three prediction weights instead of seven, nine weights in all, trained on the same 4,800 rows for the same three thousand epochs, in under half a second. It has no bias term. The physics has none, and with one in, the fit traded the bias off against the leak and ended up more than five degrees too cold after twenty steps with the heater off. I made that choice after seeing round one, so weigh the result knowing it was not decided in advance.

What came out reads like the inside of realRoom. With the field at its door-shut level the leak works out at 0.072, against a true 0.07. With the door open the field goes to 0.97 and the leak to 0.245, against a true 0.25. The heater is 1.16, against 1.2. The field became the door again, and this time the weights around it are the room's own constants.

door forecast RMSE heater off for twenty steps, step 25 (real 1.42) fewest heater steps on the model that plan in the real room
104 model 0.53 5.25 13 lowest 10.83, safe in 0 of 200
physics-shaped model 0.10 1.15 16 lowest 13.00, safe in 200 of 200

How far off each five-step forecast of the open-door room was

Every forecast of steps 14 to 18 with the door open, as the root mean square error against the real room. Claude's runs spread from 0.07 to 1.28. Each model gives one answer, every time.

Download the data (CSV)

The door forecast of 0.10 beats five of Claude's six forecasts, and the model gives the same answer every time it is asked. Then I gave Claude this model as the tool instead, with the same question. All three runs settled on sixteen heater steps after three to five calls, and the plan held the room above 12 in all 200 draws. Claude also explained the plan better than I would have:

One step of heating is worth most at the end, because its warmth has less time to leak away before step 25. An off step is cheapest early on.

Claude's plan to keep the room at or above 12 degrees, predicted against real

The plan Claude chose with each model as its tool. Dashed is what the model said would happen, solid is the real room, averaged over 200 runs with the door shut. The strip underneath shows which steps had the heater on. The 104 model promised the room would stay above 12 and it fell to 10.6; the physics-shaped model was slightly pessimistic, and the room stayed above 13.

Download the data (CSV)

Sixteen is one more than the true minimum. The model is about six tenths of a degree pessimistic at the bottom of the plan, so it asks for an extra step. The fifteen-step plan leaves a tenth of a degree to spare and dips below 12 in 75 of the 200 draws, so I would take the extra step anyway, though the model did not choose it for that reason.

What each answer cost

With the right shape, the world model matches Claude on accuracy. On cost, it is a long way ahead.

input tokens output tokens seconds dollars at list price
forecast, readings only 270 1,410 to 1,833 14 to 19 0.03 to 0.04
forecast, with the log 77,142 2,266 to 2,290 25 to 26 0.35
plan, readings only 297 2,423 to 3,863 26 to 42 0.05 to 0.08
plan, with the log 77,169 5,235 to 8,713 59 to 97 0.41 to 0.48
plan, with the 104 model as a tool 15,005 to 16,777 3,164 to 3,995 43 to 53 0.13 to 0.14
plan, with the physics-shaped model as a tool 4,184 to 10,744 2,148 to 2,912 34 to 36 0.06 to 0.10
either model, all 1,048,576 schedules about 2 0

A twenty-step rollout of either model takes about two microseconds. A forecast from Claude with the log takes 25 seconds and 35 cents. That is a factor of more than ten million in time, and it matters more than it looks. Planning properly means rolling the model forward thousands of times per decision, and a thermostat makes a decision every step. Monitoring means running the model alongside every device, all the time.

Then there is the size of the log. The 4,800 rows of one simulated room were 77,142 tokens. A real device logging once a minute produces 525,600 rows a year, about 8.4 million tokens at the same rate. That is eight times Claude's context window for a year of one device, before anyone mentions a second device or a second sensor channel. Nine weights are a summary of the log that never needs re-reading. Claude re-reads the whole log on every question, which is the 102 post's point about the LLM equation made literal: the condition on the right of the bar is the entire history, and every call pays for all of it.

The usual objection is that you cannot train an LLM on your telemetry. That turned out to be beside the point, because Claude did not need training to fit this room. It needed the log pasted in, and pasting is where the trouble is. Every question sends the customer's telemetry to someone else's servers, and the thing answering lives behind an API that, as this June showed, can stop answering overnight. A heater controller that cannot work out what to do next without a call across the Atlantic is a heater controller with a single point of failure in another jurisdiction.

Where each one belongs

Round one went to Claude, and round two went to the world model with Claude's help. What the two have is different sets of properties, and the system you want puts each kind of model where its properties fit.

The world model belongs in the loop. It is small, fixed, local and fast enough to be called a million times per decision, on the device, with nothing leaving the building. Its weakness is that it knows only its data and the shape it was given, and it does not know where either stops being true.

The LLM belongs at the edges, and the experiment showed three edges where it was the better tool.

  • Intent. "At or above 12 at every reading, as few heater steps as possible" is how a person states a goal. Claude turned it into a constraint and an objective without being asked to, and its answer to the 16 degree question was correct every time. A planner needs that translation done before it can start, and a person should not have to write the objective function themselves.
  • Names. The latent model's field went to 0.9 when the door opened, and nothing in the model can say what 0.9 means. Claude, shown the same readings, said "a window or door". A monitor that tells a person "the field has risen" is a monitor nobody acts on, and one that says "this looks like a window has been left open" gets the window closed.
  • Structure. The form Claude proposed from the log (a leak proportional to the difference, a constant heater gain and a second, larger leak that comes and goes) turned out to be a better model of this room than my thirteen weights. Round two is the small version of letting an LLM propose the shape and then fitting and running that shape locally on the real data, which puts its prior and the world model's speed in the same system.

A diagram. On the left, an LLM box with speech bubbles for Goal and Explanation. An arrow leads into a dashed box labelled on the device, every step, which holds a Planner box feeding a World model box circled by an arrow labelled thousands of rollouts. The world model exchanges heater on/off and readings with a radiator on the right. A dashed purple arrow labelled proposes the shape runs from the LLM to the world model.

The LLM takes the goal in and the explanation out, and proposes the model's shape once. Everything inside the dashed box runs on the device, every step, with no network.

So the shape is an LLM on the outside, a world model on the inside, and a planner between them. A person states a goal. The LLM turns it into an objective. The planner rolls the world model forward as many times as it needs. The LLM explains the plan, says when the model's predictions and the room have parted company, and puts a name on the reason. Only the middle of that has to run every step, and none of the middle needs a network connection.

Round one showed the condition the whole arrangement depends on. A world model handed to an LLM as a tool gets believed. If it does not say how sure it is, the LLM cannot tell its good predictions from its bad ones, and a better LLM will only exploit the bad ones more efficiently. The physics-shaped model was right this time, and it would have been believed just as readily if it had been wrong.

The 101 post said that an LLM asked "what if?" narrates a plausible story. For a room whose physics is in the textbooks, that is no longer true. Given the readings, Claude fitted a model and rolled it forward in its reasoning, and did it well. The part of 101 that held up is the part this post kept running into: Claude keeps no state between calls, so every answer starts again from the readings and pays for the whole log.

The next post builds the physics-shaped model properly, into the playground next to the 104 one, and puts a planner on top of it. Claude's three to five tool calls were a search done by hand, and the planner runs thousands of rollouts per decision, with a plan button to go with it. After that comes the warning the tool here never gave, which is a model that can say where its own predictions stop being worth trusting. Every answer from this post, with its token count and cost, is in heater-llm-runs.csv.