Evidence & Optimization · Steps 12–18Chapter 17 of 45

What Is Your Agent Actually Uncertain About?

Page content

What Is Your Agent Actually Uncertain About?

An agent reaches a difficult point in a task.

It is not sure what to do next.

A common implementation responds like this:

uncertain
call the model again
still uncertain
call a stronger model
still uncertain
search more

That is not a reasoning strategy.

It is a spending strategy.

The system is using more computation without identifying what information is actually missing.

The word confidence hides too much.

An agent can be uncertain because:

  • the user request is ambiguous,
  • the evidence is weak,
  • two routes look equally plausible,
  • the current environment state may be stale,
  • a tool result may be incomplete,
  • multiple candidate solutions look similar,
  • the verifier does not cover an important requirement,
  • or the system simply does not know whether additional computation will help.

Those are different problems.

They should not trigger the same response.

The central rule of this post is:

Before spending more computation, identify what the system is uncertain about.

The previous post introduced dynamic budget scheduling.

This post gives the scheduler a better signal.

Instead of one scalar:

confidence = 0.63

we want something closer to:

interpretation_uncertainty = 0.10
evidence_uncertainty       = 0.82
route_uncertainty          = 0.18
state_uncertainty          = 0.76
candidate_uncertainty      = 0.31
verification_uncertainty   = 0.67

Now the system can make a more useful decision.

If evidence uncertainty is high, retrieve evidence.

If state uncertainty is high, inspect the environment.

If route uncertainty is high, compare routes.

If candidate uncertainty is high, search or generate alternatives.

If verification uncertainty is high, improve verification coverage.

Calling the model again becomes only one possible action.


Confidence Is Not Uncertainty

These two ideas are often collapsed.

A model may report:

I am 92% confident.

That does not tell us:

  • whether the task was interpreted correctly,
  • whether the evidence was complete,
  • whether the environment changed,
  • whether the best route was considered,
  • or whether the acceptance criteria were actually checked.

A single confidence score can be useful in narrow calibrated prediction problems.

But an advanced agent is not one prediction.

It is a sequence of decisions over changing state.

A better mental model is:

state
uncertainty decomposition
which uncertainty matters most?
which action can reduce it?
is that reduction worth the cost?
act / inspect / search / verify / stop

The uncertainty model belongs in the control plane.

It should help determine where the agent spends compute.


Seven Useful Types of Agent Uncertainty

There are many ways to classify uncertainty.

For production agents, the useful classification is the one that changes what the runtime should do next.

We will use seven categories.

1. interpretation uncertainty
2. evidence uncertainty
3. route uncertainty
4. state uncertainty
5. tool uncertainty
6. candidate uncertainty
7. verification uncertainty

These are not philosophical categories.

They are operational ones.


1. Interpretation Uncertainty

The agent may not understand what the task actually requires.

Examples:

"Fix the cache problem."

Which cache?

"Make this faster."

Latency? Throughput? Startup time? Cost?

"Update the deployment."

Which environment? Which service? Which version?

Interpretation uncertainty usually means the system lacks a stable task specification.

The correct next action may be:

  • inspect repository context,
  • inspect ticket metadata,
  • retrieve prior decisions,
  • infer constraints from state,
  • or ask a clarification question when necessary.

It is usually not:

generate five more solutions

More candidate generation does not resolve an ambiguous objective.

Signals

Possible interpretation-uncertainty signals include:

  • multiple plausible task parses,
  • missing required entities,
  • conflicting constraints,
  • unresolved references,
  • large disagreement between task parsers,
  • low retrieval support for the inferred objective.

Useful action

interpretation uncertainty
resolve task specification
then plan or act

2. Evidence Uncertainty

The task may be understood perfectly while the supporting evidence is weak.

A research agent may know exactly what claim it needs to evaluate but have only one secondary source.

A coding agent may suspect a race condition but have no reproduction.

A support agent may know which account is involved but not have the current billing state.

Evidence uncertainty means:

The system knows what question it is trying to answer but lacks enough reliable information to answer it.

The right next action is often retrieval, inspection, measurement, or experiment.

Not more verbal reasoning.

Example

claim: deployment failed because database migration timed out

known evidence:
- deployment marked failed
- one log line mentions timeout

