The Limits of Linear Reasoning
Chain-of-thought prompting asks the model to reason in a straight line: step 1, step 2, step 3, conclusion. For most problems, this is sufficient.
But some problems do not have a single correct reasoning path. They require exploring multiple approaches, backing up when an approach fails, and selecting the best path from several tried. Linear reasoning cannot do this — it commits to a path and follows it to completion, even if a better path exists.
The techniques in this chapter address this limitation.
---
Tree of Thoughts (ToT)
Tree of Thoughts extends chain-of-thought from a linear path to a branching tree. Instead of generating one reasoning path, the model generates multiple candidate reasoning steps at each decision point, evaluates them, and selects the most promising ones to continue.
ToT Architecture
code
Problem
├── Approach A
│ ├── Step A1 -> [EVALUATE: good, continue]
│ │ ├── Step A1a -> [EVALUATE: dead end, backtrack]
│ │ └── Step A1b -> [EVALUATE: promising, continue]
│ │ └── Solution
│ └── Step A2 -> [EVALUATE: mediocre, deprioritize]
└── Approach B
└── Step B1 -> [EVALUATE: poor, abandon]
Implementing ToT via Prompting
javascript
async function treeOfThoughts(problem, nThoughts = 3, depth = 3) {
let currentLayer = [{ path: [], state: problem }];
for (let d = 0; d < depth; d++) {
const nextLayer = [];
for (const node of currentLayer) {
const thoughts = await generateThoughts(problem, node.path, nThoughts);
const evaluated = await evaluateThoughts(problem, node.path, thoughts);
const topThoughts = evaluated
.sort((a, b) => b.score - a.score)
.slice(0, 2);
for (const thought of topThoughts) {
nextLayer.push({
path: [...node.path, thought.thought],
score: thought.score,
});
}
}
currentLayer = nextLayer;
const solutions = currentLayer.filter(n => n.score > 0.9);
if (solutions.length > 0) break;
}
return currentLayer.sort((a, b) => b.score - a.score)[0];
}
When to use ToT:
- Complex planning problems with multiple valid approaches
- Creative problems where exploring diverse paths is valuable
- Problems where the correct approach is not obvious in advance
- Mathematical or logical puzzles with multiple solution paths
Cost: ToT uses significantly more tokens than CoT (N x depth calls vs. 1 call). Reserve it for problems where quality matters more than cost.
---
ReAct (Reasoning + Acting)
ReAct interleaves reasoning and tool use, allowing the model to adjust its reasoning based on what it observes from tool outputs.
code
Thought: I need to find the current price of Apple stock to calculate the portfolio value.
Act: search_web("AAPL current stock price")
Observation: AAPL is currently trading at $218.50
Thought: Now I have the stock price. The user holds 100 shares, so the position value is 100 x $218.50 = $21,850.
Act: calculate("100 * 218.50")
Observation: Result: 21850
Thought: I have all the information needed. I can now answer the question.
Answer: Your Apple stock position is currently worth $21,850 (100 shares x $218.50/share).
The ReAct prompt structure:
code
Solve this task by alternating between Thought and Action/Observation steps.
Format:
Thought: [your reasoning about what to do next]
Action: [tool_name(parameters)]
Observation: [the result of the action - provided by the system]
... (repeat as needed)
Answer: [final answer after sufficient information gathered]
Task: {{task}}
---
Reflexion (Self-Critique and Retry)
Reflexion adds a self-critique step: after the model produces an answer, it evaluates its own answer and retries if it finds flaws.
javascript
async function reflexionSolve(problem, maxAttempts = 3) {
let attempt = 1;
let previousAttempts = [];
while (attempt <= maxAttempts) {
const solution = await client.chat.completions.create({
messages: [{
role: 'user',
content: 'Problem: ' + problem + '\n\n' +
(previousAttempts.length > 0 ? 'Previous attempts that failed:\n' +
previousAttempts.map((a, i) => 'Attempt ' + (i+1) + ': ' + a.solution + '\nWhy it failed: ' + a.critique).join('\n\n') + '\n\nLearn from previous failures.\n\n' : '') +
'Generate a solution.'
}]
});
const solutionText = solution.choices[0].message.content;
const critique = await client.chat.completions.create({
messages: [{
role: 'user',
content: 'Problem: ' + problem + '\n\nProposed solution:\n' + solutionText + '\n\nCritically evaluate this solution. Is it correct? Are there edge cases it misses?\n\nOutput JSON: { "is_correct": boolean, "critique": string, "confidence": number }'
}],
response_format: { type: 'json_object' },
});
const { is_correct, critique: critiqueText, confidence } = JSON.parse(critique.choices[0].message.content);
if (is_correct && confidence > 0.85) {
return { solution: solutionText, attempts: attempt, confidence };
}
previousAttempts.push({ solution: solutionText, critique: critiqueText });
attempt++;
}
return { solution: previousAttempts[0].solution, attempts: maxAttempts, confidence: 'low', caveat: 'Maximum retries reached' };
}
---
Choosing the Right Technique
| Problem Type | Best Technique | Why |
|---|
| Single-step reasoning | Chain-of-Thought | Fast, cheap, reliable |
| Multi-path problems | Tree of Thoughts | Explores alternatives |
| Tool-using tasks | ReAct | Adjusts based on real observations |
| High-stakes single answer | Reflexion | Self-corrects before committing |
| High-stakes with uncertainty | ToT + Self-Consistency | Maximum reliability |