← All guides

Natural-language questions — let your users ask the domain

Your application's domain can be asked questions in ordinary English. EARL answers only from the facts the domain learned from your documents, shows exactly which facts each answer rests on, and says plainly when it cannot answer. There is no language model in the loop and nothing leaves the platform: one short CPU process per question, typically a few milliseconds after the domain's facts are read.

Use it for the parts of your vertical where a person needs to ask rather than submit a claim: a help panel next to a review screen ("what does a loan require?"), a pre-check before an action ("does a deployment need approval?"), an explainer for an auditor ("why does consent enable a loan?"). Use evaluate when you already have a claim and need a verdict; use ask when you have a question.

Call it

From your application, with the application's API key, on a bound workflow:

from earl_sdk import EarlClient
from earl_sdk.outcomes import Result, SemanticUncertainty, OperationalError

client = EarlClient("https://<your-platform-host>", API_KEY)
wf = client.workflow(WORKFLOW_ID)                 # the workflow over your trained domain (id from app.bind_workflow(), or the application page)

out = wf.ask("Is consent required for a loan?")
if isinstance(out, Result):
    show(out.answer, read_as=out.interpretation["asked"], evidence=out.result["claims"])
elif isinstance(out, SemanticUncertainty):
    show(out.answer)                              # a clarification or a declined request; out.reasons says which
elif isinstance(out, OperationalError):
    log(out.code, out.message)                    # e.g. engine_binary_missing, faithfulness_gate_failed

Over HTTP it is POST /api/v1/workflows/{workflow_id}/language/ask with {"question": "...", "intent": "explain"}, returning the workflow record. Like every capability call, it is an append-only, Ed25519-signed record you can verify offline (client.verify_offline(out)). intent is optional (explain, answer, teach, compare) and only changes the wording's register, never the facts.

In the platform UI, the same question box is on the domain page (Ask) and in the application's capability console, so you can try questions while you author the corpus.

What your users can ask

Kind Example Answer
Yes / no Does a loan need consent? · Is consent required for a loan? Yes (taught or derived), a taught No, or cannot establish
List, forward What does a loan require? · Which item does the register record? every held answer, each labelled taught or derived
List, inverse What requires consent? · What is enabled by consent? every held answer
Why Why does consent enable a loan? · How can review enable egress? taught (no reason invented) or derived (the rule named)
Describe, relate, compare Tell me about a replica. · How are a label and origin connected? what is held about them — never an invented category

The wording can vary. EARL reads passive voice, fronted phrases (For a loan, is consent needed?), a premise before the question (A label relates to origin; does it prove origin?), relative clauses (Is consent something a loan requires?), and the verbs your own corpus used. Direction is kept: Is consent required by a loan? and Does consent require a loan? are different questions with different answers.

Follow-up questions

EARL itself keeps nothing between questions. A workflow can still carry one thing forward: the concept a pronoun stands for, taken only from the workflow's own signed records.

wf.ask("What does the register record?")   # Register records: loan. ...
wf.ask("Is it required by a loan?")         # Reading "it" as "register". I cannot establish that a loan requires register ...
wf.ask("Does it prove ownership?")          # asked back: the previous answer names "loan" and "register"
  1. EARL reads the new question on its own. If a pronoun stands for something the question does not name, the engine asks it back as usual. It never guesses.
  2. The platform takes the workflow's most recent answered question. Clarifications and declined requests are skipped, so asking back never moves the referent. The candidates are the concepts that question was about and the concepts its answer asserted, minus any the new question already names.
  3. Each candidate is handed to the engine explicitly. It is kept only if the engine can use it in that slot: "is it true?" points at a statement, not a concept, so it is always asked back.
  4. Exactly one fits: that answer, opening with how the pronoun was read. Several fit: the question is asked back and the answer names them. None, more than four, or no earlier answer: asked back, as before.

Every step is in the record under result.antecedent_resolution:

{"status": "applied", "pronoun": "it", "concept": "register", "candidates": ["register"],
 "fitting": ["register"], "refusals": {}, "engine": {"applied": true, "refused": null, "...": "..."},
 "source_record_id": "…", "source_sequence": 2, "first_reading": "UNRESOLVED reference (it)"}

status is one of applied, ambiguous, refused, none_fit, no_candidate, too_many_candidates or no_prior_answer. The field is absent when the question needed no resolution. Show the answer as it is, since it states the reading. To start fresh, use a new workflow.

Limits, measured on a developer-authored set (EVAL-10, not an independent measurement): - No wrong referents after repair. When several concepts would fit, it asks rather than choosing by topic. After "Why does an anomaly require review?", "Does it prove fraud?" asks back naming "anomaly" and "review", where a person would assume the anomaly. - One pronoun per question. "Why does it examine it?" is asked back. - Needs a current engine. The NL engine's earl-api must report interpretation.unresolved. With an older engine, pronouns are asked back as before.

What it will not do — by design

Outcomes

outcome when what to do
Result an answer: supported Yes, a list, a why, a taught No, or "cannot establish" show answer; show interpretation["asked"] next to it; keep claims for audit
SemanticUncertainty a clarification (unknown verb), a declined request (writing, advice, authorization), a question with no resolvable referent, or one EARL could not read; reasons carries language_<mode> show answer — it says why and suggests questions it can answer
OperationalError the path could not run (engine_binary_missing, domain_not_trained, no_domain_facts, belief_store_unavailable, engine_unavailable, engine_timeout) or faithfulness_gate_failed do not show an answer; for faithfulness_gate_failed the answer was withheld by the platform — report it as a defect

Reading a response

{
  "answer": "Yes — a loan requires consent (taught directly).",
  "mode": "yes_no",
  "interpretation": {"asked": "whether(loan requires consent)", "resolved": "whether(loan requires consent)",
                     "resolved_against": null, "canonical": "is consent required for a loan?", "rewrites": [],
                     "arguments": ["loan", "consent"], "unresolved": null},
  "claims": [{"s": "loan", "rel": "requires", "o": "consent", "provenance": "taught"}],
  "gate": {"checked": 1, "passed": true, "unsupported": [], "misattributed": [], "prose_misattributed": [],
           "checks": ["claim_support", "provenance_accuracy", "non_vacuity", "prose_origin"]},
  "answer_withheld": false,
  "facts_count": 20
}

Get good answers: write the corpus for questions

The corpus rules, with measured before/after examples, are in authoring-domains §1. In short:

The answers can only be as good as what the domain learned. Before relying on questions, open the domain's Knowledge graph › Beliefs (or GET /api/domains/{id}/kg/beliefs) and check that each fact you meant to teach is there, with the right direction.

Patterns for a vertical

See capability-limitations for the measured status of language.ask and sdk-reference for the method.