Root & Reason

Maze Escape

Three agents, a maze, a notebook each and a radio. I wanted to see whether they could help each other escape.

By

No. 10

Multi-agent Harnesses 2/3

Three black playing pieces are separated by blue maze walls, with a coral doorway marking the exit.

Background

Finding your way out of a maze is a long-solved CS problem. I wasn’t expecting an LLM to improve on the existing algorithms. At best, it would match them. At worst, it would get confused and go around in circles. Either way, it wouldn’t be fast.

But what if it had company? Could three agents share what they found, meet up and help each other out?

The setup

The three agents start in different cells of a maze generated from a seed, with no loops. The early runs used 10×10 or 12×12 mazes. Each maze has a few unique landmarks: a tree, a picture, a piano. Every agent has to visit the exit at least once, but they don’t have to be there together. Finding it doesn’t end an agent’s game; it can go back and help.

They can keep notes and talk over the radio. If they meet on the same cell, they can read each other’s notebooks and copy anything useful into their own.

I also wanted to compare what different models do. Across the experiments I used Claude Haiku, Sonnet and Opus 5.5, and through OpenRouter DeepSeek v4.1 Flash 1017, MiMo-V2.6-Pro and GLM 5.3 Flash, plus some local models, mainly Qwen3.5-9B.

Each turn, every agent is called once with no conversation history. It sees:

  • the four edges of the cell it’s standing on: open, interior_wall or outer_brick;
  • whether it’s on the exit;
  • the landmark on its current cell, if there is one;
  • its own notebook (8 KB, its only memory);
  • any radio messages sent by the others last turn;
  • the full notebooks of any teammates standing on the same cell.

It replies with one JSON action: a move (N, E, S, W or STAY), an optional notebook edit (at most 1 KB of new text per turn), and an optional radio message (280 bytes, then a five-turn cooldown).

All three move at once and the engine resolves the turn. The agents decide what to do but never what is true. Every turn is written to a SQLite journal with state hashes, so any game can be replayed and checked. A viewer lets me scrub through a game and see exactly what each agent saw.

The engine is written in Python, with Pydantic AI handling the model calls, and the viewer is a React app. Full code, configs and the running observations log are on GitHub.

The default rules were deliberately sparse. Agents got no coordinates and no map, only a distance sensor: the straight-line distance to the exit and to each teammate, in tenths of a cell, ignoring walls.

Spectator view of a 12×12 maze, with red, blue and green agent trails, named landmarks and agent B at the exit.

The spectator viewer shows the whole maze and each agent’s trail. This is a completed Sonnet run at turn 110; the agents themselves never see this map.

What changed along the way

The harness grew as the experiments went on. These are the changes that come up later:

  • Prompt versions. Numbered v3 to v12, each adding a sentence or two of rules. The tables below cover the ones that mattered.
  • Exact coordinates. The engine tells each agent its [x, y] position, counted from its own start cell. Each agent has its own frame, so the same cell has different coordinates for different agents.
  • The trail. A list of the agent’s positions over the last 30 turns.
  • No distance sensors. The straight-line distances are switched off.
  • Rolling-log notebook. A notebook that only takes appends and drops its oldest lines when full. I built it for the local model (Act 5).
  • Meeting alert. The observation names any teammate standing on the same cell (Act 3).

Act 1: they write the strategy down, and still fail

The first pilots used Claude Haiku 5.5. It was capable in all the small ways:

  • it kept a tidy, concise journal from the first move;
  • it logged a move only after the next turn confirmed it had succeeded;
  • it radioed useful facts;
  • it occasionally caught its own mistakes (“labels look swapped vs obs, trust obs”).

It didn’t finish mazes though. It fell into a few habits:

  • Following the distance reading downhill. It parked on cells where every move made the number go up. One agent sat 2.2 cells from the exit in a straight line and 39 moves away by path.
  • Camping on the exit as a “beacon”. Once one agent got out, it stood on the exit cell and radioed directions. But the other agents can’t see it, and the distance to a beacon on the exit is the same as the distance to the exit.
  • Looping. Revisiting the same few cells for dozens of turns.

So I did the obvious thing and started adding sentences to its prompt. Each version targeted one habit:

Version Added Adopted? Outcome
v3 “Straight-line distance ignores walls; a cell where every move raises it can be a dead end” Rarely quoted No change in greedy behaviour
v4 Note each junction and which exits you’ve tried; prefer an untried one 17 of 18 agents (1 of 18 before) More of the maze covered, same progress towards the exit
v5 Keep your own coordinates; estimate where the exit is Every agent (none before) Progress got worse: only 28% of their self-tracked positions were correct
Higher reasoning effort — — 2 team successes in 12 runs, both on the easiest seed; neither repeated at a bigger output cap

