Skip to content

Doc 0097 min

Building an Internal Knowledge Assistant That Doesn't Confidently Lie

A default RAG demo answers everything and cites nothing. The gap between that and a tool your team trusts is grounding, inspectable retrieval, and an honest 'I don't know.'

Any weekend project can wire an embedding model to a chatbot and answer questions about your documents. The hard part is not making it talk. The hard part is making it shut up when it doesn't actually know.

Why the impressive RAG demo dies in month two

The first demo of an internal knowledge assistant always lands. You paste in the onboarding handbook, ask "how many days of parental leave do we offer," and it answers in a clean paragraph. The room nods. Someone says "ship it." Then month two arrives, and a support rep asks the same assistant about the refund window. It confidently answers "60 days." The written policy says 30. The rep issues a refund that should have been declined, and now the tool has cost more than it saved.

This is the pattern with retrieval augmented generation done casually: it is right often enough to be trusted, and wrong often enough to be dangerous. A model that is correct 92% of the time and silent about the other 8% feels worse in a demo and is far better in production, because the 8% is where the money leaks. The default RAG build inverts that. It answers everything, grounds little, and cites nothing, so no one can tell a solid answer from a fabricated one until a decision has already been made on it.

Grounding and refusal are not safety garnish you add later. They are the product. Everything below is the engineering that turns a plausible demo into an internal knowledge base AI a team will actually rely on.

Chunking for meaning, not for token count

The single most common defect I find in an inherited RAG assistant is the chunker. Someone split every document into fixed 500-token windows because a tutorial did. That slices a policy table down the middle, orphans a heading from the paragraph it governs, and glues the tail of one section to the start of the next. The retriever then fetches half a rule and the model invents the other half.

Chunk on the document's own structure. Markdown headings, HTML section tags, PDF outline bookmarks, the numbered clauses in a contract: these are boundaries the author already drew for you. A chunk should be one coherent idea, carry its section title and breadcrumb as a prefix, and never straddle two topics. When a table matters, keep it whole and store a short natural-language summary alongside it, because embedding models read prose far better than they read grid layouts.

When I rebuilt the retrieval layer for Verba, our internal document assistant, structure-aware chunking with heading context moved retrieval recall from the high seventies into the low nineties on the same question set, with no change to the model. That is the highest-leverage hour in the whole build, and it happens before a single prompt is written.

Retrieval you can debug: showing the sources behind every answer

If you cannot see what the retriever fetched, you cannot fix a wrong answer. You can only re-roll the dice on the prompt and hope. So the retrieval step has to be inspectable from day one, for engineers and for the people using it.

Every answer in a serious retrieval augmented generation system should be traceable to the exact chunks that produced it: the source document, the section, the similarity score, and the raw text passed to the model. When a user reports "this is wrong," you open that trace and the failure classifies itself in seconds. Either the right chunk was never retrieved (a chunking or embedding problem), or it was retrieved and the model ignored it (a prompting problem), or the source document itself is stale or contradictory (a content problem). Those three call for completely different fixes, and without the trace you are guessing among them.

Pure vector search is also not enough on its own. Semantic similarity misses exact identifiers, error codes, and product names that a user types verbatim. Hybrid retrieval, blending keyword search with vector search and re-ranking the merged set, catches the literal matches that embeddings quietly fumble. The re-ranker is what lets you retrieve twenty candidates cheaply and pass only the six that survive scrutiny to the expensive generation step.

Forcing citations and making 'I don't know' a first-class answer

An answer without a citation is a rumor. The model must attach, to each claim, the specific chunk that supports it, and your application should reject any answer whose citations don't check out. This is not a polite request buried in a system prompt. It is a structural contract on the output.

A knowledge assistant that cannot say "I don't know" will instead say something false with the exact same confidence, and you will not be able to tell the two apart.

