Skip to content

44 · Grounded answers from your sources

Some turns only ask for information -- how long does a refund take? -- and no tool answers them. With on_unread: answer, such a question is answered from your own documents: the passages that match it are found on your machine, the answer is made of what they say, and every sentence carries the passage it came from. A sentence a passage does not support is removed, and where nothing is left the agent says it has no answer.

Language support

The selector understands turns written in English. Portuguese is its next grammar, and has not shipped yet; until it does, a turn written in Portuguese is not read as Portuguese.

Your documents, indexed on your machine

stalwart sources build reads your documents and writes an index, here, with the licensed runtime:

$ stalwart sources build docs/delivery.md docs/refunds.md --out sources.json
Indexed 2 document(s), 3 passage(s), into sources.json (ba9e751e249e).
$ echo $?
0
$ stalwart sources ask "How long does a refund take?" --index sources.json
A refund reaches the card the order was paid with in three to five working days.  [refunds.md > How long a refund takes]
$ echo $?
0
  • Markdown (.md) and HTML (.html, .htm) are split at their headings and then at blank lines: each paragraph is a passage, under the heading above it. Plain text is split at blank lines. A PDF's text is read where pypdf is installed.
  • Each passage is indexed by its words and its heading's words; a vocabulary file (--vocabulary) adds the terms and kin your vocabulary declares, so a question in your customers' words finds a passage in yours.
  • Re-indexing is building again: the index is a file, with its digest printed, and the agent reads the file it is given.
  • A file that cannot be read -- missing, empty, not UTF-8, a PDF with no pypdf -- stops the build with its name and exit status 2.

stalwart sources ask answers a question from the index as the agent would with the extract composer, each sentence with the document and heading it came from, so you see what your users will be told.

The documents and the index stay on your machine. Stalwart's servers never receive them.

Answering in the agent

An agent answers from the index when its on_unread is answer -- the third choice beside refuse and handing the turn on (chapter 45) -- and answers: names the index:

desk = Agent("desk", tools=[get_order, charge_card], model="anthropic:claude-sonnet-5-5",
             on_unread="answer", answers={"index": "sources.json", "composer": "extract"})

for text in ("How long does a refund take?",
             "Can a gift card be refunded?",
             "charge order 4411 40.00"):
    reply = desk.chat(text, user="u-17")
    shown = reply.waiting[0].wording if reply.waiting else reply.text
    print(f"{text}\n  -> {shown}")
How long does a refund take?
  -> A refund reaches the card the order was paid with in three to five working days.
Can a gift card be refunded?
  -> An order can be refunded within 30 days of delivery. A gift card cannot be refunded.
charge order 4411 40.00
  -> Run charge_card?

The two questions were answered from the passages; charge order 4411 40.00 asks for something to be done, so it went to the charge and its confirmation, and never to the documents. An action is never answered with documentation: a passage describing how a refund works is not a refund. A question asked beside an action -- refund order 4411 and tell me how long it takes -- is answered after the action has run.

answers: takes index, k (at most this many passages, 3 by default), floor (no passage scoring below it, 0.3 by default, on a scale of 0 to 1) and composer. Those an agent does not give come from your console's Settings page, at the conversation's first turn.

The composers

  • extract: the answer is the passages' own sentences that share a term with the question, in rank order. No model is called, and every sentence is supported by construction.
  • compose: your agent's own model writes the answer from the numbered passages, with your key, marking each sentence with the passages it draws on. Each model call is counted as one answer_calls. An answer left with no sentence after the check gets one second attempt.

Which plans include each, generated from the plans' rows:

Composer Free Paid Enterprise
extract ✓ ✓ ✓
compose — included, 20,000 answer_calls a month included, answer_calls with no limit

Either way, the runtime checks every sentence against the passages it cites: each number, date, amount, name and negation in it must be in one of them. A sentence that fails is removed. Retrieval and the check run in the runtime, with no network call; compose calls your model, and nothing else.

Citations

Every sentence of an answer is bound to its passages: the document, the heading and, where the document gave one, its link. stalwart sources ask prints them, as above, and stalwart serve's interface shows them under the answer (chapter 35). A rule of chapter 43 can add a sentence whenever a passage with a given tag is cited.

No grounded answer

When no passage scores above the floor, or no sentence survives the check, the outcome is no_grounded_answer, and the agent goes on as if on_unread were refuse: the person is asked once to say it again, and then told it cannot be done. Here the refunds document is deleted and the index built again:

$ rm docs/refunds.md
$ stalwart sources build docs/delivery.md --out sources.json
Indexed 1 document(s), 1 passage(s), into sources.json (1e1f014fea6f).
$ echo $?
0
$ stalwart sources ask "How long does a refund take?" --index sources.json
No grounded answer: no passage scores above the floor. The agent would refuse or hand the turn off.
$ echo $?
1

The command exits 1 for no grounded answer.

What is kept, and where

Nothing of it is kept by us. The documents, the index, the questions and the answers are on your machine and in your storage. Only the count of answer_calls is reported, with your licence's other meters.

The limits

  • A supported sentence agrees with its passage. It does not make the passage true, or current: a document that is out of date gives answers that are out of date.
  • Retrieval matches words, weighed by how rare they are in your documents. A passage that shares enough of a question's words to score above the floor is used, whether or not it is about the question; the floor and k are how strict it is.
  • The check holds numbers, dates, amounts, names and negations to the passages. A sentence that rewords a passage and changes its meaning in other words can pass it.