Reasoning Architectures · Steps 00–11Chapter 10 of 45

Are You Combining Every Agent Technique Into One Monster? Build a Mixture-of-Agents Runtime

Page content

You have a working agent.

Then you add retrieval.

Then memory.

Then Best-of-N.

Then critique and revision.

Then Tree of Thoughts.

Then MCTS.

Then specialist models.

Then adversarial review.

Then a planner, executor, critic and verifier.

Then a stronger model for hard cases.

Eventually the architecture starts to look like this:

request
planner
retrieval
reasoning
Best-of-N
critic
Tree of Thoughts
MCTS
frontier model
second critic
verifier

Every mechanism may have been individually reasonable.

The combined system can still be terrible.

It is slow.

It is expensive.

It is difficult to debug.

It is difficult to know which component actually helped.

And most importantly:

every request pays for every capability whether it needs it or not.

The answer is not to build an even larger agent.

The answer is to stop treating advanced techniques as permanent layers.

Treat them as alternative strategies.

Then route between those strategies.

That gives us an agent-level architecture that is closer to a Mixture of Agents:

                          ┌→ direct model
                          ├→ deterministic workflow
                          ├→ retrieval + answer
request → strategy router ├→ Best-of-N
                          ├→ critique + revise
                          ├→ Tree of Thoughts
                          ├→ MCTS
                          ├→ specialist runtime
                          ├→ adversarial review
                          └→ human escalation
                              verification

The important idea is not the name.

The important idea is this:

Do not combine every reasoning mechanism into one agent. Route the task to the smallest strategy that can solve it reliably.

This is the architectural synthesis of the advanced-agent techniques we have built so far.


The Problem: Architecture Accretion

Agent systems often become complicated one feature at a time.

A production failure appears.

Someone adds another mechanism.

failure
new mechanism
new failure
new mechanism

The individual decisions can all make sense.

The failure is that the mechanisms become stacked rather than selected.

Suppose a simple repository question can be answered by reading one file.

A badly accreted system might still perform:

  1. planning,
  2. retrieval,
  3. multiple reasoning samples,
  4. critic review,
  5. plan revision,
  6. specialist routing,
  7. final verification.

The task may cost ten times more without becoming meaningfully more reliable.

The system has confused:

capabilities available to the runtime

with:

capabilities that must execute on every request

Those are completely different things.


From Mixture of Experts to Mixture of Agents

In the previous routing post we built an agent-level Mixture of Experts.

That architecture routed work between different capabilities:

task
router
 ├→ local model
 ├→ frontier model
 ├→ code specialist
 ├→ retriever
 ├→ deterministic tool
 └→ verifier

A Mixture-of-Agents runtime moves the routing boundary upward.

Instead of routing only between experts, it can route between entire control strategies.

For example:

Strategy: DIRECT
model → answer → verify

or:

Strategy: RETRIEVAL
retrieve → answer → verify

or:

Strategy: SEARCH
branch → evaluate → prune → continue → verify

or:

Strategy: MCTS
select → expand → evaluate → backpropagate → verify

or:

Strategy: REVIEW
proposal → adversarial critics → evidence checks → adjudicate

These are not merely different prompts.

They are different runtime policies for spending computation.


The Core Abstraction: A Strategy

A useful strategy interface is surprisingly small.

from dataclasses import dataclass
from typing import Protocol, Any


@dataclass
class StrategyResult:
    output: Any
    status: str
    evidence: list[dict]
    cost: float
    latency_ms: int
    calls: int


class AgentStrategy(Protocol):
    name: str

    def supports(self, state: dict) -> bool:
        ...

    def estimate_cost(self, state: dict) -> float:
        ...

    def run(self, state: dict) -> StrategyResult:
        ...

The strategy does not need to know about every other strategy.

It only needs a contract:

input state
strategy
structured result + evidence + resource usage

That gives the runtime something it can compare.


A Strategy Registry

Now we can register available strategies.

class StrategyRegistry:
    def __init__(self):
        self._strategies = {}

    def register(self, strategy: AgentStrategy):
        self._strategies[strategy.name] = strategy

    def available(self, state: dict):
        return [
            strategy
            for strategy in self._strategies.values()
            if strategy.supports(state)
        ]

A real registry might contain:

DIRECT
DETERMINISTIC_WORKFLOW
RETRIEVAL
BEST_OF_N
CRITIQUE_REVISE
TREE_OF_THOUGHTS
MCTS
SPECIALIST_ROUTING
PLANNER_EXECUTOR_CRITIC
ADVERSARIAL_REVIEW
HUMAN_ESCALATION

The important difference is architectural:

these mechanisms are alternatives available to the controller, not mandatory stages.


What Should the Router Look At?

A strategy router needs state.

Not just the user prompt.

Useful routing signals include:

task type
risk
current verification status
available evidence
uncertainty
previous failures
remaining budget
side-effect severity
latency target
required precision
search-space size
known deterministic path

Represent them explicitly.

@dataclass
class StrategyContext:
    task_type: str
    risk: float
    uncertainty: float
    previous_failures: int
    verification_status: str
    remaining_budget: float
    deterministic_path_known: bool
    irreversible_action: bool

Then routing becomes a decision over system state.


Start With a Deterministic Router

Do not immediately train another model to route your agents.

Start with rules.

def choose_strategy(ctx: StrategyContext) -> str:
    if ctx.deterministic_path_known:
        return "DETERMINISTIC_WORKFLOW"

    if ctx.risk < 0.2 and ctx.uncertainty < 0.2:
        return "DIRECT"

    if ctx.task_type == "fact_lookup":
        return "RETRIEVAL"

    if ctx.previous_failures == 0 and ctx.uncertainty < 0.5:
        return "CRITIQUE_REVISE"

    if ctx.previous_failures >= 1 and ctx.uncertainty < 0.8:
        return "TREE_OF_THOUGHTS"

    if ctx.remaining_budget > 5.0:
        return "MCTS"

    return "HUMAN_ESCALATION"

This is intentionally crude.

That is useful.

You can inspect it.

You can measure it.

You can discover where it fails.

Only then do you have evidence that a learned router may be worth building.


Strategy Routing Is Not Model Routing

These are different decisions.

Model routing

task
local model / frontier model / specialist model

Strategy routing

task state
direct / retrieval / revision / search / MCTS / review

A strategy can itself contain model routing.

For example:

TREE_OF_THOUGHTS strategy
local model generates branches
learned scorer ranks branches
frontier model handles difficult branch
external verifier checks result

This produces a hierarchy:

strategy router
strategy
expert/model/tool router

Do not collapse those layers unless the simpler representation actually works better.


The Simplest Strategy Should Be a First-Class Expert

Advanced systems often make a subtle mistake.

They include many sophisticated routes but forget to include:

just answer the question

That should be a real strategy.

class DirectStrategy:
    name = "DIRECT"

    def supports(self, state):
        return True

    def estimate_cost(self, state):
        return 0.01

    def run(self, state):
        output = call_model(state["prompt"])
        return StrategyResult(
            output=output,
            status="PROPOSED",
            evidence=[],
            cost=0.01,
            latency_ms=300,
            calls=1,
        )

Why?

Because the direct strategy is the baseline every advanced strategy must beat.

If Tree of Thoughts takes twenty calls and improves verified success from 96% to 96.2%, that may be a terrible trade.


Strategy Selection Needs Verification

A router can be perfectly confident and still choose the wrong strategy.

So the runtime should not assume:

route selected
=
task solved

Instead:

route selected
strategy runs
external verification
PASS / FAIL / UNKNOWN

The verifier produces the feedback that allows strategy selection to become adaptive.


Failure Should Change Strategy, Not Just Repeat It

A common agent anti-pattern is:

strategy fails
run same strategy again
run same strategy again

That is retry, not adaptation.

A better controller asks:

what failure occurred?

Then changes strategy.

For example:

DIRECT failed because evidence missing
RETRIEVAL
RETRIEVAL failed because sources conflict
ADVERSARIAL_REVIEW
DIRECT produced several plausible implementations
BEST_OF_N
Best-of-N candidates all fail tests differently
TREE_OF_THOUGHTS
Tree search has delayed payoff and weak early scores
MCTS

The escalation is failure-specific.


A Runtime Controller

A simple controller might look like this:

