LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 177 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/8
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/11
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageClustering and PCACore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/24
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernanceData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/16
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/19
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnApplied LLM EngineeringCoT, ToT & Self-Consistency Prompting
✍️MediumPrompt Engineering

CoT, ToT & Self-Consistency Prompting

Build and evaluate reasoning controllers: single traces, answer voting, and bounded tree search for multi-step LLM decisions.

16 min read
Learning path
Step 57 of 177 in the full curriculum
Dimensionality Reduction for EmbeddingsFunction Calling & Tool Use

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

Compressed embeddings can retrieve the right policy evidence and still leave the assistant with a decision to make. Retrieval isn't the end of the task. An assistant may have the right facts and still choose the wrong action because it skips a condition, mishandles arithmetic, or commits too early to one plan.

Treat extra inference work as an engineering decision. You'll build a small release-resolution controller for a risky code change:

  • Chain-of-Thought (CoT): request one decomposed candidate decision.
  • Self-Consistency: sample several candidates, normalize final actions, and vote.
  • Tree-of-Thoughts (ToT): expand and prune branches when a decision needs backtracking.

Long rationales aren't the target. The target is better measurable decision accuracy under a token, latency, and safety budget.

Start with a failure you can audit

Suppose a repository has a candidate release. Unit tests pass, the security scan is clear, and a reviewer has approved the diff. Policy permits auto-merge only when all three facts are true. A direct response may still overlook one condition and suggest a force-merge or escalation unnecessarily.

A useful outward artifact is a short decision record: facts used, checks applied, and final action. It's smaller than an open-ended rationale, easy to score, and safe to compare against deterministic policy logic.

decision-record-contract.py
1from dataclasses import dataclass 2 3@dataclass(frozen=True) 4class ReleaseCase: 5 tests_passed: bool 6 security_scan_clear: bool 7 reviewer_approved: bool 8 9def decision_record(case: ReleaseCase) -> dict[str, object]: 10 checks = { 11 "tests_passed": case.tests_passed, 12 "security_scan_clear": case.security_scan_clear, 13 "reviewer_approved": case.reviewer_approved, 14 } 15 action = "merge_release" if all(checks.values()) else "manual_review" 16 return {"checks": checks, "final_action": action} 17 18record = decision_record( 19 ReleaseCase(tests_passed=True, security_scan_clear=True, reviewer_approved=True) 20) 21for name, passed in record["checks"].items(): 22 print(f"{name}: {passed}") 23print(f"final_action: {record['final_action']}")
Output
1tests_passed: True 2security_scan_clear: True 3reviewer_approved: True 4final_action: merge_release

This code isn't an LLM. It's the oracle that your prompt variants must match. Before increasing model compute, define an output contract and a scorer.

When to skip CoT entirely: if the inputs are already structured machine state (booleans, enum fields, policy predicates the runtime owns), evaluate the policy in code. Sampling five CoT traces over three known flags adds cost without evidence. Use LLM reasoning only over unstructured evidence (diff text, free-form review comments, incident narratives) where there is no deterministic scorer yet. The fixtures below teach scoring and controllers; they aren't a product recommendation to put all(checks) behind a model.

The same release-gate facts feed a direct candidate that records only one of three required checks and fails the oracle, while a structured decision record preserves all three predicates and reaches the expected merge release action.
A trace matters only when it improves the scored contract: complete required checks and the correct final action.

Why define an oracle and decision record before testing reasoning prompts?

Answer

Without a scored outcome and required checks, a longer response can look thoughtful without being more correct. The oracle lets you compare strategies on the same task.

Chain-of-Thought: one decomposed candidate

Wei et al. introduced few-shot CoT by placing worked intermediate steps in prompt examples. Their experiments found gains on arithmetic, commonsense, and symbolic reasoning benchmarks for sufficiently large models, including a strong GSM8K result with PaLM 540B.[1]Reference 1Chain-of-Thought Prompting Elicits Reasoning in Large Language Models.https://arxiv.org/abs/2201.11903 Kojima et al. later showed that the zero-shot trigger "Let's think step by step" could improve several benchmark reasoning tasks without worked examples.[2]Reference 2Large Language Models are Zero-Shot Reasoners.https://arxiv.org/abs/2205.11916

