Projects/ReAct Research Agent/Part 3
The ReAct Loop
Parse Thought/Action/Action Input, run search and open, cap steps, and finish with an answer object including citations.
This part is the runtime: parse ReAct text → tool → observation → repeat. You will keep a fake model that emits ReAct strings so the parser is real. A production LLM will emit the same shape if you prompt it that way (or you will use native tool calls and skip the poetry — still keep the parser tests).
The ReAct grammar (strict)
Thought: <one line>
Action: search|open|finish
Action Input: <string or JSON>Rules you should enforce:
- All three labels present, in order.
Actionis an allowlisted name.- For
search, Action Input is a query string. - For
open, Action Input is a URL string. - For
finish, Action Input is JSON:{"answer": "...", "citations": ["..."], "cannot_answer": false}.
If the model uses Final Answer: instead of finish, reject it. One protocol. Your weather project already taught you that kindness in parsers becomes ambiguity in production.
Fake policy for the happy path
For Who founded Acme Robotics?:
- Thought: need to search.
- Action search
Acme Robotics founded. - Observation: hits include wiki/acme.
- Thought: open the wiki.
- Action open that URL.
- Thought: fact is in the body.
- Action finish with Ada Ortiz, 2014, citations
[that url].
Implement this as a state machine keyed off opened and searched flags, not as a recorded script only — so a second question can work with the same function.
Run to execute this in your browser. Nothing is sent to a server.
Step-by-step loop details
- Prompt the policy with the question, tool docs, and ReAct format reminder.
- Parse strictly. On failure, same parse-retry pattern as the weather agent.
- Execute
search/open/finish. - Append observation without the thought if you are token-poor; keep thoughts in a side trace.
- On
finish, do not trust citations yet — part 4 validates. - Hit
max_steps→ cannot-answer, not a guessed paragraph.
Max steps is a product feature
Research feels unbounded. It is not. Six tool calls is plenty for a 4-page web. If the model is still searching, it is stuck. Convert stuckness into abstention. Users prefer "I could not find this" to a confident wrong founder.
Thoughts in the transcript
If you send thoughts back into the next prompt, the model may imitate bad reasoning. Some teams strip thoughts from the next call and only keep actions+observations. Try both in part 5. For now, keep them for debugging in print.
Watch out:Never eval Action Input. JSON parse for finish; strings for search/open.Exercise
Change the fake model so it tries to finish after search without open. Let the loop still run. In part 4 you will reject that finish for missing opens. Observe the bad output today so you know why the check exists.
Check your understanding