Lens
Conversation threading & grounded chat
Chat in eli.ai is a durable, multi-turn conversation, not a stateless prompt. Every turn is persisted server-side; generated answer claims carry clickable [Sn] citations that survive the thread, and an asynchronous groundedness check attaches a "verified n/m" badge to each answer. This page covers how threads are stored, how citations stay stable across turns, and how the badge is computed.
A turn, end to end
Server-side persistence
A conversation is a first-class, workspace-scoped record. The first turn creates a conversation and returns its id; subsequent turns pass that conversationId back so the server can rehydrate the full thread. History lives in Postgres under row-level security — a conversation in another workspace is simply invisible — so a thread is durable across reloads, devices, and worker-driven agent turns alike. Nothing about the conversation lives only in the browser.
- Messages store the role, the rendered text, and — for assistant turns — the source map that resolves each
[Sn]label to achunkId/docId. - Titles are derived from the opening turn so the conversation list is scannable.
- Token & cost accountingattaches to each turn, so a long thread's spend shows up on the cost dashboard like any other model usage.
The same threading powers agents
conversationId, so an API-driven multi-turn agent and the chat UI share one persistence model. See Agents & runs (v1).Source Citations across turns
Retrieval runs per turn and labels its sources [S1], [S2], … The labels are local to the turn — turn 3's [S1]may be a different chunk than turn 1's — and each turn's source map is stored alongside its message. When you reopen a thread days later, every citation in every past answer still resolves and still jumps to the exact source chunk, because the mapping was persisted, not recomputed.
Sources stream to the client before the answer tokens, so citations are clickable the instant they render; a small hold-back buffer prevents a half-written [S1 from flickering. The underlying retrieval, fusion, and refusal behavior is identical to a single-shot answer — see Retrieval & grounded chat.
The groundedness badge
After an answer finishes streaming, the server enqueues an asynchronous groundedness check that runs the shared judgeAssertionsengine — the same checker the evals runner uses — over the answer's claims and its cited evidence. The result attaches to the message as a "verified n/m claims" badge: n claims were supported by the sources out of m checked.
- Asynchronous. The badge resolves after the answer is already on screen, so verification never adds latency to the response.
- Flag-only. In interactive chat the check never hard-blocks or silently rewrites an answer. A noisy judge must not fail in front of a client; the strict, claim-by-claim NLI variant runs only in eval runs.
- Persisted.The verified count is stored on the turn, so the badge is there when you reopen the thread — no recomputation, no drift between what you saw and what's recorded.
Reading the badge
m/mmeans every checked claim was grounded in the cited sources. A shortfall is a prompt to click through the citations — it flags claims the evidence didn't clearly support, not a verdict that the answer is wrong.Related
- Retrieval & grounded chat — hybrid retrieval, RRF, and the refusal gate behind each turn.
- Judge governance — how the groundedness judge is calibrated and rolled over.