Ask & abstain
Strict by default: the answer is generated only from retrieved passages, quoted verbatim in the source language, and every claim is checked against its citation. When nothing supports the question, CiteNexus refuses instead of guessing.
from citenexus.answer.result import Decision
response = rag.ask("Can the employee disclose this?") # mode=strict by default
if response.evidence.decision is Decision.answered: src = response.sources[0] print(response.answer, "—", src.document, "p.", src.page)else: print("abstained:", response.answer) # the pinned refusal, not a fabricationOptions
Section titled “Options”ask(question, *, mode=TrustMode.strict, k=None, answer_language=None, conversation_id=None, strategy="strict", search_languages=("en",))mode— the trust mode (below). DefaultTrustMode.strict.k— per-calltop_koverride; falls back to the client’stop_k.answer_language— force the answer’s language (see Languages).conversation_id— thread turns together (see below).strategy—"strict"(default) is the single-passage flow."deep"runs the bounded agentic loop, and raisesUnsupportedSearchLanguageErrorifsearch_languagesis anything other than the default, because the loop writes its own follow-up queries. Any other value raisesValueError.search_languages— the languages the question is searched in; default("en",). See Cross-lingual corpus.
Today the flow emits answered or refused (partial is a reserved Decision
value). See The Result object for the full return shape.
When the decision is refused, missing_evidence says which of the six refusal
paths you hit — thin retrieval, the faithfulness gate, the authority floor, an
unclaimed script on the question or on the corpus, or a conflict. Read them off
in Why did it abstain?.
Trust modes
Section titled “Trust modes”Import: from citenexus.domain.trust import TrustMode
All three modes run the same extractive, per-claim-gated flow: the answer is quoted from one retrieved passage, every claim is verified against it, and an unsupported claim is dropped in every mode. Citations are never optional, and no mode summarises ungrounded evidence. What the mode actually changes is two things — the authority floor, and what happens when the retrieved passages contradict each other.
| Mode | Authority | Conflicts |
|---|---|---|
strict (default) |
Enforces the floor. Candidates below minimum_tier are dropped outright; if nothing survives, the call refuses for lack of standing. |
Abstains on a conflict touching the cited passage, and cites both sides. |
normal |
Tie-break only — a stable sort by descending tier, nothing dropped. | Answers, and lists the conflicts on result.conflicts. |
exploratory |
Ignored — candidates pass through untouched. | Answers, and records conflicts_detected only. |
Conflicts are never resolved by rank (ADR-0007).
from citenexus.domain.trust import TrustMode
response = rag.ask("Summarize the risk factors.", mode=TrustMode.exploratory)Conversation memory
Section titled “Conversation memory”Pass a conversation_id and prior turns are folded into the retrieval query, and
the new turn is remembered (bounded by memory_max_turns, default 20):
rag.ask("What notice does termination require?", conversation_id="thread-42")rag.ask("And for a fixed-term contract?", conversation_id="thread-42") # follow-up in contextAsking, in each language
Section titled “Asking, in each language”The same cite-or-abstain flow in all three ports. Python’s ask is the
store-backed facade (the corpus was ingested first with rag.ingest(...)); Go and
JavaScript take the corpus inline.
res := answer.Ask(corpus, "Can the employee disclose confidential information?", answer.DefaultTopK)if res.Evidence.Decision == result.DecisionAnswered { fmt.Println(res.Answer, "—", res.Sources[0].Passage)} else { fmt.Println("abstained:", res.Answer) // the pinned refusal}const response = ask(corpus, "Can the employee disclose confidential information?")if (response.evidence.decision === "answered") { console.log(response.answer, "—", response.sources[0].passage)} else { console.log("abstained:", response.answer) // the pinned refusal}# the store-backed facade — corpus already ingested via rag.ingest(...)response = rag.ask("Can the employee disclose confidential information?")if response.evidence.decision is Decision.answered: print(response.answer, "—", response.sources[0].passage)else: print("abstained:", response.answer) # the pinned refusal