Research modes & the elector
How hard a claim gets checked is your choice, per debate - or per claim.
The methods
| Mode | What it does | Good for |
|---|---|---|
two_step | Search → read the results → judge | Most claims. Fast, cheap. |
decompose | Break the claim into sub-questions, research each, synthesize | Compound or multi-part claims |
decompose_invest | Decomposition with a more investigative planner | Claims needing an angle, not just facts |
local | Search only the internal indexed corpus, no external calls | Deterministic, zero search cost |
agentic | An agent searches, fetches pages, follows citations, requests PDFs | Statistical and document-backed claims |
ricochet | Search → follow relevant citations and content links → final LLM verdict and summary | Claims whose evidence is in an underlying report, study or dataset |
Supported methods are two_step, decompose, decompose_invest, local, agentic and ricochet. Unsupported methods return HTTP 422. Web research reads extracted page content where available and falls back to snippets; evidence budgets still apply.
When every main search fails because providers are unavailable or time out, the panel remains incomplete: verdict=pending and research_delay_reason=search_unavailable. Recent results, claim panels and SSE expose this state. It does not satisfy the claim-verdict completion barrier. Automatic retries run after 10 minutes, 30 minutes and 2 hours by default, then fall back to UNVERIFIABLE if search remains unavailable. A successful empty search follows normal research behavior.
Ricochet starts from web search results and follows relevant links found in fetched pages. Jev selects navigation actions; a separate text-generating LLM produces the final verdict, explanation and citations. It can request another search when a trail fails. Searches, pages, depth and elapsed time are bounded; reaching a limit still triggers the final summary from gathered evidence.
Choosing per debate
When omitted, new analyses use agentic + decompose. This default also applies to watched media unless the operator sets WATCH_RESEARCH_MODE. Existing analyses retain their configured methods. Set the array on POST /v1/debates:
{"research_modes": ["agentic", "decompose"]}More modes means more panels, more confidence, more cost - roughly linearly. Start with ["two_step"] and add when you find claims it does not settle.
An empty list is meaningful, not a mistake:
{"research_modes": []}Claims are still detected, persisted and shown - but nothing is researched until you ask. That is how you build a "review, then check the ones that matter" flow without paying for the rest.
Checking one claim another way
Any claim can be re-checked with a specific method, including one already checked:
curl -sX POST $RTFC/claim \
-H "X-Auth-Token: $TOKEN" -H 'Content-Type: application/json' \
-d '{"text": "…", "debate_id": "…", "research_mode": "agentic", "manually_added": true}'manually_added: true also widens the elector's expectations, so an election waits for the added method to finish instead of deciding without it.
For a shared claim, only the genuinely missing method runs - asking for ["two_step","decompose"] on a claim that already has a two_step panel researches decompose alone.
Administrators can submit a claim with any supported research method, even if their plan does not list it. Claim quotas and spend limits still apply, and the submitting administrator is billed for the research. Other callers need the method to be included in their plan.
One answer per claim
GET /v1/claims/{id}/results?debate_id=… exposes one claim_verdict for that claim occurrence. It is null while research is pending; show “Verification in progress”. When all expected methods agree, or one or more sourced panels agree and the others abstain with UNVERIFIABLE, the best-sourced agreement panel supplies the answer (basis: agreement). A single substantive panel can therefore establish agreement. Conflicting sourced verdicts receive adjudication using the operative rubric, admitted evidence, scope exclusions and corroboration decisions (basis: adjudicated). An operator winner always takes precedence (basis: operator). best is a compatibility alias of claim_verdict; /best exposes the same answer. conflict remains false; use basis: fallback to identify an unresolved answer. Failed or unavailable synthesis persists a cautious substantive panel as basis: fallback, using the reconciliation rules below for degree disagreements; other conflicts prefer PARTIALLY_TRUE, MISLEADING, FALSE, then TRUE, with source ranking breaking ties. Retries use the panel fingerprint, exponential backoff and a bounded attempt budget. Historical agreements are backfilled without an LLM call; conflicting sourced verdicts, including historical ones, use the shared daily call cap and retry budget.
basis: fallback identifies a persisted method answer while synthesis is unavailable. Clients should display a localized disagreement note alongside that answer.
Reconciling sourced disagreements
Disagreement by degree, including PARTIALLY_TRUE versus FALSE or TRUE, cannot produce UNVERIFIABLE. A rejected UNVERIFIABLE receives one retry with an explicit instruction to reconcile and cite admitted evidence. If synthesis still fails, source ranking selects a substantive fallback. For PARTIALLY_TRUE versus FALSE, overlapping admitted URLs favor PARTIALLY_TRUE; without overlap, highest source trust wins, then the existing source-count, confidence and query-ID ordering breaks ties. The stored answer records adjudication_reason: reconciled_panels or reconcilable_panels_fallback, alongside basis: adjudicated or fallback. A direct sourced TRUE/FALSE contradiction retains the existing UNVERIFIABLE checks: an explicit irreconcilable-evidence reason and admitted citations from both directions. An unchanged valid adjudicated answer survives a rebuild even if adjudication is disabled or capped. Citation ordering and the legacy addition of publication-date fields do not replace it. Actual changes to evidence, verdicts or dates can rebuild the answer.
{"claim_verdict": {"verdict": "true", "confidence": 0.9, "explanation": "…", "sources": [], "basis": "agreement"}, "panels": [{"research_mode": "two_step", "verdict": "true", "confidence": 0.9, "explanation": "…", "sources": []}, {"research_mode": "decompose", "verdict": "true", "confidence": 0.8, "explanation": "…", "sources": []}], "best": {"verdict": "true", "basis": "agreement"}}Every authorized caller, including reader and API roles, receives all eligible method results in panels alongside claim_verdict and its best alias. Recent-result and SSE feeds include each method panel with the current claim_verdict. The dashboard shows only the main answer by default; every reader can expand “See other results”. Existing tenant access checks, shadow-row exclusion, organisation source/explanation filtering and admin-only primary-source metadata still apply. provisional_claim_verdict is a per-debate create/update option, default false: enabling it permits a provisional two-step answer while final research is pending. Exports, FaktoLumo publishing and alert digests use only the persisted final verdict.
Cited sources in GET /v1/claims/{claim_id}/results may include nullable published_at, the recorded publication timestamp, for every authorized reader. It comes from the cited URL in that research panel; when the final citation lacks it, the matching search row supplies it. Missing dates remain null; crawl time is never substituted. Older saved answers may have no publication metadata. Admin-only provenance diagnostics remain hidden from other readers.
<!-- Reader citation contract: source_origin, is_primary and primary_kind (FR/EN). --> Every authorized reader receives citation source_origin: curated for operator-curated sources, fact_checker for desk-earned sources, or official_site for a URL admitted as evidence about its own entity. The most specific registered domain wins; scoped official admission takes precedence. official_entity_label contains the stored entity label, or null when unavailable. Every citation also carries is_primary (boolean) and nullable primary_kind: official_site, official_data, official_text, own_statement, first_publisher, or other. These describe admitted evidence for this panel only: an originator, a self-scoped official page, or the panel’s exact originator URL. Explicit relays and unverified signals are never primary. Dashboard-v2 uses blue for primary citations, ahead of amber for alternative sources and default styling for curated sources. Official primary tooltips retain the entity authority restriction. Wikidata QIDs and other provenance diagnostics remain admin-only and absent from public OpenAPI.
The elector
With elector_enabled: true, the elector remains an advisory panel picker when the methods agree. Without its advice, source count, source trust and confidence select the agreement panel. When methods disagree, adjudication resolves the answer instead of electing one contradictory panel. Its advisory election first checks for a confirmed verdict: at least two non-manual panels must hold it, strictly outnumbering every other verdict. It then selects by math only among panels with that verdict, including matching manual panels, with reason confirmed and no Jev call. Without confirmation, single-panel and unanimous shortcuts remain; other elections use Jev. Jev receives neither panel confidence nor sub-question counts, and prioritizes supported verdicts, honest missing evidence and current sources.
An operator winner always overrides the automatic claim verdict. Crowd votes remain panel feedback and do not decide the reader verdict.
Votes, translations, manual verdict corrections and winner selection apply only to research panels. An ID that does not identify an eligible panel returns HTTP 404. Rejecting a winner selection leaves the previous winner unchanged.
Escalating until a verdict is confirmed
Set escalate_enabled: true on the debate and, when every research method you configured returns unverifiable for a claim, the next stronger method is tried automatically - one step at a time, up to escalate_max_steps (default 2).
{"research_modes": ["two_step", "decompose"], "escalate_enabled": true, "escalate_max_steps": 2}The extra check is included in the completion barrier, and the claim verdict is recomputed after it finishes. Disagreement among completed non-manual panels also triggers escalation until a verdict is confirmed: at least two panels hold that verdict and it strictly outnumbers every other verdict. Escalated panels vote; manual panels do not. UNVERIFIABLE counts as a result; empty and PENDING verdicts are ignored. Tokens are compared case-insensitively and exactly (FALSE and MISLEADING differ); a 2–2 tie is unconfirmed. Escalation proceeds one rung at a time, bounded by escalate_max_steps, the ladder top and the primary-source policy, independently of the elector.
The ladder is two_step → decompose → decompose_invest → ricochet → agentic; escalation adds the first rung above the strongest method already configured, and does nothing once agentic is in play or no ladder method is configured (local is not on it). Primary-source policy debates stop before ricochet.
Scope check
Before a verdict, two_step and decompose check each document they would rely on: does it measure the same quantity (same indicator and definition), for the same place and the same period as the claim? A page on the right topic that measures something else (inflation for GDP growth, another country, an older year for a dated claim) is set aside: shown to the model as context only, never used as evidence or listed as a source. A claim stated in the present tense is not set aside for a figure one or two years old. When every document found is out of scope, the panel is unverifiable and its explanation says what the sources measure instead. This check applies to every debate.
Each source carries scope_decision: same_scope, unclear (kept, but it may not measure exactly what the claim states) or null when it was not checked. Each panel lists the documents set aside in out_of_scope_sources (url, domain, title, scope_decision = different_quantity, different_geography or different_period).
Corroboration
Before a confident verdict, two_step and decompose check the claim's load-bearing figure or named fact against the in-scope evidence. Pages count as independent only when they do not relay each other, do not credit the same originator and are not on the same publisher domain; an official statistical or text source is sufficient on its own. With one independent confirmation, RTFC runs one extra corroboration search when the claim still has enough time. If that still leaves only one source, the verdict is kept but its confidence is capped and the explanation says that only one independent document confirms the figure. If documents disagree on the value, the verdict model receives a FIGURES FOUND block so it must explain the disagreement.
Each panel may carry corroboration: element, independent_confirmations, official_source, sufficient, confirming, contradicting, searched and capped. Each confirming or contradicting entry contains only provenance-safe metadata (url, domain, value, published). The field is visible to all readers and is null for older panels or modes that did not run the check.
Waiting for speaker identification
Set speaker_gate_enabled: true to delay claim detection until every speaker heard in the debate has been identified (mapped to a name via POST /v1/session/speaker-map). This applies only to debates that run diarization - not YouTube-caption ingest or plain text.
{"speaker_gate_enabled": true}You do not pick a mode - it follows the audio source:
- Live stream - the detector runs but withholds every batch of claims while any speaker heard so far is still unidentified, releasing the backlog in order once they are all named.
- File / video ingest - the detector does not run during ingest. Once every diarization label is identified, trigger the deferred run explicitly (below); it replays the whole stored transcript through the normal pipeline with full speaker context.
For a file/video ingest, trigger the deferred run once all speakers are identified:
curl -sX POST $RTFC/debates/$DEBATE/run-detection -H "X-Auth-Token: $TOKEN"Returns 409 while any diarization label is still unidentified; the response detail.still_waiting lists them (GET /v1/debates/{id} lists them too, as speakers_waiting). Pass ?skip_speakers=true to start anyway: unnamed voices stay Speaker N and the claims that need their identity wait until they are named. Once every voice is named, detection also starts by itself within a minute.
Tunable model-selection weights
Independently of which research mode you pick, you can bias which underlying LLM gets used for a debate's detector and worker calls - cheaper, faster, or higher-quality - without changing your research mode selection at all. Pass any of the following optional fields on POST /debates (or set them later on a live debate via PATCH /session/model-weights):
{"research_modes": ["two_step"], "cost_weight": 0.7, "latency_weight": 0.3}| Field | Effect |
|---|---|
cost_weight | Favor cheaper models more heavily |
latency_weight | Favor faster-responding models more heavily |
quality_weight | Favor higher-quality models more heavily |
Each is a number from 0.0 to 1.0 and independent of the others - set only the one you care about. Omitting all three (the default) selects the way it always has; no candidate model is ever excluded outright by these weights, so a debate never fails to get a result just because its preferred model is temporarily unavailable - it falls back to the next-best option under the same bias instead.
A live/interactive debate (microphone, screen share) typically wants both cost_weight and latency_weight set - cheap and responsive. A batch job (an uploaded video, a shared YouTube/TikTok/Instagram link) usually only needs cost_weight - there's no live audience waiting on a response, so paying extra for speed isn't worth it.
In the dashboard, thumbs on the main answer review the selected panel for agreement, operator choice or fallback. An adjudicated answer has separate answer feedback that captures the shown verdict, basis and considered panel IDs; it uses the same result reasons and optional benchmark consent. Claim extraction feedback stays separate. Feedback never changes a verdict or selects a winner.
Verdicts
true, false, partially_true, misleading, unverifiable, pending.
<!-- EN/ES research is an explicit rerun action; localized behavior below. --> unverifiable is a real answer: the available evidence does not settle the claim. Research stays in the claim’s language by default. Choose “Also search in English and Spanish” on an unverifiable claim or in any method’s details to rerun with strong_language: true via POST /v1/claim (with the claim’s text and linked debate_id; omit research_mode to rerun its stored methods, or set it to select one method). This uses normal rerun billing and preserves research context and the claim’s country filter. Foreign sources may still concern another country; the scope check keeps them outside the verdict evidence. Panels that attempt the retry are labelled “English/Spanish research”. The explanation stays in the claim’s language. Operators may restore automatic retries with STRONG_LANGUAGE_AUTO_RETRY=true; the default is false.