Thinking Step by Step
The ReAct pattern.
In twenty twenty two, a research paper made a simple suggestion. Ask a model to write down its reasoning, before each action. Thought, action, observation, and repeat. They called it React. This simple idea became the foundation of modern agents.

- 2022: a simple suggestion
- Write the reasoning down
- Thought, action, observation
Thought, action, observation
Here's a stand-in model, whose replies include a thought. Read them as they appear. Run it. Thought, I need the item. Action, get order. Observed, it's a helmet. Thought, do we have any more? Action, check stock. Observed, none. Thought, so offer a notification. You can follow its reasoning, as you would with a person. That's great for debugging, as you can see exactly where it went wrong.
from scripted import Scripted
from shop import get_order, check_stock
model = Scripted([
("The customer wants a swap. First I need the item.", "get_order", {"order_id": "C09"}),
("It's a Helmet. Now I need to know if we've got more.", "check_stock", {"item": "Helmet"}),
("None are in stock, so I should offer to tell them when it's back.", None, None),
])
TOOLS = {"get_order": get_order, "check_stock": check_stock}
for step in range(1, 5):
thought, tool, args = model.ask([])
print(f"Thought {step}: {thought}")
if tool is None:
break
print(f"Action: {tool} {args}")
print(f"Observed: {str(TOOLS[tool](**args))[:60]}")Thought 1: The customer wants a swap. First I need the item.
Action: get_order {'order_id': 'C09'}
Observed: {'item': 'Helmet', 'price': 95, 'status': 'delivered', 'days
Thought 2: It's a Helmet. Now I need to know if we've got more.
Action: check_stock {'item': 'Helmet'}
Observed: {'item': 'Helmet', 'in_stock': 0}
Thought 3: None are in stock, so I should offer to tell them when it's back.Also uses shop.py
ORDERS = {
"A17": {"item": "Carbon wheelset", "price": 640, "status": "delivered", "days_ago": 12},
"B42": {"item": "Chain", "price": 28, "status": "shipped", "days_ago": 2},
"C09": {"item": "Helmet", "price": 95, "status": "delivered", "days_ago": 45},
}
STOCK = {"Carbon wheelset": 3, "Chain": 14, "Helmet": 0}
REFUNDS = []
def get_order(order_id: str) -> dict:
"""Look up one order by its ID, such as A17."""
return ORDERS.get(order_id, {"error": f"No order {order_id}"})
def check_stock(item: str) -> dict:
"""Return how many of an item are in stock."""
return {"item": item, "in_stock": STOCK.get(item, 0)}
def refund(order_id: str, amount: float) -> dict:
"""Refund money to the customer for an order."""
REFUNDS.append((order_id, amount))
return {"refunded": amount, "order_id": order_id}Also uses scripted.py
class Scripted:
"""A stand-in model that replays a fixed list of replies."""
def __init__(self, replies):
self.replies = list(replies)
self.calls = 0
def ask(self, messages):
self.calls += 1
return self.replies.pop(0)Why thinking first helps
Writing the reasoning out helps in three ways. The model plans before acting, so it makes fewer careless calls. It has a place to absorb what the last tool said. And you get a trace that you can read. Modern models do this internally, using thinking modes. But the idea is the same. Reason, then act.
When to ask for reasoning
Should you always turn it on? Not necessarily. Thinking costs tokens and time. For simple lookups, skip it. For hard decisions, it's well worth it. And remember, a model's explanation isn't always what really drove its choice, so check behaviour, not just reasoning.
| key | value |
|---|---|
| Simple lookup | skip it |
| Multi-step task | use it |
| Hard decisions | use more of it |
| Explanations | aren't proof |
Summary
Reason, act, and observe. React works one step at a time. But sometimes you need to see the whole road first. Next, planning.
Key takeaways- Reason before each action
- Traces make debugging easier
- Explanations aren't proof