Chain-of-thought commits to one line of reasoning and lives with it. Tree of Thoughts generates several candidate thoughts at each step, scores them, keeps the best few and throws the rest away. The search itself is the algorithm - and it is a beam search with a pruning rule.
You are given a pre-scored tree, so no model calls are needed. Each node has a score (the evaluator's judgement of that thought) and a list of children.
A path is a sequence of node ids from the root. Its value is the sum of the scores of its nodes excluding the root - the root is the problem statement, not a thought.
The search:
Implement:
def tot_beam_search(nodes, root, beam_width, max_depth, min_score):
Return {"best_path": list[str], "best_value": float, "expanded": int} with best_value rounded to 4 decimal places. If nothing at all is finished, return [], 0.0 and the expansion count.
Compare values for sorting after rounding to 6 decimal places so that arithmetically-equal branches tie cleanly; report best_value rounded to 4.
nodes = {
"r": {"score": 0.0, "children": ["a", "b"]},
"a": {"score": 0.8, "children": []},
"b": {"score": 0.9, "children": []},
}
print(tot_beam_search(nodes, "r", 2, 3, 0.0))
# {'best_path': ['r', 'b'], 'best_value': 0.9, 'expanded': 2}
nodes = {'r': {'score': 0.0, 'children': ['a', 'b']}, 'a': {'score': 0.8, 'children': []}, 'b': {'score': 0.9, 'children': []}}
print(tot_beam_search(nodes, 'r', 2, 3, 0.0)){'best_path': ['r', 'b'], 'best_value': 0.9, 'expanded': 2}Both children of the root are examined, so expanded is 2. Neither is pruned (both scores clear min_score of 0.0) and both fit in a beam of width 2. On the next iteration each is childless, so both paths finish; ['r','b'] has value 0.9 against 0.8 and wins.
1 <= len(nodes) <= 500; the tree is acyclic and every child id exists in nodesexpanded counts every child examined, including pruned onesmin_score, not the path valuebest_value is rounded to 4 decimal places - never print a raw floatmax_depth is exhausted are valid answers