Those papers establish techniques to evaluate, not a production law. On your endpoint and task, a visible scratchpad may help, do nothing, or add cost. If an API offers a native reasoning control, evaluate that option as another strategy rather than assuming that extra visible text helps.

🔬 Research insight: More reasoning text isn't always better. Zheng et al. tested CoT and its variants (including ToT and ReAct) on nine pattern-based in-context-learning benchmarks across 16 models and found they consistently underperformed plain direct answering, with the gap widening as more demonstrations were added. Even long-CoT reasoning models that spent far more tokens didn't overcome the effect.[3]Reference 3The Curse of CoT: On the Limitations of Chain-of-Thought in In-Context Learning.https://arxiv.org/abs/2504.05081 When a task is really pattern matching from examples, a visible scratchpad can add noise instead of signal. That's the point of the eval gate: measure the win, don't assume it.

This tradeoff also shapes a build-versus-buy decision you'll face in production. You can orchestrate reasoning yourself with prompt scaffolding (the CoT, self-consistency, and tree-search controllers in this chapter), or call a native reasoning model that performs its own internal reasoning before answering. Scaffolding keeps each step visible, auditable, and cheap to swap, but you own the token and latency budget. A native reasoning model can lift accuracy on genuinely multi-step problems, yet it hides its reasoning, bills you for it, and gives you fewer control points to gate. Treat the two as competing candidates behind the same eval gate rather than assuming the newer option wins.

For a production workflow, don't ask the model to reveal unrestricted inner reasoning. Ask it to produce reviewable artifacts:

single-trace-prompt.txt
1Use only the supplied release facts and policy rules. 2Return: 31. required_checks: each policy predicate with pass/fail 42. final_action: one allowed action enum 53. operator_message: one sentence 6 7Facts: 8- tests_passed: true 9- security_scan_clear: true 10- reviewer_approved: true 11 12Policy: 13- merge_release is allowed only when tests pass, 14 the security scan is clear, and review approval exists.

The checks are useful because a missed predicate becomes observable. They aren't proof of faithful hidden cognition. Turpin et al. showed that CoT explanations can rationalize outputs influenced by hidden biasing features without mentioning those features.[4]Reference 4Language Models Don't Always Say What They Think: Unfaithful Explanations in Chain-of-Thought Promptinghttps://arxiv.org/abs/2305.04388 Log inputs, actions, validations, and outcomes; don't treat eloquent reasoning text as an audit guarantee.

score-candidate-decisions.py
1from dataclasses import dataclass 2 3@dataclass(frozen=True) 4class Candidate: 5 name: str 6 checks: set[str] 7 final_action: str 8 9required_checks = { 10 "tests_passed", 11 "security_scan_clear", 12 "reviewer_approved", 13} 14expected_action = "merge_release" 15candidates = [ 16 Candidate("direct", {"tests_passed"}, "manual_review"), 17 Candidate("structured_trace", required_checks, "merge_release"), 18] 19 20for candidate in candidates: 21 coverage = len(candidate.checks & required_checks) / len(required_checks) 22 action_ok = candidate.final_action == expected_action 23 print(f"{candidate.name}: coverage={coverage:.0%}, action_ok={action_ok}")
Output
1direct: coverage=33%, action_ok=False 2structured_trace: coverage=100%, action_ok=True

Here, the trace is useful because it meets the scored contract. It doesn't win merely because it contains more words.

Zero-shot or few-shot?

Zero-shot CoT supplies an instruction and lets the model choose its intermediate format. Few-shot CoT gives one or more solved examples, so it can teach both decomposition and the final answer shape. Native structured output enforcement is better when available; examples are still useful when the prompt must communicate task-specific checks.

Comparison showing zero-shot prompting drifting across facts, checks, and action fields while few-shot examples keep the same schema slots for examples and target.
Few-shot examples spend more prompt tokens to stabilize field order. Prefer native structured output when the endpoint can enforce the schema.
build-few-shot-decision-prompt.py
1example = """Example: 2facts: tests_passed=true, security_scan_clear=false, reviewer_approved=true 3required_checks: tests_passed=true, security_scan_clear=false, reviewer_approved=true 4final_action: manual_review""" 5 6case = "facts: tests_passed=true, security_scan_clear=true, reviewer_approved=true" 7zero_shot = f"Evaluate release policy step by step.\n{case}\nfinal_action:" 8few_shot = f"{example}\n\nNow evaluate:\n{case}\nrequired_checks:\nfinal_action:" 9 10print(f"zero_shot_has_example: {'Example:' in zero_shot}") 11print(f"few_shot_has_example: {'Example:' in few_shot}") 12print(f"few_shot_requests_checks: {'required_checks:' in few_shot}")
Output
1zero_shot_has_example: False 2few_shot_has_example: True 3few_shot_requests_checks: True

