Advanced Agents From First Principles 01: Does Your AI Agent Fail on Complex Reasoning Tasks? Treat Chain of Thought as Computation, Not Proof

Page content

Most developers first encounter chain of thought as a prompting trick:

Think step by step.

That framing is too shallow for agent engineering.

For an advanced agent, the useful idea is not that the model should produce a long explanation. The useful idea is that a difficult task may benefit from intermediate computational state before the system commits to an action or answer.

That is a very different claim.

A reasoning trace can help a system decompose a problem, preserve intermediate conclusions, identify missing information, decide what to verify next, and expose places where search or tools should be used.

But none of that makes the trace true.

The central rule of this post is:

Chain of thought is computation, not proof.

A plausible reasoning trace is still model output. It can contain incorrect assumptions, invented facts, invalid arithmetic, bad causal links, or a perfectly coherent path to the wrong answer.

So the engineering question is not:

How do I make the model explain itself more?

It is:

Where does intermediate computation improve verified task success, and how should the runtime use that computation without mistaking it for evidence?

This post builds that distinction from first principles.


The Search Problem: “Why Does My AI Agent Fail on Complex Reasoning Tasks?”

A common pattern looks like this:

simple task
model succeeds

complex task
model jumps directly to answer
important constraint omitted
wrong answer

The failure is often described as “the model cannot reason.”

Sometimes that is true.

But often the problem is more specific: the runtime asks the model to collapse too much computation into one opaque output.

Consider a coding agent asked to repair a failing test suite.

A one-shot path may look like:

issue description
model
patch

A more useful computational path might be:

issue description
identify failing behavior
inspect relevant code
form hypotheses
select next diagnostic
observe result
revise hypothesis
propose patch
run tests

The improvement does not come from making the prose longer.

It comes from creating intermediate decisions that can interact with evidence.


One-Shot Generation Compresses Too Much

The simplest agent asks a model to go directly from input to output:

def solve(task: str, model) -> str:
    return model(task)

That is often the correct architecture.

Do not add a reasoning mechanism merely because the task looks intellectual.

But for some tasks, direct generation forces the model to implicitly perform several operations at once:

understand task
    +
identify constraints
    +
retrieve relevant facts
    +
choose method
    +
perform calculation
    +
check result
    +
format answer

If any hidden sub-operation fails, the final answer can fail without giving the runtime a useful point at which to intervene.

Intermediate computation changes the shape:

input
intermediate state 1
intermediate state 2
verification / tool use
intermediate state 3
answer

Now the runtime has places where it can inspect, verify, branch, retry, or gather more information.


Chain of Thought Is Not Necessarily Visible Prose

This distinction matters enormously.

Developers often equate chain of thought with a long text field containing sentences such as:

First I will consider...
Then I will calculate...
Therefore...

That is only one representation.

For agent systems, intermediate computation can be represented as structured state instead.

For example:

from dataclasses import dataclass, field


@dataclass
class ReasoningState:
    goal: str
    known_facts: list[str] = field(default_factory=list)
    unknowns: list[str] = field(default_factory=list)
    hypotheses: list[str] = field(default_factory=list)
    next_checks: list[str] = field(default_factory=list)
    constraints: list[str] = field(default_factory=list)

That is often more useful than unrestricted prose.

Why?

Because the runtime can validate and operate on it.

free-form reasoning
hard to inspect mechanically

structured reasoning state
known fields
validation
tool routing
search / verification

This is a recurring theme in agent engineering:

When intermediate reasoning affects control flow, represent the parts that matter as data.


A Minimal Reasoning Pipeline

Suppose we want the model to decompose a difficult task before answering.

We can separate decomposition from execution.

from dataclasses import dataclass


@dataclass
class ReasoningPlan:
    interpretation: str
    constraints: list[str]
    steps: list[str]
    verification: list[str]