Equally important is a real refusal path. If retrieval returns nothing above a relevance threshold, the correct output is an explicit abstention, not a best guess. "I don't have a source for that" has to be a legitimate, encouraged answer, not a failure the model tries to avoid. In practice I have the model emit structured output and validate it before anything reaches the user:

{
  "answer": "Refunds are available within 30 days of purchase.",
  "citations": ["policy-refunds#section-2"],
  "confidence": "high"
}
// If citations is empty, the app discards the answer
// and returns the honest fallback instead.

The rule is simple and strict: no valid citation, no answer shown. That one guardrail does more to reduce AI hallucinations than any amount of prompt tuning, because it removes the model's ability to bluff. It can only speak from something it actually retrieved.

Access control: the assistant must respect who can see what

An internal assistant reads everything, which means it can leak everything. If salary bands, unreleased roadmaps, and legal memos all sit in one index, a well-phrased question can surface a document the asker was never cleared to read. The model has no notion of permissions unless you build one.

Filter at retrieval time, not at answer time. Every chunk carries the access tags of its source document, and the query runs against only the subset the current user is entitled to. Never retrieve a restricted chunk and then ask the model to politely decline to use it, because a clever prompt will talk it back out. The safe pattern is that forbidden content is never a candidate in the first place. This also means your ingestion pipeline has to capture and keep source permissions in sync, which is real work and the reason enterprise AI search is harder than a public FAQ bot. It is also non-negotiable.

Evaluating retrieval quality before you trust a single answer

You cannot manage what you refuse to measure, and "it felt good in the demo" is not measurement. Before I let any assistant near real users, I build a golden set: 150 real questions drawn from what people actually ask, each paired with the document that should answer it and a note on the correct response.

That set gives you two numbers that matter. Retrieval recall asks whether the right chunk appeared in the top results at all; if it didn't, generation never had a chance. Answer faithfulness asks whether the generated response is fully supported by its cited chunks, with nothing added. I grade faithfulness with a separate model acting as a strict judge, then spot-check its verdicts by hand, because an unaudited automated grader drifts. When a chunking change lifts recall by twelve points on that fixed set, that is a fact you can stand behind, not a vibe. Run the suite on every meaningful change, the same way you run a test suite, or regressions will ship silently.

Keeping the index fresh without a re-index nightmare

Documents change, and a knowledge assistant answering from last quarter's policy is a liability wearing a helpful face. But re-embedding the entire corpus nightly is wasteful and slow once you pass a few thousand documents. The answer is incremental indexing keyed on content hashes: when a source changes, re-chunk and re-embed only that document, and delete the chunks belonging to anything that was removed. Stale chunks left behind after a deletion are a quiet source of confidently wrong answers, so deletion has to be as reliable as insertion.

Track a last-updated timestamp on every chunk and surface it in the citation. When a user sees "source updated three days ago," they calibrate their trust accordingly, and when they see a date from two years back, they know to double-check. Freshness you can see beats freshness you merely promise.

What it costs to run, and where the money actually goes

The model bill is almost never the real cost, and buyers who fixate on it are optimizing the wrong line. Take a team running 2,500 questions a week. Each answered query passes roughly six chunks plus the system prompt into a mid-tier model and gets a few hundred tokens back, landing near two cents a query. That is about 50 dollars a week, near 200 dollars a month in model spend. Embeddings and incremental re-indexing add a rounding error on top.

The money that matters goes into evaluation, the ingestion and permissions plumbing, and the ongoing curation of source content, because the assistant is only ever as good as the documents beneath it. Point a language model at a contradictory, out-of-date wiki and you get a fluent, well-cited wrong answer. When someone quotes you a suspiciously low number for "an AI knowledge base," that quote is usually missing exactly the parts covered above, which are the parts that make it safe to trust.

Build the boring guardrails, and you get an assistant your team believes. Skip them, and you get a confident liar with a search bar. The difference is entirely in the engineering, and it is the difference between a demo and a tool.

Doc 009 · 2026-07-10 · PUTILOV.DEV · End of document