When is few-shot CoT worth more prompt tokens than zero-shot CoT?

Answer

When examples teach task-specific checks or output fields that your evaluator needs, and held-out results justify the added tokens.

Self-Consistency: sample answers, then vote

One structured trace can fail because generation takes an unlucky path. Self-Consistency replaces reliance on one path with several sampled paths and chooses the most consistent final answer.[5]Reference 5Self-Consistency Improves Chain of Thought Reasoning in Language Models.https://arxiv.org/abs/2203.11171 The vote operates on extracted answers, not on whose rationale sounds best.

To obtain different candidates, use stochastic decoding, usually with a nonzero temperature, rather than rerunning one deterministic decode. The earlier Decoding Strategies lesson explains that control. Keep it fixed in your experiment and log it with the sample count.

On the original benchmark setting, Wang et al. reported a 17.9 percentage-point GSM8K improvement over CoT prompting for PaLM 540B.[5]Reference 5Self-Consistency Improves Chain of Thought Reasoning in Language Models.https://arxiv.org/abs/2203.11171 That number is evidence for the method on those benchmarks. It's not your expected support-resolution gain. Measure your own cases and sample cost.

A self-consistency controller fans one release-gate prompt into five stochastic traces, canonicalizes three merge variants into one action, keeps one manual-review vote, rejects one unknown action, and releases the 3-of-5 winner only because it meets the 0.60 share threshold.
Self-consistency votes on canonical actions, not rationale style. Rejected outputs remain in the denominator, and weak or tied votes route to review.

Normalize before counting

Model outputs rarely use exactly one spelling. Your controller should map harmless variants to one allowed action and reject unknown outputs before voting.

canonicalize-and-vote.py
1from collections import Counter 2 3ALIASES = { 4 "merge release": "merge_release", 5 "merge_release": "merge_release", 6 "auto merge approved change": "merge_release", 7 "manual review": "manual_review", 8} 9 10def canonicalize(text: str) -> str | None: 11 normalized = text.strip().lower().replace("-", " ") 12 return ALIASES.get(normalized) 13 14samples = [ 15 "Merge release", 16 "merge_release", 17 "Auto merge approved change", 18 "manual review", 19 "force merge immediately", 20] 21votes = Counter(action for text in samples if (action := canonicalize(text))) 22winner = votes.most_common(1)[0][0] if votes else "manual_review" 23 24print(f"accepted_samples: {sum(votes.values())}/{len(samples)}") 25print(f"votes: {dict(votes)}") 26print(f"winner: {winner}")
Output
1accepted_samples: 4/5 2votes: {'merge_release': 3, 'manual_review': 1} 3winner: merge_release

A winner isn't always confident enough

A 2 to 2 tie, zero accepted outputs, or a narrow plurality with many rejected outputs shouldn't silently become an automated action. Add an abstention rule. The winning share must use all sampled outputs as its denominator, including strings that failed to parse.

abstain-on-weak-votes.py
1from collections import Counter 2 3def decide(votes: list[str], total_samples: int, minimum_share: float = 0.6) -> str: 4 if not votes: 5 return "manual_review" 6 counts = Counter(votes) 7 winner, count = counts.most_common(1)[0] 8 share = count / total_samples 9 tied = len(counts) > 1 and counts.most_common(2)[0][1] == counts.most_common(2)[1][1] 10 if tied or share < minimum_share: 11 return "manual_review" 12 return winner 13 14strong = ["merge_release", "merge_release", "merge_release", "manual_review"] 15split = ["merge_release", "merge_release", "manual_review", "manual_review"] 16mostly_rejected = ["merge_release", "merge_release"] 17 18print(f"strong_vote: {decide(strong, total_samples=4)}") 19print(f"split_vote: {decide(split, total_samples=4)}") 20print(f"mostly_rejected_vote: {decide(mostly_rejected, total_samples=5)}") 21print(f"no_valid_votes: {decide([], total_samples=5)}")
Output
1strong_vote: merge_release 2split_vote: manual_review 3mostly_rejected_vote: manual_review 4no_valid_votes: manual_review

