Internal knowledge assistant: answers with sources, or an honest refusal
Runs on free hosting, so the first load can take up to a minute.
On this page
In short
- WhatAnswers only from the documents, and cites them.
- HowGuards, hybrid search, a relevance gate and grounding checks.
- Result89.6% of 48 labelled questions passed, 100% citation precision, zero safety failures, on llama-3.1-8b-instant.
My own project. The documents describe Contoso Financial, a fictional company.
Answer, cite or refuse
Passing every check gives an answer with sources; any failed check ends in the same refusal sentence.
Every reply either carries its sources or says plainly that the documents cannot answer.
- Unsafe questions are refused before any search.
- No relevant passage means no model call.
- A claim that cannot be traced is refused.
Technical detail
Every refusal is the same sentence: “I don’t have sufficient information in the provided documents.” The reason (no relevant context, an ungrounded figure, an injection attempt and so on) goes in a separate field for operators, so a caller probing the system learns nothing about which rule fired. The confidence label is computed from the evidence (citation validity, figures traced to the cited text, lexical support and retrieval strength), not from the model’s tone.
Measured, not assumed
- 48 labelled questionsFactual, multi-document, unanswerable, injection and confidential.
- 89.6% passedAll 48, on llama-3.1-8b-instant.
- Zero safety failuresNo forbidden content reached an answer.
A fixed question set, scored the same way on every run, with the misses published.
- 20 factual, 9 multi-document, 8 unanswerable, 6 injection, 5 confidential.
Technical detail
With no model at all, the offline extractive baseline still passes injection 6/6 and confidential 5/5, because those controls are code rather than model behaviour. On llama-3.1-8b-instant the p95 latency is 1,438 ms. The weakest category is multi-document questions: 5 of 9 pass, where the answer has to combine two or three documents and the model sometimes drops a clause.
Retrieval measured on its own
- Dense only: 0.948Meaning-based search alone.
- BM25 only: 0.960Keyword search alone.
- Hybrid, fused: 0.977Both, with their rankings fused.
An answer is only as good as the passages behind it, so retrieval is scored without the model.
- Recall@6 over the 29 answerable questions, with no model calls.
Technical detail
Selection is scored on the fused score because scoring it on dense similarity alone threw the keyword evidence away at the last step: a policy that was the top keyword match never reached the final six passages. With selection on dense similarity, hybrid recall@6 was 0.925; on the fused score it is 0.977. The dense and BM25 rows return their raw top six passages, while both hybrid rows also remove near-duplicates.
Guards outside the model
- Input guardInjection and customer-data requests refused before any search.
- Trust tiersNotice-board text is untrusted, so planted instructions are stripped.
- Grounding checkEvery citation and every figure must trace to the cited text.
The controls that matter are code, so a persuasive prompt cannot talk its way past them.
Technical detail
The input guard combines pattern rules with a classifier when the hosted model is on. Each document gets a trust tier from what it says about itself, not from its filename; instruction lines inside untrusted passages are replaced before the prompt is built, and an answer whose citations all point at untrusted content is refused, so a notice-board claim is never stated as policy.
Built to run
- 94 tests
- CI with a safety gate
- Docker
- No API key neededRuns on an offline extractive model.
Anyone can clone it and run it without a key.
Want to talk about this work? Email me or download my CV.