Prompting and parsing for a simple ReAct-style agent

Thought / Action / Observation separation

You’re building a loop where the model can think, call tools, see results, and finally answer.
Separating Thought, Action, and Observation keeps that loop predictable and debuggable.

  • Thought: model’s internal reasoning, in natural language.
  • Action: a single structured command: which tool to call, with which arguments.
  • Observation: the tool’s raw result, fed back into the model as context.

Why this separation matters

If the model’s reasoning is mixed with commands, your code can’t safely tell:

  • what is just “thinking out loud”
  • what is the actual tool call you must execute

By forcing the model into a pattern like:

  1. Think about the next step (Thought:).
  2. Either call one tool (Action:) or produce the final answer (Final Answer:).
  3. You then run the tool and feed back the result as Observation:.

…the control flow is clear and your loop stays in charge.

You’re not letting the model execute code directly; you’re letting it propose actions that you inspect, parse, and run.

Concrete example

Let’s say you have tools:

  • search — calls a web search API.
  • calculator — evaluates math expressions.

A single turn in your loop might look like this (model output):

textThought: I should first search for the current population of Berlin. Action: search Action Input: population of Berlin in 2024

Your code:

  1. Reads this.
  2. Extracts tool_name = "search", tool_input = "population of Berlin in 2024".
  3. Runs your search tool.
  4. Gets an Observation, e.g.:
textObservation: The population of Berlin in 2024 is estimated to be about 3.8 million people.
  1. Appends this observation to the conversation.
  2. Calls the model again to continue.

Later, the model might output:

textThought: I now know the answer. Final Answer: The population of Berlin in 2024 is about 3.8 million people.

Your loop sees Final Answer:, sends that back to the user, and stops.

Keep the Action part extremely regular (same labels, one tool, clear argument block) so your parser stays simple and safe.

1 / 5