Measure gains against call cost

Use a held-out fixture set before calling the strategy ready. The fixture represents five sampled final actions returned for each case; the controller compares first-sample accuracy with five-sample voting accuracy.

measure-voting-gain.py
1from collections import Counter 2 3fixtures = { 4 "approved_clean_release": { 5 "expected": "merge", 6 "samples": ["review", "merge", "merge", "merge", "force"], 7 }, 8 "scan_failed": { 9 "expected": "review", 10 "samples": ["review", "review", "force", "review", "hold"], 11 }, 12 "review_missing": { 13 "expected": "review", 14 "samples": ["merge", "review", "review", "review", "merge"], 15 }, 16} 17 18single_correct = 0 19vote_correct = 0 20for item in fixtures.values(): 21 winner = Counter(item["samples"]).most_common(1)[0][0] 22 single_correct += item["samples"][0] == item["expected"] 23 vote_correct += winner == item["expected"] 24 25total = len(fixtures) 26print(f"single_trace_accuracy: {single_correct / total:.0%}") 27print(f"vote_5_accuracy: {vote_correct / total:.0%}") 28print(f"model_calls: single={total}, vote_5={total * 5}")
Output
1single_trace_accuracy: 33% 2vote_5_accuracy: 100% 3model_calls: single=3, vote_5=15

This fixture is intentionally small and deterministic: it tests controller logic. A real release decision needs representative labeled cases, real model samples, token counts, latency, refusal rates, and cost.

What should a self-consistency controller do when votes do not form a strong majority?

Answer

Abstain or route to review. More samples are useful only when a measured gain justifies their cost and the action remains safe.

Tree-of-Thoughts: search when branches can dead-end

Voting helps when independent paths tend to converge on the same short answer. It doesn't deliberately revisit earlier choices. Tree-of-Thoughts (ToT) represents partial solutions as search states, generates alternatives, evaluates the states, and preserves only branches worth extending.[6]Reference 6Tree of Thoughts: Deliberate Problem Solving with Large Language Models.https://arxiv.org/abs/2305.10601

Yao et al. evaluated ToT on tasks built for planning and search. On Game of 24, their GPT-4 ToT setup solved 74% of tasks while their CoT baseline solved 4%.[6]Reference 6Tree of Thoughts: Deliberate Problem Solving with Large Language Models.https://arxiv.org/abs/2305.10601 The narrower conclusion is not "use trees everywhere." If a problem has verifiable partial states and meaningful backtracking, search can rescue a bad early move.

Search states in a release plan

For a release gate, consider a workflow whose final recommendation must be supported by two observations: a current security scan and a reviewer approval record. A controller can expand legal steps instead of letting a model invent a final action before evidence is present.

expand-release-plan-states.py
1from dataclasses import dataclass 2 3@dataclass(frozen=True) 4class State: 5 security_clear: bool | None = None 6 reviewer_approved: bool | None = None 7 final_action: str | None = None 8 9def expand(state: State) -> list[tuple[str, State]]: 10 next_states: list[tuple[str, State]] = [] 11 if state.security_clear is None: 12 next_states.extend([ 13 ("security_scan:clear", State(True, state.reviewer_approved)), 14 ("security_scan:failed", State(False, state.reviewer_approved)), 15 ]) 16 if state.reviewer_approved is None: 17 next_states.extend([ 18 ("review:approved", State(state.security_clear, True)), 19 ("review:rejected", State(state.security_clear, False)), 20 ]) 21 if ( 22 state.security_clear is not None 23 and state.reviewer_approved is not None 24 and state.final_action is None 25 ): 26 action = "merge_release" if state.security_clear and state.reviewer_approved else "block_release" 27 next_states.append((action, State(state.security_clear, state.reviewer_approved, action))) 28 return next_states 29 30frontier = [State()] 31for depth in range(3): 32 generated = [item for state in frontier for item in expand(state)] 33 print(f"depth_{depth + 1}: {[action for action, _ in generated]}") 34 frontier = list(dict.fromkeys(state for _, state in generated))
Output
1depth_1: ['security_scan:clear', 'security_scan:failed', 'review:approved', 'review:rejected'] 2depth_2: ['review:approved', 'review:rejected', 'review:approved', 'review:rejected', 'security_scan:clear', 'security_scan:failed', 'security_scan:clear', 'security_scan:failed'] 3depth_3: ['merge_release', 'block_release', 'block_release', 'block_release']