def reason(task: str, model) -> ReasoningPlan:
    raw = model({
        "task": task,
        "instruction": (
            "Return a structured plan containing an interpretation, "
            "constraints, steps, and verification checks."
        ),
    })
    return parse_reasoning_plan(raw)

Then execution can be separated:

def execute_reasoning_plan(plan: ReasoningPlan, runtime):
    observations = []

    for step in plan.steps:
        observation = runtime.execute(step)
        observations.append(observation)

    return observations

And success can be evaluated separately again:

def verify(plan: ReasoningPlan, observations, verifier):
    return verifier.check(
        constraints=plan.constraints,
        verification=plan.verification,
        observations=observations,
    )

The key architecture is:

reasoning
execution
verification

That separation is far more useful than merely asking for “more reasoning.”


Why Long Reasoning Can Still Be Wrong

Consider this hypothetical trace:

The service is failing because the database connection pool is exhausted.
The latency spike supports this.
Therefore increasing the pool size should fix the incident.

It is coherent.

It may even sound expert.

But perhaps the actual problem is a downstream dependency timing out, causing connections to remain occupied longer.

Increasing the pool size might simply delay the failure.

The reasoning trace contains a hypothesis:

pool exhaustion is root cause

That is not proof.

A better runtime turns the hypothesis into a test:

hypothesis
what observation would support/refute it?
inspect pool saturation
inspect connection duration
inspect downstream latency
update belief

Now chain of thought is being used as a generator of testable intermediate state.

That is much stronger.


Separate Claims From Evidence

A useful reasoning representation can distinguish claims from evidence.

from dataclasses import dataclass, field


@dataclass
class Claim:
    text: str
    evidence_ids: list[str] = field(default_factory=list)
    status: str = "unverified"

Then the runtime can enforce a simple rule:

def can_promote(claim: Claim, evidence_store) -> bool:
    return all(
        evidence_store.exists(evidence_id)
        for evidence_id in claim.evidence_ids
    )

The model may say:

The authentication middleware is dropping the header.

The runtime records:

CLAIM: authentication middleware drops header
STATUS: unverified

Then tools gather evidence:

request trace
middleware logs
integration test

Only then does the status change.

This prevents a dangerous collapse:

model thought it
therefore system believes it

The Reasoning Scratchpad and the Control Plane Are Different

Another important distinction is between temporary reasoning state and durable runtime state.

A scratchpad might contain:

Maybe file A is relevant.
Could also be file B.
Need to inspect the parser first.

That is useful temporary computation.

But it should not automatically become durable memory.

Compare:

scratchpad
provisional hypotheses
throw away freely

with:

runtime state
verified files inspected
commands executed
artifacts produced
remaining tasks

The latter needs deterministic persistence.

The previous memory post made this distinction for long-term memory. The same principle applies here:

Do not turn provisional reasoning into system truth merely because it appeared earlier in the trajectory.


Reasoning as a State Machine

A more disciplined design is to represent reasoning stages explicitly.

from enum import Enum


class ReasoningPhase(str, Enum):
    UNDERSTAND = "understand"
    DECOMPOSE = "decompose"
    INVESTIGATE = "investigate"
    PROPOSE = "propose"
    VERIFY = "verify"
    COMPLETE = "complete"

Now the system can control transitions:

UNDERSTAND
DECOMPOSE
INVESTIGATE
PROPOSE
VERIFY
COMPLETE

But transitions should not be model wishful thinking.

For example:

def can_enter_verify(state) -> bool:
    return bool(state.proposed_solution)


def can_complete(state) -> bool:
    return state.verification_status == "PASS"

This turns a vague reasoning process into a runtime protocol.


Use Tools to Break Reasoning Out of the Model

Suppose an agent needs to answer:

Which function introduced the regression?

The weakest architecture is:

repository text
LLM guesses

A stronger reasoning architecture is:

form hypothesis
git diff
inspect call sites
run targeted test
inspect failure
update hypothesis

The computation is distributed across:

