Case study · AI assistant · October 2026

A Q&A bot that cites its page, and admits what it does not know.

On questions it had never seen, it answered 4 of 6 correctly, refused all 4 whose answer is not in the documents, and invented nothing. On the questions it was tuned on, it had scored 15 of 15. The difference between those two numbers is the point of this write-up.

The problem

Staff at a small contractor ask questions like "how long is probation?" or "can I accept this gift?", and the answer is somewhere in a city employee handbook. The bot has three jobs:

  1. Answer from the handbooks only.
  2. Cite the page, so the person can check.
  3. Say "I can't find that in the handbooks" when the answer is not there, instead of guessing.

The third matters most. A confident wrong answer about leave or gifts is worse than no answer.

The client is invented and no private data is used. The documents are three real, public city employee handbooks, 193 pages in total.

How it works

How it was tested

Thirty test questions were written before the bot was built, each with its expected answer and page. They were split in two:

Scoring was mechanical, the rules were fixed before any score counted, and a person read every failed answer.

Results

MeasureBuilding questions, after tuningHeld-back questions, one run
Correct answers15 of 154 of 6
All expected pages cited15 of 154 of 6
"Not in the documents" questions refused5 of 54 of 4
Answers stating something false00

Six answerable questions is a small sample. The fair statement is "4 of 6 on unseen questions, and it never invented an answer", not a percentage.

Both failures were search failures. In one, the right page was found but the passage holding the answer was not among those shown to the model, so it refused. In the other, it gave half the answer correctly and was never shown the page with the other half. When search failed, the bot refused or answered partly. It did not fill the gap with a guess. That behaviour is what the design was for, and it held on questions it had never seen.

What went wrong

A bigger model was not better

In a second round a large hosted model was swapped in, with the search left alone. On the same 30 questions it scored 18 of 21 against the small local model's 19 of 21, at a measured cost of about 0.44 cents per question. Both failed the same two questions for the same reason: search never showed them the answer. With this search, the search was the limit, not the model. And with a hosted model, the question and the retrieved text leave the building; with the local one, nothing does.

The search was then changed to keep rules whole across passage and page breaks. That fixed both known failures on the old questions. It is not yet confirmed: those questions are now tuning data, and the change will only count once it is run a single time on new ones.

How it was built

I designed the project, wrote the test set, set the targets, and made every decision about what counted as correct. The code was written by AI coding tools working from written specifications. Each change was reviewed, covered by automatic tests that run on Windows and Linux, and recorded in a dated log. Round 1 was finished about 20 hours after the first commit.

Limits