Advanced Agents From First Principles 04: Does Your Agent Prune Good Ideas Too Early? Use Monte Carlo Tree Search for Long-Horizon Reasoning

Page content

A common failure in search-based agents is easy to miss.

The agent generates several plausible branches.

It scores them.

One branch looks weak.

So the runtime prunes it.

Later, you discover that the discarded branch was the only one that could have reached the correct solution.

The problem was not generation.

The problem was not necessarily the model.

The problem was search allocation.

The agent spent too much compute exploiting what looked good early and too little compute exploring alternatives whose value only became visible later.

That is the kind of problem Monte Carlo Tree Search is designed to address.

This article builds MCTS from first principles and asks the question that matters in production:

When does MCTS actually solve a problem that simpler search does not?

The answer is not “when the task is hard.”

The answer is closer to:

Use MCTS when early evaluations are unreliable, consequences unfold over multiple steps, and additional exploration can reveal delayed value that greedy pruning would otherwise destroy.

That distinction matters.

Because MCTS can also be expensive, noisy, slow, and completely unnecessary.


The Problem: Early Scores Can Lie

In the previous post we built Tree of Thoughts as search over intermediate semantic states.

The simplest version looked like this:

state
branch
evaluate
keep top K
branch again

That is often implemented as beam-style search.

At every depth, the best-looking partial states survive.

That works well when the evaluator is reasonably predictive of eventual success.

But suppose the search space looks like this:

root
├── A  score 0.90
│   └── A1 score 0.88
│       └── terminal FAIL
├── B  score 0.74
│   └── B1 score 0.80
│       └── terminal PASS
└── C  score 0.68
    └── C1 score 0.70
        └── terminal FAIL

A beam width of one chooses A.

A shallow heuristic says A is clearly best.

But the terminal evidence says B was the correct route.

The search algorithm has confused:

looks promising now

with:

produces good outcomes later

MCTS exists to manage that uncertainty.


Search Is Compute Allocation

It helps to stop thinking of MCTS as a mysterious game-playing algorithm.

At the agent level, MCTS is a way of deciding:

Which partial trajectory deserves the next unit of compute?

Suppose you have 100 model calls available.

You could spend them evenly:

A: 25
B: 25
C: 25
D: 25

Or greedily:

A: 70
B: 10
C: 10
D: 10

Or adaptively:

A initially looks strongest
explore A
A's deeper outcomes disappoint
redirect compute toward B
B begins producing stronger terminal evidence
allocate more compute to B

That last pattern is the heart of MCTS.

It continuously balances two competing objectives:

exploit what currently looks good

and:

explore what remains uncertain

The Four Operations

A basic MCTS loop contains four operations:

1. selection
2. expansion
3. simulation / evaluation
4. backpropagation

Then it repeats.

root
selection
leaf
expansion
new child
simulation / evaluation
reward
backpropagation
update tree statistics
repeat

Let’s build each part.


1. Selection

Selection chooses the existing tree node that should receive more search effort.

If we always choose the node with the highest average reward, we become greedy.

That creates a familiar failure:

first lucky branch
looks best
gets more samples
other branches remain underexplored

We need a mechanism that rewards both:

  • high estimated value
  • insufficient exploration

A common form is UCB1.

For child node i:

UCB(i) = mean_reward(i)
       + c * sqrt(log(parent_visits) / child_visits(i))

The first term is exploitation.

mean_reward(i)

The second term is exploration.

sqrt(log(parent_visits) / child_visits(i))

The constant c controls how aggressively we explore.

Large c:

more exploration

Small c:

more exploitation

The important thing is not the exact equation.

The important thing is the policy:

A branch that looks good should receive more compute, but a branch that has barely been tested should not be discarded merely because its current estimate is uncertain.


A Tiny UCB Function

from __future__ import annotations

import math


def ucb_score(
    mean_reward: float,
    parent_visits: int,
    child_visits: int,
    exploration: float = 1.4,
) -> float:
    if child_visits == 0:
        return float("inf")

    return mean_reward + exploration * math.sqrt(
        math.log(max(parent_visits, 1)) / child_visits
    )

