Skip to content

Verified answers

Concept 5 of 8

Telha does not generate an answer and then hope it is grounded. It plans the evidence first, generates strictly from that plan, then breaks the draft apart into individual claims and checks each one against the evidence it was actually allowed to use. What comes back is not just an answer, it is a judgment about that answer, and the judgment persists.

Normative spec

Claim decomposition, span matching, and verdict scoring are defined in claim-verification, one of the F7-reviewed algorithm families. Evidence planning is defined in the memory planner spec; see Architecture › Memory planner and Architecture › Generation orchestration.

The loop: plan, generate, verify

flowchart LR
    Q["Question"] --> PLAN["1. Plan evidence<br/>(query + semantic search + expansion,<br/>packed under a token budget)"]
    PLAN --> GEN["2. Generate<br/>(LLM writes from the plan only,<br/>[S1]-style citations)"]
    GEN --> DECOMP["3. Decompose<br/>(draft -> atomic claims)"]
    DECOMP --> VERIFY["4. Verify<br/>(each claim scored against planned spans)"]
    VERIFY --> VERDICT["Verdicts: supported / partial / unsupported"]
    VERDICT --> TRACE["Immutable trace<br/>(plan, prompt, claims, verdicts, spans)"]

1. Plan evidence under a token budget

Before anything is written, the memory planner recalls candidate evidence (structured query results, semantic search, graph expansion), breaks it down to source spans, and packs the best spans into a budget measured in tokens, with per-source diversity caps so one document cannot crowd out everything else. Evidence comes in two classes: truth spans, which trace to actual source bytes, and derived cache summaries, which can speed up planning but can never themselves support a claim during verification.

2. Generate from the plan only

The LLM (Anthropic, or any OpenAI-compatible endpoint configured operator-side) writes its answer from the planned evidence and nothing else, using [S1]-style citation markers to point at specific spans. Because the prompt is built deterministically from the plan, the same plan produces the same prompt every time, which is part of what makes the resulting trace reproducible and auditable later.

3. Decompose the draft into atomic claims

The draft is not verified as one blob of text. It is decomposed, by an LLM call constrained to structured output, into individual atomic declarative statements. Hedges, transitions, and questions are recognized as non-factual and skipped rather than scored. Decomposing first is what stops a compound sentence from hiding a false statement behind a true one: each claim stands or falls on its own.

4. Verify each claim against the planned spans

Each claim is scored against the candidate spans (the ones its citation markers point to, plus the plan's most semantically similar spans) by combining semantic similarity with lexical evidence. Citation markers are only a hint: a claim citing [S3] whose actual content does not support the claim is not rescued by the citation. A claim that asserts a number is held to a stricter standard, figures require figure evidence, so a claim with the wrong number lands as unsupported even if the surrounding sentence reads as an almost-verbatim paraphrase of the source.

Verdicts: supported, partial, unsupported

Every claim lands in one of three bands:

Verdict What it means
Supported Strong semantic and lexical match to a planned span; the claim is grounded.
Partial Meaningful but incomplete match, the claim is in the neighborhood of the evidence but not fully anchored by it.
Unsupported No planned span backs the claim closely enough, including any claim asserting a number the evidence does not state.

A claim that closely restates what a planned span actually says, in either the source's own words or a genuine paraphrase, clears the supported band.

A claim that is topically related to a span but not fully anchored, for example, a claim that combines information from more than one span in a way no single span fully backs, lands as partial.

A claim that is topically similar to a span but fabricates a detail the span does not contain, most sharply a wrong figure, is deliberately scored as unsupported rather than partial. Topical similarity alone must not be enough to rescue a fabrication.

Unsupported claims are flagged or redacted by policy

What happens to unsupported text is a policy decision, not a fixed behavior: operators can choose to flag unsupported claims (annotate them in place so the reader sees the caveat) or redact them (replace the unsupported sentence with an explicit omission notice before the answer is delivered). Either way, the underlying draft and its verdicts are preserved in the trace even when the delivered text has been redacted.

If decomposition itself fails, or if the claims and the plan's spans were embedded under inconsistent models, the response is delivered marked verification unavailable rather than silently treated as verified. A generation with no evidence at all is not given the benefit of the doubt either: every decomposed claim from a zero-evidence draft is unsupported by construction.

The judgment persists as an immutable trace

Every generation writes a full trace record: the evidence plan, a hash of the prompt, every decomposed claim, its verdict and score, and the span it matched best against. The trace is deterministic given its inputs (the plan, the draft, the embeddings); only the LLM's own generation and decomposition steps are non-deterministic, and those are pinned by recording the exact model and a hash of what they produced. Traces are never re-scored after the fact, even if the verification model changes later, a trace always records which model judged it.

This is what makes an answer defensible months later: you are not asking anyone to trust the model's memory of why it said something, you are reading the actual plan, the actual spans, and the actual verdicts that were computed at the time.

Seeing it end to end

POST /v1/generate returns the draft, its citations, the plan, and the verification result inline. GET /v1/trace/<traceId> returns the full persisted record, and the trace viewer renders it clickable down to the exact source text. See Developers › Generation traces.