Skip to content
Agents: The Core LoopReAct as history

Mid · 6 chapters

ReAct as history

Why native tool calling superseded hand-parsed Thought:/Action: strings

ReAct is useful history, but implementing its text protocol today adds parsing and recovery failures without a compensating capability.

Read these first

What you will know

  • Run a minimal ReAct trace that alternates Thought, Action, and Observation, then identify where parsing controls execution.

    A single malformed delimiter, quoted argument, or unexpected model reply can turn the transcript into an invalid or unsafe command.

  • Compare a hand-parsed Thought:/Action: loop with native tool calling using malformed, missing, and extra arguments.

    The useful comparison includes schema validation, provider behavior, retry handling, and whether invalid model output reaches the tool.

  • Recognise ReAct-style string parsing in existing code as legacy

    Search for delimiters, regular expressions, or branches on labels such as Thought and Action; these are protocol clues even when the code has different names.

  • Write a migration note that maps ReAct's Thought, Action, and Observation loop to messages, tool calls, and tool results.

    The control-flow idea survives, while private reasoning need not be exposed as a text field that downstream code must parse.

  • Decline to implement it

    A successful toy demo can conceal escaping bugs, ambiguous retries, prompt injection paths, and poor observability once tools have side effects.


Chapters

  1. 1

    ReAct's real content was a text protocol, not a prompt style

    Thought, Action, Observation: three labels inside a completion, a stop sequence set on the observation label, and a regular expression that pulls the action back out. Running a minimal trace makes the crucial point visible — the parser, not the provider, decides which command executes. Calling ReAct "telling the model to think before acting" stops at the prompt and never reaches the part that actually changed.

  2. 2

    A schema rejects a bad call before dispatch; a regex does not

    Feed both designs the same three abuses — a malformed argument, a missing required one, an extra one nobody declared — and the difference stops being a matter of taste. Native tool calling validates against a declared schema and refuses the call; the hand-parsed loop hands whatever it extracted to the tool and finds out afterwards. Retry behaviour splits the same way: one has a typed failure to report, the other has a string.

  3. 3

    A parsed transcript is a control channel attackers can write to

    When execution depends on splitting text, any text that reaches the transcript can steer it. A retrieved web page containing the observation label, a quoted argument with an embedded newline, a tool result echoing user input — each can append a second action the model never requested. Trace that path from a fetched page through to a shell command, and see why one more validation regex only patches the symptom while the transcript remains the control channel. Then triage it on a one-day budget: what you do immediately about the shell tool's reach and the sandbox it runs in, and what has to wait for the protocol replacement the rest of this topic sets out.

  4. 4

    A stop sequence on "Observation:" dates the code you inherited

    Codebases rarely announce their protocol. What gives it away is a stop sequence set to an observation label, a split on an action label, branches keyed to words like Thought, or a prompt template spelling the cycle out in prose — even when every identifier has been renamed to something local. Recognising the pattern tells you which failures to expect before you have read the rest of the file.

  5. 5

    Thought becomes content nothing branches on; Action becomes a call

    The migration is a mapping, not a rewrite of the idea: the alternation of reasoning, action and result survives intact as assistant turns, tool calls and tool results. What does not survive is Thought as a parsed field, and that is the point — preserving it as an audit trail turns self-reported model prose into a load-bearing artifact. Say instead what you would record as real evidence of why the agent acted.

  6. 6

    One remaining reason to hand-parse, and what it costs you

    A local model with no tool-calling API is the honest exception, and it is still not an argument for parsing prose: schema-constrained decoding gets a validated JSON call out of that same model, which is the middle option most teams never put on the table. Take the parser instead and you have adopted escaping, argument validation, retry semantics and an injection surface as your own code, plus traces carrying a blob of text where structure should be. This chapter is the argument you make when the demo works and someone proposes shipping it.

External resources