Unvisited children get infinite priority.

That forces the runtime to gather at least some evidence before assuming they are worthless.


2. Expansion

Once selection reaches a node that can be expanded, the agent generates one or more possible next states.

For a coding agent:

current repository state
├── inspect failing test
├── inspect implementation
├── inspect recent git diff
└── reproduce failure locally

For incident response:

current incident state
├── inspect application logs
├── inspect database latency
├── inspect queue depth
└── inspect deployment history

For research:

current hypothesis
├── search primary source A
├── search dataset B
├── test alternative explanation C
└── inspect contradiction D

Expansion is normally model-driven.

But it does not have to be.

The available actions might come from a deterministic action space.

That is often better when the domain already has clearly defined operations.


3. Simulation or Evaluation

Classic MCTS often performs a rollout from the newly expanded node until a terminal outcome is reached.

For an agent, that may be expensive.

A rollout could mean:

  • multiple LLM calls
  • tool execution
  • running tests
  • querying external systems
  • simulated actions
  • invoking a cheaper value model

So in practice we usually have several options.

Full rollout

partial state
continue until terminal state
measure terminal reward

Strong evidence.

Potentially expensive.

Partial rollout

partial state
advance 2-3 steps
evaluate resulting state

Cheaper.

Less reliable.

Value model

partial state
learned scorer estimates eventual success

Very cheap.

But now MCTS depends heavily on value-model calibration.

Environment proxy

candidate patch
run targeted tests
reward from failing-test reduction

Often excellent when available.

For agent systems, this is crucial:

Use real environment evidence whenever the environment can cheaply reveal progress.

Do not replace a test suite with an LLM saying the patch “looks likely to work.”


4. Backpropagation

After evaluating a node, MCTS propagates the resulting reward back through the path that produced it.

Suppose the selected path was:

root
B
B3
B3a

And the rollout reward was:

0.9

Then every node in the path updates its statistics.

For example:

node.visits += 1
node.value_sum += reward

Its mean value becomes:

mean = node.value_sum / node.visits

The important consequence is that early nodes slowly accumulate evidence about what their descendants tend to produce.

That is why MCTS can recover from misleading shallow evaluations.


A Minimal Node

from dataclasses import dataclass, field
from typing import Any


@dataclass
class SearchNode:
    state: Any
    parent: "SearchNode | None" = None
    action: str | None = None
    children: list["SearchNode"] = field(default_factory=list)
    visits: int = 0
    value_sum: float = 0.0
    terminal: bool = False

    @property
    def mean_value(self) -> float:
        if self.visits == 0:
            return 0.0
        return self.value_sum / self.visits

This node contains two different kinds of information.

The semantic state:

state

And the search statistics:

visits
value_sum
mean_value

That distinction becomes important later.


A Minimal MCTS Skeleton

class MCTS:
    def __init__(self, exploration: float = 1.4):
        self.exploration = exploration

    def select_child(self, node: SearchNode) -> SearchNode:
        return max(
            node.children,
            key=lambda child: ucb_score(
                mean_reward=child.mean_value,
                parent_visits=node.visits,
                child_visits=child.visits,
                exploration=self.exploration,
            ),
        )

    def backpropagate(self, node: SearchNode, reward: float) -> None:
        current = node

        while current is not None:
            current.visits += 1
            current.value_sum += reward
            current = current.parent

This is not yet a complete agent.

But the allocation mechanism is already visible.


Tree of Thoughts vs MCTS

This distinction gets confused constantly.

A typical beam-style Tree of Thoughts system asks:

Which K branches look best right now?

Then it keeps those branches.

MCTS asks something closer to:

Which branch should receive the next unit of search compute,
considering both what we know and what we still do not know?

That difference matters when value is delayed.

evaluate frontier
keep top K
discard rest

Once a branch is pruned, it is gone.

MCTS

retain tree statistics
allocate visits adaptively
underexplored branches remain eligible

The branch does not have to look best immediately.

It has to justify further exploration relative to alternatives.


When MCTS Is Actually Useful