The two evidence-gathering orders converge on outcome-bearing states. Only the state with a clear scan and an approved review can merge; a failed scan or rejected review blocks the release. In the next lesson, tool calls will populate those outcomes from real APIs.

A fully runnable search example

Game of 24 is useful because the evaluator is exact: arithmetic either reaches 24 using each input once or it doesn't. The solver below explores partial equations with breadth-first search and returns a verified solution.

breadth-first-game-of-24.py
1from fractions import Fraction 2from itertools import combinations 3 4def combine(left: tuple[Fraction, str], right: tuple[Fraction, str]) -> list[tuple[Fraction, str]]: 5 a, a_expr = left 6 b, b_expr = right 7 outcomes = [ 8 (a + b, f"({a_expr} + {b_expr})"), 9 (a - b, f"({a_expr} - {b_expr})"), 10 (b - a, f"({b_expr} - {a_expr})"), 11 (a * b, f"({a_expr} * {b_expr})"), 12 ] 13 if b: 14 outcomes.append((a / b, f"({a_expr} / {b_expr})")) 15 if a: 16 outcomes.append((b / a, f"({b_expr} / {a_expr})")) 17 return outcomes 18 19def solve_24(numbers: list[int]) -> str | None: 20 frontier = [[(Fraction(number), str(number)) for number in numbers]] 21 while frontier: 22 state = frontier.pop(0) 23 if len(state) == 1 and state[0][0] == 24: 24 return state[0][1] 25 for i, j in combinations(range(len(state)), 2): 26 remainder = [item for k, item in enumerate(state) if k not in (i, j)] 27 frontier.extend([remainder + [result] for result in combine(state[i], state[j])]) 28 return None 29 30solution = solve_24([4, 5, 6, 7]) 31print(f"solution_found: {solution is not None}") 32print(f"expression: {solution}")
Output
1solution_found: True 2expression: ((6 - 4) * (5 + 7))
Tree-of-thought search comparing branch expansion for Game of 24 with a beam-width-one scorer that keeps a dead branch under weak scoring but preserves the solution under exact checks.
ToT helps only when the evaluator keeps viable states. Weak pruning can keep a dead branch; exact checks preserve the branch that reaches 24.

Pruning is a source of failure

An LLM evaluator isn't an arithmetic oracle. If it scores an apparently simple but dead branch above a non-obvious solvable branch, an aggressive beam can remove the answer before expansion.

beam-pruning-risk.py
1branches = [ 2 {"move": "6 * 4 = 24 first", "solvable": False, "weak_score": 0.95, "exact_score": 0.0}, 3 {"move": "5 + 7 = 12 first", "solvable": True, "weak_score": 0.40, "exact_score": 1.0}, 4 {"move": "7 - 5 = 2 first", "solvable": False, "weak_score": 0.35, "exact_score": 0.0}, 5] 6 7def keep_one(score_name: str) -> dict[str, object]: 8 return max(branches, key=lambda branch: branch[score_name]) 9 10weak_choice = keep_one("weak_score") 11exact_choice = keep_one("exact_score") 12print(f"weak_evaluator_keeps_solution: {weak_choice['solvable']}") 13print(f"exact_evaluator_keeps_solution: {exact_choice['solvable']}") 14print(f"risk: beam_width_1 can prune the valid branch")
Output
1weak_evaluator_keeps_solution: False 2exact_evaluator_keeps_solution: True 3risk: beam_width_1 can prune the valid branch

The production implications are concrete:

  • Keep ToT for tasks with real branch structure, not ordinary classification.
  • Prefer deterministic validators when a partial state can be checked in code.
  • Measure solver recall at each beam width alongside final successes.
  • Cap expansions and latency before an open-ended search reaches users.

