Skip to main content
TreeOfThoughts implements Tree of Thoughts: Deliberate Problem Solving with Large Language Models (Yao et al., 2023). A single Agent answers in one pass. TreeOfThoughts grows a tree of partial solutions instead: it proposes candidate next steps, scores each one from 0 to 1, prunes the weak ones, searches the tree breadth-first or depth-first, and writes the answer from the best path it found.
TreeOfThoughts is not in swarms 15.0.3, the latest release on PyPI. Until the next release, install from GitHub:
The GitHub install also reports version 15.0.3, so check for the class, not the version number.

Quickstart

run returns the answer. last_result keeps the best path, the whole tree, and what the search cost.
The model must support function calling. Every model output is a function call validated against a Pydantic schema, so the search never parses free-form prose. Any LiteLLM model string works, for example model_name="claude-sonnet-5".

How the search works

  1. Generate. From a node, ask for num_thoughts candidate next steps: all in one call ("propose") or one call per step ("sample"). Duplicate candidates from the same parent are dropped.
  2. Evaluate. Score every candidate from 0 to 1: rate each one on its own ("value") or compare them and vote ("vote").
  3. Prune. A candidate that scores below value_threshold is never expanded and never accepted as the answer.
  4. Search. "bfs" keeps the best breadth open nodes per level. "dfs" follows the best child first and backtracks when a branch is pruned or runs out.
  5. Answer. Write the final answer from the best path.
A candidate that completes the task is final. A step at max_depth is always final, whether or not the model flagged it. A final candidate that survives pruning is a solution. BFS stops once its best solution scores at least as high as every open partial path. DFS returns the first solution it reaches. If the search finds no solution, the answer is completed from the highest-scoring partial path and solved is False. Each model call goes through a fresh, stateless Agent that carries only the schema for that call. Calls at the same level of the tree run concurrently. A call that fails or returns no valid function call is logged as a warning and costs that one candidate, not the search.

When to use it instead of a single Agent

Use TreeOfThoughts when a task breaks into steps you can check, and one wrong early step ruins the answer: arithmetic and proofs, logic puzzles, case analysis, scheduling under constraints, planning, and estimation. The search pays for that with many model calls per task (see Cost). Use a single Agent when one pass is usually right, when steps have no meaningful score, or when latency and cost matter more than accuracy.

Import

Constructor

Identity and model

Scoring

Adapting to a task

Execution and output

The constructor raises ValueError when a strategy name is unknown, when num_thoughts, breadth, max_depth, n_evaluate_samples or max_workers is below 1, when max_expansions is below 1, or when value_threshold is outside 0 to 1.

Choosing a strategy

How "value" scores. Each candidate is rated n_evaluate_samples times and the ratings are averaged. The evaluator writes its critique before its score. A candidate whose every rating failed scores 0 and is pruned. How "vote" scores. The evaluator sees all candidates at once and votes for the best, n_evaluate_samples times. A candidate’s score is its votes divided by the leader’s votes, so the leader always scores 1. BFS compares every candidate on the level in one vote; DFS compares the children of one node. Two consequences follow:
  • Vote mode cannot reject a lone candidate. It scores 1 without a call.
  • With one vote per comparison, only the winner survives the default value_threshold. Raise n_evaluate_samples to keep more branches.

Adapting the search to a task

The built-in prompts are domain-agnostic. thought_description and evaluation_criteria adapt the search to a task more than any other setting.
  • thought_description tells the generator what one step looks like. It is added to every generation prompt under WHAT ONE STEP LOOKS LIKE. Size the step so the evaluator can check it: “one deduction”, “one arithmetic operation”, “one SQL clause”.
  • evaluation_criteria tells the evaluator how to judge progress. It is added to every value and vote prompt under HOW TO JUDGE PROGRESS. Name what makes a step wrong, not only what makes it good.

run

run takes the task as a string, searches, writes the answer, and returns the conversation formatted by output_type: the answer string for the default "final". The conversation holds the task, the reasoning path, and the answer. Each call replaces last_result and conversation. It raises ValueError if task is empty, and RuntimeError if the search found no solution and the final answer call also failed, so there is no answer to return.

last_result

After run, agent.last_result is a TreeOfThoughtsResult for that search. It is None before the first run. to_dict() serializes all of it, with the whole tree under "tree":
Every node in the tree is a ThoughtNode:

usage

A Dict[str, int] of provider token usage summed over every search this agent has run. The keys match Agent.usage: input_tokens, output_tokens, cached_tokens, reasoning_tokens and total_tokens. It returns a new dict, so changing it does not affect the agent. For one search only, read last_result.usage.

Cost

Calls grow with num_thoughts, breadth, max_depth and n_evaluate_samples. With t = num_thoughts, b = breadth, D = max_depth and k = n_evaluate_samples, one search makes at most:
  • Generation: 1 call per expanded node with "propose", t calls with "sample".
  • Evaluation: with "value", k calls per candidate, so up to t * k per expanded node. With "vote", k calls per comparison: one comparison per BFS level or per DFS expansion, and none when there is only one candidate.
  • Answer: 1 call.
The number of expanded nodes is at most 1 + b * (D - 1) for BFS and 1 + t + t^2 + ... + t^(D - 1) for DFS. DFS grows exponentially with depth, so cap it. max_expansions caps both: the search stops expanding once it reaches the cap and answers from what it has. These are worst cases. A search usually stops sooner: BFS once a solution beats every open path, DFS at its first solution. Read last_result.llm_calls and last_result.usage to see what a search actually cost.

Examples

Number theory

BFS with a beam of 3 and averaged ratings.

Fermi estimate

Sampled steps compared by vote.

Knights and knaves

DFS case analysis with backtracking.
More examples live in examples/reasoning_agents/tree_of_thoughts_examples in the framework repo.