MCTS is especially attractive when four conditions hold.

1. Early scores are weak predictors

If a shallow score already predicts terminal success extremely well, beam search is simpler.

MCTS helps when:

partial-state score
poorly predicts
terminal outcome

2. The task has delayed consequences

For example:

architectural change
seems larger initially
removes entire class of downstream failures

A greedy search might prefer the smaller patch.

MCTS can keep allocating some compute to the more uncertain structural branch.

3. Branch statistics are reusable

Repeated visits must tell us something useful.

If every rollout is unrelated noise, backpropagating averages does little.

4. Search is expensive enough that allocation matters

If you can exhaustively evaluate every branch cheaply, you do not need MCTS.

Just evaluate everything.


When MCTS Is Overkill

There are many cases where MCTS should not be your first answer.

If the task is:

one prompt
one good answer

use one model call.

If the task is:

generate 5 complete candidates
verify each
choose best

use Best-of-N.

If the task is:

branch a few times
partial score is reliable
keep top K

use beam search / Tree of Thoughts.

MCTS becomes interesting when:

branch quality is uncertain
+
reward arrives late
+
search budget is limited
+
repeated evidence improves value estimates

The Most Important Question: What Is the Reward?

A beautiful search policy cannot rescue a bad reward signal.

Suppose your coding agent rewards:

number of tests passed

That may encourage:

remove failing tests

or:

skip test execution

or:

modify assertions

depending on what actions the runtime permits.

The reward must represent the actual objective.

For example:

required tests pass
+
no regression tests fail
+
changed behavior matches specification
+
patch scope remains bounded

The exact weights matter less than the principle:

The reward function is part of the agent specification.


Reward Shaping Can Help—and Mislead

Terminal rewards are often sparse.

PASS = 1
FAIL = 0

That is clean but may provide little guidance.

We can add intermediate signals.

For a coding agent:

+0.2 target test failure reproduced
+0.3 number of failing tests reduced
+0.3 target test passes
+0.2 full suite passes

But reward shaping creates a new risk.

The agent may optimize the proxy instead of the goal.

So we should always retain a distinction between:

search reward

and:

final verification

The search reward guides compute.

The verifier decides whether the result is actually acceptable.


Search Reward Is Not Verification

This distinction is so important that it is worth making explicit.

high MCTS value
verified success

A branch can accumulate excellent expected reward and still fail final acceptance.

Therefore the architecture should look like:

MCTS
select candidate trajectory
execute / materialize result
external verifier
PASS / FAIL / UNKNOWN

The tree decides where to search.

The verifier decides whether reality supports the result.


Coding Agent Example

Consider a coding agent debugging a failing integration test.

The root state contains:

repository state
failing test
error message
current diff

Initial branches might be:

A: patch failing function directly
B: inspect call graph
C: inspect recent schema change
D: inspect test fixture

A superficial score might rank A first.

It proposes the obvious fix.

But perhaps the real defect is a schema mismatch introduced two layers earlier.

MCTS can allocate some rollouts to B and C.

A rollout for B might become:

inspect call graph
identify shared parser
modify parser
run targeted tests
run regression suite

The terminal evidence can then flow backward to B.

Over repeated visits, the search policy learns that B produces stronger outcomes than A even though A looked better initially.


Coding Agents: A Good MCTS Reward

For code, the strongest reward often comes from the environment.

For example:

def coding_reward(result) -> float:
    score = 0.0

    if result.reproduced_failure:
        score += 0.10

    if result.target_test_passes:
        score += 0.40

    if result.regression_suite_passes:
        score += 0.40

    if result.diff_is_bounded:
        score += 0.10

    return score

But final acceptance still remains separate.

def verify_patch(result) -> bool:
    return (
        result.target_test_passes
        and result.regression_suite_passes
        and result.requirements_satisfied
    )

Research Agent Example

A research agent may have several competing explanations.

hypothesis A
hypothesis B
hypothesis C

Early evidence may favor A.

But additional evidence could overturn it.

MCTS can treat evidence acquisition itself as part of the action space.

A
├── inspect source supporting A
├── inspect source contradicting A
└── search for primary dataset

