<!-- Sema documentation — std.belief
     Sema is a new AI-native, neurosymbolic programming language most models
     have not been trained on. Full corpus: https://sema.49.12.246.95.sslip.io/llms-full.txt
     Install language support: https://sema.49.12.246.95.sslip.io/install-skill/ -->

# std.belief

> Beta-Bernoulli calibrated-confidence tracking — soft Bernoulli updates over a parametrizable prior, as a first-class Sema type.

`std.belief` gives you a first-class **calibrated-confidence type**. Instead of
juggling a running average and a count, you hold a `Belief` — a Beta distribution
over the probability that some hypothesis is true — and feed it observations. Each
observation is a *soft* Bernoulli update: `alpha += conf`, `beta += 1 - conf`,
where `conf` is a confidence in `[0, 1]`. The posterior mean is your calibrated
confidence.

```sema
from std.belief import Belief, prior, prior_with
```

## Why it exists

AI pipelines constantly need to answer "how sure are we, given the evidence so
far?" — and typically grow a hand-rolled tracker (often a ~120-line
`BeliefTracker`). `std.belief` replaces that with a small, verified type. Crucially
**nothing about the distribution is hardcoded in the runtime**: the prior is a
parameter you choose, and the update rule is plain Sema you can read.

## Constructing a belief

Start from a prior. `prior()` is the uniform Beta(1, 1) — maximally uncommitted.
`prior_with(alpha, beta)` sets any prior via pseudo-counts, letting you encode a
head start (e.g. a skeptical `prior_with(1.0, 4.0)`):

```sema
b = prior()                 # uniform Beta(1, 1); confidence() == 0.5
skeptical = prior_with(1.0, 4.0)   # starts around 0.2
```

## Updating with evidence

`update(conf)` mutates the belief in place and records the new confidence in its
`history`. Confidence values are soft — `1.0` is a fully-confident positive
observation, `0.5` is uninformative, `0.0` a fully-confident negative:

```sema
b = prior()
b.update(0.9)     # strong positive evidence
b.update(0.8)
b.update(0.3)     # some negative evidence
print(b.confidence())   # posterior mean E[theta] = alpha / (alpha + beta)
```

For immutable state threading — for example inside an `agent_loop.loop_until`
step, where a lambda may not mutate captured state — use `bumped(conf)`, which
returns a **new** belief instead of mutating:

```sema
from std.agent_loop import loop_until
from std.belief import prior

final = loop_until(
    prior(),
    max_iters=10,
    step=(s => s.bumped(observe_confidence())),
    done=(s => s.confidence() > 0.95),
)
```

## Reading the belief

| Method | Meaning |
|---|---|
| `confidence()` | Posterior mean `alpha / (alpha + beta)` — your calibrated confidence |
| `mode()` | Posterior mode (defined only when `alpha > 1` and `beta > 1`) |
| `variance()` | Posterior variance — how tightly the belief is concentrated |

```sema
b = prior_with(8.0, 2.0)
print(b.confidence())   # 0.8   — the point estimate
print(b.mode())         # ~0.875 — the most likely theta
print(b.variance())     # shrinks as evidence accumulates
```

Use `variance()` (or the width it implies) to decide *when you have seen enough
evidence to stop* — a natural termination signal for agentic search.

:::tip[Confidence is a calibrated quantity]
Because a `Belief` is a real posterior, `variance()` gives you honest
uncertainty, not just a point estimate. Pair it with the calibrated
[`~=` similarity operator](/neurosymbolic/similarity/) to turn fuzzy matches
into evidence you can accumulate.
:::

See the full signature list — all fields and effect rows — in the generated
[std.belief API reference](/reference/stdlib-api/belief/).