missing evidence:
- migration duration
- database lock state
- retry history
- actual failing step

The agent should buy evidence.

inspect logs
query deployment state
inspect database lock metrics

not:

ask another model what it thinks happened

3. Route Uncertainty

The objective and evidence may be clear, but there may be several plausible strategies.

For example:

failing test
route A: fix parser
route B: fix fixture
route C: change schema

Or:

research question
route A: primary documentation
route B: source code inspection
route C: benchmark experiment

Route uncertainty is where routing policies, planners, or shallow search can help.

Signals

  • top routes have similar predicted success,
  • router margin is small,
  • multiple specialists are plausible,
  • historical route regret is high for this task class,
  • previous attempts disagree about the appropriate route.

Useful action

route uncertainty high
compare routes cheaply
select one
commit more budget

This is where Best-of-N or beam-style search can be useful.

But only if the branches represent meaningfully different strategies.


4. State Uncertainty

The agent may know what to do but not know the actual current environment state.

This is extremely common.

Examples:

  • Which Git commit is currently checked out?
  • Did the previous write succeed?
  • Is the deployment already live?
  • Has another process changed the file?
  • Is the browser still authenticated?
  • Did the database transaction commit?
  • Has the external API state changed since the last observation?

State uncertainty should usually trigger an observation, not more reasoning.

state uncertain
inspect authoritative state
update runtime state
continue

This is one of the biggest differences between an agent and a chatbot.

The environment is not static.

The model’s previous belief about it may be wrong.

Source-of-truth rule

When exact state can be read directly, do not reconstruct it from conversation history or semantic memory.

For coding agents:

git status
git diff
current commit
test result

are stronger evidence than:

"I remember editing that file earlier."

5. Tool Uncertainty

Sometimes the agent knows which tool family it needs but does not know whether the tool result is trustworthy or complete.

Examples:

  • API call returned partial data,
  • subprocess exited zero but wrote an error artifact,
  • browser action clicked an element but page state did not change,
  • search endpoint returned only the first page,
  • test runner skipped tests,
  • command timed out after partial side effects.

Tool uncertainty is not the same as tool-selection uncertainty.

The route may have been correct.

The problem is whether the observation is reliable.

Useful signals

  • timeout,
  • partial page,
  • truncated response,
  • retryable status,
  • missing expected fields,
  • stale timestamp,
  • inconsistent follow-up observation,
  • tool success without postcondition evidence.

Useful action

tool result uncertain
validate result
re-observe state
retry or recover only if needed

6. Candidate Uncertainty

Candidate uncertainty appears when the system has multiple plausible outputs but does not know which is better.

This is where techniques such as:

  • self-consistency,
  • Best-of-N,
  • pairwise ranking,
  • Tree of Thoughts,
  • beam search,
  • MCTS,
  • evolutionary search,

may become relevant.

But candidate uncertainty should not be confused with evidence uncertainty.

If the real problem is missing evidence, generating more candidates can create confident nonsense faster.

Example

three patch strategies

A: smallest change, passes tests
B: cleaner abstraction, passes tests
C: broader refactor, passes tests

Now candidate comparison is meaningful.

The environment has produced enough evidence to distinguish alternatives.

Useful signals

  • close evaluator scores,
  • inconsistent pairwise ranking,
  • high selection regret historically,
  • diverse candidates with similar verification status,
  • objective checks pass for multiple alternatives.

7. Verification Uncertainty

An agent may produce an apparently strong result but still not know whether the actual goal was achieved.

This is verification uncertainty.

Examples:

  • tests cover only part of the requested behavior,
  • deployment succeeded but health checks are incomplete,
  • research claim has sources but no independent corroboration,
  • browser form submitted but confirmation was not observed,
  • migration completed but data invariants were not checked.

Verification uncertainty should trigger stronger acceptance evidence.

Not another revision.

candidate looks good
verification incomplete
run missing acceptance checks
PASS / FAIL / UNKNOWN

This uncertainty deserves special treatment because it controls whether the system is allowed to claim completion.


Build an Uncertainty Vector

Instead of one confidence number, represent the current uncertainty explicitly.

from dataclasses import dataclass


@dataclass(frozen=True)
class UncertaintyVector:
    interpretation: float
    evidence: float
    route: float
    state: float
    tool: float
    candidate: float
    verification: float