model
+
tools
+
environment
+
verification

This is one of the most important advanced-agent ideas:

The model does not need to internally simulate work that the environment can perform more reliably.

Use computation where it is strongest.

Arithmetic belongs in a calculator.

Repository history belongs in Git.

Database state belongs in the database.

Test outcomes belong in the test runner.

The model’s intermediate computation should help decide which external computation to request next.


Chain of Thought as Tool Selection

This gives us a useful interpretation of reasoning:

current evidence
what uncertainty matters most?
which observation would reduce it?
select tool
observe
update state

That is much closer to an agent than a long essay about what the model thinks.

For example, a debugging agent might hold:

state.unknowns = [
    "whether parser receives malformed input",
    "whether failure occurs before persistence",
]

The model chooses which unknown to reduce first.

next_check = {
    "tool": "inspect_logs",
    "arguments": {
        "component": "parser",
        "request_id": "abc123",
    },
}

Now reasoning directly guides evidence acquisition.


What Should Be Structured?

Not everything needs a schema.

But fields that influence control flow usually benefit from structure.

Useful candidates include:

goal
constraints
unknowns
hypotheses
candidate actions
required evidence
verification checks
confidence / uncertainty
termination reason

For example:

@dataclass
class InvestigationState:
    goal: str
    constraints: list[str]
    hypotheses: list[str]
    unresolved_questions: list[str]
    evidence: list[str]
    next_action: dict | None

This allows the runtime to answer questions that free-form prose obscures:

How many unresolved questions remain?
Which hypothesis has no evidence?
Did the agent already test this idea?
Which constraint has not been checked?
Why did the agent choose this tool?

The Common Failure: Verbosity Masquerading as Reasoning

One of the most common mistakes is to increase reasoning-token usage and assume capability improved.

You may see:

short answer: 300 tokens
reasoning answer: 4,000 tokens

and conclude:

more tokens
more thought
better reasoning

That inference is not safe.

Longer traces can contain:

  • repetition
  • rationalization
  • irrelevant decomposition
  • invented details
  • premature assumptions
  • circular reasoning
  • duplicated checks
  • post-hoc explanations

The right metric is not reasoning length.

It is verified task performance.

A useful experiment is:

baseline: direct answer
variant A: structured decomposition
variant B: decomposition + tools
variant C: decomposition + tools + verification

Measure:

verified success
latency
model calls
tool calls
tokens
cost per successful task

Then keep the simplest architecture that wins.


The Common Failure: Self-Consistency of One Bad Assumption

A model can construct a long, internally coherent trace around a bad premise.

For example:

Assumption: request reaches service B.

Step 1: service B parses request.
Step 2: service B writes database row.
Step 3: database constraint fails.

If the request never reached service B, every later step is irrelevant.

This motivates assumption checkpoints.

@dataclass
class Assumption:
    statement: str
    must_verify: bool
    evidence_id: str | None = None

Before expensive downstream reasoning, the runtime can check critical assumptions.

assumption
cheap verification available?
   ↓ yes
verify first
continue only if supported

This often saves both tokens and wrong work.


The Common Failure: Reasoning After the Answer

Another subtle failure occurs when the model effectively decides on an answer first and then generates a plausible rationale.

For agent engineering, the remedy is not philosophical.

It is architectural.

Make intermediate decisions causally matter.

Instead of:

model produces answer + explanation

use:

model proposes next check
runtime executes it
observation enters state
model receives observation
next decision

The external observation constrains the next step.

That makes the reasoning trajectory operational rather than merely descriptive.


Reasoning Checkpoints

Long tasks benefit from checkpoints where the runtime asks whether the trajectory is still valid.

A checkpoint might inspect:

Are the original constraints still represented?
Have any assumptions been falsified?
Has new evidence changed the problem?
Are we repeating the same hypothesis?
Is the current branch making progress?
Can any remaining unknown be verified directly?

A simple checkpoint object:

@dataclass
class ReasoningCheckpoint:
    constraints_satisfied: bool
    assumptions_supported: bool
    progress_detected: bool
    unresolved_questions: list[str]
    recommended_action: str

Possible recommendations:

CONTINUE
GATHER_EVIDENCE
REVISE_HYPOTHESIS
BACKTRACK
VERIFY
STOP

Notice how this begins to connect chain of thought to later advanced techniques such as search and MCTS.


Chain of thought and search are related but different.

A single reasoning trajectory looks like:

A
B
C
D

Search preserves alternatives:

        A
      /   \
     B     C
    / \   / \
   D   E F   G

Chain of thought can improve a single path.

Search asks whether we should maintain multiple paths.

The escalation rule is:

Does one structured reasoning trajectory work?
        ↓ yes
stop there

        ↓ no
Are early decisions uncertain and consequential?
        ↓ yes
preserve alternatives with search

Do not build a tree merely because you can.


Reasoning vs Best-of-N

Best-of-N generates several complete candidates:

prompt
 ├→ answer A
 ├→ answer B
 ├→ answer C
 └→ answer D
      select

Reasoning decomposes one trajectory:

prompt
state 1
state 2
state 3
answer

Use Best-of-N when diversity among complete outputs helps.

Use intermediate reasoning when later decisions should depend on intermediate observations.

You can combine them:

multiple reasoning strategies
execute / verify
select best supported result

But again, every extra path costs inference.


Reasoning vs Critique and Revision

Critique/revision operates after a candidate exists:

candidate
critique
revision

Intermediate reasoning operates before or during construction:

problem
decompose
investigate
construct

The distinction matters because some failures are easier to prevent than repair.

If the agent misses a fundamental constraint at the beginning, repeated rewriting may never recover efficiently.

A reasoning checkpoint can surface the constraint before generation commits too far.


Reasoning and Verification Must Remain Separate

Suppose the reasoning state contains:

All required tests should now pass.

That is still only a prediction.

The verifier must run:

pytest

and return:

PASS
FAIL
UNKNOWN

The correct architecture is:

reasoning predicts
environment tests
verification decides

Not:

reasoning predicts
reasoning confirms itself

This separation is one of the main reasons the verification post closed the core series before we entered advanced orchestration.


A Complete Small Reasoning Agent

Here is a simplified architecture that keeps the pieces separate.

from dataclasses import dataclass, field
from typing import Protocol


class Model(Protocol):
    def __call__(self, payload: dict) -> dict:
        ...


@dataclass
class Evidence:
    id: str
    source: str
    content: str


@dataclass
class ReasoningState:
    goal: str
    constraints: list[str] = field(default_factory=list)
    hypotheses: list[str] = field(default_factory=list)
    unknowns: list[str] = field(default_factory=list)
    evidence: list[Evidence] = field(default_factory=list)
    proposed_answer: str | None = None


class ReasoningAgent:
    def __init__(self, model: Model, tools, verifier):
        self.model = model
        self.tools = tools
        self.verifier = verifier

    def run(self, goal: str, max_steps: int = 8):
        state = ReasoningState(goal=goal)

        for step in range(max_steps):
            decision = self.model({
                "goal": state.goal,
                "constraints": state.constraints,
                "hypotheses": state.hypotheses,
                "unknowns": state.unknowns,
                "evidence": [e.content for e in state.evidence],
                "instruction": (
                    "Choose one action: gather_evidence, update_state, "
                    "propose_answer, or request_verification."
                ),
            })

            action = decision["action"]

            if action == "gather_evidence":
                tool_name = decision["tool"]
                arguments = decision["arguments"]
                result = self.tools.execute(tool_name, arguments)
                state.evidence.append(
                    Evidence(
                        id=f"evidence-{step}",
                        source=tool_name,
                        content=result,
                    )
                )
                continue

            if action == "update_state":
                state.constraints = decision.get(
                    "constraints", state.constraints
                )
                state.hypotheses = decision.get(
                    "hypotheses", state.hypotheses
                )
                state.unknowns = decision.get(
                    "unknowns", state.unknowns
                )
                continue

            if action == "propose_answer":
                state.proposed_answer = decision["answer"]
                continue

            if action == "request_verification":
                if state.proposed_answer is None:
                    continue

                result = self.verifier.verify(
                    goal=state.goal,
                    answer=state.proposed_answer,
                    evidence=state.evidence,
                )

                if result.status == "PASS":
                    return state.proposed_answer

                if result.status == "FAIL":
                    state.unknowns.extend(result.failures)
                    state.proposed_answer = None
                    continue

        raise RuntimeError("Reasoning budget exhausted")