class MixtureOfAgentsRuntime:
    def __init__(self, registry, router, verifier):
        self.registry = registry
        self.router = router
        self.verifier = verifier

    def run(self, state: dict):
        history = []

        while True:
            strategy = self.router.choose(state, history)
            result = strategy.run(state)
            verification = self.verifier.verify(state, result)

            history.append({
                "strategy": strategy.name,
                "result": result,
                "verification": verification,
            })

            if verification.status == "PASS":
                return result, history

            if verification.status == "FAIL":
                state = update_state_from_failure(
                    state,
                    verification,
                )

            if verification.status == "UNKNOWN":
                state = request_more_evidence(
                    state,
                    verification,
                )

            if budget_exhausted(state, history):
                return None, history

Notice what is missing.

There is no giant universal agent prompt.

The runtime controls composition.


Dynamic Composition

Sometimes one strategy is not enough.

The router may compose strategies sequentially.

For example:

RETRIEVAL
DIRECT
VERIFY

or:

TREE_OF_THOUGHTS
BEST_OF_N on final branches
VERIFY

or:

PLANNER_EXECUTOR
ADVERSARIAL_REVIEW
VERIFY

The danger is that dynamic composition can recreate the original monster architecture.

So composition needs the same rule:

Every added stage must be triggered by a measured failure or uncertainty.

Do not compose mechanisms merely because they are available.


The Monster-Agent Failure Mode

Suppose every request always runs:

planner
+ retrieval
+ self-consistency
+ Tree of Thoughts
+ MCTS
+ two critics
+ frontier verifier

That architecture has almost no useful adaptation.

It is simply a very expensive fixed pipeline.

A mixture architecture should instead look like:

Easy request
→ DIRECT

Known deterministic task
→ WORKFLOW

Knowledge gap
→ RETRIEVAL

Output variance
→ SELF_CONSISTENCY

Premature reasoning commitment
→ TREE_OF_THOUGHTS

Delayed-payoff search
→ MCTS

Specialist capability needed
→ EXPERT_ROUTING

High-risk unresolved claims
→ ADVERSARIAL_REVIEW

Different tasks should produce visibly different trajectories.

If they do not, your router is probably decorative.


Measure Strategy Utilization

Track how often each strategy is selected.

DIRECT                 54%
RETRIEVAL              18%
CRITIQUE_REVISE        11%
TREE_OF_THOUGHTS        7%
SPECIALIST_ROUTING      5%
MCTS                    2%
ADVERSARIAL_REVIEW      2%
HUMAN_ESCALATION        1%

The target is not equal utilization.

Equal utilization may actually be suspicious.

If MCTS is only justified for 2% of tasks, 2% may be exactly right.

The question is:

did each strategy earn the tasks it received?

Strategy Confusion Matrix

If you have labeled evaluation tasks, build a confusion matrix.

                    selected
                D   R   T   M   A
actual DIRECT  81   4   3   0   2
actual RETR.    5  44   2   0   1
actual TOT      3   1  21   4   2
actual MCTS     0   0   5  12   1

This reveals systematic routing mistakes.

For example:

MCTS tasks → Tree of Thoughts

may indicate the router cannot detect delayed reward.

Or:

DIRECT tasks → MCTS

may indicate severe over-escalation.


Oracle Strategy Selection

A powerful offline diagnostic is oracle strategy selection.

For each benchmark task, run every eligible strategy.

Record which ones produce a verified success.

Task 17
DIRECT             FAIL
RETRIEVAL          PASS
BEST_OF_N          PASS
TREE_OF_THOUGHTS   PASS
MCTS               PASS

The cheapest successful strategy is RETRIEVAL.

That gives us an oracle target:

oracle strategy = RETRIEVAL

Now compare the router.

If it chose MCTS, the task succeeded but the router wasted compute.

If it chose DIRECT and stopped after failure, the router missed an available successful strategy.


Strategy Regret

Define strategy regret as the difference between the selected strategy and the best available strategy under the objective.

The objective might include:

verified success
cost
latency
risk

For example:

utility = (
    verified_success * 100
    - cost * 4
    - latency_seconds * 0.2
)

Then:

strategy regret
=
oracle utility - selected utility

This is much more useful than raw routing accuracy.

A router that selects a slightly different but equally successful cheap strategy may be perfectly acceptable.


Success Per Unit Compute

The main production metric should not be:

most sophisticated strategy selected

It should be something closer to:

verified successful tasks
-------------------------
compute / cost / latency