Values might be normalized into [0, 1].

Do not pretend they are perfectly probabilistic unless they are calibrated as probabilities.

They can simply be comparable risk or uncertainty scores.

The important part is that they are typed.


Uncertainty Should Be Attached to Evidence

A score without provenance is weak.

Instead of:

route_uncertainty = 0.71

prefer something like:

@dataclass(frozen=True)
class UncertaintySignal:
    kind: str
    score: float
    evidence_ids: tuple[str, ...]
    reason: str
    policy_version: str

Now the runtime can answer:

Why was route uncertainty high?

Example:

route uncertainty = 0.71

because:
- top two route scores differ by 0.03
- historical routing regret is high for migration tasks
- no exact repository ownership signal was found

That is debuggable.


Map Uncertainty to Actions

The point of decomposition is not to produce more dashboards.

It is to choose better next actions.

A simple mapping might be:

interpretation uncertainty → resolve task constraints

evidence uncertainty       → retrieve / inspect / measure

route uncertainty          → compare routes / planner / router

state uncertainty          → observe authoritative state

tool uncertainty           → validate tool output / re-observe

candidate uncertainty      → generate / search / rank alternatives

verification uncertainty   → run acceptance checks

Notice how few of these require another generic model call.


A Small Action Selector

The action policy can begin as ordinary code.

from enum import Enum


class NextAction(str, Enum):
    RESOLVE_TASK = "resolve_task"
    GATHER_EVIDENCE = "gather_evidence"
    COMPARE_ROUTES = "compare_routes"
    OBSERVE_STATE = "observe_state"
    VALIDATE_TOOL = "validate_tool"
    SEARCH_CANDIDATES = "search_candidates"
    VERIFY = "verify"
    CONTINUE = "continue"


def choose_action(u: UncertaintyVector) -> NextAction:
    scores = {
        NextAction.RESOLVE_TASK: u.interpretation,
        NextAction.GATHER_EVIDENCE: u.evidence,
        NextAction.COMPARE_ROUTES: u.route,
        NextAction.OBSERVE_STATE: u.state,
        NextAction.VALIDATE_TOOL: u.tool,
        NextAction.SEARCH_CANDIDATES: u.candidate,
        NextAction.VERIFY: u.verification,
    }

    action, score = max(scores.items(), key=lambda item: item[1])

    if score < 0.35:
        return NextAction.CONTINUE

    return action

This implementation is intentionally simple.

It is not the final scheduler.

It demonstrates the control principle:

Different uncertainty should purchase different computation.


Use Expected Uncertainty Reduction

The previous post introduced expected value of computation.

We can now refine it.

For each possible action, estimate:

expected reduction in relevant uncertainty
------------------------------------------
               expected cost

For example:

inspect git status
cost: tiny
expected state uncertainty reduction: high

versus:

call frontier model
cost: high
expected state uncertainty reduction: almost zero

The correct action becomes obvious.

Example scheduler input

action: git_status
reduces: state uncertainty
expected reduction: 0.70
cost: 0.001


action: generate_more_patches
reduces: candidate uncertainty
expected reduction: 0.20
cost: 0.12


action: frontier_model_review
reduces: candidate uncertainty
expected reduction: 0.30
cost: 0.45

If state uncertainty is currently the dominant problem, git_status wins decisively.


Do Not Collapse Everything Back Into One Magic Score

A common mistake is to decompose uncertainty and then immediately compute:

overall_uncertainty =
    0.2 * interpretation +
    0.2 * evidence +
    0.2 * route +
    ...

Now we are back where we started.

A single scalar can be useful for dashboards or escalation summaries.

But the scheduler should preserve which uncertainty is high because that determines which action is useful.

The identity of the uncertainty matters more than the aggregate magnitude.


Uncertainty Can Be Correlated

The categories are not independent.

Poor task interpretation can create route uncertainty.

Poor evidence can create candidate uncertainty.

Stale state can create apparent tool uncertainty.

Weak verification can make candidate ranking unreliable.

So the system should preserve dependencies.

Example:

state stale
tool output seems inconsistent
candidate evaluator disagrees

The wrong fix would be:

run more candidate search

The correct fix may simply be:

refresh state

This is why causal lineage from the previous observability post matters.