Haiku started logging junctions and keeping coordinates when asked, but the outcome barely moved. It could write the strategy down and quote it back without reliably carrying it out.

Across 78 Haiku runs on three seeds, covering every prompt and reasoning variant above, there were two team successes, both from the higher-reasoning runs: $7.59 spent, about $3.80 per success. Then I ran Claude Sonnet 5.5 with no prompt changes at all, and three of six teams succeeded.

Was it the radio?

Agents on the exit radioed confident, greedy advice (“avoid E”, “hold at 5.0”). I suspected the radio was spreading bad habits, so I turned it off across 18 runs.

Outcomes didn’t change. With radio on, the agents that were still searching just stood still for about 17 more turns, waiting for messages, and the games cost about 15% more.

Act 2: coordinates and frames

Act 1’s numbers come from batches. From here on, most results are single games: one run per prompt version or setting, usually on the same maze. They show what changed in that game, not how large the effect would be in general.

Exact coordinates gave Haiku its biggest single lift. It couldn’t track its own position (only 28% of its v5 positions were right), so the engine did it instead. There was still no map, and the coordinates count from the agent’s own start.

With them, Haiku did things Sonnet had been doing unprompted:

  • It estimated the exit’s exact position in its own frame from distance readings taken at known cells. Two agents in one game were both exactly right.
  • One agent worked out the offset to a teammate’s frame, using the exit as the shared reference: “B = A + (-4,-3)”, which was exactly right.

But each agent counts from its own start, so every coordinate on the radio was in someone else’s frame. Haiku understood this: 109 of its 110 radio messages with coordinates said whose frame they were in (“A-frame [-2,1]”, “offset unknown”). Qwen, given the same coordinates, never once did.

Knowing where the exit is turned out not to be the same as knowing how to get there. One agent knew the exit’s exact cell from turn 31 and spent the rest of the game looking for a corridor to it.

Turning off the distance sensors

Did the distance readings make the agents bounce back and forth? Partly. In eight earlier Haiku games, 62 of 104 reversals came straight after a move that raised the reading. With coordinates and the trail, though, bouncing was already low, and removing the sensors changed it very little. The real effect was that landmarks became the only reference two agents could both recognise, and they started using them as one.

Act 3: teaching cooperation, one step at a time

The objective only needs each agent to visit the exit once. So the agent that finds it first is free, and in theory the best thing it can do is go and help. Getting Haiku there took four more prompt versions, one game apiece, each patching the specific way the previous one failed:

Version Added What happened
v8 “Once you’ve reached the exit, standing on it helps nobody… head back and find a teammate” A left the exit, then went back to its own start and waited there for 37 turns, radioing the same route 13 times
v9 “Give positions relative to a landmark” The searchers asked for exactly that (“send your exit position relative to the piano”). The exit-finder gave it relative to its own start and waited there for 69 turns
v10 “Your own start is not a landmark: only you know where it is” A stopped camping (2 STAYs in 80 turns), visited four landmarks and radioed the exit exactly from each: “10 west and 3 south of the key”. Nobody else had visited those four landmarks
v11 “When you meet, copy what you lack from their notebook. Don’t wait for information; go and find the landmark” Waiting fell (197 STAYs → 72). Nobody copied anything from a teammate’s notebook

Under v10, Sonnet and Opus each got two of three agents out in 80 turns; Opus’s third agent was two moves from the exit when time ran out. So I gave Sonnet 120 turns instead. The agent that found the exit radioed it relative to the picture landmark. A second agent found the picture, walked to the exit, and radioed a step-by-step route from the picture:

“C reached exit. Exit is 5W,1N of PICTURE… From PICTURE go N3,W,S,W,S,E,S,W,W,N,N,W,S,W”

The third agent, B, reached the picture at turn 95, six turns after C’s message, followed the route, and the team met the objective at turn 110. No frame conversion was needed at any point: everything went through a landmark they could all stand on.

Sonnet radio messages showing C sharing a route from the picture landmark and B reporting that it has found the picture.

C sends the route from the picture at turn 89. B finds the picture at turn 95 and starts following it.

Sonnet did it again on a fresh 10×10 maze with the same setup, finishing at turn 88. The other hosted models didn’t. DeepSeek got two agents out, GLM 5.3 Flash explored 95% of the maze for $0.20 but got one out, and MiMo got none, for reasons covered in Act 6. Haiku got one agent out at turn 34, and then all three waited for coordinates nobody could convert: the 197 STAYs that v11 brought down.

Making the meeting explicit helped