For example:

Strategy Verified success Avg cost Avg latency
Direct 82% $0.01 0.5s
Retrieval 91% $0.02 0.8s
Best-of-N 94% $0.06 2.2s
ToT 95% $0.18 6.5s
MCTS 96% $0.70 21s

The conclusion is not:

MCTS wins

The conclusion may be:

MCTS belongs only on the small set of tasks where cheaper strategies fail.

That is exactly what the router is for.


Latency Budgets Matter

A strategy may be technically superior and operationally unusable.

For an interactive coding assistant:

500 ms

and:

30 seconds

are not interchangeable.

So routing constraints should include latency.

@dataclass
class StrategyBudget:
    max_cost: float
    max_latency_ms: int
    max_calls: int
    max_search_nodes: int

A strategy that cannot fit inside the remaining budget should not be eligible.


Risk Changes Routing

Difficulty and risk are different dimensions.

A simple action can be high risk.

Example:

delete production database

The reasoning may be trivial.

The verification and authorization requirements are not.

So strategy selection should consider:

difficulty
uncertainty
risk

separately.

A low-difficulty/high-risk task might route to:

deterministic workflow
   +
strong authorization
   +
independent verifier

rather than to a complex reasoning strategy.


Authorization Is Not a Strategy Choice

The router may choose a powerful executor.

That must not grant additional permissions.

router chooses deployment expert

does not imply:

deployment expert may deploy to production

Authorization remains an independent runtime boundary.

strategy selection
capability request
authorization check
execution

Never let a model route itself into broader authority.


Coding Agents

A coding agent is an excellent example of strategy routing.

Direct

Use for:

explain this function
rename this local variable
write a small pure helper

Retrieval

Use when repository knowledge is missing:

where is authentication configured?
which module owns migrations?

Deterministic workflow

Use for known mechanical changes:

format
lint
run tests
update generated files

Best-of-N

Use when several candidate implementations are cheap to generate and test.

Tree of Thoughts

Use when multiple architectural directions should remain alive.

MCTS

Use only when early implementation quality is a weak predictor of eventual test/integration success and search statistics can be reused.

Adversarial review

Use for security, regression risk, API compatibility and architecture-boundary checks.

The repository state remains external truth throughout.


Research Agents

Research strategy routing might look like:

known factual lookup
→ retrieval

single clear primary source
→ retrieval + extraction

conflicting sources
→ adversarial evidence review

multiple plausible hypotheses
→ Tree of Thoughts

long multi-stage investigation
→ planner-executor

high disagreement after evidence
→ specialist adjudication

The point is not to make every research task a debate.

Most factual lookups should stay cheap.


Customer Support

Support agents benefit enormously from not treating all tickets equally.

password reset
→ deterministic workflow

order status
→ database/tool lookup

policy question
→ retrieval

ambiguous billing dispute
→ specialist route

high-value account escalation
→ human

A giant reasoning agent is usually worse than a well-routed support system.


Data and Analytics Agents

Different strategies fit different analytical tasks.

simple arithmetic
→ deterministic calculator

known SQL query pattern
→ deterministic query builder

schema discovery
→ retrieval/catalog tools

ambiguous metric definition
→ reasoning + evidence

several competing causal explanations
→ hypothesis search

high-impact conclusion
→ adversarial review + verification

Again, not everything should become multi-agent reasoning.


Browser Automation

Browser agents have especially strong reasons to route strategies carefully because actions can have side effects.

read page
→ browser tool

extract table
→ deterministic parser

navigate known flow
→ workflow

unknown site structure
→ planning/search

purchase / submit / delete
→ authorization + explicit verification

Search strategies should operate over read-only or reversible actions where possible.


DevOps and Incident Response

A DevOps mixture might contain:

log retriever
metrics analyzer
runbook workflow
root-cause hypothesis search
rollback planner
specialist reviewer
human incident commander

Routing should use live evidence:

alerts
logs
metrics
recent deployments
service topology
current incident state

Not just a textual description of the incident.


Router Confidence Is Not Success Confidence

These are separate signals.

router confidence
=
how sure are we this strategy is appropriate?
success confidence
=
how much evidence supports task completion?

A router might be 99% sure that MCTS is the right strategy.

The MCTS result can still fail verification.

Do not collapse the two.