Use the Cheapest Source of Truth

The best uncertainty-reduction action is often deterministic.

Examples:

uncertainty: did file change?
action: git diff
uncertainty: is deployment healthy?
action: query health endpoint
uncertainty: did transaction commit?
action: query database
uncertainty: does API support field X?
action: inspect schema/docs
uncertainty: does patch compile?
action: run compiler

This reinforces a recurring rule of the series:

Use deterministic evidence when deterministic evidence exists.

Do not spend LLM inference to answer a question that a command, schema, test, or state query can answer directly.


Coding Agent Example

Suppose a coding agent receives:

Fix the intermittent checkout failure.

It finds two suspicious modules and one failing test.

Initial uncertainty:

interpretation = 0.15
evidence       = 0.80
route          = 0.55
state          = 0.20
tool           = 0.10
candidate      = 0.30
verification   = 0.75

The naive strategy might generate several patches immediately.

The uncertainty-aware strategy sees that evidence uncertainty dominates.

So it purchases diagnostics:

run failing test repeatedly
inspect timestamps
inspect concurrent access
trace shared mutable state

After evidence gathering:

interpretation = 0.10
evidence       = 0.25
route          = 0.30
state          = 0.15
tool           = 0.10
candidate      = 0.65
verification   = 0.60

Now candidate uncertainty dominates.

The agent can generate two repair strategies.

After both compile and pass focused tests:

candidate      = 0.20
verification   = 0.82

The next spend should be broader verification.

run full regression suite
stress test intermittent scenario
check invariant

This is a much more efficient trajectory than repeatedly asking a model to reconsider the problem.


Research Agent Example

A research agent is asked:

Did company X actually reduce inference cost by 70%?

The claim is clear.

interpretation = 0.05

But the only evidence is a company blog post.

evidence = 0.90

More reasoning will not help.

The agent needs:

  • benchmark methodology,
  • baseline definition,
  • model version,
  • hardware,
  • workload,
  • independent reproduction if available.

After evidence retrieval, the problem may become verification uncertainty:

verification = 0.75

because the claimed comparison cannot be reproduced from published details.

The correct result may be:

UNKNOWN

not:

call three more models and average their opinions

Browser Agent Example

A browser agent submits a purchase form.

The click succeeds.

Does that mean the purchase succeeded?

No.

Current uncertainty:

state = 0.80
verification = 0.85

The right action is:

observe confirmation page
inspect order status
look for transaction identifier

not:

click submit again

Typed uncertainty prevents destructive retries.


Data Agent Example

A data agent runs a transformation pipeline.

The process exits successfully.

But row counts changed dramatically.

Possible uncertainty vector:

tool = 0.15
state = 0.20
candidate = 0.10
verification = 0.92

The execution mechanism is not the problem.

The postconditions are uncertain.

The scheduler should buy:

schema checks
row-count invariants
null distribution
key uniqueness
sample reconciliation

not another transformation attempt.


DevOps Agent Example

A deployment agent sees elevated latency after rollout.

There are three possibilities:

application regression
infrastructure saturation
external dependency degradation

This may initially be route uncertainty.

But the cheapest next move may be evidence gathering:

compare service latency
inspect CPU / memory
inspect dependency timing
compare previous deployment

The uncertainty decomposition might change from:

route = 0.75
evidence = 0.80

into:

route = 0.20
evidence = 0.25
verification = 0.70

Now the task is no longer diagnosis.

It is proving that the remediation actually restored service.


Multi-Agent Systems Need Typed Uncertainty Too

A multi-agent system often mistakes disagreement for useful diversity.

Three agents may disagree because:

  • the task is ambiguous,
  • they saw different evidence,
  • the router gave them different context,
  • they are genuinely exploring different strategies,
  • or one agent is simply wrong.

Before launching a debate, classify the disagreement.

disagreement
what is uncertain?

If evidence uncertainty is high, give the agents better evidence.

If route uncertainty is high, debate may help.

If state uncertainty is high, inspect the environment.

If verification uncertainty is high, improve the verifier.

Debate should not become the universal response to disagreement.


Measure Uncertainty Reduction

If uncertainty drives spend, you need to test whether actions actually reduce the uncertainty they target.

For every scheduler action, log:

uncertainty_before
chosen_action
expected_reduction
actual_evidence_obtained
uncertainty_after
cost
verified_outcome

Now you can compute:

actual uncertainty reduction
----------------------------
            cost

and compare action classes.

This becomes a calibration problem.


Metrics

Useful metrics include:

Uncertainty identification accuracy

Did the system correctly identify the dominant failure source?

Action targeting accuracy

Did the chosen action address the uncertainty that was actually blocking progress?

Uncertainty reduction per unit cost

Δ uncertainty / cost

Misallocated-compute rate

How often did the system spend compute on a mechanism unrelated to the dominant uncertainty?

Examples:

search while evidence was missing
model escalation while state was stale
revision while verification was incomplete

Verification-starvation rate

Did speculative uncertainty reduction consume resources needed for final proof?

Route flip rate

How often does route selection change after cheap evidence gathering?

A high route-flip rate may indicate that the router is operating before it has enough evidence.

Candidate-search waste

How often does candidate search occur before the task and evidence are stable?

State-refresh rescue rate

How often does refreshing state resolve an apparent reasoning failure?

This can be surprisingly high in production systems.


Calibrate Each Uncertainty Separately

Do not assume that a score of 0.7 means the same thing across categories.

For route uncertainty:

0.7

might mean:

70% chance the current route is suboptimal

if you actually calibrate it that way.

For state uncertainty it might simply be a heuristic indicating stale observations.

Each score should have its own interpretation and validation dataset.

Possible methods:

  • reliability curves,
  • Brier score,
  • expected calibration error,
  • threshold precision/recall,
  • outcome-stratified confusion matrices.

But only use probabilistic terminology when the score is actually calibrated probabilistically.


Unknown Is Different From Uncertain

Another important distinction:

uncertain ≠ unavailable

Sometimes the system can reduce uncertainty by spending more compute.

Sometimes the needed evidence is simply unavailable.

Example:

required production log was deleted

No amount of MCTS fixes that.

The correct state may be:

UNKNOWN

The scheduler needs a terminal condition for irreducible uncertainty.

uncertainty high
relevant evidence unavailable
no safe inference path
UNKNOWN

This is a feature, not a failure.


Do Not Let the Agent Manufacture Evidence

When evidence uncertainty is high, a dangerous failure mode is to reduce the score by generating a plausible explanation.

That is not uncertainty reduction.

That is narrative completion.

The evidence store should distinguish:

external observation
primary source
structured tool result
deterministic test
model-generated hypothesis
model-generated summary

A model-generated hypothesis should not carry the same evidential weight as an external observation.


Keep Safety Outside the Uncertainty Policy

An uncertainty-aware scheduler should not decide whether safety constraints apply.

Examples that stay deterministic:

  • authorization boundaries,
  • prohibited tools,
  • credential scopes,
  • sandbox requirements,
  • tenant isolation,
  • mandatory approval gates,
  • required verification for consequential actions.

High confidence should never bypass them.

Low uncertainty should never bypass them.

The adaptive policy decides where to spend allowed computation.

It does not redefine what is allowed.


A Better Scheduler Interface

The scheduler from the previous post can now receive typed uncertainty.

from dataclasses import dataclass
from typing import Sequence


@dataclass(frozen=True)
class CandidateAction:
    name: str
    cost: float
    targets: tuple[str, ...]
    expected_reduction: float


@dataclass(frozen=True)
class SchedulerDecision:
    action: str
    targeted_uncertainty: str
    expected_reduction: float
    expected_cost: float
    reason: str
    policy_version: str


def choose_next_action(
    uncertainty: UncertaintyVector,
    actions: Sequence[CandidateAction],
    policy_version: str,
) -> SchedulerDecision:
    values = {
        "interpretation": uncertainty.interpretation,
        "evidence": uncertainty.evidence,
        "route": uncertainty.route,
        "state": uncertainty.state,
        "tool": uncertainty.tool,
        "candidate": uncertainty.candidate,
        "verification": uncertainty.verification,
    }

    dominant = max(values, key=values.get)

    applicable = [a for a in actions if dominant in a.targets]

    if not applicable:
        return SchedulerDecision(
            action="stop_unknown",
            targeted_uncertainty=dominant,
            expected_reduction=0.0,
            expected_cost=0.0,
            reason="No permitted action is expected to reduce dominant uncertainty",
            policy_version=policy_version,
        )

    best = max(
        applicable,
        key=lambda a: a.expected_reduction / max(a.cost, 1e-9),
    )

    return SchedulerDecision(
        action=best.name,
        targeted_uncertainty=dominant,
        expected_reduction=best.expected_reduction,
        expected_cost=best.cost,
        reason=f"Highest expected reduction per cost for {dominant}",
        policy_version=policy_version,
    )