This is still intentionally small.

But notice what has changed compared with a prompt that says “think step by step.”

The runtime now owns:

  • step budget
  • tool execution
  • evidence records
  • verification
  • completion

The model proposes intermediate computation.

The runtime controls the process.


Application: Coding Agents

Coding agents are an excellent example because the environment provides strong evidence.

A weak reasoning loop:

read issue
think about code
write patch
claim fixed

A stronger one:

read issue
identify expected behavior
locate relevant code
form failure hypothesis
run targeted diagnostic
update hypothesis
edit isolated branch/worktree
run targeted tests
run regression checks
inspect diff

Intermediate reasoning is useful for:

  • choosing what file to inspect
  • choosing which test to run
  • identifying likely invariants
  • deciding whether failure evidence supports a hypothesis
  • deciding when to broaden search

But Git, tests, type checkers, linters, and runtime behavior remain stronger evidence than prose reasoning.


Application: Research Agents

A research agent often fails because it jumps from query to conclusion too quickly.

Useful intermediate state includes:

question
subquestions
candidate claims
missing evidence
source quality
contradictions
unresolved uncertainty

A stronger research path:

question
decompose claims
search primary sources
extract evidence
identify contradictions
search missing evidence
write synthesis
check every material claim has support

The reasoning trace helps organize the investigation.

The sources provide the evidence.

That distinction protects the system from citing its own generated explanation as if it were research.


Application: Debugging and Incident Response

Incident response is where unverified reasoning can become expensive.

A model might quickly conclude:

CPU saturation caused the outage.

A disciplined agent instead records:

hypothesis: CPU saturation is causal

needed evidence:
- CPU timing relative to errors
- request latency
- queue depth
- dependency latency

Then it collects observations.

This is essentially scientific debugging:

hypothesis
prediction
measurement
update

Chain of thought becomes a mechanism for choosing informative experiments.


Application: Data and Analytics Agents

Suppose an analytics agent must answer:

Why did conversion fall yesterday?

A bad one-shot answer might invent a plausible narrative.

A useful reasoning state might be:

metric definition
comparison window
possible segmentation changes
tracking changes
traffic-source mix
funnel-stage deltas
known incidents

Then the agent queries actual data.

reasoning
SQL / metrics tools
observations
updated hypothesis
verified explanation

The model helps coordinate analysis.

The data determines what happened.


Application: Browser Agents

Browser tasks require reasoning about hidden state and sequential constraints.

For example:

goal: submit application

Intermediate state may include:

required fields
completed fields
missing documents
current page
validation errors
submission status

The reasoning mechanism decides what to do next.

But successful submission should be verified from the page/application state—not from a generated sentence saying “the form has been submitted.”


Application: Planning and Scheduling Agents

Planning agents benefit when constraints must be combined before committing.

Useful intermediate computation:

hard constraints
soft preferences
resource availability
dependencies
conflicts
candidate schedules

Then deterministic solvers or constraint checks can test candidate plans.

This is an important pattern:

LLM proposes structure
solver checks feasibility
LLM interprets / revises

Do not ask a language model to replace a deterministic constraint solver when one exists.


Application: Customer Support Agents

A support agent may need to reason about:

customer intent
account state
order state
policy constraints
possible resolution
escalation criteria

But customer-specific facts must come from account/order systems.

A strong architecture is:

interpret request
identify required facts
query systems
apply policy
propose resolution
validate permissions
execute / escalate

The reasoning is useful coordination.

The business systems remain authoritative.


Application Matrix

Software Useful intermediate computation Strong external evidence Typical reasoning failure
Coding agent hypotheses, constraints, next diagnostic tests, diff, compiler, runtime plausible patch without validation
Research agent subquestions, claims, missing evidence primary sources narrative before evidence
Incident agent failure hypotheses, diagnostic priority metrics, logs, traces correlation treated as cause
Data agent segment hypotheses, metric decomposition database queries plausible analytics story
Browser agent required state, incomplete fields DOM/page state assumes action succeeded
Planner dependencies, hard/soft constraints solver / feasibility checks impossible plan
Support agent intent, missing facts, policy path account/order systems invented customer state

The recurring architecture is:

model computation
external observation
state update
model computation

not:

model computation
more model computation
model agrees with itself

When Chain of Thought Makes Things Worse

Intermediate reasoning adds cost and new failure modes.

It can hurt when:

  • the task is already trivial
  • the model over-decomposes
  • reasoning anchors on an early mistake
  • intermediate state bloats context
  • repeated reflection adds no evidence
  • the runtime stores provisional thoughts as facts
  • reasoning latency dominates the task
  • private or sensitive intermediate data is retained unnecessarily
  • the final answer quality does not improve

A good baseline experiment is mandatory.

A: direct generation
B: structured reasoning
C: structured reasoning + tools
D: structured reasoning + tools + verification

Do not compare only average answer scores.

Measure operational outcomes.


Metrics That Actually Matter

For reasoning agents, useful metrics include:

Outcome

verified task success

Efficiency

model calls
tokens
latency
tool calls
cost per verified success

Reasoning usefulness

fraction of intermediate checks that change the trajectory
fraction of hypotheses tested
assumption failure rate
repeated-hypothesis rate
unverified-claim rate

Verification

false-pass rate
false-fail rate
UNKNOWN rate

Progress

unknowns reduced per step
constraints resolved per step
useful evidence acquired per call

A reasoning architecture that produces twice as many tokens but no measurable improvement is not more advanced.

It is simply more expensive.


A Useful Diagnostic: Did the Reasoning Change Anything?

This question is surprisingly powerful.

For every intermediate reasoning step, ask:

Did it change:
- the next action?
- the selected tool?
- the active hypothesis?
- the candidate solution?
- the verification plan?

If not, the step may be decorative.

You can log this directly:

@dataclass
class ReasoningEvent:
    event_type: str
    content: str
    changed_control_flow: bool

Then measure:

control-flow-changing reasoning events
--------------------------------------
all reasoning events

If that number is tiny, the system may be generating explanations rather than useful computation.


Another Diagnostic: What Could Falsify This Step?

For a material hypothesis, ask:

What observation would prove this wrong?

If the system cannot name one, the hypothesis may be too vague to guide action.

For example:

"Something is wrong with caching"

is weak.

Better:

"The cache key omits tenant_id, causing cross-tenant reuse."

Now we can inspect:

key construction
cache entries
request tenant IDs

Reasoning becomes operational.


Confidence Is Not Evidence Either

Models may attach confidence to intermediate conclusions.

That can help prioritize checks, but confidence should not replace verification.

high confidence
correct

A better use is compute allocation:

low uncertainty
cheap path

high uncertainty
more evidence / search / specialist

This is where chain of thought connects directly to adaptive agents later in the series.


Reasoning Traces and Privacy

Intermediate reasoning can contain more information than the final answer.

Production systems should decide deliberately:

  • what reasoning state is retained
  • what is logged
  • what is sent to external services
  • what is visible to users
  • what contains secrets or personal data
  • what should expire