This is much healthier than repeatedly asking an LLM:

How confident are you in hypothesis A?

The search should spend compute reducing uncertainty through evidence.


Planning and Scheduling

Scheduling problems are another natural fit.

A partial schedule may look mediocre because it reserves capacity.

But that capacity may prevent a major conflict later.

Greedy planning often prefers immediate utilization.

MCTS can evaluate longer-horizon consequences.

Examples include:

  • job-shop scheduling
  • delivery routing
  • resource allocation
  • workflow orchestration
  • project sequencing

The important condition remains the same:

Early local quality is not a reliable proxy for eventual global quality.


Incident Response

Incident-response agents also have delayed consequences.

Suppose the agent sees high database latency.

Possible actions:

restart database
inspect query plan
inspect recent deployment
inspect connection pool

Restarting might produce an immediate improvement.

That gives it a high short-term reward.

But if the root cause is a bad deployment, the problem returns.

A longer branch:

inspect deployment
identify query regression
rollback safely
verify latency
verify error rate

may have better long-term value.

This is exactly the kind of delayed-payoff structure where MCTS can become useful.


Browser and UI Agents

MCTS can also help browser agents when navigation choices create delayed constraints.

For example:

checkout
├── guest checkout
├── create account
└── login existing account

One path might appear shorter.

But later it may require information the agent does not have.

A more expensive early branch could reduce downstream uncertainty.

Again:

shortest immediate path
best complete trajectory

The Action Space Matters

MCTS assumes the search tree contains useful actions.

If expansion generates nonsense, search cannot rescue it.

So we still need strong action-space design.

For every node ask:

What actions are actually legal here?

not:

What random ideas can the model imagine?

For a coding agent, legal actions might include:

read_file
search_symbol
run_test
inspect_diff
apply_patch

The runtime can constrain expansion according to state.

if no patch exists:
    do not offer revert_patch

This dramatically reduces branching noise.


Progressive Widening

Large agent action spaces create another problem.

Suppose each node can produce 50 plausible children.

Expanding them all immediately is expensive.

Progressive widening addresses this by allowing the number of children to grow with node visits.

Conceptually:

few visits
few children

many visits
allow more children

That lets the search deepen promising nodes before exploding the branching factor.

A simple policy might be:

allowed_children = max(1, int(node.visits ** 0.5))

The exact schedule should be benchmarked.

The principle is what matters.


Value Noise

LLM-based value functions can be extremely noisy.

Ask the same model to score the same partial branch repeatedly and you may get:

0.82
0.67
0.91
0.73

If MCTS treats each score as precise, the search statistics become misleading.

Useful diagnostics include:

value mean
value variance
rank stability
confidence interval

For high-noise evaluators, repeated evaluation may be justified.

But before adding more model calls, ask whether the environment offers a stronger signal.


Correlated Rollouts

Ten rollouts are not necessarily ten independent pieces of evidence.

If every rollout uses:

  • the same model
  • the same prompt
  • the same retrieval context
  • the same tool policy

then their errors may be highly correlated.

You can have:

10 visits

but much less than:

10 independent observations

This is the same problem we saw with self-consistency.

So rollout diversity matters.

Possible variations include:

  • strategy-conditioned rollout policies
  • different model tiers
  • deterministic environment checks
  • different retrieved evidence
  • different tool routes

But diversity should be purposeful.

Randomness alone is not evidence quality.


Exploration Constant Failure Modes

The exploration coefficient controls how aggressively MCTS revisits uncertain branches.

Too low:

search becomes nearly greedy

Too high:

search wastes compute on obviously weak branches

Do not treat 1.4 as a magical constant just because it appears in examples.

Sweep it.

c = 0.25
c = 0.5
c = 1.0
c = 1.4
c = 2.0

Then measure verified outcomes, not tree aesthetics.


Search Budgets Must Be Explicit

MCTS is dangerous without budgets because the algorithm always has another branch worth investigating.

Bound at least:

max iterations
max tree nodes
max model calls
max tool calls
max wall time
max cost
max depth

For example:

@dataclass
class SearchBudget:
    max_iterations: int = 100
    max_nodes: int = 500
    max_model_calls: int = 250
    max_seconds: float = 60.0
    max_cost_usd: float = 2.0

A production search system should have a named termination reason.

VERIFIED_SUCCESS
ITERATION_BUDGET
NODE_BUDGET
TIME_BUDGET
COST_BUDGET
NO_PROGRESS
NO_VALID_ACTIONS

Early Stopping

MCTS does not need to consume the entire budget.

Useful stopping conditions include:

verified terminal success

or:

best branch value stable for N iterations

or:

additional search produces no meaningful improvement

or:

value gap between best and alternatives exceeds threshold

But be careful.

Stopping because one branch’s model score looks dominant can recreate the exact premature-pruning problem MCTS was introduced to solve.

Whenever possible, use stronger evidence.


Do Not Search Irreversible Side Effects

This rule from the earlier search post becomes even more important with MCTS.

You do not want rollouts that repeatedly:

send emails
charge credit cards
restart production services
merge pull requests
delete data

Search should usually operate over:

  • plans
  • simulated state
  • sandboxed environments
  • temporary worktrees
  • read-only diagnostics
  • reversible transformations

Then select a trajectory.

Then execute the real side effect once.

search
select
verify preconditions
commit irreversible action
verify postconditions

A More Complete MCTS Skeleton

Here is a deliberately small implementation structure.

from dataclasses import dataclass
from typing import Protocol


class Environment(Protocol):
    def actions(self, state): ...
    def transition(self, state, action): ...
    def is_terminal(self, state) -> bool: ...
    def reward(self, state) -> float: ...


@dataclass
class MCTSConfig:
    iterations: int = 100
    exploration: float = 1.4
    max_depth: int = 8


class MCTSAgent:
    def __init__(self, env: Environment, config: MCTSConfig):
        self.env = env
        self.config = config

    def search(self, root: SearchNode) -> SearchNode:
        for _ in range(self.config.iterations):
            leaf = self._select(root)
            child = self._expand(leaf)
            reward = self._evaluate(child)
            self._backpropagate(child, reward)

        return self._best_root_child(root)

Now the methods.

    def _select(self, node: SearchNode) -> SearchNode:
        current = node

        while current.children and not current.terminal:
            current = max(
                current.children,
                key=lambda child: ucb_score(
                    child.mean_value,
                    current.visits,
                    child.visits,
                    self.config.exploration,
                ),
            )

        return current

Expansion:

    def _expand(self, node: SearchNode) -> SearchNode:
        if node.terminal:
            return node

        actions = self.env.actions(node.state)

        if not actions:
            node.terminal = True
            return node

        action = actions[0]
        next_state = self.env.transition(node.state, action)

        child = SearchNode(
            state=next_state,
            parent=node,
            action=str(action),
            terminal=self.env.is_terminal(next_state),
        )

        node.children.append(child)
        return child

Evaluation:

    def _evaluate(self, node: SearchNode) -> float:
        return self.env.reward(node.state)

Backpropagation:

    def _backpropagate(self, node: SearchNode, reward: float) -> None:
        current = node

        while current is not None:
            current.visits += 1
            current.value_sum += reward
            current = current.parent

And final selection:

    def _best_root_child(self, root: SearchNode) -> SearchNode:
        return max(root.children, key=lambda child: child.visits)

This is intentionally incomplete.

A serious implementation still needs:

  • unexpanded-action tracking
  • rollout policies
  • duplicate-state handling
  • budgets
  • provenance
  • concurrency control
  • state hashing
  • failure handling
  • verification

But the architecture is visible.


Why Select by Visits at the End?

During search we need exploration.

At the end, we do not.

So the final decision often uses:

most visited child

or:

highest mean value child

rather than UCB.

The exploration bonus is a search allocation device.

It should not automatically become the production decision rule.


Transpositions and Duplicate States

Agent trees often contain different paths that reach the same state.

For example:

read tests → inspect implementation

and:

inspect implementation → read tests

may lead to the same knowledge state.