Across about 2,200 agent-turns where two agents shared a cell, a check for 40-character passages copied from the teammate’s notebook found almost nothing: none for Sonnet, 8 incidental matches for Haiku, 1 for DeepSeek. The same held under v11, which explicitly told them to copy. That check only catches literal copying, though. An agent that summarised what it read in its own words and acted on it would also score zero, so the count alone doesn’t show that they ignored each other’s notes.

The notes themselves were clearer. At Haiku’s first v11 meeting, two agents stood on the same cell. Each wrote a note about its own next move and said nothing about the other. The only sign they had met was an extra block of text in the prompt, and both were mid-plan.

So I made the meeting explicit. The observation gained "colocated_with": ["B"], and a new rule said what to do with it. That changed two things at once, in a single game, so I can’t say how much each one contributed. Same maze, same seed, Haiku again:

“Tick26: at [-1,-3] with B. B’s notebook says B at [5,-3] (B frame), so offset is probably B = A + (6,0)”

“Offset: B frame = C frame + (4,-3), since B candle [2,3] = my [-2,6]. Exit in B frame is [8,4]”

All three meetings got used. The copy check still read zero, because Haiku summarised its teammate’s notebook rather than copying it, but the notes plainly use what it read. The team met the objective at turn 91, its fastest Haiku finish so far, for $0.13.

Haiku notebook entries showing C waiting at the candle, then recording a meeting with B and calculating B frame = C frame + (4,-3).

The meeting now appears in the notebook: C uses the candle’s coordinates in both frames to calculate the offset and express the exit in B’s frame. The earlier entries still show the beacon habit.

The other Haiku success on this 10×10 maze came from removing the problem altogether. With one shared frame, counted from A’s start, the exit is just “[2,5]” to everyone. That team succeeded too, on the last turn.

Act 4: bigger mazes, longer games

For the last Haiku runs, I gave it v11, the meeting alert, exact coordinates from its own start, the 30-position trail, no distance sensors and the rolling-log notebook. I ran one game at each of five sizes from 9×9 to 17×17, with 10 to 14 landmarks and a 600-turn limit.

“Fastest possible finish” assumes every agent already knows its shortest route to the exit. Since they move at the same time, it’s the longest of the three shortest paths from their starting cells. It depends on where they start, which is why the 17×17 has a lower minimum than the 13×13 or 15×15.

Maze Fastest possible finish Result Cost
9×9 turn 30 objective met, turn 116 $0.18
11×11 turn 41 objective met, turn 125 $0.18
13×13 turn 83 objective met, turn 238 $0.40
15×15 turn 95 time limit, no exits $1.05
17×17 turn 71 objective met, turn 236 $0.37

Four out of five, each in about three to four times that minimum. The successful games cost $0.18–0.40 each. Including the $1.05 failure, the batch cost $2.18: about $0.55 per team success, compared with $3.80 in the early batch. The maze sizes and turn limits changed too, so this isn’t a controlled comparison of the prompts alone.

The first agent out was usually quick: turn 27, 42 or 66 in three of the four, and turn 87 in the 11×11. Most of each game went on the relay: describing the exit relative to landmarks (12 to 47 such messages per game), and the others finding those landmarks and walking the route.

They synced their frames often in these games. Offsets read off directly at a meeting were usually right. Offsets they derived went wrong: inverting one, an agent wrote “A = B + (8,4)” when the truth was (8,-4), and chaining two offsets went off by one. The wrong values were then repeated for hundreds of turns.

The 15×15 failure sent me back for another look. All three agents started 72 to 95 moves from the exit. One stopped searching and waited for news, so I tried v12: keep exploring until someone finds the exit, and split the search. Radio about covered ground went from 12 messages to 65. Still no exits.

In that rerun, the exit’s whole region hung off a single cell on the south edge. Every agent passed it twice. Each time the note recorded the open side, and each time the agent carried on along the edge:

“N open, E open (came from), S brick, W open. Continuing W along row -6.”

Six passes, six misses. A plan to keep sweeping the edge overrode the rule about untried exits, and by the end of the game no notebook mentioned the cell at all.

The 15×15 maze at turn 600, with overlapping agent trails along the south edge, a red circle around the missed gateway, and no trails in the exit region on the left.

The v12 rerun at turn 600: six passes past the circled gateway, and the exit region on the left is still untouched.

Act 5: the local model

Alongside the hosted runs, I kept coming back to the question from my earlier posts: could a local model on an 8 GB GPU do this? I used Qwen3.5-9B, the Unsloth IQ4_XS build that had been my best local model for Models & Monsters, running on Ollama with reasoning off and an 8k context.

It walked into walls on a third of its decisions. The hosted models I was comparing it with did that at most 0.3% of the time. Its notebook edits kept missing too. The original notebook took splice edits, replacing the text between two character positions, and Qwen kept getting the positions wrong. That’s where the rolling log came from: a single append field, with the engine dropping the oldest lines when the notebook fills.