Why can Tree-of-Thoughts perform worse when the evaluator is weak and the beam is narrow?

Answer

Search spends compute only on retained states. If an incorrect evaluator prunes the valid branch early, later expansions cannot recover it.

Choose compute with an eval gate

Direct prompting, one trace, voting, and tree search aren't maturity levels. They are candidates with different accuracy and serving cost. Start with the cheapest candidate, then promote a more expensive strategy through an eval gate only when held-out results require it.

An accuracy-versus-p95-latency chart compares direct prompting at 76% and 190 milliseconds, one trace at 84% and 360 milliseconds, five-sample voting at 94% and 740 milliseconds, and tree search at 96% and 1840 milliseconds; only voting clears both the 90% accuracy floor and 900 millisecond latency budget.
Apply both constraints before optimizing cost. Here, five-sample voting is the only strategy eligible to ship.
reasoning-release-gate.py
1results = [ 2 {"strategy": "direct", "accuracy": 0.76, "p95_ms": 190, "calls": 1}, 3 {"strategy": "single_trace", "accuracy": 0.84, "p95_ms": 360, "calls": 1}, 4 {"strategy": "vote_5", "accuracy": 0.94, "p95_ms": 740, "calls": 5}, 5 {"strategy": "tree_search", "accuracy": 0.96, "p95_ms": 1840, "calls": 14}, 6] 7 8minimum_accuracy = 0.90 9latency_budget_ms = 900 10eligible = [ 11 row for row in results 12 if row["accuracy"] >= minimum_accuracy and row["p95_ms"] <= latency_budget_ms 13] 14selected = min(eligible, key=lambda row: (row["calls"], row["p95_ms"])) 15 16for row in results: 17 print(f"{row['strategy']}: accuracy={row['accuracy']:.0%}, p95_ms={row['p95_ms']}, calls={row['calls']}") 18print(f"selected: {selected['strategy']}")
Output
1direct: accuracy=76%, p95_ms=190, calls=1 2single_trace: accuracy=84%, p95_ms=360, calls=1 3vote_5: accuracy=94%, p95_ms=740, calls=5 4tree_search: accuracy=96%, p95_ms=1840, calls=14 5selected: vote_5

These numbers are example evaluation results, not a benchmark claim. In your system, keep a table with:

MetricWhy it matters
Action accuracy or task successExtra reasoning must change correct outcomes
Unsafe-action and abstention ratesReliability includes knowing when not to act
Input, output, and reasoning tokensSampling and search multiply spend
p50 and p95 latencyLong tails can make support interactions unusable
Parse and schema failuresA correct thought is useless if the runtime can't consume its action

When not to use CoT (or heavier search)

Use this checklist before you promote scaffolding or multi-sample voting:

SituationPrefer instead
Inputs are already structured policy predicatesDeterministic policy engine in the runtime
Pattern-matching few-shot ICL (curse-of-CoT regime)Direct answer; measure before adding a scratchpad
Single-label classification, field extraction, or formatting with clear labelsStructured output, no visible reasoning channel
No verifiable partial state and no backtracking valueDirect or one trace, not ToT
Accuracy already meets the gate at one callKeep one call; skip vote_5 and tree search
Hard latency budgets where extra tokens blow p95Cheapest strategy that still clears quality
User-visible channels where intermediate tokens can leak secretsKeep traces server-side or omit them
Need a compliance audit of whyLogged inputs, tool observations, and policy checks, not free-form CoT text
Missing live factsTool call (next lesson), not a longer rationale
A native reasoning model already wins the evalSkip your own scaffolding; compare behind the same gate

Forcing a visible CoT scaffold on a model that already does hidden long-CoT reasoning can add cost without accuracy gain. Treat that as another arm in the same release table.

Where reasoning ends and tools begin

All runnable experiments above operate on provided facts or deterministic state. A real release decision requires current CI status, security scan output, and review state from source systems. Reasoning alone can't obtain those observations.

ReAct interleaves reasoning traces and task-specific actions so new observations can update the next decision.[7]Reference 7ReAct: Synergizing Reasoning and Acting in Language Models.https://arxiv.org/abs/2210.03629 The production handoff isn't a saved inner monologue. It's a validated action request, a controlled execution result, and a bounded next decision:

reasoning-to-tool-handoff.txt
1Need: security scan status is not present in supplied facts. 2Next action request: get_security_scan(run_id="ci-1482") 3Runtime responsibility: validate authorization, execute call, log result. 4Next decision: apply policy only after observation is returned.

The next lesson implements that action boundary with typed function calls, schemas, errors, and safe execution.

What to remember

  • Define the scorer first. A decision record lets you test whether extra inference work improves outcomes.
  • One trace is one candidate. CoT can reveal missed steps, but a plausible rationale isn't a faithful audit log.[4]Reference 4Language Models Don't Always Say What They Think: Unfaithful Explanations in Chain-of-Thought Promptinghttps://arxiv.org/abs/2305.04388
  • Vote on normalized outcomes. Self-consistency is useful only when its measured gain beats its sample cost.[5]Reference 5Self-Consistency Improves Chain of Thought Reasoning in Language Models.https://arxiv.org/abs/2203.11171
  • Search only with branch structure. ToT needs meaningful states, evaluators, pruning limits, and failure measurements.[6]Reference 6Tree of Thoughts: Deliberate Problem Solving with Large Language Models.https://arxiv.org/abs/2305.10601
  • Promote strategies through evals. Direct, trace, vote, and search should compete under quality and latency gates.
  • Skip CoT when policy is already machine state. Deterministic predicates, pattern-ICL, and tasks already above the accuracy SLO need no scratchpad.
Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.For a release candidate with tests_passed=true, security_scan_clear=true, and reviewer_approved=true, policy allows merge_release only when all three facts are true. Which decision record matches the oracle?

Correct answer: required_checks: tests_passed=true, security_scan_clear=true, reviewer_approved=true; final_action=merge_release

All three policy predicates pass: tests passed, the security scan is clear, and review approval exists. A useful record includes each required check plus the final enum, so a response that skips checks or marks a passing predicate false does not match the oracle.

2.An assistant returns the right final enum and a fluent explanation for a release decision. What should be logged for audit instead of treating the explanation as proof of faithful reasoning?

Correct answer: The supplied inputs, validated checks, final action, observations, and outcomes used by the scorer

A structured trace can expose missed predicates, but it is not proof that the model's hidden reasoning was faithful. Auditable systems log the facts, validations, actions, observations, and outcomes that can be checked against policy logic.

3.A release-policy evaluator parses required_checks and final_action enum fields. You can either use a zero-shot instruction ('Evaluate release policy step by step') or include a solved example that demonstrates those exact fields. Held-out tests show the example improves parsing and action accuracy enough to justify the extra prompt tokens. Which prompt choice should be used?

Correct answer: Use the few-shot contract example because it teaches task-specific checks and output fields that the scorer consumes

Few-shot examples are worth their prompt cost when they teach the decomposition or output contract the evaluator needs and measured results justify the cost. They do not replace schema enforcement, and published CoT gains do not guarantee improvement on a particular endpoint or task.

4.A team reruns one deterministic decode five times, gets five identical outputs, and calls it self-consistency. Which revision matches the intended experiment?

Correct answer: Sample multiple candidates with stochastic decoding, keep settings fixed, and vote on normalized final actions

Self-consistency is meant to reduce reliance on one unlucky generation path by sampling varied candidates. Repeating or duplicating a deterministic decode does not create independent alternatives. The experiment should hold the sampling configuration fixed, log it, and aggregate canonical final actions rather than rationale quality or raw strings.

5.A self-consistency controller samples five candidate final_action strings with the same nonzero-temperature setting: 'Merge release', 'merge_release', 'Auto merge approved change', 'manual review', and 'force merge immediately'. The alias table maps the first three merge variants to merge_release, maps 'manual review' to manual_review, and rejects unknown strings. What vote result should it use before any confidence threshold?

Correct answer: accepted_samples=4/5 with votes merge_release=3 and manual_review=1

Self-consistency votes on extracted, normalized outcomes. Three harmless merge variants collapse to merge_release, 'manual review' becomes manual_review, and the unsafe force-merge action is rejected rather than counted as a new allowed action.

6.A self-consistency controller samples five outputs. After canonicalization, two valid outputs are merge_release and three outputs are rejected as unknown. The abstention rule requires the winning action to have at least a 0.60 share using all five samples as the denominator. What should the controller return?