If we represent them as completely independent nodes, the tree duplicates work.

A state fingerprint can help.

import hashlib
import json


def state_fingerprint(state: dict) -> str:
    canonical = json.dumps(state, sort_keys=True, default=str)
    return hashlib.sha256(canonical.encode()).hexdigest()

Then the runtime can detect repeated states.

This converts the search structure from a pure tree toward a graph.

That can substantially improve efficiency.


Provenance Is Mandatory

Advanced search quickly becomes impossible to debug without lineage.

Every node should record at least:

node_id
parent_id
action
state fingerprint
visit count
value mean
value variance
creation source
model / tool used
observations
verification evidence

Then when a surprising branch wins, you can reconstruct why.

Without provenance, MCTS becomes a stochastic black box wrapped around another stochastic black box.

That is not a production architecture.


Parallel MCTS

Agent workloads often involve expensive model or tool calls.

Parallel search is tempting.

But naive parallelism creates a problem.

Several workers may all select the same currently promising node before any of them updates its statistics.

That duplicates effort.

A common technique is a virtual loss.

Temporarily penalize a node while a worker explores it.

worker selects node
apply virtual loss
other workers prefer alternatives
worker finishes rollout
remove virtual loss
backpropagate real reward

This is useful when model calls dominate latency.

But parallelism should still be benchmarked against cost and duplicated work.


Learned Value Models

The model series now becomes directly useful again.

Instead of asking an LLM judge to score every node, we could use a learned value model.

Conceptually:

partial agent state
encoder
value model
estimated probability of eventual success

That value model might be simple.

For example:

MR.Q-style scalar scorer

or recurrent if trajectory history matters.

But the evidence rule remains:

A value model earns its place only if it predicts eventual verified outcomes well enough to improve search allocation.

Do not use it because the architecture diagram looks sophisticated.


Calibrating the Value Model

Suppose the scorer predicts:

0.8

for 100 states.

Roughly 80 should eventually succeed if the score is well calibrated as a probability.

Useful diagnostics include:

Brier score
calibration curve
rank correlation
AUC
precision among top-ranked branches

But the most important metric remains downstream:

Does MCTS using this scorer improve verified task success?

Failure Attribution

When MCTS fails, do not say:

MCTS did not work

Localize the failure.

No correct branch ever appears

generator / action-space failure

Correct branch appears but receives few visits

selection / exploration failure

Correct branch receives visits but low rewards

evaluator / rollout failure

Correct branch has good evidence but loses at root selection

aggregation / final-selection failure

Correct branch wins search but task still fails

execution / verification failure

That decomposition is extremely useful in production.


Search Metrics

For every run, log:

iterations
nodes created
nodes revisited
max depth
branching factor
rollouts
model calls
tool calls
value evaluations
unique states
wall time
cost
termination reason
verified outcome

Then add search-quality metrics.

oracle generated success
best verified branch discovered
visit concentration
value variance
root action entropy
pruning / allocation regret

For example:

oracle discovered success = true
selected branch verified success = false

This immediately tells us the search generated a winning trajectory but failed to allocate/select correctly.


The Experiment That Justifies MCTS

Do not compare MCTS against nothing.

Compare it against increasingly complex baselines.

A: one-shot agent
B: Best-of-N
C: Tree of Thoughts / beam width 2
D: Tree of Thoughts / beam width 4
E: MCTS 25 iterations
F: MCTS 100 iterations

Measure:

verified success
oracle discovered success
model calls
tool calls
latency
cost
cost per verified success

Then ask:

Does adaptive exploration outperform simple frontier pruning?

If not, keep the simpler system.


Example Benchmark Table

architecture      success   calls   latency   cost
---------------------------------------------------
one-shot            62%        1      1.0x    1.0x
Best-of-5           71%        5      1.8x    5.0x
beam width 3        78%       14      3.2x   14.0x
MCTS 25             79%       23      4.5x   23.0x
MCTS 100            80%       88     12.0x   88.0x

If your real data looks like this, MCTS may not be worth it.

The gain from beam search to MCTS is tiny.

A better result might look like:

architecture      success   calls
----------------------------------
beam width 3        61%       14
MCTS 25             74%       23

Now the adaptive search policy may be solving a real delayed-value problem.


Application Matrix

Software type Search state Delayed payoff example Strong reward signal
Coding agent repository + hypotheses + diff larger architectural fix beats small local patch tests + regression suite
Research agent hypotheses + evidence graph initially weak hypothesis gains primary-source support source-backed claim verification
Incident response system observations + interventions root-cause fix beats temporary restart latency/error recovery + stability
Scheduling partial schedule reserved capacity avoids later conflict constraint satisfaction + objective score
Browser automation navigation/form state longer early route avoids blocked later state successful final transaction/state
Data agent transformation pipeline more expensive cleanup prevents downstream validation failure schema/data validation
Optimization partial candidate weak local score leads to superior global optimum objective function

The same architecture appears in all of them.

What changes is the state, action space, rollout policy, and reward.


A Practical Decision Rule

Use this progression.

Can one model call solve it reliably?
        ↓ no
Can Best-of-N expose enough alternatives?
        ↓ no
Can shallow scores reliably rank partial states?
        ↓ yes
Use beam / Tree of Thoughts
        ↓ no
Do branches have delayed consequences?
        ↓ yes
Does repeated exploration improve estimates?
        ↓ yes
Consider MCTS

That is the right way to reach MCTS.

Not:

MCTS sounds advanced
let's add MCTS

If Your MCTS Agent Is Too Slow

Search for the source of cost.

Too many expansions

Reduce branching factor.

Use state-dependent action sets.

Use progressive widening.

Rollouts too expensive

Use staged evaluation.

cheap heuristic
only promising nodes
expensive environment check

Too much duplicate work

Add state fingerprints and transposition reuse.

Too many similar rollouts

Increase strategy diversity or reduce redundant sampling.

Tree keeps growing after answer is obvious

Add evidence-based stopping conditions.


If MCTS Keeps Choosing the Wrong Branch

Inspect four things.

1. Reward alignment

Does the search reward actually correlate with verified success?

2. Exploration

Are uncertain branches getting enough visits?

3. Rollout quality

Are rollouts representative of what would actually happen?

4. Final selection

Are you selecting by visits, mean value, verifier result, or some accidental mixture?

Do not immediately increase the iteration budget.

More search with the wrong reward often makes the wrong answer more confident.


If More Iterations Make Results Worse

That is a very useful signal.

Possible causes include:

biased value model
misaligned reward
correlated rollouts
exploration coefficient too low
systematic simulator error
final selector mismatch

If search quality falls as compute rises, you have likely built an optimization process around the wrong signal.

That is more serious than simple randomness.


MCTS Is Not Intelligence

It is easy to anthropomorphize the behavior.

The agent “considers alternatives.”

The agent “changes its mind.”

The agent “plans ahead.”

But operationally:

MCTS = structured allocation of finite search compute

That is enough.

And it gives us a much better engineering question:

Does this allocation policy produce more verified successes per unit of compute than the simpler alternatives?


The Full Advanced Stack So Far

We can now see the progression clearly.

chain of thought
explicit intermediate computation

self-consistency
sample multiple completed reasoning trajectories

Tree of Thoughts
branch during reasoning and prune partial states

MCTS
adaptively allocate search compute using accumulated value evidence

These are not four levels of intelligence.

They are four different compute-allocation mechanisms.

Use the smallest one that fixes the failure you can actually measure.


What Comes Next

MCTS assumes the branches are competing trajectories through roughly the same problem-solving machinery.

But sometimes the deeper problem is not search.

It is specialization.

A cheap local model might be excellent at classification.

A code model might be best at patch generation.

A deterministic tool might be best at arithmetic.

A frontier model might only be needed for genuinely ambiguous reasoning.

Instead of asking:

Which branch should receive more search?

we can ask:

Which expert should receive this task at all?

That leads to the next post:

Advanced Agents From First Principles 05: Is One Model Doing Everything? Build a Mixture of Experts at the Agent Level.

And that takes us from search allocation to expert allocation.