A useful architecture separates:

ephemeral computation
minimum durable trace

You often need provenance like:

tool called
input hash
observation ID
decision selected
verification result

without retaining every free-form intermediate token forever.


Reasoning Trace vs Decision Trace

For production agents, a decision trace is often more useful than a full free-form reasoning transcript.

Example:

{
  "step": 4,
  "state_hash": "...",
  "decision": "run_targeted_test",
  "reason": "hypothesis H2 lacks runtime evidence",
  "tool": "pytest",
  "result_id": "obs-421"
}

This captures the information needed for debugging:

what did the agent believe mattered?
what did it do?
what evidence came back?

without making the entire system dependent on prose traces.


Treat Reasoning as a Budget

Reasoning consumes compute.

So give it a budget.

@dataclass
class ReasoningBudget:
    max_steps: int
    max_model_calls: int
    max_tool_calls: int
    max_tokens: int

Then escalate only when needed.

simple task
direct path

failure / uncertainty
structured reasoning

still unresolved
search / specialist / stronger model

This is much better than enabling maximum reasoning for every request.


Adaptive Reasoning Depth

One advanced extension is to vary reasoning effort based on the task.

For example:

def reasoning_budget(task, classifier):
    difficulty = classifier(task)

    if difficulty == "low":
        return 1

    if difficulty == "medium":
        return 4

    return 10

But difficulty estimation itself can fail.

So runtime evidence should also trigger escalation:

direct answer fails verification
reason more

reasoning branch remains uncertain
search alternatives

search cannot discriminate
stronger verifier / specialist

This is the beginning of adaptive compute allocation.


The Evidence-First Rule

The Advanced Agents series keeps the same rule as the previous series:

Do not add a mechanism because it sounds more intelligent. Add it because you can name the failure it is supposed to fix and measure whether it fixes it.

For chain of thought, the hypothesis might be:

Complex tasks fail because important constraints are lost before action selection.

Then test:

baseline
    direct generation

variant
    structured constraints + intermediate checks

Measure:

constraint violation rate
verified success
latency
cost

If constraints are still violated at the same rate, the mechanism did not solve the claimed problem.


An Experiment Template

from dataclasses import dataclass


@dataclass
class ReasoningExperimentResult:
    architecture: str
    verified_success_rate: float
    constraint_failure_rate: float
    avg_model_calls: float
    avg_tool_calls: float
    avg_latency_ms: float
    avg_tokens: float
    cost_per_success: float

Compare:

direct
structured reasoning
reasoning + tools
reasoning + tools + verification

Then ask:

What did the extra computation buy us?

That question should follow every advanced-agent mechanism in this series.


What This Unlocks Next

Once intermediate computation is explicit, we can do something more interesting.

Instead of generating one reasoning trajectory:

A → B → C → D

we can generate several:

A → B → C
A → E → F
A → G → H

Then compare where they agree.

That leads to the next advanced technique:

self-consistency

But self-consistency introduces another trap:

many agents / samples agree
answer is true

Agreement can come from correlated model errors.

So the same evidence-first boundary remains.


Final Architecture

The useful form of chain of thought for an agent looks less like this:

Please explain your reasoning in detail.

and more like this:

goal
structured intermediate state
identify uncertainty
choose evidence-producing action
observe environment
update hypotheses
propose result
external verification

The model provides flexible computation.

The runtime supplies structure.

Tools supply observations.

Verification supplies evidence.

That is the important transition from prompting to agent architecture.

And it gives us the rule to carry into every later post:

Reasoning can tell the agent what to investigate next. Only evidence can tell the system what actually happened.


Next: Self-Consistency

The next post will ask another common question:

Why does my reasoning agent give a different answer every time?

We will build self-consistency from first principles: sample multiple reasoning trajectories, measure agreement, separate consensus from correctness, detect correlated failures, and decide when disagreement should trigger search, verification, or specialist escalation instead of a majority vote.