Again, the point is not this exact formula.

The point is the architecture.

observe
decompose uncertainty
identify dominant uncertainty
consider actions that can reduce it
choose the best expected reduction per cost
act
measure actual reduction

Test the Scheduler Against Fixed Policies

Do not assume this architecture is better.

Benchmark it.

Use the same task set and maximum resource envelope.

Compare:

A. fixed max_steps
B. fixed beam width
C. always escalate on low confidence
D. always run critic
E. typed uncertainty scheduler

Measure:

verified success
false success
UNKNOWN rate
cost per verified success
p50 / p95 latency
model calls
tool calls
search nodes
verification spend
misallocated-compute rate

The uncertainty scheduler earns its place only if it improves the production frontier.


Failure Injection

A good benchmark should deliberately create uncertainty of different types.

Interpretation injection

Remove a key task constraint.

Does the system detect ambiguity rather than inventing one?

Evidence injection

Hide an important source.

Does it gather evidence instead of over-reasoning?

Route injection

Create two plausible repair paths.

Does it compare routes?

State injection

Change the environment between observations.

Does it refresh state?

Tool injection

Return a partial or stale tool result.

Does it validate the observation?

Candidate injection

Provide multiple viable solutions.

Does it compare them effectively?

Verification injection

Remove an acceptance criterion.

Does it refuse to claim PASS?

This turns uncertainty decomposition into a testable runtime mechanism.


The Production Trace Should Explain the Spend

A useful trajectory event might look like:

{
  "event": "scheduler_decision",
  "dominant_uncertainty": "state",
  "score": 0.81,
  "action": "refresh_git_state",
  "expected_reduction": 0.72,
  "expected_cost": 0.001,
  "actual_reduction": 0.68,
  "policy_version": "uncertainty-v3"
}

Now when a run becomes expensive, you can answer:

Where did the budget go?
Why did we buy that action?
What uncertainty was it supposed to reduce?
Did it actually reduce it?

That is much better than:

The agent used 47 model calls.

The Bigger Architectural Shift

We started the basic series with an agent loop:

observe
decide
act
observe

The advanced runtime is becoming something more structured:

observe
construct state
decompose uncertainty
choose which uncertainty matters
select uncertainty-reducing action
allocate budget
act
verify effect
update state and uncertainty
continue / verify / stop

This is still an agent loop.

But now the runtime understands why it is spending computation.

That is the important part.


Practical Decision Rules

  1. Do not use one generic confidence score for every control decision.

  2. Interpretation uncertainty should be resolved before expensive planning.

  3. Evidence uncertainty should purchase evidence, not more narrative.

  4. State uncertainty should purchase observation from the authoritative source.

  5. Route uncertainty is where routing comparison and shallow search are useful.

  6. Candidate uncertainty is where Best-of-N, search and ranking become useful.

  7. Verification uncertainty should purchase stronger acceptance evidence.

  8. Measure whether each action actually reduces the uncertainty it targets.

  9. Preserve UNKNOWN when required evidence cannot be obtained.

  10. Keep safety and authorization outside adaptive uncertainty policies.


Final Principle

The most expensive mistake in an advanced agent is often not choosing the wrong answer.

It is spending computation on the wrong problem.

A system with high evidence uncertainty does not need more imagination.

A system with stale state does not need a bigger model.

A system with weak verification does not need another revision.

A system with ambiguous requirements does not need deeper search.

So the rule is:

Do not ask only, “How uncertain is the agent?” Ask, “What is the agent uncertain about, and what is the cheapest reliable action that can reduce that uncertainty?”

That turns uncertainty from a vague feeling into a control signal.

And once uncertainty becomes a control signal, the next stage becomes possible:

selecting information-gathering actions by expected value of information rather than by model confidence or fixed orchestration rules.