Strategy Routing Can Learn

The previous post introduced learning from verified trajectories.

Strategy routing is an obvious policy surface.

A trajectory can record:

task features
selected strategy
alternative strategies tested offline
verification outcome
cost
latency
failure class

From this we can learn candidate routing rules.

For example:

When repository tests fail after two independent direct patches,
Tree of Thoughts has 31% higher verified success than repeated revision.

That is a candidate lesson.

It still needs evaluation before promotion.

Do not let one successful MCTS run rewrite the router.


Shadow Routing

A safe way to improve the router is shadow evaluation.

Production uses:

router_v4

while a candidate router computes:

router_v5_shadow

without controlling execution.

Compare:

selected strategy
oracle strategy
verified outcome
estimated cost
actual cost

Only then promote the new router.


Strategy-Level Failure Attribution

Once strategies are explicit, failures become easier to classify.

Routing failure

A better strategy existed but was not selected.

Strategy failure

The selected strategy was appropriate but executed poorly.

Expert failure

The strategy selected the wrong model/tool/specialist internally.

Budget failure

The correct strategy was selected but lacked enough resources.

Verification failure

The strategy succeeded but the verifier rejected it, or failed but the verifier passed it.

Authorization failure

A strategy attempted a capability it was not allowed to use.

Learning failure

Past verified evidence should have changed routing policy but did not, or a bad rule was promoted.

This separation is extremely valuable in production.


Do Not Build a Meta-Agent That Merely Thinks About Agents

A common temptation is:

user request
meta-agent
"which agent should I use?"

That can work.

But if the meta-agent is simply another unconstrained LLM call, you may have moved the problem instead of solving it.

Start with observable routing features and deterministic rules.

Add model-based strategy selection only when semantic classification genuinely helps.

Then measure its routing regret.


A Better Escalation Ladder

A useful production sequence is:

1. deterministic path if known
2. direct model
3. retrieval / tool evidence
4. revision or Best-of-N
5. specialist routing
6. Tree of Thoughts
7. adversarial review
8. MCTS
9. stronger model / larger budget
10. human escalation

This is not a universal ordering.

The correct ordering depends on the task.

But the principle is useful:

cheap, testable mechanisms first
expensive, speculative mechanisms later

Benchmark the Runtime, Not Just the Strategies

Individual strategy benchmarks are not enough.

The mixture itself needs evaluation.

Compare:

Baseline A

frontier model for everything

Baseline B

fixed advanced stack

Baseline C

deterministic rule router

Baseline D

adaptive router with verification feedback

Baseline E

learned router trained on verified trajectories

Measure:

verified success
cost
latency
strategy regret
unnecessary escalation rate
missed escalation rate
human escalation rate
failure recovery rate

That is the real test.


Important Metrics

Verified task success

The only success metric that ultimately matters.

Strategy utilization

How often each strategy runs.

Strategy regret

How much utility was lost relative to the best available strategy.

Unnecessary escalation rate

advanced strategy selected
when cheaper strategy would have verified successfully

Missed escalation rate

cheap strategy failed
and advanced strategy would have succeeded
but was not selected

Recovery rate

How often strategy switching turns a failed attempt into verified success.

Cost per verified success

The real economic denominator.

Latency per verified success

Critical for interactive systems.

Strategy-switch count

Too many switches may indicate thrashing.

Router calibration

Does route confidence correspond to actual route quality?


Strategy Thrashing

Adaptive systems can oscillate.

DIRECT
→ RETRIEVAL
→ DIRECT
→ RETRIEVAL
→ DIRECT

Or:

TREE_OF_THOUGHTS
→ MCTS
→ TREE_OF_THOUGHTS

That is strategy thrashing.

Prevent it with:

strategy history
failure fingerprints
switch limits
no-progress windows
budget limits

The router should know what has already been tried.


The Runtime Should Preserve Shared State

Strategies should not communicate by repeatedly summarizing the entire task in prose.

Use shared structured state.

@dataclass
class AgentState:
    goal: str
    constraints: list[str]
    observations: list[dict]
    evidence: list[dict]
    failures: list[dict]
    attempts: list[dict]
    budget_remaining: float

Then:

DIRECT fails

and:

TREE_OF_THOUGHTS starts

without losing the evidence already collected.