Correct answer: manual_review, because 2 of 5 samples is below the required share

The abstention rule counts confidence against the total number of samples, not successfully parsed strings alone. Here the winner has share 2/5 = 0.40, which is below 0.60, so the safe behavior is to abstain or route to review.

7.Which task is a good fit for Tree-of-Thoughts rather than ordinary answer voting?

Correct answer: A plan where partial states can be checked, bad branches can be pruned, and earlier choices may need backtracking

Tree-of-Thoughts is useful when the controller can represent intermediate states, generate alternatives, evaluate them, and recover from bad early choices. If the task is a simple lookup or flat classification, tree search adds cost without meaningful search structure.

8.A Tree-of-Thoughts Game of 24 solver keeps only one branch after the first move. Branches are: '6 * 4 = 24 first' with solvable=false and weak_score=0.95; '5 + 7 = 12 first' with solvable=true and weak_score=0.40; '7 - 5 = 2 first' with solvable=false and weak_score=0.35. If the controller prunes by weak_score with beam_width=1, what failure occurs?

Correct answer: It keeps '6 * 4 = 24 first' and discards the only solvable branch, so later expansion cannot recover the answer

Tree search spends compute only on retained states. With beam_width=1, the weak evaluator's top score selects an unsolvable branch and removes the lower-scored solvable branch before it can be expanded.

9.A release gate requires accuracy >= 0.90 and p95 latency <= 900 ms, then selects the eligible strategy with the fewest model calls and lower p95 latency as a tie-breaker. Results are: direct accuracy 0.76, p95 190 ms, calls 1; single_trace accuracy 0.84, p95 360 ms, calls 1; vote_5 accuracy 0.94, p95 740 ms, calls 5; tree_search accuracy 0.96, p95 1840 ms, calls 14. Which strategy should ship?

Correct answer: vote_5, because it is the cheapest eligible strategy within the latency budget

The gate first filters by both quality and latency. Direct and single_trace fail the 0.90 accuracy requirement, while tree_search fails the 900 ms p95 latency budget. vote_5 is the only eligible row, so its extra calls are justified by the measured result under the stated constraints.

10.A release request includes tests_passed=true and reviewer_approved=true, but no current security scan result is present in the supplied facts. Which controller behavior matches the reasoning-to-tool boundary?

Correct answer: Request get_security_scan through a validated runtime, log the result, then apply policy after the observation returns

Reasoning over supplied facts cannot create a missing observation. The correct handoff is a typed, validated action request to obtain the security scan, followed by a bounded next decision after the runtime returns the result.

10 questions remaining.

Next Step
Continue to Function Calling & Tool Use

You can now choose and evaluate a reasoning budget over supplied facts; next you'll convert missing facts and intended actions into validated tool calls.

PreviousDimensionality Reduction for Embeddings
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

Chain-of-Thought Prompting Elicits Reasoning in Large Language Models.

Wei, J., et al. · 2022 · NeurIPS

https://arxiv.org/abs/2201.11903

Large Language Models are Zero-Shot Reasoners.

Kojima, T., et al. · 2022

https://arxiv.org/abs/2205.11916

The Curse of CoT: On the Limitations of Chain-of-Thought in In-Context Learning.

Zheng, T., Chen, Y., Li, C., et al. · 2025 · arXiv preprint

https://arxiv.org/abs/2504.05081

Language Models Don't Always Say What They Think: Unfaithful Explanations in Chain-of-Thought Prompting

Miles Turpin, Julian Michael, Ethan Perez, Samuel R. Bowman · 2023

https://arxiv.org/abs/2305.04388

Self-Consistency Improves Chain of Thought Reasoning in Language Models.

Wang, X., et al. · 2022

https://arxiv.org/abs/2203.11171

Tree of Thoughts: Deliberate Problem Solving with Large Language Models.

Yao, S., et al. · 2023 · NeurIPS

https://arxiv.org/abs/2305.10601

ReAct: Synergizing Reasoning and Acting in Language Models.

Yao, S., Zhao, J., Yu, D., Du, N., Shafran, I., Narasimhan, K., & Cao, Y. · 2023 · ICLR 2023

https://arxiv.org/abs/2210.03629

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.