Temperature looked like a fix, and wasn’t. Replaying 60 recorded decisions, dropping the temperature to 0.2 cut wall moves from 24% to 9%. In a full game at 0.2, they rose to 56%. One agent moved west into the same wall for 71 turns while its note correctly said W=wall every time. The prompt barely changed between turns, so at low temperature the same reply came back each time. The replay had tested independent decisions; a real game feeds each reply into the next prompt.

Qwen’s notebook from turns 9–16, repeatedly recording W=wall while insisting that the path continues west.

At temperature 0.2, Qwen records the wall correctly but keeps writing “Path continues West.” The changing teammate distances don’t break the pattern.

What did help was a required one-line check before each move: restate the cell’s four edges, where you came from, which way you’ll go and why. By then I had switched from the Unsloth build to the official qwen3.5:9b build, for a reason covered in Act 6, and these comparisons are all on that build. Wall moves fell from 37.5% to 7.5%, for roughly double the output tokens. With the engine also listing the exits it had already used, Qwen picked an unused one 89% of the time. It still spent 57% of its turns on cells where every exit had been used, with no route back to unexplored ground. I could have had the engine point at the nearest unexplored exit. At that point the engine is solving the maze and Qwen is just pressing the buttons, so that’s where I stopped.

Act 6: when the harness is the problem

The other recurring theme was finding out, often late, that a failure I had blamed on a model was mine.

  • The trail wording. The rule said the trail lists “where you were on each of the last 30 turns”. Haiku read the last entry as its current cell, and wrote “trail lagged” or “position conflicts with trail” about 22 times a game. One agent trusted the trail over its exact position. Rewording it (“it ends with last turn’s cell, so it never includes where you are now”) took the doubts to zero. That game was then Haiku’s best exploration so far.
  • Output modes. Xiaomi’s MiMo answers with JSON as plain text instead of a tool call. In tool mode, that cost 99 turns of one game. In prompted mode, it sometimes copied "additionalProperties": false from the schema into its reply, which the strict parser rejects.
  • Key order. I tested whether Qwen walked into walls because it chose its move before writing anything about its cell. The Unsloth build writes JSON keys in alphabetical order whatever the schema says, so move always came first and the test was invalid until I switched to the official build.
  • The literal string “null”. Qwen wiped its own notebook five times by sending "null" as the new contents. In one game, 21 of its 28 radio messages were "null", and both teammates dutifully received “null”.
  • A Qwen fix leaking into everyone else’s games. The rolling-log notebook was built for Qwen and carried on into the hosted baseline. For Haiku it nearly doubled the cost of a game: given a plain append field, Haiku wrote a note every turn, so the notebook block changed every turn and the prompt cache after it was rewritten each time. Sonnet appends and never edits, so for it the format doesn’t matter until the notebook fills. DeepSeek, it turns out, compacts its own notes regularly (73 times in 9 games), and the rolling log takes that away.

None of these show up as “the harness is broken”. They show up as a model that seems confused, unreliable or bad at following instructions. My Models & Monsters post concluded that the engine has to be the single source of truth. The corollary here is that the engine, the schema and the prompt wording are all part of what you’re measuring, whether you mean them to be or not.

What it cost

Cost per success is the cost of every run in the row, failures included, divided by team successes.

Setup Team successes Batch cost per success
Haiku, early prompts, 12×12, 80 turns 2 of 78 ~$3.80
Sonnet, early prompts, 12×12, 80 turns 3 of 6 ~$3.00
Sonnet, v10, 12×12, 120 turns, rolling log and splice 1 of 2 $6.73
Sonnet, v10, 10×10, 120 turns 1 of 1 $1.97
Haiku, v11 + meeting alert, 10×10, 120 turns 1 of 1 $0.13
Haiku, v11 + meeting alert, 9×9–17×17, 600 turns 4 of 5 ~$0.55 ($2.18 total)
Qwen3.5-9B, local, every variant none —

The separate v12 rerun of the 15×15 failure cost $1.19 and also had no team success; it isn’t included in the five-game batch above. These are model API charges, not a full accounting of local hardware or electricity costs.

Below the first two rows, every result is a single game per maze and configuration. I’d repeat them before claiming how much each change helped. The temperature experiment in Act 5 is the warning: replaying individual decisions said it would help, and a full game said the opposite.

Conclusion

I wanted to see whether three agents could help each other out of a maze. They could. Sonnet’s route from the picture is probably the clearest example: one agent found the exit, another described the route, and the last one followed it.

I also spent a fair amount of time fixing problems I’d created. The trail wording, notebook format and output schema all changed how the agents played. I’ll be checking those earlier next time, before spending another batch trying to prompt my way out of a bug.