This avoids handoff loss across strategies.


Do Not Let Strategies Rewrite the Goal

The user goal should remain immutable unless the user changes it.

Strategies may propose interpretations.

They may not silently redefine success.

original goal
immutable goal contract
strategy-specific plan

The verifier checks against the original goal contract.

Not against whatever interpretation made the selected strategy look successful.


The Final Architecture

We can now describe a fairly complete advanced runtime.

                         ┌─────────────────────┐
                         │     Goal Contract   │
                         └──────────┬──────────┘
                         ┌─────────────────────┐
                         │   Structured State  │
                         └──────────┬──────────┘
                         ┌─────────────────────┐
                         │   Strategy Router   │
                         └──────────┬──────────┘
        ┌────────────────────────────────────────────────┐
        │                                                │
        ↓            ↓             ↓            ↓       ↓
     Direct       Retrieval       Search       MCTS    Review
        │            │             │            │       │
        └────────────┴─────────────┴────────────┴───────┘
                         ┌─────────────────────┐
                         │ External Verification│
                         └──────────┬──────────┘
                  PASS / FAIL / UNKNOWN / ESCALATE
                         ┌─────────────────────┐
                         │ Trajectory Evidence │
                         └──────────┬──────────┘
                         offline learning loop

This is not one giant intelligent agent.

It is a runtime that coordinates several bounded computational strategies.

That distinction matters.


The Deeper Pattern

Across this entire series, the advanced techniques can now be seen as different answers to one question:

Where should the next unit of computation go?

Chain of Thought:

into another reasoning step

Self-Consistency:

into another sample

Tree of Thoughts:

into another branch

MCTS:

into the branch whose exploration/exploitation value is highest

Mixture of Experts:

into the specialist with the right capability

Planner–Executor–Critic:

into the role responsible for the current failure

Adversarial Review:

into testing the most important unresolved claim

Adaptive Agents:

into whichever mechanism the current evidence justifies

Learning:

into improving future allocation policy

Mixture of Agents:

into the entire reasoning strategy with the highest expected utility

That is the architectural endpoint.


Do You Actually Need a Mixture of Agents?

Probably not at first.

Start here:

model
structured action
tool
verification

Only add strategy routing when you can show that different task classes genuinely benefit from different computational policies.

The evidence should look something like:

Task class A
DIRECT: 98% verified success
MCTS: 98% verified success, 25x cost

Task class B
DIRECT: 52%
TREE_OF_THOUGHTS: 81%
MCTS: 84%

Task class C
DIRECT: 70%
RETRIEVAL: 96%

Now routing has a reason to exist.

Without that evidence, a Mixture-of-Agents architecture may simply be another abstraction layer.


Production Checklist

Before shipping strategy routing, ask:

  • Are strategies explicit runtime units rather than vague prompt roles?
  • Is the direct/simple path a first-class strategy?
  • Are routing features observable?
  • Can deterministic rules solve routing adequately?
  • Is every strategy externally verified?
  • Are cost and latency part of routing?
  • Are risk and difficulty modeled separately?
  • Is authorization independent from strategy choice?
  • Can failures trigger a different strategy rather than blind retry?
  • Do strategies share structured state?
  • Is the original goal immutable?
  • Can you calculate oracle strategy performance offline?
  • Can you measure strategy regret?
  • Can you detect unnecessary escalation?
  • Can you detect missed escalation?
  • Can you detect strategy thrashing?
  • Are strategy changes versioned and reversible?
  • Does the mixture beat a fixed frontier-model baseline?
  • Does it beat a fixed advanced-stack baseline?

If not, the architecture is probably not ready.


Final Rule

A powerful agent platform should not ask:

How many advanced techniques can we combine?

It should ask:

What is the cheapest computational strategy that can produce a verified result for this task state?

That changes the architecture completely.

The runtime stops being a giant stack of intelligence-shaped components.

It becomes a policy for allocating computation under evidence, cost, latency and risk constraints.

And that is the real point of a Mixture of Agents.

Do not build one monster agent. Build a runtime that knows which kind of agentic computation to spend next — and make every escalation earn its cost with verified evidence.

The next step is to stop looking at mechanisms one at a time and evaluate the system as a whole: how do you benchmark an advanced agent architecture without letting demos, model judges or cherry-picked tasks fool you?